Sets up calendar package with core infrastructure

Adds core calendar package components including:
- Base services for events, resources, and settings
- Calendar app and orchestrator
- Build and bundling configuration
- IndexedDB storage setup

Prepares foundational architecture for calendar functionality
This commit is contained in:
Janus C. H. Knudsen 2026-01-28 15:24:03 +01:00
parent 12e7594f30
commit ceb44446f0
97 changed files with 13858 additions and 1 deletions

View file

@ -0,0 +1,102 @@
/**
* 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 { DateService } from '../core/DateService';
import { CoreEvents } from '../constants/CoreEvents';
import { IDragEndPayload } from '../types/DragTypes';
import { IResizeEndPayload } from '../types/ResizeTypes';
export class EventPersistenceManager {
constructor(
private eventService: EventService,
private eventBus: IEventBus,
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;
const { swpEvent } = payload;
// Get existing event to merge with
const event = await this.eventService.get(swpEvent.eventId);
if (!event) {
console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`);
return;
}
// Parse resourceId from columnKey if present
const { resource } = this.dateService.parseColumnKey(swpEvent.columnKey);
// Update and save - start/end already calculated in SwpEvent
// Set allDay based on drop target:
// - header: allDay = true
// - grid: allDay = false (converts allDay event to timed)
const updatedEvent: ICalendarEvent = {
...event,
start: swpEvent.start,
end: swpEvent.end,
resourceId: resource ?? event.resourceId,
allDay: payload.target === 'header',
syncStatus: 'pending'
};
await this.eventService.save(updatedEvent);
// Emit EVENT_UPDATED for EventRenderer to re-render affected columns
const updatePayload: IEventUpdatedPayload = {
eventId: updatedEvent.id,
sourceColumnKey: payload.sourceColumnKey,
targetColumnKey: swpEvent.columnKey
};
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;
const { swpEvent } = payload;
// Get existing event to merge with
const event = await this.eventService.get(swpEvent.eventId);
if (!event) {
console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`);
return;
}
// Update and save - end already calculated in SwpEvent
const updatedEvent: ICalendarEvent = {
...event,
end: swpEvent.end,
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 updatePayload: IEventUpdatedPayload = {
eventId: updatedEvent.id,
sourceColumnKey: swpEvent.columnKey,
targetColumnKey: swpEvent.columnKey
};
this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload);
};
}