| <!DOCTYPE html> |
| <html lang="en" dir="ltr" data-theme="light"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <meta name="description" content="Crypto Monitor ULTIMATE - Premium Dashboard with Real-time Market Data, AI Analysis & Sentiment"> |
| <meta name="theme-color" content="#14b8a6"> |
| <title>Dashboard | Crypto Monitor</title> |
|
|
| |
| <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%232dd4bf'/%3E%3Cstop offset='50%25' stop-color='%2322d3ee'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='50' cy='50' r='45' fill='url(%23g)'/%3E%3Cpath d='M50 25 L65 45 L50 40 L35 45 Z M50 75 L35 55 L50 60 L65 55 Z' fill='white'/%3E%3C/svg%3E"> |
|
|
| |
| <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin> |
| <link rel="dns-prefetch" href="https://fonts.googleapis.com"> |
| <link rel="dns-prefetch" href="https://cdnjs.cloudflare.com"> |
|
|
| |
| <style> |
| /* Critical above-the-fold styles */ |
| :root{--teal-dark:#0d7377;--teal:#14b8a6;--teal-light:#2dd4bf;--cyan:#22d3ee;--text-primary:#0f2926;--text-secondary:#2a5f5a;--bg-main:#ffffff;--bg-secondary:#f8fdfc;--sidebar-width:260px} |
| *,*::before,*::after{margin:0;padding:0;box-sizing:border-box} |
| html{font-size:14px;-webkit-font-smoothing:antialiased} |
| body{font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.5;color:var(--text-secondary);background:var(--bg-main);min-height:100vh} |
| .app-container{display:flex;min-height:100vh} |
| .sidebar{position:fixed;left:0;top:0;bottom:0;width:var(--sidebar-width);background:linear-gradient(180deg,#fff 0%,#f8fdfc 100%);border-right:1px solid rgba(20,184,166,0.12);z-index:100} |
| .main-content{flex:1;margin-left:var(--sidebar-width);min-height:100vh} |
| .page-content{padding:1.5rem;max-width:1400px;margin:0 auto} |
| @media(max-width:768px){.sidebar{transform:translateX(-100%)}.main-content{margin-left:0}} |
| </style> |
|
|
| |
| <link rel="stylesheet" href="/static/shared/css/design-system.css?v=3.0" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/shared/css/design-system.css?v=3.0"></noscript> |
| <link rel="stylesheet" href="/static/shared/css/global.css?v=3.0" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/shared/css/global.css?v=3.0"></noscript> |
| <link rel="stylesheet" href="/static/shared/css/components.css" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/shared/css/components.css"></noscript> |
| <link rel="stylesheet" href="/static/shared/css/layout.css" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/shared/css/layout.css"></noscript> |
| <link rel="stylesheet" href="/static/pages/dashboard/dashboard.css?v=3.0" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/pages/dashboard/dashboard.css?v=3.0"></noscript> |
| <link rel="stylesheet" href="/static/shared/css/status-drawer.css" media="print" onload="this.media='all'"> |
| <noscript><link rel="stylesheet" href="/static/shared/css/status-drawer.css"></noscript> |
| |
| <script src="/static/shared/js/utils/error-suppressor.js"></script> |
| |
| <script src="/static/shared/js/components/status-drawer.js"></script> |
| |
| <script src="/static/assets/icons/crypto-icons.js"></script> |
| |
| <script src="/static/js/api-config.js"></script> |
| <script> |
| |
| window.apiReady = new Promise((resolve) => { |
| if (window.apiClient) { |
| console.log('✅ API Client ready'); |
| resolve(window.apiClient); |
| } else { |
| console.error('❌ API Client not loaded'); |
| } |
| }); |
| </script> |
|
|
| </head> |
| <body> |
| <div class="app-container"> |
| |
| <aside id="sidebar-container"></aside> |
|
|
| |
| <main class="main-content"> |
| |
| <header id="header-container"></header> |
|
|
| |
| <div class="page-content"> |
| |
| <div class="page-header"> |
| <div class="page-title"> |
| <h1> |
| <span class="page-icon"> |
| |
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="url(#iconGradient)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| <defs> |
| <linearGradient id="iconGradient" x1="0%" y1="0%" x2="100%" y2="100%"> |
| <stop offset="0%" stop-color="#2dd4bf"/> |
| <stop offset="50%" stop-color="#22d3ee"/> |
| <stop offset="100%" stop-color="#3b82f6"/> |
| </linearGradient> |
| </defs> |
| <rect x="3" y="3" width="7" height="7" rx="1"/> |
| <rect x="14" y="3" width="7" height="7" rx="1"/> |
| <rect x="14" y="14" width="7" height="7" rx="1"/> |
| <rect x="3" y="14" width="7" height="7" rx="1"/> |
| </svg> |
| </span> |
| Dashboard |
| </h1> |
| <p class="page-subtitle">Real-time Market Data & AI Analysis</p> |
| </div> |
| <div class="page-actions"> |
| <button id="refresh-btn" class="btn-icon" title="Refresh" aria-label="Refresh data"> |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| <path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/> |
| <path d="M21 3v5h-5"/> |
| </svg> |
| </button> |
| <span id="last-update" class="last-update">Loading...</span> |
| </div> |
| </div> |
|
|
| |
| </div> |
| </main> |
| </div> |
|
|
| |
| <div id="toast-container" aria-live="polite"></div> |
|
|
| <script type="module"> |
| |
| (async function() { |
| const initApp = async () => { |
| try { |
| const { LayoutManager } = await import('/static/shared/js/core/layout-manager.js?v=3.0'); |
| await LayoutManager.init('dashboard'); |
| |
| |
| const { default: dashboardPage } = await import('/static/pages/dashboard/dashboard.js?v=3.0'); |
| window.dashboardPage = dashboardPage; |
| |
| window.addEventListener('beforeunload', () => { |
| if (window.dashboardPage) window.dashboardPage.destroy(); |
| }); |
| } catch (error) { |
| console.error('Failed to initialize dashboard:', error); |
| } |
| }; |
| |
| |
| if (window.requestIdleCallback) { |
| window.requestIdleCallback(initApp, { timeout: 2000 }); |
| } else { |
| setTimeout(initApp, 100); |
| } |
| })(); |
| </script> |
| </body> |
| </html> |
|
|