/** * Dashboard Page - REAL DATA ONLY * NO MOCK DATA - Uses actual backend APIs */ import { api } from '../../shared/js/core/api-client.js'; import { LayoutManager } from '../../shared/js/core/layout-manager.js'; import { Toast } from '../../shared/js/components/toast.js'; import { formatNumber, formatCurrency, formatPercentage } from '../../shared/js/utils/formatters.js'; class DashboardPage { constructor() { this.marketData = []; this.sentimentChart = null; this.categoriesChart = null; this.lastUpdate = null; } async init() { try { console.log('[Dashboard] Initializing with REAL data only...'); await LayoutManager.injectLayouts(); LayoutManager.setActiveNav('dashboard'); this.bindEvents(); // Load Chart.js await this.loadChartJS(); // Load real data await this.loadAllData(); // Setup auto-refresh (30s) setInterval(() => this.loadAllData(), 30000); Toast.success('Dashboard loaded - Real data'); } catch (error) { console.error('[Dashboard] Init error:', error); Toast.error('Failed to load dashboard'); } } async loadChartJS() { if (window.Chart) return; return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js'; script.onload = () => { console.log('[Dashboard] Chart.js loaded'); resolve(); }; script.onerror = reject; document.head.appendChild(script); }); } bindEvents() { document.getElementById('refresh-btn')?.addEventListener('click', () => { Toast.info('Refreshing...'); this.loadAllData(); }); } async loadAllData() { try { const startTime = Date.now(); // Load data in parallel const [stats, market, sentiment, resources] = await Promise.all([ this.loadStats(), this.loadMarket(), this.loadSentiment(), this.loadResources() ]); const duration = Date.now() - startTime; console.log(`[Dashboard] Data loaded in ${duration}ms`); // Update UI this.renderStats(stats); this.renderMarket(market); this.renderSentiment(sentiment); this.renderCategories(resources); // Update last update time this.lastUpdate = new Date(); document.getElementById('last-update').textContent = `Updated: ${this.lastUpdate.toLocaleTimeString()}`; } catch (error) { console.error('[Dashboard] Load error:', error); Toast.error('Failed to load data'); } } async loadStats() { try { const [resources, models, providers] = await Promise.all([ api.get('/resources/count'), api.get('/models/summary'), api.get('/providers/summary') ]); return { totalResources: resources.resources?.total || 0, freeResources: resources.resources?.apis || 0, aiModels: models.summary?.loaded_models || 0, activeProviders: providers.summary?.online || 0 }; } catch (error) { console.error('[Dashboard] Stats error:', error); return { totalResources: 0, freeResources: 0, aiModels: 0, activeProviders: 0 }; } } async loadMarket() { try { // Try to get top coins from backend const response = await api.get('/coins/top?limit=10'); return response.coins || response.data || []; } catch (error) { console.error('[Dashboard] Market error:', error); // Try alternative endpoint try { const response = await api.get('/market'); return response.data?.coins || []; } catch (e) { console.error('[Dashboard] Market fallback error:', e); return []; } } } async loadSentiment() { try { const response = await api.get('/sentiment/global'); return response.sentiment || response; } catch (error) { console.error('[Dashboard] Sentiment error:', error); // Try alternative endpoint try { const response = await api.get('/sentiment'); return response; } catch (e) { return { value: 50, label: 'neutral', available: false }; } } } async loadResources() { try { const response = await api.get('/resources'); // Count by category const categories = {}; const resources = response.resources || response.data || []; resources.forEach(r => { const cat = r.category || 'other'; categories[cat] = (categories[cat] || 0) + 1; }); return categories; } catch (error) { console.error('[Dashboard] Resources error:', error); return {}; } } renderStats(stats) { const statsGrid = document.getElementById('stats-grid'); if (!statsGrid) return; statsGrid.innerHTML = `
${formatNumber(stats.totalResources)}
Total Resources
${formatNumber(stats.freeResources)}
Free APIs
${formatNumber(stats.aiModels)}
AI Models
${formatNumber(stats.activeProviders)}
Providers
`; } renderMarket(coins) { const container = document.getElementById('market-table-container'); if (!container) return; if (!coins || coins.length === 0) { container.innerHTML = `

No market data available

Backend API may not be accessible

`; return; } this.marketData = coins; const table = ` ${coins.map((coin, idx) => ` `).join('')}
# Name Price 24h Change Market Cap Volume
${idx + 1}
${coin.name || coin.symbol} ${coin.symbol || ''}
${formatCurrency(coin.price || coin.current_price || 0)} ${formatPercentage(coin.change_24h || coin.price_change_percentage_24h || 0)} ${formatCurrency(coin.market_cap || 0)} ${formatCurrency(coin.volume_24h || coin.total_volume || 0)}
`; container.innerHTML = table; } renderSentiment(sentiment) { const canvas = document.getElementById('sentiment-chart'); if (!canvas) return; if (this.sentimentChart) { this.sentimentChart.destroy(); } // Create simple sentiment data const value = sentiment.value || 50; const data = { labels: ['Bearish', 'Neutral', 'Bullish'], datasets: [{ label: 'Market Sentiment', data: [ value < 40 ? 60 : 20, value >= 40 && value <= 60 ? 60 : 20, value > 60 ? 60 : 20 ], backgroundColor: [ 'rgba(239, 68, 68, 0.6)', 'rgba(156, 163, 175, 0.6)', 'rgba(34, 197, 94, 0.6)' ], borderColor: [ 'rgba(239, 68, 68, 1)', 'rgba(156, 163, 175, 1)', 'rgba(34, 197, 94, 1)' ], borderWidth: 2 }] }; this.sentimentChart = new Chart(canvas, { type: 'doughnut', data: data, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: '#fff' } }, title: { display: true, text: `Current: ${sentiment.label || 'Neutral'} (${value})`, color: '#fff' } } } }); } renderCategories(categories) { const canvas = document.getElementById('categories-chart'); if (!canvas) return; if (this.categoriesChart) { this.categoriesChart.destroy(); } const labels = Object.keys(categories); const values = Object.values(categories); if (labels.length === 0) { return; // No data } this.categoriesChart = new Chart(canvas, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Resources', data: values, backgroundColor: 'rgba(45, 212, 191, 0.6)', borderColor: 'rgba(45, 212, 191, 1)', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, ticks: { color: '#fff' }, grid: { color: 'rgba(255,255,255,0.1)' } }, x: { ticks: { color: '#fff' }, grid: { color: 'rgba(255,255,255,0.1)' } } }, plugins: { legend: { labels: { color: '#fff' } } } } }); } } // Initialize const dashboard = new DashboardPage(); window.dashboardPage = dashboard; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => dashboard.init()); } else { dashboard.init(); }