import { CoreEvents } from '../constants/CoreEvents'; import { calendarConfig } from '../core/CalendarConfig'; import { CalendarView, IEventBus } from '../types/CalendarTypes'; import { EventManager } from './EventManager'; import { GridManager } from './GridManager'; import { EventRenderingService } from '../renderers/EventRendererManager'; import { ScrollManager } from './ScrollManager'; /** * CalendarManager - Main coordinator for all calendar managers * Uses singleton calendarConfig for consistent configuration access */ export class CalendarManager { private eventBus: IEventBus; private eventManager: EventManager; private gridManager: GridManager; private eventRenderer: EventRenderingService; private scrollManager: ScrollManager; private currentView: CalendarView = 'week'; private currentDate: Date = new Date(); private isInitialized: boolean = false; constructor( eventBus: IEventBus, eventManager: EventManager, gridManager: GridManager, eventRenderer: EventRenderingService, scrollManager: ScrollManager ) { this.eventBus = eventBus; this.eventManager = eventManager; this.gridManager = gridManager; this.eventRenderer = eventRenderer; this.scrollManager = scrollManager; const timezone = calendarConfig.getTimezone?.(); this.setupEventListeners(); } /** * Initialize calendar system using simple direct calls */ public async initialize(): Promise { if (this.isInitialized) { return; } try { // Debug: Check calendar type const calendarType = calendarConfig.getCalendarMode(); // Step 1: Load data await this.eventManager.loadData(); // Step 2: Pass data to GridManager and render grid structure if (calendarType === 'resource') { const resourceData = this.eventManager.getResourceData(); this.gridManager.setResourceData(this.eventManager.getRawData() as import('../types/CalendarTypes').ResourceCalendarData); } await this.gridManager.render(); this.scrollManager.initialize(); this.setView(this.currentView); this.setCurrentDate(this.currentDate); this.isInitialized = true; // Emit initialization complete event this.eventBus.emit(CoreEvents.INITIALIZED, { currentDate: this.currentDate, currentView: this.currentView }); } catch (error) { throw error; } } /** * Skift calendar view (dag/uge/måned) */ public setView(view: CalendarView): void { if (this.currentView === view) { return; } const previousView = this.currentView; this.currentView = view; // Emit view change event this.eventBus.emit(CoreEvents.VIEW_CHANGED, { previousView, currentView: view, date: this.currentDate }); } /** * Sæt aktuel dato */ public setCurrentDate(date: Date): void { const previousDate = this.currentDate; this.currentDate = new Date(date); // Emit date change event this.eventBus.emit(CoreEvents.DATE_CHANGED, { previousDate, currentDate: this.currentDate, view: this.currentView }); } /** * Genindlæs calendar data */ public refresh(): void { this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, { view: this.currentView, date: this.currentDate }); } /** * Ryd calendar og nulstil til standard tilstand */ public reset(): void { this.currentView = 'week'; this.currentDate = new Date(); this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, { view: this.currentView, date: this.currentDate }); } /** * Setup event listeners for at håndtere events fra andre managers */ private setupEventListeners(): void { // Listen for workweek changes only this.eventBus.on(CoreEvents.WORKWEEK_CHANGED, (event: Event) => { const customEvent = event as CustomEvent; this.handleWorkweekChange(); }); } /** * Beregn næste dato baseret på aktuel view */ private calculateNextDate(): Date { const nextDate = new Date(this.currentDate); switch (this.currentView) { case 'day': nextDate.setDate(nextDate.getDate() + 1); break; case 'week': const workWeekSettings = calendarConfig.getWorkWeekSettings(); nextDate.setDate(nextDate.getDate() + 7); // Move to next calendar week regardless of work days break; case 'month': nextDate.setMonth(nextDate.getMonth() + 1); break; } return nextDate; } /** * Beregn forrige dato baseret på aktuel view */ private calculatePreviousDate(): Date { const previousDate = new Date(this.currentDate); switch (this.currentView) { case 'day': previousDate.setDate(previousDate.getDate() - 1); break; case 'week': const workWeekSettings = calendarConfig.getWorkWeekSettings(); previousDate.setDate(previousDate.getDate() - 7); // Move to previous calendar week regardless of work days break; case 'month': previousDate.setMonth(previousDate.getMonth() - 1); break; } return previousDate; } /** * Calculate the current period based on view and date */ private calculateCurrentPeriod(): { start: string; end: string } { const current = new Date(this.currentDate); switch (this.currentView) { case 'day': const dayStart = new Date(current); dayStart.setHours(0, 0, 0, 0); const dayEnd = new Date(current); dayEnd.setHours(23, 59, 59, 999); return { start: dayStart.toISOString(), end: dayEnd.toISOString() }; case 'week': // Find start of week (Monday) const weekStart = new Date(current); const dayOfWeek = weekStart.getDay(); const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Sunday = 0, so 6 days back to Monday weekStart.setDate(weekStart.getDate() - daysToMonday); weekStart.setHours(0, 0, 0, 0); // Find end of week (Sunday) const weekEnd = new Date(weekStart); weekEnd.setDate(weekEnd.getDate() + 6); weekEnd.setHours(23, 59, 59, 999); return { start: weekStart.toISOString(), end: weekEnd.toISOString() }; case 'month': const monthStart = new Date(current.getFullYear(), current.getMonth(), 1); const monthEnd = new Date(current.getFullYear(), current.getMonth() + 1, 0, 23, 59, 59, 999); return { start: monthStart.toISOString(), end: monthEnd.toISOString() }; default: // Fallback to week view const fallbackStart = new Date(current); fallbackStart.setDate(fallbackStart.getDate() - 3); fallbackStart.setHours(0, 0, 0, 0); const fallbackEnd = new Date(current); fallbackEnd.setDate(fallbackEnd.getDate() + 3); fallbackEnd.setHours(23, 59, 59, 999); return { start: fallbackStart.toISOString(), end: fallbackEnd.toISOString() }; } } /** * Handle workweek configuration changes */ private handleWorkweekChange(): void { // Force a complete grid rebuild by clearing existing structure const container = document.querySelector('swp-calendar-container'); if (container) { container.innerHTML = ''; // Clear everything to force full rebuild } // Re-render the grid with new workweek settings (will now rebuild everything) this.gridManager.render(); // Re-initialize scroll manager after grid rebuild this.scrollManager.initialize(); // Re-render events in the new grid structure this.rerenderEvents(); // Notify HeaderManager with correct current date after grid rebuild this.eventBus.emit('workweek:header-update', { currentDate: this.currentDate, currentView: this.currentView, workweek: calendarConfig.getCurrentWorkWeek() }); } /** * Re-render events after grid structure changes */ private rerenderEvents(): void { // Get current period data to determine date range const periodData = this.calculateCurrentPeriod(); // Find the grid container to render events in const container = document.querySelector('swp-calendar-container'); if (!container) { return; } // Trigger event rendering for the current date range using correct method this.eventRenderer.renderEvents({ container: container as HTMLElement, startDate: new Date(periodData.start), endDate: new Date(periodData.end) }); } }