File size: 11,317 Bytes
b190b45 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
<!DOCTYPE html>
<html lang="en" dir="ltr" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Crypto Monitor - Premium Dashboard with Real-time Market Data, AI Analysis & Sentiment">
<meta name="theme-color" content="#8B5CF6">
<title>Dashboard | Crypto Monitor</title>
<!-- Favicon -->
<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='%238B5CF6'/%3E%3Cstop offset='100%25' stop-color='%236D28D9'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='50' cy='50' r='45' fill='url(%23g)'/%3E%3Ctext x='50' y='65' font-size='50' text-anchor='middle' fill='white' font-weight='bold'%3EC%3C/text%3E%3C/svg%3E">
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<!-- Critical CSS - Inline for faster first paint -->
<style>
/* Critical above-the-fold styles - Cursor-inspired */
:root{
--bg-primary:#0A0A0A;
--surface-primary:#1E1E1E;
--text-primary:#EFEFEF;
--accent-purple:#8B5CF6;
--sidebar-width:240px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{font-size:15px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif;font-size:15px;line-height:1.5;color:var(--text-primary);background:var(--bg-primary);min-height:100vh}
.app-container{display:flex;min-height:100vh}
.sidebar{position:fixed;left:0;top:0;bottom:0;width:var(--sidebar-width);background:#0F0F0F;border-right:1px solid #2A2A2A;z-index:1200;transition:width 300ms cubic-bezier(0.4,0,0.2,1)}
.main-content{flex:1;margin-left:var(--sidebar-width);min-height:100vh;display:flex;flex-direction:column;transition:margin-left 300ms cubic-bezier(0.4,0,0.2,1)}
.page-content{padding:24px;max-width:1400px;margin:0 auto;width:100%}
@media(max-width:1024px){.sidebar{transform:translateX(-100%)}.main-content{margin-left:0}}
</style>
<!-- Cursor-Inspired Design System -->
<link rel="stylesheet" href="/static/shared/css/design-system-cursor.css">
<link rel="stylesheet" href="/static/shared/css/layout-cursor.css">
<link rel="stylesheet" href="/static/shared/css/components-cursor.css">
<link rel="stylesheet" href="/static/shared/css/animations-cursor.css">
<!-- Page-specific CSS -->
<link rel="stylesheet" href="/static/pages/dashboard/dashboard.css?v=4.0">
<!-- Error Suppressor -->
<script src="/static/shared/js/utils/error-suppressor.js"></script>
<!-- Crypto Icons Library -->
<script src="/static/assets/icons/crypto-icons.js"></script>
<!-- API Configuration -->
<script src="/static/js/api-config.js"></script>
<script>
// Initialize API client
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">
<!-- Sidebar -->
<aside id="sidebar-container"></aside>
<!-- Main Content -->
<main class="main-content">
<!-- Header -->
<header id="header-container"></header>
<!-- Dashboard Content -->
<div class="page-content stagger-fade-in">
<!-- Page Header -->
<div class="page-header">
<h1 class="page-title">Dashboard</h1>
<p class="page-description">Real-time Market Data & AI Analysis</p>
</div>
<!-- Stats Grid -->
<div id="stats-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-4); margin-bottom: var(--space-8);">
<!-- Stats cards will be injected here -->
<div class="stat-card skeleton-wave" style="min-height: 140px;"></div>
<div class="stat-card skeleton-wave" style="min-height: 140px;"></div>
<div class="stat-card skeleton-wave" style="min-height: 140px;"></div>
<div class="stat-card skeleton-wave" style="min-height: 140px;"></div>
</div>
<!-- Charts & Analysis -->
<div style="display: grid; grid-template-columns: 1fr; gap: var(--space-6); margin-bottom: var(--space-8);">
<!-- Market Overview Chart -->
<div class="card" id="market-chart-container">
<div class="card-header">
<div>
<h3 class="card-title">Market Overview</h3>
<p class="card-subtitle">Real-time price movements</p>
</div>
<div style="display: flex; gap: var(--space-2);">
<button class="btn btn-ghost btn-sm" data-timeframe="1h">1H</button>
<button class="btn btn-ghost btn-sm" data-timeframe="4h">4H</button>
<button class="btn btn-secondary btn-sm" data-timeframe="1d">1D</button>
<button class="btn btn-ghost btn-sm" data-timeframe="1w">1W</button>
</div>
</div>
<div class="card-body">
<div id="market-chart" style="min-height: 400px; display: flex; align-items: center; justify-content: center;">
<div class="spinner spinner-lg"></div>
</div>
</div>
</div>
</div>
<!-- Two Column Layout -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); gap: var(--space-6); margin-bottom: var(--space-8);">
<!-- Top Gainers -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Top Gainers</h3>
<span class="badge badge-success pill">24h</span>
</div>
<div class="card-body">
<div id="top-gainers-list">
<!-- Skeleton loaders -->
<div style="display: flex; flex-direction: column; gap: var(--space-3);">
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</div>
</div>
</div>
</div>
<!-- Top Losers -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Top Losers</h3>
<span class="badge badge-danger pill">24h</span>
</div>
<div class="card-body">
<div id="top-losers-list">
<!-- Skeleton loaders -->
<div style="display: flex; flex-direction: column; gap: var(--space-3);">
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</div>
</div>
</div>
</div>
</div>
<!-- AI Sentiment Analysis -->
<div class="card" id="sentiment-container">
<div class="card-header">
<div>
<h3 class="card-title">AI Sentiment Analysis</h3>
<p class="card-subtitle">Market sentiment powered by AI models</p>
</div>
<span class="badge badge-primary">AI</span>
</div>
<div class="card-body">
<div id="sentiment-content" style="min-height: 200px; display: flex; align-items: center; justify-content: center;">
<div class="dots-loader">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<!-- Recent Activity / News -->
<div class="card" style="margin-top: var(--space-6);" id="recent-activity">
<div class="card-header">
<h3 class="card-title">Recent Activity</h3>
<button class="btn btn-ghost btn-sm">View All</button>
</div>
<div class="card-body">
<div id="activity-list">
<!-- Activity items will be injected here -->
<div style="display: flex; flex-direction: column; gap: var(--space-3);">
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text"></div>
</div>
</div>
</div>
</div>
<!-- Footer Info -->
<div style="margin-top: var(--space-16); padding-top: var(--space-6); border-top: 1px solid var(--border-default); text-align: center; color: var(--text-tertiary); font-size: var(--text-sm);">
<p>Last updated: <span id="footer-last-update">Just now</span></p>
</div>
</div>
</main>
</div>
<!-- Toast Container -->
<div id="toast-container" aria-live="polite"></div>
<script type="module">
// Defer non-critical initialization
(async function() {
const initApp = async () => {
try {
// Load layout (header + sidebar)
const { LayoutManager } = await import('/static/shared/js/core/layout-manager.js?v=4.0');
await LayoutManager.init('dashboard');
// Load dashboard module after layout is ready
const { default: dashboardPage } = await import('/static/pages/dashboard/dashboard.js?v=4.0');
window.dashboardPage = dashboardPage;
window.addEventListener('beforeunload', () => {
if (window.dashboardPage) window.dashboardPage.destroy();
});
console.log('✅ Dashboard initialized with Cursor UI');
} catch (error) {
console.error('❌ Failed to initialize dashboard:', error);
// Show error alert
const pageContent = document.querySelector('.page-content');
if (pageContent) {
pageContent.insertAdjacentHTML('afterbegin', `
<div class="alert alert-danger" style="margin-bottom: var(--space-6);">
<svg class="alert-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
<div class="alert-content">
<div class="alert-title">Initialization Error</div>
<div class="alert-message">Failed to load dashboard. Please refresh the page.</div>
</div>
</div>
`);
}
}
};
// Use requestIdleCallback if available, otherwise setTimeout
if (window.requestIdleCallback) {
window.requestIdleCallback(initApp, { timeout: 2000 });
} else {
setTimeout(initApp, 100);
}
})();
</script>
</body>
</html>
|