// CryptoVista Dashboard Pro - Enhanced JavaScript // API Configuration with fallbacks const COINGECKO_API = 'https://api.coingecko.com/api/v3'; const CRYPTO_NEWS_API = 'https://min-api.cryptocompare.com/data/v2/news/'; const ALTERNATIVE_API = 'https://api.coincap.io/v2/assets?limit=5'; // Global State with enhanced monitoring let cryptoData = { marketData: null, topGainers: [], news: [], userPreferences: { currency: 'usd', theme: 'dark', refreshInterval: 30000, notifications: true }, performance: { lastUpdate: null, apiLatency: {}, errors: [] } }; let selectedCurrency = localStorage.getItem('preferredCurrency') || 'usd'; let theme = localStorage.getItem('theme') || 'dark'; let isInitialized = false; // Performance monitoring const perfMonitor = { start: (label) => { if (window.performance) { perfMonitor[label] = performance.now(); } }, end: (label) => { if (window.performance && perfMonitor[label]) { const duration = performance.now() - perfMonitor[label]; console.log(`⏱️ ${label}: ${duration.toFixed(2)}ms`); return duration; } return 0; } }; // Initialize Dashboard with enhanced UX async function initializeDashboard() { if (isInitialized) { console.warn('Dashboard already initialized'); return; } perfMonitor.start('init'); try { console.log('🚀 Initializing CryptoVista Dashboard Pro...'); // Set initial theme applyTheme(theme); // Show loading state showLoadingState(); // Load critical data first await Promise.allSettled([ loadTopGainers(), loadNews(), loadMarketData() ]); // Setup enhanced auto-refresh setupAutoRefresh(); // Setup enhanced event listeners setupEventListeners(); // Initialize UI components initializeUIComponents(); // Mark as initialized isInitialized = true; // Hide loading state hideLoadingState(); perfMonitor.end('init'); // Show welcome message setTimeout(() => { showToast('Dashboard loaded successfully!', 'success'); }, 500); console.log('✅ Dashboard initialized successfully'); } catch (error) { console.error('❌ Dashboard initialization failed:', error); showError('global', 'Failed to initialize dashboard. Please refresh.'); hideLoadingState(); } } // Load Top Gainers from CoinGecko API async function loadTopGainers() { try { const response = await fetch(`${COINGECKO_API}/coins/markets?vs_currency=${selectedCurrency}&order=market_cap_desc&per_page=5&sparkline=false&price_change_percentage=24h`); const gainers = await response.json(); const container = document.getElementById('topGainers'); container.innerHTML = ''; gainers.forEach((coin, index) => { const isPositive = coin.price_change_percentage_24h > 0; const changeClass = isPositive ? 'positive' : 'negative'; const coinElement = `
${coin.symbol.toUpperCase()}

${coin.name}

$${coin.current_price.toLocaleString()}

${isPositive ? '+' : ''}${coin.price_change_percentage_24h?.toFixed(2) || 0}%
`; container.innerHTML += coinElement; }); } catch (error) { console.error('Error loading top gainers:', error); showError('topGainers', 'Failed to load top gainers data'); } } // Load Crypto News from CryptoCompare API async function loadNews() { try { // Note: In production, you'd use your actual API key const response = await fetch(CRYPTO_NEWS_API); const data = await response.json(); const news = data.Data?.slice(0, 3) || []; const container = document.getElementById('newsFeed'); container.innerHTML = ''; news.forEach((article, index) => { const imageUrl = article.imageurl || `http://static.photos/technology/640x360/${index + 1}`; const newsElement = `
${article.title}

${article.title}

${article.source} ${formatTimeAgo(article.published_on)}
`; container.innerHTML += newsElement; }); } catch (error) { console.error('Error loading news:', error); showError('newsFeed', 'Failed to load news feed'); // Fallback to static news const container = document.getElementById('newsFeed'); container.innerHTML = `

News feed temporarily unavailable

Check back soon for updates

`; feather.replace(); } } // Load Market Overview Data async function loadMarketData() { try { const response = await fetch(`${COINGECKO_API}/global`); const data = await response.json(); cryptoData.marketData = data.data; // Update market stats updateMarketStats(data.data); } catch (error) { console.error('Error loading market data:', error); } } // Update Market Stats Display function updateMarketStats(marketData) { const elements = { totalMarketCap: document.querySelector('[data-market-cap]'), totalVolume: document.querySelector('[data-volume]'), btcDominance: document.querySelector('[data-btc-dominance]'), activeCryptos: document.querySelector('[data-active-cryptos]') }; if (marketData && marketData.total_market_cap) { Object.keys(elements).forEach(key => { if (elements[key] && marketData[key]) { const value = marketData[key]; let formattedValue; switch(key) { case 'total_market_cap': case 'total_volume': formattedValue = `$${(value.usd / 1e12).toFixed(2)}T`; break; case 'btc_dominance': formattedValue = `${value.toFixed(2)}%`; break; case 'active_cryptocurrencies': formattedValue = value.toLocaleString(); break; default: formattedValue = value; } elements[key].textContent = formattedValue; } }); } } // Setup Auto Refresh function setupAutoRefresh() { // Refresh specific components at different intervals setInterval(() => { loadTopGainers(); console.log('🔄 Refreshed top gainers'); }, 30000); // 30 seconds setInterval(() => { loadMarketData(); console.log('📊 Refreshed market data'); }, 60000); // 1 minute setInterval(() => { loadNews(); console.log('📰 Refreshed news feed'); }, 120000); // 2 minutes } // Setup Event Listeners function setupEventListeners() { // Currency selector const currencyButtons = document.querySelectorAll('[data-currency]'); currencyButtons.forEach(button => { button.addEventListener('click', () => { selectedCurrency = button.dataset.currency; currencyButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); loadTopGainers(); }); }); // Theme toggle const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.addEventListener('click', toggleTheme); } // Search functionality const searchInput = document.getElementById('cryptoSearch'); if (searchInput) { searchInput.addEventListener('input', debounce(handleSearch, 300)); } // Time range buttons const timeRangeButtons = document.querySelectorAll('[data-time-range]'); timeRangeButtons.forEach(button => { button.addEventListener('click', () => { timeRangeButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // In production, update chart time range here }); }); // Mobile menu toggle const mobileMenuButton = document.getElementById('mobileMenuButton'); const mobileMenu = document.getElementById('mobileMenu'); if (mobileMenuButton && mobileMenu) { mobileMenuButton.addEventListener('click', () => { mobileMenu.classList.toggle('hidden'); }); } } // Handle Search Functionality async function handleSearch(event) { const query = event.target.value.toLowerCase().trim(); if (query.length < 2) { hideSearchResults(); return; } try { const response = await fetch(`${COINGECKO_API}/search?query=${query}`); const data = await response.json(); displaySearchResults(data.coins || []); } catch (error) { console.error('Search error:', error); } } // Display Search Results function displaySearchResults(coins) { const container = document.getElementById('searchResults'); if (!container) return; container.innerHTML = coins.slice(0, 5).map(coin => ` ${coin.name}

${coin.name}

${coin.symbol.toUpperCase()}

`).join(''); container.classList.remove('hidden'); } // Hide Search Results function hideSearchResults() { const container = document.getElementById('searchResults'); if (container) { container.classList.add('hidden'); } } // Toggle Theme function toggleTheme() { const html = document.documentElement; if (theme === 'dark') { theme = 'light'; html.classList.remove('dark'); html.classList.add('light'); } else { theme = 'dark'; html.classList.remove('light'); html.classList.add('dark'); } // Save preference localStorage.setItem('theme', theme); // Update UI updateThemeUI(); } // Update Theme UI function updateThemeUI() { const themeToggle = document.getElementById('themeToggle'); if (!themeToggle) return; const icon = themeToggle.querySelector('i[data-feather]'); if (icon) { const newIcon = theme === 'dark' ? 'sun' : 'moon'; icon.setAttribute('data-feather', newIcon); feather.replace(); } } // Show Error Message function showError(elementId, message) { const element = document.getElementById(elementId); if (element) { element.innerHTML = `

${message}

`; feather.replace(); } } // Format Time Ago function formatTimeAgo(timestamp) { const seconds = Math.floor((Date.now() - timestamp * 1000) / 1000); const intervals = [ { label: 'year', seconds: 31536000 }, { label: 'month', seconds: 2592000 }, { label: 'day', seconds: 86400 }, { label: 'hour', seconds: 3600 }, { label: 'minute', seconds: 60 }, { label: 'second', seconds: 1 } ]; for (const interval of intervals) { const count = Math.floor(seconds / interval.seconds); if (count >= 1) { return `${count} ${interval.label}${count !== 1 ? 's' : ''} ago`; } } return 'just now'; } // Debounce Function function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Load saved preferences function loadPreferences() { const savedTheme = localStorage.getItem('theme'); const savedCurrency = localStorage.getItem('currency'); if (savedTheme) { theme = savedTheme; document.documentElement.classList.add(theme); } if (savedCurrency) { selectedCurrency = savedCurrency; } } // Format Currency function formatCurrency(amount, currency = 'usd') { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency.toUpperCase(), minimumFractionDigits: 2, maximumFractionDigits: 6 }).format(amount); } // Copy to Clipboard function copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => { showToast('Copied to clipboard!', 'success'); }).catch(err => { console.error('Copy failed:', err); showToast('Copy failed', 'error'); }); } // Show Toast Notification function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transition-all duration-300 transform translate-y-0 opacity-100 ${ type === 'success' ? 'bg-green-900 text-green-100' : type === 'error' ? 'bg-red-900 text-red-100' : 'bg-dark-800 text-gray-100' }`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.transform = 'translateY(100%)'; toast.style.opacity = '0'; setTimeout(() => document.body.removeChild(toast), 300); }, 3000); } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', () => { loadPreferences(); initializeDashboard(); // Handle clicks outside search to close results document.addEventListener('click', (event) => { if (!event.target.closest('#searchContainer')) { hideSearchResults(); } }); }); // Export functions for use in templates window.cryptoUtils = { formatCurrency, formatTimeAgo, copyToClipboard, showToast, loadTopGainers, loadNews }; // Simplex noise for chart background (placeholder) class CryptoChartSimulator { constructor(canvasId) { this.canvas = document.getElementById(canvasId); if (!this.canvas) return; this.ctx = this.canvas.getContext('2d'); this.width = this.canvas.width; this.height = this.canvas.height; this.setupChart(); this.animate(); } setupChart() { // Chart setup logic would go here this.ctx.lineWidth = 2; this.ctx.strokeStyle = '#0ea5e9'; } animate() { // Animation logic for demo chart requestAnimationFrame(() => this.animate()); } }