import { ICalendarEvent, EntityType } from '../types/CalendarTypes'; import { CalendarEventType } from '../types/BookingTypes'; import { IApiRepository } from './IApiRepository'; interface RawEventData { // Core fields (required) id: string; title: string; start: string | Date; end: string | Date; type: string; allDay?: boolean; // Denormalized references (CRITICAL for booking architecture) bookingId?: string; // Reference to booking (customer events only) resourceId?: string; // Which resource owns this slot customerId?: string; // Customer reference (denormalized from booking) // Optional fields description?: string; // Detailed event notes recurringId?: string; // For recurring events metadata?: Record; // Flexible metadata // Legacy (deprecated, keep for backward compatibility) color?: string; // UI-specific field [key: string]: unknown; } /** * MockEventRepository - Loads event data from local JSON file * * This repository implementation fetches mock event data from a static JSON file. * Used for development and testing instead of API calls. * * Data Source: data/mock-events.json * * NOTE: Create/Update/Delete operations are not supported - throws errors. * Only fetchAll() is implemented for loading initial mock data. */ export class MockEventRepository implements IApiRepository { public readonly entityType: EntityType = 'Event'; private readonly dataUrl = 'data/mock-events.json'; /** * Fetch all events from mock JSON file */ public async fetchAll(): Promise { try { const response = await fetch(this.dataUrl); if (!response.ok) { throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`); } const rawData: RawEventData[] = await response.json(); return this.processCalendarData(rawData); } catch (error) { console.error('Failed to load event data:', error); throw error; } } /** * NOT SUPPORTED - MockEventRepository is read-only */ public async sendCreate(event: ICalendarEvent): Promise { throw new Error('MockEventRepository does not support sendCreate. Mock data is read-only.'); } /** * NOT SUPPORTED - MockEventRepository is read-only */ public async sendUpdate(id: string, updates: Partial): Promise { throw new Error('MockEventRepository does not support sendUpdate. Mock data is read-only.'); } /** * NOT SUPPORTED - MockEventRepository is read-only */ public async sendDelete(id: string): Promise { throw new Error('MockEventRepository does not support sendDelete. Mock data is read-only.'); } private processCalendarData(data: RawEventData[]): ICalendarEvent[] { return data.map((event): ICalendarEvent => { // Validate event type constraints if (event.type === 'customer') { if (!event.bookingId) { console.warn(`Customer event ${event.id} missing bookingId`); } if (!event.resourceId) { console.warn(`Customer event ${event.id} missing resourceId`); } if (!event.customerId) { console.warn(`Customer event ${event.id} missing customerId`); } } return { id: event.id, title: event.title, description: event.description, start: new Date(event.start), end: new Date(event.end), type: event.type as CalendarEventType, allDay: event.allDay || false, // Denormalized references (CRITICAL for booking architecture) bookingId: event.bookingId, resourceId: event.resourceId, customerId: event.customerId, // Optional fields recurringId: event.recurringId, metadata: event.metadata, syncStatus: 'synced' as const }; }); } }