Calendar/src/managers/EventManager.ts
Janus Knudsen 9c65143df2 Refactors event rendering and display
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
2025-08-24 00:13:07 +02:00

191 lines
No EOL
6.4 KiB
TypeScript

import { EventBus } from '../core/EventBus';
import { IEventBus, CalendarEvent, ResourceCalendarData } from '../types/CalendarTypes';
import { CoreEvents } from '../constants/CoreEvents';
import { calendarConfig } from '../core/CalendarConfig';
/**
* EventManager - Administrerer event lifecycle og CRUD operationer
* Håndterer mock data og event synchronization
*/
export class EventManager {
private eventBus: IEventBus;
private events: CalendarEvent[] = [];
constructor(eventBus: IEventBus) {
console.log('EventManager: Constructor called');
this.eventBus = eventBus;
this.setupEventListeners();
console.log('EventManager: Waiting for CALENDAR_INITIALIZED before loading data');
}
private setupEventListeners(): void {
// NOTE: Removed POC event listener to prevent interference with production code
// POC sliding animation should not trigger separate event rendering
// this.eventBus.on(CoreEvents.WEEK_CONTENT_RENDERED, ...);
}
/**
* Public method to load data - called directly by CalendarManager
*/
public async loadData(): Promise<void> {
console.log('EventManager: Loading data via direct call');
await this.loadMockData();
console.log(`EventManager: Data loaded successfully - ${this.events.length} events`);
// Debug: Log first few events
if (this.events.length > 0) {
console.log('EventManager: First event:', {
title: this.events[0].title,
start: this.events[0].start,
end: this.events[0].end
});
}
}
private async loadMockData(): Promise<void> {
try {
const calendarType = calendarConfig.getCalendarMode();
let jsonFile: string;
console.log(`EventManager: Calendar type detected: '${calendarType}'`);
if (calendarType === 'resource') {
jsonFile = '/src/data/mock-resource-events.json';
} else {
jsonFile = '/src/data/mock-events.json';
}
console.log(`EventManager: Loading ${calendarType} calendar data from ${jsonFile}`);
const response = await fetch(jsonFile);
if (!response.ok) {
throw new Error(`Failed to load mock events: ${response.status}`);
}
const data = await response.json();
console.log(`EventManager: Loaded data for ${calendarType} calendar`);
// Store raw data for GridManager
this.rawData = data;
// Process data for internal use
this.processCalendarData(calendarType, data);
} catch (error) {
console.error('EventManager: Failed to load mock events:', error);
this.events = []; // Fallback to empty array
}
}
private processCalendarData(calendarType: string, data: any): void {
if (calendarType === 'resource') {
const resourceData = data as ResourceCalendarData;
this.events = resourceData.resources.flatMap(resource =>
resource.events.map(event => ({
...event,
resourceName: resource.name,
resourceDisplayName: resource.displayName,
resourceEmployeeId: resource.employeeId
}))
);
console.log(`EventManager: Processed ${this.events.length} events from ${resourceData.resources.length} resources`);
} else {
this.events = data as CalendarEvent[];
console.log(`EventManager: Processed ${this.events.length} date events`);
}
}
private syncEvents(): void {
// Events are synced during initialization
// This method maintained for internal state management only
console.log(`EventManager: Internal sync - ${this.events.length} events in memory`);
}
public getEvents(): CalendarEvent[] {
return [...this.events];
}
/**
* Get raw resource data for resource calendar mode
*/
public getResourceData(): any {
return this.rawData;
}
private rawData: any = null;
public getEventById(id: string): CalendarEvent | undefined {
return this.events.find(event => event.id === id);
}
/**
* Get events for a specific time period
*/
public getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[] {
console.log(`EventManager.getEventsForPeriod: Checking ${this.events.length} events for period ${startDate.toDateString()} - ${endDate.toDateString()}`);
return this.events.filter(event => {
const eventStart = new Date(event.start);
const eventEnd = new Date(event.end);
// Event overlaps period if it starts before period ends AND ends after period starts
return eventStart <= endDate && eventEnd >= startDate;
});
}
public addEvent(event: Omit<CalendarEvent, 'id'>): CalendarEvent {
const newEvent: CalendarEvent = {
...event,
id: Date.now().toString()
};
this.events.push(newEvent);
this.syncEvents();
this.eventBus.emit(CoreEvents.EVENT_CREATED, {
event: newEvent
});
return newEvent;
}
public updateEvent(id: string, updates: Partial<CalendarEvent>): CalendarEvent | null {
const eventIndex = this.events.findIndex(event => event.id === id);
if (eventIndex === -1) return null;
const updatedEvent = { ...this.events[eventIndex], ...updates };
this.events[eventIndex] = updatedEvent;
this.syncEvents();
this.eventBus.emit(CoreEvents.EVENT_UPDATED, {
event: updatedEvent
});
return updatedEvent;
}
public deleteEvent(id: string): boolean {
const eventIndex = this.events.findIndex(event => event.id === id);
if (eventIndex === -1) return false;
const deletedEvent = this.events[eventIndex];
this.events.splice(eventIndex, 1);
this.syncEvents();
this.eventBus.emit(CoreEvents.EVENT_DELETED, {
event: deletedEvent
});
return true;
}
public refresh(): void {
this.syncEvents();
}
public destroy(): void {
this.events = [];
}
}