From d88956f47bbeae8cf11d63ce56c798b3bed5d62b Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Thu, 11 Dec 2025 23:04:48 +0100 Subject: [PATCH] Adds header drawer event rendering support Extends calendar rendering to support all-day events in header drawer Implements rendering logic for all-day events in header drawer: - Fetches events for visible date range - Filters and creates header items for all-day events - Dynamically positions events with color classes - Expands header drawer when events are present Enhances event persistence with header drop detection --- src/v2/core/CalendarOrchestrator.ts | 9 +- .../headerdrawer/HeaderDrawerRenderer.ts | 85 ++++++++++++++++++- src/v2/managers/EventPersistenceManager.ts | 2 + 3 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/v2/core/CalendarOrchestrator.ts b/src/v2/core/CalendarOrchestrator.ts index 591014a..c0d60e2 100644 --- a/src/v2/core/CalendarOrchestrator.ts +++ b/src/v2/core/CalendarOrchestrator.ts @@ -2,13 +2,15 @@ import { IRenderer, IRenderContext } from './IGroupingRenderer'; import { buildPipeline } from './RenderBuilder'; import { EventRenderer } from '../features/event/EventRenderer'; import { ScheduleRenderer } from '../features/schedule/ScheduleRenderer'; +import { HeaderDrawerRenderer } from '../features/headerdrawer/HeaderDrawerRenderer'; import { ViewConfig } from './ViewConfig'; export class CalendarOrchestrator { constructor( private allRenderers: IRenderer[], private eventRenderer: EventRenderer, - private scheduleRenderer: ScheduleRenderer + private scheduleRenderer: ScheduleRenderer, + private headerDrawerRenderer: HeaderDrawerRenderer ) {} async render(viewConfig: ViewConfig, container: HTMLElement): Promise { @@ -48,8 +50,11 @@ export class CalendarOrchestrator { // Render schedule unavailable zones (før events) await this.scheduleRenderer.render(container, filter); - // Render events med hele filter (date + resource) + // Render timed events in grid await this.eventRenderer.render(container, filter); + + // Render allDay events in header drawer + await this.headerDrawerRenderer.render(container, filter); } private selectRenderers(viewConfig: ViewConfig): IRenderer[] { diff --git a/src/v2/features/headerdrawer/HeaderDrawerRenderer.ts b/src/v2/features/headerdrawer/HeaderDrawerRenderer.ts index 86e2609..8be369d 100644 --- a/src/v2/features/headerdrawer/HeaderDrawerRenderer.ts +++ b/src/v2/features/headerdrawer/HeaderDrawerRenderer.ts @@ -1,7 +1,9 @@ -import { IEventBus } from '../../types/CalendarTypes'; +import { IEventBus, ICalendarEvent } from '../../types/CalendarTypes'; import { IGridConfig } from '../../core/IGridConfig'; import { CoreEvents } from '../../constants/CoreEvents'; import { HeaderDrawerManager } from '../../core/HeaderDrawerManager'; +import { EventService } from '../../storage/events/EventService'; +import { DateService } from '../../core/DateService'; import { IDragEnterHeaderPayload, IDragMoveHeaderPayload, @@ -26,11 +28,90 @@ export class HeaderDrawerRenderer { constructor( private eventBus: IEventBus, private gridConfig: IGridConfig, - private headerDrawerManager: HeaderDrawerManager + private headerDrawerManager: HeaderDrawerManager, + private eventService: EventService, + private dateService: DateService ) { this.setupListeners(); } + /** + * Render allDay events into the header drawer + */ + async render(container: HTMLElement, filter: Record): Promise { + const drawer = container.querySelector('swp-header-drawer'); + if (!drawer) return; + + const visibleDates = filter['date'] || []; + if (visibleDates.length === 0) return; + + // Fetch events for date range + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + + const events = await this.eventService.getByDateRange(startDate, endDate); + + // Filter to allDay events only (allDay !== false) + const allDayEvents = events.filter(event => event.allDay !== false); + + // Clear existing items + drawer.innerHTML = ''; + + // Render each allDay event + allDayEvents.forEach(event => { + const item = this.createHeaderItem(event, visibleDates); + if (item) drawer.appendChild(item); + }); + + // Expand drawer if there are items + if (allDayEvents.length > 0) { + this.headerDrawerManager.expand(); + } + } + + /** + * Create a header item element for an allDay event + */ + private createHeaderItem(event: ICalendarEvent, visibleDates: string[]): HTMLElement | null { + const dateKey = this.dateService.getDateKey(event.start); + const colIndex = visibleDates.indexOf(dateKey); + if (colIndex === -1) return null; // Event not in visible range + + const item = document.createElement('swp-header-item'); + item.dataset.eventId = event.id; + item.dataset.itemType = 'event'; + item.dataset.date = dateKey; + item.textContent = event.title; + + // Color class + const colorClass = this.getColorClass(event); + if (colorClass) item.classList.add(colorClass); + + // Grid position (1-indexed) + const col = colIndex + 1; + item.style.gridArea = `1 / ${col} / 2 / ${col + 1}`; + + return item; + } + + /** + * Get color class based on event metadata or type + */ + private getColorClass(event: ICalendarEvent): string { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors: Record = { + 'customer': 'is-blue', + 'vacation': 'is-green', + 'break': 'is-amber', + 'meeting': 'is-purple', + 'blocked': 'is-red' + }; + return typeColors[event.type] || 'is-blue'; + } + /** * Setup event listeners for drag events */ diff --git a/src/v2/managers/EventPersistenceManager.ts b/src/v2/managers/EventPersistenceManager.ts index 8e5907d..f833630 100644 --- a/src/v2/managers/EventPersistenceManager.ts +++ b/src/v2/managers/EventPersistenceManager.ts @@ -41,11 +41,13 @@ export class EventPersistenceManager { } // Update and save - start/end already calculated in SwpEvent + // If dropped in header, mark as allDay const updatedEvent: ICalendarEvent = { ...event, start: swpEvent.start, end: swpEvent.end, resourceId: swpEvent.resourceId ?? event.resourceId, + allDay: payload.target === 'header' ? true : event.allDay, syncStatus: 'pending' };