Improves event rendering by introducing dedicated event renderers and streamlining event display logic. - Adds a base event renderer and specialized date and resource-based renderers to handle event display logic. - Renders all-day events within a dedicated container in the calendar header. - Removes the direct filtering of all-day events from the `GridManager`. - Fixes an issue where the 'Summer Festival' event started on the wrong date. The changes enhance the flexibility and maintainability of the calendar, provide dedicated containers and styling for allday events and fix date issues related to certain events
484 lines
No EOL
16 KiB
TypeScript
484 lines
No EOL
16 KiB
TypeScript
import { EventBus } from '../core/EventBus.js';
|
|
import { CoreEvents } from '../constants/CoreEvents.js';
|
|
import { CalendarConfig } from '../core/CalendarConfig.js';
|
|
import { CalendarEvent, CalendarView, IEventBus } from '../types/CalendarTypes.js';
|
|
import { EventManager } from './EventManager.js';
|
|
import { GridManager } from './GridManager.js';
|
|
import { EventRenderingService } from '../renderers/EventRendererManager.js';
|
|
import { ScrollManager } from './ScrollManager.js';
|
|
import { DateCalculator } from '../utils/DateCalculator.js';
|
|
import { EventFilterManager } from './EventFilterManager.js';
|
|
|
|
/**
|
|
* CalendarManager - Main coordinator for all calendar managers
|
|
* Uses simple direct method calls instead of complex state management
|
|
*/
|
|
export class CalendarManager {
|
|
private eventBus: IEventBus;
|
|
private config: CalendarConfig;
|
|
private eventManager: EventManager;
|
|
private gridManager: GridManager;
|
|
private eventRenderer: EventRenderingService;
|
|
private scrollManager: ScrollManager;
|
|
private eventFilterManager: EventFilterManager;
|
|
private dateCalculator: DateCalculator;
|
|
private currentView: CalendarView = 'week';
|
|
private currentDate: Date = new Date();
|
|
private isInitialized: boolean = false;
|
|
|
|
constructor(
|
|
eventBus: IEventBus,
|
|
config: CalendarConfig,
|
|
eventManager: EventManager,
|
|
gridManager: GridManager,
|
|
eventRenderer: EventRenderingService,
|
|
scrollManager: ScrollManager
|
|
) {
|
|
this.eventBus = eventBus;
|
|
this.config = config;
|
|
this.eventManager = eventManager;
|
|
this.gridManager = gridManager;
|
|
this.eventRenderer = eventRenderer;
|
|
this.scrollManager = scrollManager;
|
|
this.eventFilterManager = new EventFilterManager();
|
|
this.dateCalculator = new DateCalculator(config);
|
|
this.setupEventListeners();
|
|
console.log('📋 CalendarManager: Created with proper dependency injection');
|
|
}
|
|
|
|
/**
|
|
* Initialize calendar system using simple direct calls
|
|
*/
|
|
public async initialize(): Promise<void> {
|
|
if (this.isInitialized) {
|
|
console.warn('CalendarManager is already initialized');
|
|
return;
|
|
}
|
|
|
|
console.log('🚀 CalendarManager: Starting simple initialization');
|
|
|
|
try {
|
|
// Debug: Check calendar type
|
|
const calendarType = this.config.getCalendarMode();
|
|
console.log(`🔍 CalendarManager: Initializing ${calendarType} calendar`);
|
|
|
|
// Step 1: Load data
|
|
console.log('📊 Loading event data...');
|
|
await this.eventManager.loadData();
|
|
|
|
// Step 2: Pass data to GridManager and render grid structure
|
|
console.log('🏗️ Rendering grid...');
|
|
if (calendarType === 'resource') {
|
|
const resourceData = this.eventManager.getResourceData();
|
|
this.gridManager.setResourceData(resourceData);
|
|
}
|
|
await this.gridManager.render();
|
|
|
|
// Step 2b: Trigger event rendering now that data is loaded
|
|
// Re-emit GRID_RENDERED to trigger EventRendererManager
|
|
console.log('🎨 Triggering event rendering...');
|
|
const gridContainer = document.querySelector('swp-calendar-container');
|
|
if (gridContainer) {
|
|
const periodRange = this.gridManager.getDisplayDates();
|
|
this.eventBus.emit(CoreEvents.GRID_RENDERED, {
|
|
container: gridContainer,
|
|
currentDate: new Date(),
|
|
startDate: periodRange[0],
|
|
endDate: periodRange[periodRange.length - 1],
|
|
columnCount: periodRange.length
|
|
});
|
|
}
|
|
|
|
// Step 3: Initialize scroll synchronization
|
|
console.log('📜 Setting up scroll synchronization...');
|
|
this.scrollManager.initialize();
|
|
|
|
// Step 4: Set initial view and date BEFORE event rendering
|
|
console.log('⚙️ Setting initial view and date...');
|
|
this.setView(this.currentView);
|
|
this.setCurrentDate(this.currentDate);
|
|
|
|
// Step 5: Event rendering will be triggered by GRID_RENDERED event
|
|
console.log('🎨 Event rendering will be triggered automatically by grid events...');
|
|
|
|
this.isInitialized = true;
|
|
console.log('✅ CalendarManager: Simple initialization complete');
|
|
|
|
// Emit initialization complete event
|
|
this.eventBus.emit(CoreEvents.INITIALIZED, {
|
|
currentDate: this.currentDate,
|
|
currentView: this.currentView
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('❌ CalendarManager initialization failed:', 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;
|
|
|
|
console.log(`Changing view from ${previousView} to ${view}`);
|
|
|
|
// Emit view change event
|
|
this.eventBus.emit(CoreEvents.VIEW_CHANGED, {
|
|
previousView,
|
|
currentView: view,
|
|
date: this.currentDate
|
|
});
|
|
|
|
// Grid re-rendering will trigger event rendering automatically via GRID_RENDERED event
|
|
}
|
|
|
|
/**
|
|
* Sæt aktuel dato
|
|
*/
|
|
public setCurrentDate(date: Date): void {
|
|
// Validate input date
|
|
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
console.error('CalendarManager.setCurrentDate: Invalid date provided', date);
|
|
return;
|
|
}
|
|
|
|
const previousDate = this.currentDate;
|
|
this.currentDate = new Date(date);
|
|
|
|
// Validate that both dates are valid before logging
|
|
const prevDateStr = previousDate && !isNaN(previousDate.getTime()) ? previousDate.toISOString() : 'Invalid Date';
|
|
const newDateStr = this.currentDate.toISOString();
|
|
|
|
console.log(`Changing date from ${prevDateStr} to ${newDateStr}`);
|
|
|
|
// Emit date change event
|
|
this.eventBus.emit(CoreEvents.DATE_CHANGED, {
|
|
previousDate,
|
|
currentDate: this.currentDate,
|
|
view: this.currentView
|
|
});
|
|
|
|
// Grid update for new date will trigger event rendering automatically via GRID_RENDERED event
|
|
}
|
|
|
|
/**
|
|
* Naviger til i dag
|
|
*/
|
|
public goToToday(): void {
|
|
this.setCurrentDate(new Date());
|
|
}
|
|
|
|
/**
|
|
* Naviger til næste periode (dag/uge/måned afhængig af view)
|
|
*/
|
|
public goToNext(): void {
|
|
const nextDate = this.calculateNextDate();
|
|
this.setCurrentDate(nextDate);
|
|
}
|
|
|
|
/**
|
|
* Naviger til forrige periode (dag/uge/måned afhængig af view)
|
|
*/
|
|
public goToPrevious(): void {
|
|
const previousDate = this.calculatePreviousDate();
|
|
this.setCurrentDate(previousDate);
|
|
}
|
|
|
|
/**
|
|
* Hent aktuel view
|
|
*/
|
|
public getCurrentView(): CalendarView {
|
|
return this.currentView;
|
|
}
|
|
|
|
/**
|
|
* Hent aktuel dato
|
|
*/
|
|
public getCurrentDate(): Date {
|
|
return new Date(this.currentDate);
|
|
}
|
|
|
|
/**
|
|
* Hent calendar konfiguration
|
|
*/
|
|
public getConfig(): CalendarConfig {
|
|
return this.config;
|
|
}
|
|
|
|
/**
|
|
* Check om calendar er initialiseret
|
|
*/
|
|
public isCalendarInitialized(): boolean {
|
|
return this.isInitialized;
|
|
}
|
|
|
|
/**
|
|
* Get initialization report for debugging
|
|
*/
|
|
public getInitializationReport(): any {
|
|
return {
|
|
isInitialized: this.isInitialized,
|
|
currentView: this.currentView,
|
|
currentDate: this.currentDate,
|
|
initializationTime: 'N/A - simple initialization'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Genindlæs calendar data
|
|
*/
|
|
public refresh(): void {
|
|
console.log('Refreshing calendar...');
|
|
|
|
this.eventBus.emit(CoreEvents.REFRESH_REQUESTED, {
|
|
view: this.currentView,
|
|
date: this.currentDate
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ryd calendar og nulstil til standard tilstand
|
|
*/
|
|
public reset(): void {
|
|
console.log('Resetting calendar...');
|
|
|
|
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;
|
|
console.log('CalendarManager: Workweek changed to', customEvent.detail.workWeekId);
|
|
this.handleWorkweekChange();
|
|
|
|
// Also update week info display since workweek affects date range display
|
|
this.updateWeekInfoForWorkweekChange();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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 = this.config.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 = this.config.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 {
|
|
console.log('CalendarManager: Handling workweek change - forcing full grid rebuild');
|
|
|
|
// 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();
|
|
}
|
|
|
|
/**
|
|
* Re-render events after grid structure changes
|
|
*/
|
|
private rerenderEvents(): void {
|
|
console.log('CalendarManager: Re-rendering events for new workweek');
|
|
|
|
// 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) {
|
|
console.warn('CalendarManager: No container found for event re-rendering');
|
|
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)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update week info display after workweek changes
|
|
*/
|
|
private updateWeekInfoForWorkweekChange(): void {
|
|
// Don't do anything here - let GRID_RENDERED event handle it
|
|
console.log('CalendarManager: Workweek changed - week info will update after grid renders');
|
|
}
|
|
|
|
/**
|
|
* Update week info based on actual rendered columns
|
|
*/
|
|
private updateWeekInfoFromRenderedColumns(): void {
|
|
console.log('CalendarManager: Updating week info from rendered columns');
|
|
|
|
// Get actual dates from rendered columns
|
|
const columns = document.querySelectorAll('swp-day-column');
|
|
if (columns.length === 0) {
|
|
console.warn('CalendarManager: No columns found for week info update');
|
|
return;
|
|
}
|
|
|
|
// Get first and last column dates
|
|
const firstColumn = columns[0] as HTMLElement;
|
|
const lastColumn = columns[columns.length - 1] as HTMLElement;
|
|
|
|
const firstDateStr = firstColumn.dataset.date;
|
|
const lastDateStr = lastColumn.dataset.date;
|
|
|
|
if (!firstDateStr || !lastDateStr) {
|
|
console.warn('CalendarManager: Column dates not found');
|
|
return;
|
|
}
|
|
|
|
// Parse dates
|
|
const firstDate = new Date(firstDateStr);
|
|
const lastDate = new Date(lastDateStr);
|
|
|
|
// Calculate week number from first date
|
|
const weekNumber = this.dateCalculator.getWeekNumber(firstDate);
|
|
|
|
// Format date range
|
|
const dateRange = this.dateCalculator.formatDateRange(firstDate, lastDate);
|
|
|
|
console.log('CalendarManager: Week info from columns:', {
|
|
firstDate: firstDateStr,
|
|
lastDate: lastDateStr,
|
|
weekNumber,
|
|
dateRange
|
|
});
|
|
|
|
// Emit week info update
|
|
this.eventBus.emit(CoreEvents.WEEK_CHANGED, {
|
|
weekNumber,
|
|
dateRange,
|
|
weekStart: firstDate,
|
|
weekEnd: lastDate
|
|
});
|
|
}
|
|
|
|
|
|
} |