// Initialize charts let userChart, revenueChart, performanceChart, activityChart; document.addEventListener('DOMContentLoaded', () => { setupCharts(); startDataUpdates(); }); function setupCharts() { const chartOptions = { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000' } } }, scales: { y: { beginAtZero: true, grid: { color: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)' }, ticks: { color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000' } }, x: { grid: { color: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)' }, ticks: { color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000' } } } }; // User Growth Chart const userCtx = document.getElementById('userChart').getContext('2d'); userChart = new Chart(userCtx, { type: 'line', data: { labels: Array.from({length: 12}, (_, i) => `${i+1}AM`), datasets: [{ label: 'Active Users', data: Array(12).fill(0).map((_,i) => { const hourFactor = Math.sin((i / 11) * Math.PI) * 0.5 + 0.7; return Math.floor(3000 + (hourFactor * 2000) + Math.random() * 500 - 250); }), borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.1)', tension: 0.4, fill: true }] }, options: chartOptions }); // Revenue Chart const revenueCtx = document.getElementById('revenueChart').getContext('2d'); revenueChart = new Chart(revenueCtx, { type: 'doughnut', data: { labels: ['Subscriptions', 'Ads', 'Merchandise', 'Services'], datasets: [{ data: [35, 25, 20, 20].map(v => v + Math.floor(Math.random() * 10 - 5)), backgroundColor: [ '#6366f1', '#8b5cf6', '#ec4899', '#f43f5e' ], borderWidth: 0 }] }, options: { ...chartOptions, cutout: '70%' } }); // Performance Chart const performanceCtx = document.getElementById('performanceChart').getContext('2d'); performanceChart = new Chart(performanceCtx, { type: 'bar', data: { labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], datasets: [ { label: 'Page Load (ms)', data: Array(7).fill(0).map(() => 100 + Math.floor(Math.random() * 300)), backgroundColor: '#6366f1' }, { label: 'API Response (ms)', data: Array(7).fill(0).map(() => 50 + Math.floor(Math.random() * 150)), backgroundColor: '#8b5cf6' } ] }, options: chartOptions }); // Activity Chart const activityCtx = document.getElementById('activityChart').getContext('2d'); activityChart = new Chart(activityCtx, { type: 'line', data: { labels: Array.from({length: 24}, (_, i) => i), datasets: [ { label: 'API Calls', data: Array(24).fill(0).map((_, i) => { const isDaytime = i >= 9 && i < 18; const base = isDaytime ? 800 : 300; const hourFactor = Math.sin((i / 24) * Math.PI * 1.5) * (isDaytime ? 400 : 150); const noise = Math.random() * 150 - 75; return Math.max(100, base + hourFactor + noise); }), borderColor: '#6366f1', backgroundColor: 'rgba(99, 102, 241, 0.1)', tension: 0.3, fill: true } ] }, options: chartOptions }); } function startDataUpdates() { // Update metric cards every 3 seconds setInterval(() => { const cards = document.querySelectorAll('metrics-card'); const now = new Date(); const hour = now.getHours(); // Active Users - follows daily pattern with peak around 2PM const userBase = hour < 6 ? 2000 : hour < 12 ? 4000 : hour < 18 ? 6000 : 3000; const users = userBase + Math.floor(Math.random() * 500 - 250); const userChange = (Math.random() > 0.6 ? 1 : -1) * (0.5 + Math.random() * 2); cards[0].setAttribute('value', users.toLocaleString()); cards[0].setAttribute('change', userChange.toFixed(1)); // Conversion Rate - higher during business hours const conversionBase = hour >= 9 && hour < 17 ? 3.5 : 1.8; const conversion = (conversionBase + Math.random() * 0.8 - 0.4).toFixed(2); const conversionChange = (Math.random() * 0.4 - 0.2).toFixed(2); cards[1].setAttribute('value', conversion + '%'); cards[1].setAttribute('change', conversionChange); // API Latency - degrades slightly during peak hours const latencyBase = hour >= 10 && hour < 16 ? 80 : 40; const latency = Math.floor(latencyBase + Math.random() * 30); const latencyChange = (Math.random() > 0.3 ? 1 : -1) * (1 + Math.random() * 4); cards[2].setAttribute('value', latency + 'ms'); cards[2].setAttribute('change', latencyChange.toFixed(1)); // Revenue Today - follows similar pattern to users but with larger random fluctuations const revenueBase = hour < 6 ? 8000 : hour < 12 ? 12000 : hour < 18 ? 15000 : 9000; const revenue = revenueBase + Math.floor(Math.random() * 3000 - 1500); const revenueChange = (Math.random() > 0.5 ? 1 : -1) * (2 + Math.random() * 5); cards[3].setAttribute('value', '// Update charts with new data points updateCharts(); }, 3000); } function updateCharts() { // User Chart - follows daily pattern with realistic variations const userData = userChart.data.datasets[0].data; userData.shift(); const userTrend = hour < 6 ? 0.8 : hour < 12 ? 1.2 : hour < 18 ? 1.5 : 0.9; userData.push(Math.max(0, userData[userData.length-1] * (0.98 + Math.random() * 0.04) * userTrend)); userChart.update(); // Revenue Chart - more stable proportions with slight variations revenueChart.data.datasets[0].data = [30, 25, 25, 20].map(v => v * (0.95 + Math.random() * 0.1)); revenueChart.update(); // Performance Chart - weekday patterns with better/worse performance const weekday = now.getDay(); const isWeekday = weekday > 0 && weekday < 6; performanceChart.data.datasets[0].data = Array(7).fill(0).map((_,i) => isWeekday ? 80 + Math.floor(Math.random() * 120) : 60 + Math.floor(Math.random() * 80)); performanceChart.data.datasets[1].data = Array(7).fill(0).map((_,i) => isWeekday ? 40 + Math.floor(Math.random() * 70) : 30 + Math.floor(Math.random() * 40)); performanceChart.update(); // Activity Chart - more realistic API call patterns const activityData = activityChart.data.datasets[0].data; activityData.shift(); const activityTrend = hour < 6 ? 0.7 : hour < 9 ? 0.9 : hour < 17 ? 1.3 : 1.0; const newActivity = Math.max(100, activityData[activityData.length-1] * (0.97 + Math.random() * 0.06) * activityTrend); activityData.push(newActivity); activityChart.update(); } // Handle dark mode changes window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { document.documentElement.classList.toggle('dark', e.matches); setupCharts(); }); + revenue.toLocaleString()); cards[3].setAttribute('change', revenueChange.toFixed(1)); // Update charts with new data points updateCharts(); }, 3000); } function updateCharts() { // User Chart - add new point and remove oldest const userData = userChart.data.datasets[0].data; userData.shift(); userData.push(Math.floor(Math.random() * 5000)); userChart.update(); // Revenue Chart - update values with slight variations revenueChart.data.datasets[0].data = [35, 25, 20, 20].map(v => v + Math.floor(Math.random() * 10 - 5)); revenueChart.update(); // Performance Chart - update with new random values performanceChart.data.datasets[0].data = Array(7).fill(0).map(() => 100 + Math.floor(Math.random() * 300)); performanceChart.data.datasets[1].data = Array(7).fill(0).map(() => 50 + Math.floor(Math.random() * 150)); performanceChart.update(); // Activity Chart - shift data and add new point const activityData = activityChart.data.datasets[0].data; activityData.shift(); const lastValue = activityData[activityData.length - 1]; activityData.push(Math.max(0, lastValue + (Math.random() * 100 - 50))); activityChart.update(); } // Handle dark mode changes window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { document.documentElement.classList.toggle('dark', e.matches); setupCharts(); });