// GigLez - Statistics Dashboard let frequencyChart; let timelineChart; document.addEventListener('DOMContentLoaded', () => { // Load stats when stats section becomes active const statsSection = document.getElementById('stats-section'); const observer = new MutationObserver(() => { if (statsSection.classList.contains('active')) { loadStatistics(); } }); observer.observe(statsSection, { attributes: true }); }); async function loadStatistics() { try { const response = await fetch('/api/v1/stats/summary'); if (!response.ok) { console.error('Failed to load statistics'); return; } const stats = await response.json(); displayStatistics(stats); } catch (error) { console.error('Error loading statistics:', error); displayDefaultStats(); } } function displayStatistics(stats) { // Update stat cards document.getElementById('stat-captures').textContent = formatNumber(stats.total_captures || 0); document.getElementById('stat-devices').textContent = formatNumber(stats.unique_devices || 0); document.getElementById('stat-coverage').textContent = formatCoverage(stats.coverage_area_km2 || 0); document.getElementById('stat-contributors').textContent = formatNumber(stats.total_contributors || 0); // Update charts updateFrequencyChart(stats.frequency_distribution || {}); updateTimelineChart(stats.captures_timeline || []); } function displayDefaultStats() { // Display placeholder stats document.getElementById('stat-captures').textContent = '-'; document.getElementById('stat-devices').textContent = '-'; document.getElementById('stat-coverage').textContent = '-'; document.getElementById('stat-contributors').textContent = '-'; } function updateFrequencyChart(distribution) { const ctx = document.getElementById('frequency-chart').getContext('2d'); // Prepare data const labels = Object.keys(distribution).map(freq => `${(freq / 1e6).toFixed(0)} MHz`); const data = Object.values(distribution); // Destroy existing chart if (frequencyChart) { frequencyChart.destroy(); } // Create chart frequencyChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Number of Captures', data: data, backgroundColor: [ 'rgba(16, 185, 129, 0.7)', // 315 MHz - Green 'rgba(59, 130, 246, 0.7)', // 433 MHz - Blue 'rgba(245, 158, 11, 0.7)', // 868 MHz - Orange 'rgba(239, 68, 68, 0.7)', // 915 MHz - Red ], borderColor: [ 'rgb(16, 185, 129)', 'rgb(59, 130, 246)', 'rgb(245, 158, 11)', 'rgb(239, 68, 68)', ], borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } function updateTimelineChart(timeline) { const ctx = document.getElementById('timeline-chart').getContext('2d'); // Prepare data const labels = timeline.map(item => new Date(item.date).toLocaleDateString()); const data = timeline.map(item => item.count); // Destroy existing chart if (timelineChart) { timelineChart.destroy(); } // Create chart timelineChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'Captures per Day', data: data, borderColor: 'rgb(37, 99, 235)', backgroundColor: 'rgba(37, 99, 235, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } function formatNumber(num) { if (num >= 1000000) { return (num / 1000000).toFixed(1) + 'M'; } if (num >= 1000) { return (num / 1000).toFixed(1) + 'K'; } return num.toString(); } function formatCoverage(km2) { if (km2 === 0) { return 'N/A'; } if (km2 >= 10000) { return (km2 / 10000).toFixed(1) + ' x 10K km²'; } if (km2 >= 1000) { return (km2 / 1000).toFixed(1) + 'K km²'; } return km2.toFixed(0) + ' km²'; }