import apiClient from './apiClient.js'; import { formatCurrency, formatPercent, renderMessage, createSkeletonRows } from './uiUtils.js'; class OverviewView { constructor(section) { this.section = section; this.statsContainer = section.querySelector('[data-overview-stats]'); this.topCoinsBody = section.querySelector('[data-top-coins-body]'); this.sentimentCanvas = section.querySelector('#sentiment-chart'); this.sentimentChart = null; } async init() { this.renderStatSkeletons(); this.topCoinsBody.innerHTML = createSkeletonRows(6, 6); await Promise.all([this.loadStats(), this.loadTopCoins(), this.loadSentiment()]); } renderStatSkeletons() { if (!this.statsContainer) return; this.statsContainer.innerHTML = Array.from({ length: 4 }) .map(() => '
') .join(''); } async loadStats() { if (!this.statsContainer) return; const result = await apiClient.getMarketStats(); if (!result.ok) { renderMessage(this.statsContainer, { state: 'error', title: 'Unable to load market stats', body: result.error || 'Unknown error', }); return; } const stats = result.data || {}; const cards = [ { label: 'Total Market Cap', value: formatCurrency(stats.total_market_cap) }, { label: '24h Volume', value: formatCurrency(stats.total_volume_24h) }, { label: 'BTC Dominance', value: formatPercent(stats.btc_dominance) }, { label: 'ETH Dominance', value: formatPercent(stats.eth_dominance) }, ]; this.statsContainer.innerHTML = cards .map( (card) => `

${card.label}

${card.value}
Updated ${new Date().toLocaleTimeString()}
`, ) .join(''); } async loadTopCoins() { const result = await apiClient.getTopCoins(10); if (!result.ok) { this.topCoinsBody.innerHTML = `
Failed to load coins

${result.error}

`; return; } const rows = (result.data || []).map( (coin, index) => ` ${index + 1} ${coin.symbol || coin.ticker || '—'} ${coin.name || 'Unknown'} ${formatCurrency(coin.price)} ${formatPercent(coin.change_24h)} ${formatCurrency(coin.volume_24h)} ${formatCurrency(coin.market_cap)} `); this.topCoinsBody.innerHTML = rows.join(''); } async loadSentiment() { if (!this.sentimentCanvas) return; const result = await apiClient.runQuery({ query: 'global crypto sentiment breakdown' }); if (!result.ok) { this.sentimentCanvas.replaceWith(this.buildSentimentFallback(result.error)); return; } const payload = result.data || {}; const sentiment = payload.sentiment || payload.data || {}; const data = { bullish: sentiment.bullish ?? 40, neutral: sentiment.neutral ?? 35, bearish: sentiment.bearish ?? 25, }; if (this.sentimentChart) { this.sentimentChart.destroy(); } this.sentimentChart = new Chart(this.sentimentCanvas, { type: 'doughnut', data: { labels: ['Bullish', 'Neutral', 'Bearish'], datasets: [ { data: [data.bullish, data.neutral, data.bearish], backgroundColor: ['#22c55e', '#38bdf8', '#ef4444'], borderWidth: 0, }, ], }, options: { cutout: '65%', plugins: { legend: { labels: { color: 'var(--text-primary)', usePointStyle: true }, }, }, }, }); } buildSentimentFallback(message) { const wrapper = document.createElement('div'); wrapper.className = 'inline-message inline-info'; wrapper.innerHTML = ` Sentiment insight unavailable

${message || 'AI sentiment endpoint did not respond in time.'}

`; return wrapper; } } export default OverviewView;