import { contentPlanningApi } from './contentPlanningApi'; export interface ServiceStatus { name: string; status: 'loading' | 'success' | 'error' | 'idle'; progress: number; message: string; data?: any; error?: string; } export interface DashboardData { strategies: any[]; gapAnalyses: any[]; aiInsights: any[]; aiRecommendations: any[]; calendarEvents: any[]; healthStatus: { backend: boolean; database: boolean; aiServices: boolean; }; } export class ContentPlanningOrchestrator { private serviceStatuses: Map = new Map(); private onProgressUpdate?: (statuses: ServiceStatus[]) => void; private onDataUpdate?: (data: Partial) => void; private latestDashboardData: DashboardData | null = null; constructor() { this.initializeServiceStatuses(); } private initializeServiceStatuses() { const services = [ { name: 'strategies', displayName: 'Content Strategies' }, { name: 'gapAnalyses', displayName: 'Gap Analysis' }, { name: 'aiAnalytics', displayName: 'AI Analytics' }, { name: 'calendarEvents', displayName: 'Calendar Events' }, { name: 'healthCheck', displayName: 'System Health' } ]; services.forEach(service => { this.serviceStatuses.set(service.name, { name: service.displayName, status: 'idle', progress: 0, message: 'Ready to load' }); }); } public setProgressCallback(callback: (statuses: ServiceStatus[]) => void) { this.onProgressUpdate = callback; } public setDataUpdateCallback(callback: (data: Partial) => void) { this.onDataUpdate = callback; } private updateServiceStatus(name: string, updates: Partial) { const current = this.serviceStatuses.get(name); if (current) { const updated = { ...current, ...updates }; this.serviceStatuses.set(name, updated); this.notifyProgressUpdate(); } } private notifyProgressUpdate() { if (this.onProgressUpdate) { this.onProgressUpdate(Array.from(this.serviceStatuses.values())); } } private notifyDataUpdate(data: Partial) { if (this.onDataUpdate) { this.onDataUpdate(data); } } public async loadDashboardData(): Promise { // Reset all service statuses this.serviceStatuses.forEach((status, name) => { this.updateServiceStatus(name, { status: 'loading', progress: 0, message: 'Initializing...' }); }); // Start parallel requests const promises = [ this.loadStrategies(), this.loadGapAnalyses(), this.loadAIAnalytics(), this.loadCalendarEvents(), this.loadHealthStatus() ]; // Wait for all to complete but handle each independently const results = await Promise.allSettled(promises); // Compile final data const dashboardData: DashboardData = { strategies: [], gapAnalyses: [], aiInsights: [], aiRecommendations: [], calendarEvents: [], healthStatus: { backend: false, database: false, aiServices: false } }; results.forEach((result) => { if (result.status === 'fulfilled') { const data = result.value; // Type-safe data assignment if ('strategies' in data) dashboardData.strategies = data.strategies; if ('gapAnalyses' in data) dashboardData.gapAnalyses = data.gapAnalyses; if ('aiInsights' in data) dashboardData.aiInsights = data.aiInsights; if ('aiRecommendations' in data) dashboardData.aiRecommendations = data.aiRecommendations; if ('calendarEvents' in data) dashboardData.calendarEvents = data.calendarEvents; if ('healthStatus' in data) dashboardData.healthStatus = data.healthStatus; } }); this.latestDashboardData = dashboardData; return dashboardData; } private async loadStrategies() { try { this.updateServiceStatus('strategies', { status: 'loading', progress: 10, message: 'Loading content strategies...' }); const strategies = await contentPlanningApi.getStrategiesSafe(); this.updateServiceStatus('strategies', { status: 'loading', progress: 50, message: 'Processing strategy data...' }); // Simulate processing time for better UX await new Promise(resolve => setTimeout(resolve, 500)); this.updateServiceStatus('strategies', { status: 'success', progress: 100, message: `Loaded ${strategies.length} content strategies`, data: strategies }); this.notifyDataUpdate({ strategies }); return { strategies }; } catch (error: any) { this.updateServiceStatus('strategies', { status: 'error', progress: 0, message: 'Failed to load strategies', error: error.message }); return { strategies: [] }; } } private async loadGapAnalyses() { try { this.updateServiceStatus('gapAnalyses', { status: 'loading', progress: 10, message: 'Initializing gap analysis...' }); const response = await contentPlanningApi.getGapAnalysesSafe(); this.updateServiceStatus('gapAnalyses', { status: 'loading', progress: 30, message: 'Analyzing content gaps...' }); // Simulate processing time await new Promise(resolve => setTimeout(resolve, 800)); this.updateServiceStatus('gapAnalyses', { status: 'loading', progress: 70, message: 'Processing gap analysis results...' }); await new Promise(resolve => setTimeout(resolve, 500)); this.updateServiceStatus('gapAnalyses', { status: 'success', progress: 100, message: `Found ${response.gap_analyses?.length || 0} content gaps`, data: response }); this.notifyDataUpdate({ gapAnalyses: response.gap_analyses || [] }); return { gapAnalyses: response.gap_analyses || [] }; } catch (error: any) { this.updateServiceStatus('gapAnalyses', { status: 'error', progress: 0, message: 'Failed to load gap analysis', error: error.message }); return { gapAnalyses: [] }; } } private async loadAIAnalytics() { try { this.updateServiceStatus('aiAnalytics', { status: 'loading', progress: 10, message: 'Initializing AI analysis...' }); // New approach: stream strategic intelligence data and show status from AI generation SSE return await new Promise<{ aiInsights: any[]; aiRecommendations: any[] }>(async (resolve) => { // 1) Execution status stream (best-effort; ignore if no active strategy) try { const currentStrategyId = this.latestDashboardData?.strategies?.[0]?.id; if (currentStrategyId) { const statusSource = await contentPlanningApi.streamAIGenerationStatus(currentStrategyId); statusSource.onmessage = (event: MessageEvent) => { try { const data = JSON.parse(event.data); if (data.type === 'progress') { this.updateServiceStatus('aiAnalytics', { status: 'loading', progress: Math.min(99, data.progress || 20), message: data.detail || 'AI generation in progress...' }); } if (data.type === 'result') { this.updateServiceStatus('aiAnalytics', { status: data.status === 'completed' ? 'success' : 'error', progress: 100, message: data.status === 'completed' ? 'AI generation completed' : 'AI generation failed' }); statusSource.close(); } } catch {} }; statusSource.onerror = () => statusSource.close(); } } catch {} // 2) Data stream for insights (Strategic Intelligence) const intelSource = await contentPlanningApi.streamStrategicIntelligence(1); contentPlanningApi.handleSSEData( intelSource, (data) => { if (data.type === 'progress') { this.updateServiceStatus('aiAnalytics', { status: 'loading', progress: Math.max(20, data.progress || 40), message: data.message || 'Analyzing strategic intelligence...' }); } else if (data.type === 'result' && data.status === 'success') { this.updateServiceStatus('aiAnalytics', { status: 'success', progress: 100, message: 'Strategic intelligence ready', data: data.data }); // Map to orchestrator fields if needed this.notifyDataUpdate({ aiInsights: data.data?.recommendations || [], aiRecommendations: [] }); resolve({ aiInsights: data.data?.recommendations || [], aiRecommendations: [] }); } else if (data.type === 'error') { this.updateServiceStatus('aiAnalytics', { status: 'error', progress: 0, message: data.message || 'Failed to load strategic intelligence' }); resolve({ aiInsights: [], aiRecommendations: [] }); } }, () => { resolve({ aiInsights: [], aiRecommendations: [] }); } ); }); } catch (error: any) { this.updateServiceStatus('aiAnalytics', { status: 'error', progress: 0, message: 'Failed to load AI analytics', error: error.message }); return { aiInsights: [], aiRecommendations: [] }; } } private async loadCalendarEvents() { try { this.updateServiceStatus('calendarEvents', { status: 'loading', progress: 10, message: 'Loading calendar events...' }); const calendarEvents = await contentPlanningApi.getEventsSafe(); this.updateServiceStatus('calendarEvents', { status: 'loading', progress: 50, message: 'Processing calendar data...' }); // Simulate processing time await new Promise(resolve => setTimeout(resolve, 300)); this.updateServiceStatus('calendarEvents', { status: 'success', progress: 100, message: `Loaded ${calendarEvents.length} calendar events`, data: calendarEvents }); this.notifyDataUpdate({ calendarEvents }); return { calendarEvents }; } catch (error: any) { this.updateServiceStatus('calendarEvents', { status: 'error', progress: 0, message: 'Failed to load calendar events', error: error.message }); return { calendarEvents: [] }; } } private async loadHealthStatus() { try { this.updateServiceStatus('healthCheck', { status: 'loading', progress: 25, message: 'Checking system health...' }); const [backendHealth, aiHealth] = await Promise.allSettled([ contentPlanningApi.checkBackendHealth(), contentPlanningApi.checkAIHealth() ]); const healthStatus = { backend: backendHealth.status === 'fulfilled' && backendHealth.value.status === 'healthy', database: backendHealth.status === 'fulfilled' && backendHealth.value.services?.database_connection === true, aiServices: aiHealth.status === 'fulfilled' && aiHealth.value.status === 'healthy' }; this.updateServiceStatus('healthCheck', { status: 'success', progress: 100, message: 'System health check complete', data: healthStatus }); this.notifyDataUpdate({ healthStatus }); return { healthStatus }; } catch (error: any) { this.updateServiceStatus('healthCheck', { status: 'error', progress: 0, message: 'Health check failed', error: error.message }); return { healthStatus: { backend: false, database: false, aiServices: false } }; } } public getServiceStatuses(): ServiceStatus[] { return Array.from(this.serviceStatuses.values()); } public refreshService(serviceName: string) { const status = this.serviceStatuses.get(serviceName); if (status) { this.updateServiceStatus(serviceName, { status: 'loading', progress: 0, message: 'Refreshing...' }); // Re-run the specific service switch (serviceName) { case 'strategies': this.loadStrategies(); break; case 'gapAnalyses': this.loadGapAnalyses(); break; case 'aiAnalytics': this.loadAIAnalytics(); break; case 'calendarEvents': this.loadCalendarEvents(); break; case 'healthCheck': this.loadHealthStatus(); break; } } } } // Export singleton instance export const contentPlanningOrchestrator = new ContentPlanningOrchestrator();