${news.title}
${news.summary || ''}
${new Date(news.published_at || news.date).toLocaleString()}import apiClient from './apiClient.js'; import { formatCurrency, formatPercent, createSkeletonRows } from './uiUtils.js'; class MarketView { constructor(section, wsClient) { this.section = section; this.wsClient = wsClient; this.tableBody = section.querySelector('[data-market-body]'); this.searchInput = section.querySelector('[data-market-search]'); this.timeframeButtons = section.querySelectorAll('[data-timeframe]'); this.liveToggle = section.querySelector('[data-live-toggle]'); this.drawer = section.querySelector('[data-market-drawer]'); this.drawerClose = section.querySelector('[data-close-drawer]'); this.drawerSymbol = section.querySelector('[data-drawer-symbol]'); this.drawerStats = section.querySelector('[data-drawer-stats]'); this.drawerNews = section.querySelector('[data-drawer-news]'); this.chartWrapper = section.querySelector('[data-chart-wrapper]'); this.chartCanvas = this.chartWrapper?.querySelector('#market-detail-chart'); this.chart = null; this.coins = []; this.filtered = []; this.currentTimeframe = '7d'; this.liveUpdates = false; } async init() { this.tableBody.innerHTML = createSkeletonRows(10, 7); await this.loadCoins(); this.bindEvents(); } bindEvents() { if (this.searchInput) { this.searchInput.addEventListener('input', () => this.filterCoins()); } this.timeframeButtons.forEach((btn) => { btn.addEventListener('click', () => { this.timeframeButtons.forEach((b) => b.classList.remove('active')); btn.classList.add('active'); this.currentTimeframe = btn.dataset.timeframe; if (this.drawer?.classList.contains('active') && this.drawerSymbol?.dataset.symbol) { this.openDrawer(this.drawerSymbol.dataset.symbol); } }); }); if (this.liveToggle) { this.liveToggle.addEventListener('change', (event) => { this.liveUpdates = event.target.checked; if (this.liveUpdates) { this.wsSubscription = this.wsClient.subscribe('price_update', (payload) => this.applyLiveUpdate(payload)); } else if (this.wsSubscription) { this.wsSubscription(); } }); } if (this.drawerClose) { this.drawerClose.addEventListener('click', () => this.drawer.classList.remove('active')); } } async loadCoins() { const result = await apiClient.getTopCoins(50); if (!result.ok) { this.tableBody.innerHTML = `
Loading...
'; this.drawerNews.innerHTML = 'Loading news...
'; await Promise.all([this.loadCoinDetails(symbol), this.loadCoinNews(symbol)]); } async loadCoinDetails(symbol) { const [details, chart] = await Promise.all([ apiClient.getCoinDetails(symbol), apiClient.getPriceChart(symbol, this.currentTimeframe), ]); if (!details.ok) { this.drawerStats.innerHTML = ``; } else { const coin = details.data || {}; this.drawerStats.innerHTML = `${formatCurrency(coin.price)}
${formatPercent(coin.change_24h)}
${formatCurrency(coin.high_24h)} / ${formatCurrency(coin.low_24h)}
${formatCurrency(coin.market_cap)}
No related headlines available.
'; return; } this.drawerNews.innerHTML = related .map( (news) => `${news.summary || ''}
${new Date(news.published_at || news.date).toLocaleString()}