import { IEventBus, CalendarEvent } from '../types/CalendarTypes'; import { CoreEvents } from '../constants/CoreEvents'; import { CalendarConfig } from '../core/CalendarConfig'; import { DateService } from '../utils/DateService'; interface RawEventData { id: string; title: string; start: string | Date; end: string | Date; type : string; color?: string; allDay?: boolean; [key: string]: unknown; } /** * EventManager - Event lifecycle and CRUD operations * Handles data loading and event management */ export class EventManager { private events: CalendarEvent[] = []; private rawData: RawEventData[] | null = null; private dateService: DateService; private config: CalendarConfig; constructor( private eventBus: IEventBus, dateService: DateService, config: CalendarConfig ) { this.dateService = dateService; this.config = config; } /** * Load event data from JSON file */ public async loadData(): Promise { try { await this.loadMockData(); } catch (error) { console.error('Failed to load event data:', error); this.events = []; this.rawData = null; } } /** * Optimized mock data loading */ private async loadMockData(): Promise { const jsonFile = 'data/mock-events.json'; const response = await fetch(jsonFile); if (!response.ok) { throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`); } const data = await response.json(); // Store raw data and process in one operation this.rawData = data; this.events = this.processCalendarData(data); } /** * Process raw event data and convert to CalendarEvent objects */ private processCalendarData(data: RawEventData[]): CalendarEvent[] { return data.map((event): CalendarEvent => ({ ...event, start: new Date(event.start), end: new Date(event.end), type : event.type, allDay: event.allDay || false, syncStatus: 'synced' as const })); } /** * Get events with optional copying for performance */ public getEvents(copy: boolean = false): CalendarEvent[] { return copy ? [...this.events] : this.events; } /** * Optimized event lookup with early return */ public getEventById(id: string): CalendarEvent | undefined { // Use find for better performance than filter + first return this.events.find(event => event.id === id); } /** * Get event by ID and return event info for navigation * @param id Event ID to find * @returns Event with navigation info or null if not found */ public getEventForNavigation(id: string): { event: CalendarEvent; eventDate: Date } | null { const event = this.getEventById(id); if (!event) { return null; } // Validate event dates const validation = this.dateService.validateDate(event.start); if (!validation.valid) { console.warn(`EventManager: Invalid event start date for event ${id}:`, validation.error); return null; } // Validate date range if (!this.dateService.isValidRange(event.start, event.end)) { console.warn(`EventManager: Invalid date range for event ${id}: start must be before end`); return null; } return { event, eventDate: event.start }; } /** * Navigate to specific event by ID * Emits navigation events for other managers to handle * @param eventId Event ID to navigate to * @returns true if event found and navigation initiated, false otherwise */ public navigateToEvent(eventId: string): boolean { const eventInfo = this.getEventForNavigation(eventId); if (!eventInfo) { console.warn(`EventManager: Event with ID ${eventId} not found`); return false; } const { event, eventDate } = eventInfo; // Emit navigation request event this.eventBus.emit(CoreEvents.NAVIGATE_TO_EVENT, { eventId, event, eventDate, eventStartTime: event.start }); return true; } /** * Get events that overlap with a given time period */ public getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[] { // Event overlaps period if it starts before period ends AND ends after period starts return this.events.filter(event => { return event.start <= endDate && event.end >= startDate; }); } /** * Create a new event and add it to the calendar */ public addEvent(event: Omit): CalendarEvent { const newEvent: CalendarEvent = { ...event, id: `event_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` }; this.events.push(newEvent); this.eventBus.emit(CoreEvents.EVENT_CREATED, { event: newEvent }); return newEvent; } /** * Update an existing event */ public updateEvent(id: string, updates: Partial): 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.eventBus.emit(CoreEvents.EVENT_UPDATED, { event: updatedEvent }); return updatedEvent; } }