/** * Dashboard Application Controller * Crypto Monitor HF - Enterprise Edition */ class DashboardApp { constructor() { this.initialized = false; this.charts = {}; this.refreshIntervals = {}; } /** * Initialize dashboard */ async init() { if (this.initialized) return; console.log('[Dashboard] Initializing...'); // Wait for dependencies await this.waitForDependencies(); // Set up global error handler this.setupErrorHandler(); // Set up refresh intervals this.setupRefreshIntervals(); this.initialized = true; console.log('[Dashboard] Initialized successfully'); } /** * Wait for required dependencies to load */ async waitForDependencies() { const maxWait = 5000; const startTime = Date.now(); while (!window.apiClient || !window.tabManager || !window.themeManager) { if (Date.now() - startTime > maxWait) { throw new Error('Timeout waiting for dependencies'); } await new Promise(resolve => setTimeout(resolve, 100)); } } /** * Set up global error handler */ setupErrorHandler() { window.addEventListener('error', (event) => { console.error('[Dashboard] Global error:', event.error); }); window.addEventListener('unhandledrejection', (event) => { console.error('[Dashboard] Unhandled rejection:', event.reason); }); } /** * Set up automatic refresh intervals */ setupRefreshIntervals() { // Refresh market data every 60 seconds this.refreshIntervals.market = setInterval(() => { if (window.tabManager.currentTab === 'market') { window.tabManager.loadMarketTab(); } }, 60000); // Refresh API monitor every 30 seconds this.refreshIntervals.apiMonitor = setInterval(() => { if (window.tabManager.currentTab === 'api-monitor') { window.tabManager.loadAPIMonitorTab(); } }, 30000); } /** * Clear all refresh intervals */ clearRefreshIntervals() { Object.values(this.refreshIntervals).forEach(interval => { clearInterval(interval); }); this.refreshIntervals = {}; } // ===== Tab Rendering Methods ===== /** * Render Market tab */ renderMarketTab(data) { const container = document.querySelector('#market-tab .tab-body'); if (!container) return; try { let html = '
'; // Market stats if (data.market_cap_usd) { html += this.createStatCard('💰', 'Market Cap', this.formatCurrency(data.market_cap_usd), 'primary'); } if (data.total_volume_usd) { html += this.createStatCard('📊', '24h Volume', this.formatCurrency(data.total_volume_usd), 'purple'); } if (data.btc_dominance) { html += this.createStatCard('₿', 'BTC Dominance', `${data.btc_dominance.toFixed(2)}%`, 'yellow'); } if (data.active_cryptocurrencies) { html += this.createStatCard('🪙', 'Active Coins', data.active_cryptocurrencies.toLocaleString(), 'green'); } html += '
'; // Trending coins if available if (data.trending && data.trending.length > 0) { html += '

🔥 Trending Coins

'; html += this.renderTrendingCoins(data.trending); html += '
'; } container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering market tab:', error); this.showError(container, 'Failed to render market data'); } } /** * Render API Monitor tab */ renderAPIMonitorTab(data) { const container = document.querySelector('#api-monitor-tab .tab-body'); if (!container) return; try { const providers = data.providers || data || []; let html = '

📡 API Providers Status

'; if (providers.length === 0) { html += this.createEmptyState('No providers configured', 'Add providers in the Providers tab'); } else { html += '
'; html += ''; html += ''; providers.forEach(provider => { const status = provider.status || 'unknown'; const health = provider.health_status || provider.health || 'unknown'; const route = provider.last_route || provider.route || 'direct'; const category = provider.category || 'general'; html += ''; html += ``; html += ``; html += ``; html += ``; html += ``; html += ``; html += ''; }); html += '
ProviderStatusCategoryHealthRouteActions
${provider.name || provider.id}${this.createStatusBadge(status)}${category}${this.createHealthIndicator(health)}${this.createRouteBadge(route, provider.proxy_enabled)}
'; } html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering API monitor tab:', error); this.showError(container, 'Failed to render API monitor data'); } } /** * Render Providers tab */ renderProvidersTab(data) { const container = document.querySelector('#providers-tab .tab-body'); if (!container) return; try { const providers = data.providers || data || []; let html = '
'; if (providers.length === 0) { html += this.createEmptyState('No providers found', 'Configure providers to monitor APIs'); } else { providers.forEach(provider => { html += this.createProviderCard(provider); }); } html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering providers tab:', error); this.showError(container, 'Failed to render providers'); } } /** * Render Pools tab */ renderPoolsTab(data) { const container = document.querySelector('#pools-tab .tab-body'); if (!container) return; try { const pools = data.pools || data || []; let html = '
'; html += '
'; if (pools.length === 0) { html += this.createEmptyState('No pools configured', 'Create a pool to manage provider groups'); } else { pools.forEach(pool => { html += this.createPoolCard(pool); }); } html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering pools tab:', error); this.showError(container, 'Failed to render pools'); } } /** * Render Logs tab */ renderLogsTab(data) { const container = document.querySelector('#logs-tab .tab-body'); if (!container) return; try { const logs = data.logs || data || []; let html = '
'; html += '

📝 Recent Logs

'; html += ''; html += '
'; if (logs.length === 0) { html += this.createEmptyState('No logs available', 'Logs will appear here as the system runs'); } else { html += '
'; logs.forEach(log => { const level = log.level || 'info'; const timestamp = log.timestamp ? new Date(log.timestamp).toLocaleString() : ''; const message = log.message || ''; html += `
`; html += `${timestamp}`; html += `${level.toUpperCase()}`; html += `${this.escapeHtml(message)}`; html += `
`; }); html += '
'; } html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering logs tab:', error); this.showError(container, 'Failed to render logs'); } } /** * Render HuggingFace tab */ renderHuggingFaceTab(data) { const container = document.querySelector('#huggingface-tab .tab-body'); if (!container) return; try { let html = '

🤗 HuggingFace Integration

'; if (data.status === 'available' || data.available) { html += '
✅ HuggingFace API is available
'; html += `

Models loaded: ${data.models_count || 0}

`; html += ''; } else { html += '
⚠️ HuggingFace API is not available
'; if (data.error) { html += `

${this.escapeHtml(data.error)}

`; } } html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering HuggingFace tab:', error); this.showError(container, 'Failed to render HuggingFace data'); } } /** * Render Reports tab */ renderReportsTab(data) { const container = document.querySelector('#reports-tab .tab-body'); if (!container) return; try { let html = ''; // Discovery Report if (data.discoveryReport) { html += this.renderDiscoveryReport(data.discoveryReport); } // Models Report if (data.modelsReport) { html += this.renderModelsReport(data.modelsReport); } container.innerHTML = html || this.createEmptyState('No reports available', 'Reports will appear here when data is available'); } catch (error) { console.error('[Dashboard] Error rendering reports tab:', error); this.showError(container, 'Failed to render reports'); } } /** * Render Admin tab */ renderAdminTab(data) { const container = document.querySelector('#admin-tab .tab-body'); if (!container) return; try { let html = '

⚙️ Feature Flags

'; html += '
'; html += '
'; container.innerHTML = html; // Render feature flags using the existing manager if (window.featureFlagsManager) { window.featureFlagsManager.renderUI('feature-flags-container'); } } catch (error) { console.error('[Dashboard] Error rendering admin tab:', error); this.showError(container, 'Failed to render admin panel'); } } /** * Render Advanced tab */ renderAdvancedTab(data) { const container = document.querySelector('#advanced-tab .tab-body'); if (!container) return; try { let html = '

⚡ System Statistics

'; html += '
' + JSON.stringify(data, null, 2) + '
'; html += '
'; container.innerHTML = html; } catch (error) { console.error('[Dashboard] Error rendering advanced tab:', error); this.showError(container, 'Failed to render advanced data'); } } // ===== Helper Methods ===== createStatCard(icon, label, value, variant = 'primary') { return `
${icon}
${value}
${label}
`; } createStatusBadge(status) { const statusMap = { 'online': 'success', 'offline': 'danger', 'degraded': 'warning', 'unknown': 'secondary' }; const badgeClass = statusMap[status] || 'secondary'; return `${status}`; } createHealthIndicator(health) { const healthMap = { 'healthy': { icon: '✅', class: 'provider-health-online' }, 'degraded': { icon: '⚠️', class: 'provider-health-degraded' }, 'unhealthy': { icon: '❌', class: 'provider-health-offline' }, 'unknown': { icon: '❓', class: '' } }; const indicator = healthMap[health] || healthMap.unknown; return `${indicator.icon} ${health}`; } createRouteBadge(route, proxyEnabled) { if (proxyEnabled || route === 'proxy') { return '🔀 Proxy'; } return 'Direct'; } createProviderCard(provider) { const status = provider.status || 'unknown'; const health = provider.health_status || provider.health || 'unknown'; return `

${provider.name || provider.id}

${this.createStatusBadge(status)}

Category: ${provider.category || 'N/A'}

Health: ${this.createHealthIndicator(health)}

Endpoint: ${provider.endpoint || provider.url || 'N/A'}

`; } createPoolCard(pool) { const members = pool.members || []; return `

${pool.name || pool.id}

${members.length} members

Strategy: ${pool.strategy || 'round-robin'}

Members: ${members.join(', ') || 'None'}

`; } createEmptyState(title, description) { return `
📭
${title}
${description}
`; } renderTrendingCoins(coins) { let html = ''; return html; } renderDiscoveryReport(report) { return `

🔍 Discovery Report

Enabled: ${report.enabled ? '✅ Yes' : '❌ No'}

Last Run: ${report.last_run ? new Date(report.last_run.started_at).toLocaleString() : 'Never'}

`; } renderModelsReport(report) { return `

🤖 Models Report

Total Models: ${report.total_models || 0}

Available: ${report.available || 0}

Errors: ${report.errors || 0}

`; } showError(container, message) { container.innerHTML = `
❌ ${message}
`; } formatCurrency(value) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact' }).format(value); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } getLogLevelClass(level) { const map = { error: 'danger', warning: 'warning', info: 'primary', debug: 'secondary' }; return map[level] || 'secondary'; } // ===== Action Handlers ===== async checkProviderHealth(providerId) { try { const result = await window.apiClient.checkProviderHealth(providerId); alert(`Provider health check result: ${JSON.stringify(result)}`); } catch (error) { alert(`Failed to check provider health: ${error.message}`); } } async clearLogs() { if (confirm('Clear all logs?')) { try { await window.apiClient.clearLogs(); window.tabManager.loadLogsTab(); } catch (error) { alert(`Failed to clear logs: ${error.message}`); } } } async runSentiment() { try { const result = await window.apiClient.runHFSentiment({ text: 'Bitcoin is going to the moon!' }); alert(`Sentiment result: ${JSON.stringify(result)}`); } catch (error) { alert(`Failed to run sentiment: ${error.message}`); } } async rotatePool(poolId) { try { await window.apiClient.rotatePool(poolId); window.tabManager.loadPoolsTab(); } catch (error) { alert(`Failed to rotate pool: ${error.message}`); } } createPool() { alert('Create pool functionality - to be implemented with a modal form'); } /** * Cleanup */ destroy() { this.clearRefreshIntervals(); Object.values(this.charts).forEach(chart => { if (chart && chart.destroy) chart.destroy(); }); this.charts = {}; } } // Create global instance window.dashboardApp = new DashboardApp(); // Auto-initialize document.addEventListener('DOMContentLoaded', () => { window.dashboardApp.init(); }); // Cleanup on unload window.addEventListener('beforeunload', () => { window.dashboardApp.destroy(); }); console.log('[Dashboard] Module loaded');