/** * EventPersistenceManager - Persists event changes to IndexedDB * * Listens to drag/resize events and updates IndexedDB via EventService. * This bridges the gap between UI interactions and data persistence. */ import { ICalendarEvent, IEventBus, IEventUpdatedPayload } from '../types/CalendarTypes'; import { EventService } from '../storage/events/EventService'; import { IGridConfig } from '../core/IGridConfig'; import { DateService } from '../core/DateService'; import { CoreEvents } from '../constants/CoreEvents'; import { IDragEndPayload } from '../types/DragTypes'; import { IResizeEndPayload } from '../types/ResizeTypes'; import { pixelsToMinutes } from '../utils/PositionUtils'; export class EventPersistenceManager { constructor( private eventService: EventService, private eventBus: IEventBus, private gridConfig: IGridConfig, private dateService: DateService ) { this.setupListeners(); } private setupListeners(): void { this.eventBus.on(CoreEvents.EVENT_DRAG_END, this.handleDragEnd); this.eventBus.on(CoreEvents.EVENT_RESIZE_END, this.handleResizeEnd); } /** * Handle drag end - update event position in IndexedDB */ private handleDragEnd = async (e: Event): Promise => { const payload = (e as CustomEvent).detail; // Get existing event const event = await this.eventService.get(payload.eventId); if (!event) { console.warn(`EventPersistenceManager: Event ${payload.eventId} not found`); return; } // Calculate new start time from snappedY const minutesFromDayStart = pixelsToMinutes(payload.snappedY, this.gridConfig); const totalMinutes = (this.gridConfig.dayStartHour * 60) + minutesFromDayStart; // Preserve duration const durationMs = event.end.getTime() - event.start.getTime(); // Create new dates with correct day from dateKey const newStart = new Date(payload.dateKey); newStart.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0); const newEnd = new Date(newStart.getTime() + durationMs); // Update and save const updatedEvent: ICalendarEvent = { ...event, start: newStart, end: newEnd, resourceId: payload.resourceId ?? event.resourceId, syncStatus: 'pending' }; await this.eventService.save(updatedEvent); // Emit EVENT_UPDATED for EventRenderer to re-render affected columns const updatePayload: IEventUpdatedPayload = { eventId: updatedEvent.id, sourceDateKey: payload.sourceDateKey, sourceResourceId: payload.sourceResourceId, targetDateKey: payload.dateKey, targetResourceId: payload.resourceId }; this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); }; /** * Handle resize end - update event duration in IndexedDB */ private handleResizeEnd = async (e: Event): Promise => { const payload = (e as CustomEvent).detail; // Get existing event const event = await this.eventService.get(payload.eventId); if (!event) { console.warn(`EventPersistenceManager: Event ${payload.eventId} not found`); return; } // Calculate new end time const newEnd = new Date(event.start.getTime() + payload.newDurationMinutes * 60 * 1000); // Update and save const updatedEvent: ICalendarEvent = { ...event, end: newEnd, syncStatus: 'pending' }; await this.eventService.save(updatedEvent); // Emit EVENT_UPDATED for EventRenderer to re-render the column // Resize stays in same column, so source and target are the same const dateKey = this.dateService.getDateKey(event.start); const updatePayload: IEventUpdatedPayload = { eventId: updatedEvent.id, sourceDateKey: dateKey, sourceResourceId: event.resourceId, targetDateKey: dateKey, targetResourceId: event.resourceId }; this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); }; }