Introduces sophisticated event layout algorithm for handling complex scheduling scenarios Adds support for: - Grid and stacked event rendering - Automatic column allocation - Nested event stacking - Threshold-based event grouping Improves visual representation of overlapping and concurrent events
116 lines
4 KiB
TypeScript
116 lines
4 KiB
TypeScript
/**
|
|
* 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<void> => {
|
|
const payload = (e as CustomEvent<IDragEndPayload>).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<void> => {
|
|
const payload = (e as CustomEvent<IResizeEndPayload>).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);
|
|
};
|
|
}
|