# Improved Calendar Initialization Strategy ## Current Problems 1. **Race Conditions**: Managers try DOM operations before DOM is ready 2. **Sequential Blocking**: All initialization happens sequentially 3. **Poor Error Handling**: No timeouts or retry mechanisms 4. **Late Data Loading**: Data only loads after all managers are created ## Recommended New Architecture ### Phase 1: Early Parallel Startup ```typescript // index.ts - Improved initialization export class CalendarInitializer { async initialize(): Promise { console.log('📋 Starting Calendar initialization...'); // PHASE 1: Early parallel setup const setupPromises = [ this.initializeConfig(), // Load URL params, DOM attrs this.initializeFactory(), // Setup strategy patterns this.preloadCalendarData(), // Start data loading early this.waitForDOMReady() // Ensure basic DOM exists ]; await Promise.all(setupPromises); console.log('✅ Phase 1 complete: Config, Factory, Data preloading started'); // PHASE 2: Manager creation with dependencies await this.createManagersWithDependencies(); // PHASE 3: Coordinated activation await this.activateAllManagers(); console.log('🎊 Calendar fully initialized!'); } } ``` ### Phase 2: Dependency-Aware Manager Creation ```typescript private async createManagersWithDependencies(): Promise { const managers = new Map(); // Core managers (no DOM dependencies) managers.set('config', calendarConfig); managers.set('eventBus', eventBus); managers.set('calendarManager', new CalendarManager(eventBus, calendarConfig)); // DOM-dependent managers (wait for DOM readiness) await this.waitForRequiredDOM(['swp-calendar', 'swp-calendar-nav']); managers.set('navigationManager', new NavigationManager(eventBus)); managers.set('viewManager', new ViewManager(eventBus)); // Data managers (can work with preloaded data) managers.set('eventManager', new EventManager(eventBus)); managers.set('dataManager', new DataManager()); // Layout managers (need DOM structure + other managers) await this.waitForRequiredDOM(['swp-calendar-container']); // CRITICAL ORDER: ScrollManager subscribes before GridManager renders managers.set('scrollManager', new ScrollManager()); managers.set('gridManager', new GridManager()); // Rendering managers (need grid structure) managers.set('eventRenderer', new EventRenderer(eventBus)); this.managers = managers; } ``` ### Phase 3: Coordinated Activation ```typescript private async activateAllManagers(): Promise { // All managers created and subscribed, now activate in coordinated fashion const calendarManager = this.managers.get('calendarManager'); // This triggers CALENDAR_INITIALIZED, but now all managers are ready await calendarManager.initialize(); // Wait for critical initialization events await Promise.all([ this.waitForEvent('CALENDAR_DATA_LOADED', 10000), this.waitForEvent('GRID_RENDERED', 5000), this.waitForEvent('EVENTS_LOADED', 10000) ]); // Ensure event rendering completes await this.waitForEvent('EVENT_RENDERED', 3000); } ``` ## Specific Timing Improvements ### 1. Early Data Preloading ```typescript private async preloadCalendarData(): Promise { const currentDate = new Date(); const mode = calendarConfig.getCalendarMode(); // Start loading data for current period immediately const dataManager = new DataManager(); const currentPeriod = this.getCurrentPeriod(currentDate, mode); // Don't await - let this run in background const dataPromise = dataManager.fetchEventsForPeriod(currentPeriod); // Also preload adjacent periods const prevPeriod = this.getPreviousPeriod(currentDate, mode); const nextPeriod = this.getNextPeriod(currentDate, mode); // Store promises for later use this.preloadPromises = { current: dataPromise, previous: dataManager.fetchEventsForPeriod(prevPeriod), next: dataManager.fetchEventsForPeriod(nextPeriod) }; console.log('📊 Data preloading started for current, previous, and next periods'); } ``` ### 2. DOM Readiness Verification ```typescript private async waitForRequiredDOM(selectors: string[]): Promise { const maxWait = 5000; // 5 seconds max const checkInterval = 100; // Check every 100ms const startTime = Date.now(); while (Date.now() - startTime < maxWait) { const missing = selectors.filter(selector => !document.querySelector(selector)); if (missing.length === 0) { console.log(`✅ Required DOM elements found: ${selectors.join(', ')}`); return; } await new Promise(resolve => setTimeout(resolve, checkInterval)); } throw new Error(`❌ Timeout waiting for DOM elements: ${selectors.join(', ')}`); } ``` ### 3. Manager Base Class with Proper Lifecycle ```typescript export abstract class BaseManager { protected isInitialized = false; protected requiredDOMSelectors: string[] = []; constructor() { // Don't call init() immediately in constructor! console.log(`${this.constructor.name}: Created but not initialized`); } async initialize(): Promise { if (this.isInitialized) { console.log(`${this.constructor.name}: Already initialized, skipping`); return; } // Wait for required DOM elements if (this.requiredDOMSelectors.length > 0) { await this.waitForDOM(this.requiredDOMSelectors); } // Perform manager-specific initialization await this.performInitialization(); this.isInitialized = true; console.log(`${this.constructor.name}: Initialization complete`); } protected abstract performInitialization(): Promise; private async waitForDOM(selectors: string[]): Promise { // Same DOM waiting logic as above } } ``` ### 4. Enhanced GridManager ```typescript export class GridManager extends BaseManager { protected requiredDOMSelectors = ['swp-calendar-container']; constructor() { super(); // Don't call this.init()! this.currentWeek = this.getWeekStart(new Date()); } protected async performInitialization(): Promise { // Now safe to find elements - DOM guaranteed to exist this.findElements(); this.subscribeToEvents(); // Wait for CALENDAR_INITIALIZED before rendering await this.waitForEvent('CALENDAR_INITIALIZED'); console.log('GridManager: Starting initial render'); this.render(); } } ``` ### 5. Enhanced EventRenderer with Better Synchronization ```typescript export class EventRenderer extends BaseManager { private dataReady = false; private gridReady = false; private pendingEvents: CalendarEvent[] = []; protected async performInitialization(): Promise { this.subscribeToEvents(); // Wait for both data and grid in parallel const [eventsData] = await Promise.all([ this.waitForEvent('EVENTS_LOADED'), this.waitForEvent('GRID_RENDERED') ]); console.log('EventRenderer: Both events and grid ready, rendering now'); this.renderEvents(eventsData.events); } private subscribeToEvents(): void { this.eventBus.on(EventTypes.EVENTS_LOADED, (e: Event) => { const detail = (e as CustomEvent).detail; this.pendingEvents = detail.events; this.dataReady = true; this.tryRender(); }); this.eventBus.on(EventTypes.GRID_RENDERED, () => { this.gridReady = true; this.tryRender(); }); } private tryRender(): void { if (this.dataReady && this.gridReady && this.pendingEvents.length > 0) { this.renderEvents(this.pendingEvents); this.pendingEvents = []; } } } ``` ## Benefits of New Architecture 1. **🚀 Parallel Operations**: Data loading starts immediately while managers are being created 2. **🛡️ Race Condition Prevention**: DOM readiness verified before operations 3. **⚡ Better Performance**: Critical path optimized, non-critical operations parallelized 4. **🔧 Better Error Handling**: Timeouts and retry mechanisms 5. **📊 Predictable Timing**: Clear phases with guaranteed completion order 6. **🐛 Easier Debugging**: Clear lifecycle events and logging ## Implementation Strategy 1. **Phase 1**: Create BaseManager class and update existing managers 2. **Phase 2**: Implement CalendarInitializer with parallel setup 3. **Phase 3**: Add DOM readiness verification throughout 4. **Phase 4**: Implement data preloading strategy 5. **Phase 5**: Add comprehensive error handling and timeouts This architecture ensures reliable, fast, and maintainable calendar initialization.