diff --git a/src/v2/core/CalendarOrchestrator.ts b/src/v2/core/CalendarOrchestrator.ts index 4fd0e56..933e8a5 100644 --- a/src/v2/core/CalendarOrchestrator.ts +++ b/src/v2/core/CalendarOrchestrator.ts @@ -43,7 +43,7 @@ export class CalendarOrchestrator { // Resolve belongsTo relations (e.g., team.resourceIds) const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter); - const context: IRenderContext = { headerContainer, columnContainer, filter, parentChildMap, childType }; + const context: IRenderContext = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType }; // Clear headerContainer.innerHTML = ''; diff --git a/src/v2/core/IGroupingRenderer.ts b/src/v2/core/IGroupingRenderer.ts index 0dd31fa..a1bc507 100644 --- a/src/v2/core/IGroupingRenderer.ts +++ b/src/v2/core/IGroupingRenderer.ts @@ -1,7 +1,10 @@ +import { GroupingConfig } from './ViewConfig'; + export interface IRenderContext { headerContainer: HTMLElement; columnContainer: HTMLElement; filter: Record; // { team: ['alpha'], resource: ['alice', 'bob'], date: [...] } + groupings?: GroupingConfig[]; // Full grouping configs (for hideHeader etc.) parentChildMap?: Record; // { team1: ['EMP001', 'EMP002'], team2: ['EMP003', 'EMP004'] } childType?: string; // The type of the child grouping (e.g., 'resource' when team has belongsTo) } diff --git a/src/v2/core/ViewConfig.ts b/src/v2/core/ViewConfig.ts index 9b4334c..c964562 100644 --- a/src/v2/core/ViewConfig.ts +++ b/src/v2/core/ViewConfig.ts @@ -15,4 +15,5 @@ export interface GroupingConfig { idProperty?: string; // Property-navn på event (f.eks. 'resourceId') - kun for event matching derivedFrom?: string; // Hvis feltet udledes fra anden property (f.eks. 'date' fra 'start') belongsTo?: string; // Parent-child relation (f.eks. 'team.resourceIds') + hideHeader?: boolean; // Skjul header-rækken for denne grouping (f.eks. dato i day-view) } diff --git a/src/v2/demo/DemoApp.ts b/src/v2/demo/DemoApp.ts index 47c1746..f0e7e22 100644 --- a/src/v2/demo/DemoApp.ts +++ b/src/v2/demo/DemoApp.ts @@ -14,14 +14,16 @@ import { EventPersistenceManager } from '../managers/EventPersistenceManager'; import { HeaderDrawerRenderer } from '../features/headerdrawer/HeaderDrawerRenderer'; import { AuditService } from '../storage/audit/AuditService'; import { SettingsService } from '../storage/settings/SettingsService'; +import { ResourceService } from '../storage/resources/ResourceService'; import { IWorkweekPreset } from '../types/SettingsTypes'; export class DemoApp { private animator!: NavigationAnimator; private container!: HTMLElement; private weekOffset = 0; - private currentView: 'day' | 'simple' | 'resource' | 'team' | 'department' = 'simple'; + private currentView: 'day' | 'simple' | 'resource' | 'picker' | 'team' | 'department' = 'simple'; private workweekPreset: IWorkweekPreset | null = null; + private selectedResourceIds: string[] = []; constructor( private orchestrator: CalendarOrchestrator, @@ -37,7 +39,8 @@ export class DemoApp { private headerDrawerRenderer: HeaderDrawerRenderer, private eventPersistenceManager: EventPersistenceManager, private auditService: AuditService, - private settingsService: SettingsService + private settingsService: SettingsService, + private resourceService: ResourceService ) {} async init(): Promise { @@ -88,6 +91,9 @@ export class DemoApp { this.setupDrawerToggle(); this.setupViewSwitching(); + // Setup resource selector for picker view + await this.setupResourceSelector(); + // Initial render this.render(); } @@ -109,7 +115,7 @@ export class DemoApp { templateId: 'day', groupings: [ { type: 'resource', values: ['EMP001', 'EMP002'], idProperty: 'resourceId' }, - { type: 'date', values: today, idProperty: 'date', derivedFrom: 'start' } + { type: 'date', values: today, idProperty: 'date', derivedFrom: 'start', hideHeader: true } ] }; @@ -149,6 +155,15 @@ export class DemoApp { { type: 'date', values: dates, idProperty: 'date', derivedFrom: 'start' } ] }; + + case 'picker': + return { + templateId: 'picker', + groupings: [ + { type: 'resource', values: this.selectedResourceIds, idProperty: 'resourceId' }, + { type: 'date', values: dates, idProperty: 'date', derivedFrom: 'start' } + ] + }; } } @@ -177,6 +192,7 @@ export class DemoApp { const view = (chip as HTMLElement).dataset.view as typeof this.currentView; if (view) { this.currentView = view; + this.updateSelectorVisibility(); this.render(); } }); @@ -199,4 +215,38 @@ export class DemoApp { this.headerDrawerManager.toggle(); }; } + + private async setupResourceSelector(): Promise { + const resources = await this.resourceService.getAll(); + const container = document.querySelector('.resource-checkboxes') as HTMLElement; + if (!container) return; + + // Clear existing + container.innerHTML = ''; + + // Create checkboxes for each resource + resources.forEach(r => { + const label = document.createElement('label'); + label.innerHTML = ` + + ${r.displayName} + `; + container.appendChild(label); + }); + + // Default: all selected + this.selectedResourceIds = resources.map(r => r.id); + + // Event listener for checkbox changes + container.addEventListener('change', () => { + const checked = container.querySelectorAll('input:checked') as NodeListOf; + this.selectedResourceIds = Array.from(checked).map(cb => cb.value); + this.render(); + }); + } + + private updateSelectorVisibility(): void { + const selector = document.querySelector('swp-resource-selector'); + selector?.classList.toggle('hidden', this.currentView !== 'picker'); + } } diff --git a/src/v2/features/date/DateRenderer.ts b/src/v2/features/date/DateRenderer.ts index 57f75f7..4f1cfad 100644 --- a/src/v2/features/date/DateRenderer.ts +++ b/src/v2/features/date/DateRenderer.ts @@ -10,6 +10,10 @@ export class DateRenderer implements IRenderer { const dates = context.filter['date'] || []; const resourceIds = context.filter['resource'] || []; + // Check if date headers should be hidden (e.g., in day view) + const dateGrouping = context.groupings?.find(g => g.type === 'date'); + const hideHeader = dateGrouping?.hideHeader === true; + // Render dates for HVER resource (eller 1 gang hvis ingen resources) const iterations = resourceIds.length || 1; let columnCount = 0; @@ -32,6 +36,9 @@ export class DateRenderer implements IRenderer { if (resourceId) { header.dataset.resourceId = resourceId; } + if (hideHeader) { + header.dataset.hidden = 'true'; + } header.innerHTML = ` ${this.dateService.getDayName(date, 'short')} ${date.getDate()} diff --git a/wwwroot/css/v2/calendar-v2-layout.css b/wwwroot/css/v2/calendar-v2-layout.css index 5dedc79..2bb9ab8 100644 --- a/wwwroot/css/v2/calendar-v2-layout.css +++ b/wwwroot/css/v2/calendar-v2-layout.css @@ -68,6 +68,48 @@ swp-view-switcher { &:focus { outline: 2px solid var(--color-primary); outline-offset: 1px; } } +/* Resource selector (picker view) */ +swp-resource-selector { + &.hidden { display: none; } + + fieldset { + border: 1px solid var(--color-border); + border-radius: 6px; + padding: 6px 12px; + margin: 0; + } + + legend { + font-size: 11px; + font-weight: 500; + color: var(--color-text-secondary); + padding: 0 6px; + } + + .resource-checkboxes { + display: flex; + flex-wrap: wrap; + gap: 4px 16px; + } + + label { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + cursor: pointer; + white-space: nowrap; + + &:hover { color: var(--color-primary); } + } + + input[type="checkbox"] { + width: 14px; + height: 14px; + cursor: pointer; + } +} + /* Navigation group */ swp-nav-group { display: flex; @@ -289,6 +331,10 @@ swp-day-header { font-size: 24px; font-weight: 300; } + + &[data-hidden="true"] { + display: none; + } } /* Scrollable content */ diff --git a/wwwroot/data/tenant-settings.json b/wwwroot/data/tenant-settings.json index be14e4c..7509d65 100644 --- a/wwwroot/data/tenant-settings.json +++ b/wwwroot/data/tenant-settings.json @@ -12,13 +12,13 @@ }, "compressed": { "id": "compressed", - "workDays": [1, 2, 3, 4], - "label": "Man-Tor" + "workDays": [1, 2, 3], + "label": "Man-Ons" }, "midweek": { "id": "midweek", - "workDays": [3, 4, 5], - "label": "Ons-Fre" + "workDays": [4, 5], + "label": "Tors-Fre" }, "weekend": { "id": "weekend", diff --git a/wwwroot/v2.html b/wwwroot/v2.html index c19150c..28e558d 100644 --- a/wwwroot/v2.html +++ b/wwwroot/v2.html @@ -15,15 +15,24 @@ + + + +