import { IApiRepository } from '../repositories/IApiRepository'; import { IEntityService } from '../storage/IEntityService'; /** * DataSeeder - Orchestrates initial data loading from repositories into IndexedDB * * ARCHITECTURE: * - Repository (Mock/Api): Fetches data from source (JSON file or backend API) * - DataSeeder (this class): Orchestrates fetch + save operations * - Service (EventService, etc.): Saves data to IndexedDB * * SEPARATION OF CONCERNS: * - Repository does NOT know about IndexedDB or storage * - Service does NOT know about where data comes from * - DataSeeder connects them together * * POLYMORPHIC DESIGN: * - Uses arrays of IEntityService[] and IApiRepository[] * - Matches services with repositories using entityType property * - Open/Closed Principle: Adding new entity requires no code changes here * * USAGE: * Called once during app initialization in index.ts: * 1. IndexedDBService.initialize() - open database * 2. dataSeeder.seedIfEmpty() - load initial data if needed * 3. CalendarManager.initialize() - start calendar * * NOTE: This is for INITIAL SEEDING only. Ongoing sync is handled by SyncManager. */ export class DataSeeder { constructor( // Arrays injected via DI - automatically includes all registered services/repositories private services: IEntityService[], private repositories: IApiRepository[] ) {} /** * Seed all entity stores if they are empty * Runs on app initialization to load initial data from repositories * * Uses polymorphism: loops through all services and matches with repositories by entityType */ async seedIfEmpty(): Promise { console.log('[DataSeeder] Checking if database needs seeding...'); try { // Loop through all entity services (Event, Booking, Customer, Resource, etc.) for (const service of this.services) { // Find matching repository for this service based on entityType const repository = this.repositories.find(repo => repo.entityType === service.entityType); if (!repository) { console.warn(`[DataSeeder] No repository found for entity type: ${service.entityType}, skipping`); continue; } // Seed this entity type await this.seedEntity(service.entityType, service, repository); } console.log('[DataSeeder] Seeding complete'); } catch (error) { console.error('[DataSeeder] Seeding failed:', error); throw error; } } /** * Generic method to seed a single entity type * * @param entityType - Entity type ('Event', 'Booking', 'Customer', 'Resource') * @param service - Entity service for IndexedDB operations * @param repository - Repository for fetching data */ private async seedEntity( entityType: string, service: IEntityService, repository: IApiRepository ): Promise { // Check if store is empty const existing = await service.getAll(); if (existing.length > 0) { console.log(`[DataSeeder] ${entityType} store already has ${existing.length} items, skipping seed`); return; } console.log(`[DataSeeder] ${entityType} store is empty, fetching from repository...`); // Fetch from repository (Mock JSON or backend API) const data = await repository.fetchAll(); console.log(`[DataSeeder] Fetched ${data.length} ${entityType} items, saving to IndexedDB...`); // Save each entity to IndexedDB // Note: Entities from repository should already have syncStatus='synced' for (const entity of data) { await service.save(entity); } console.log(`[DataSeeder] ${entityType} seeding complete (${data.length} items saved)`); } }