/** * Analytics Logic (Simple CSS Charts) */ const analytics = { mockData: [ { name: 'Mon', requests: 4000, latency: 240 }, { name: 'Tue', requests: 3000, latency: 198 }, { name: 'Wed', requests: 2000, latency: 310 }, { name: 'Thu', requests: 2780, latency: 208 }, { name: 'Fri', requests: 1890, latency: 250 }, { name: 'Sat', requests: 2390, latency: 210 }, { name: 'Sun', requests: 3490, latency: 225 }, ], init: async () => { console.log('📈 Initializing Live Analytics...'); const headers = auth.getAuthHeader(); // 1. Fetch Summary for Success Ratio try { const summaryRes = await fetch('/api/usage/summary', { headers }); if (summaryRes.ok) { const summary = await summaryRes.json(); analytics.renderSuccessRatio(summary.successRate); } } catch (e) { console.error(e); } // 2. Fetch History for Volume Chart const history = await analytics.fetchHistory(); if (history && history.length > 0) { analytics.renderVolumeChart(history); } else { analytics.renderPlaceholder(); } analytics.renderLatencyChart(); }, renderSuccessRatio: (percent) => { const circle = document.getElementById('success-circle'); const display = document.getElementById('success-percent-display'); if (!circle || !display) return; display.textContent = `${percent}%`; // Circumference is 552.9 (2 * PI * 88) const offset = 552.9 - (percent / 100) * 552.9; circle.style.strokeDashoffset = offset; circle.classList.toggle('text-emerald-500', percent >= 95); circle.classList.toggle('text-blue-600', percent < 95); }, fetchHistory: async () => { try { const headers = auth.getAuthHeader(); const res = await fetch('/api/usage/history?days=7', { headers }); if (res.ok) { const raw = await res.json(); // Map raw database records to chart data return raw.map(item => { const date = new Date(item.date); return { name: date.toLocaleDateString('en-US', { weekday: 'short' }), requests: parseInt(item.count, 10) }; }); } } catch (error) { console.error('Failed to fetch analytics history', error); } return null; }, renderVolumeChart: (data) => { const container = document.getElementById('v-chart'); if (!container) return; const max = Math.max(...data.map(d => d.requests), 1); container.innerHTML = data.map((d, i) => `
${d.requests.toLocaleString()}
${d.name}
`).join(''); // Trigger animations after render setTimeout(() => { container.querySelectorAll('.w-full.bg-gradient-to-t').forEach((el, index) => { const height = el.parentElement.parentElement.dataset.height; el.style.height = el.getAttribute('data-target-height'); }); }, 100); }, renderPlaceholder: () => { const container = document.getElementById('v-chart'); if (container) container.innerHTML = `

No activity recorded for this period

`; if (window.lucide) lucide.createIcons(); }, renderLatencyChart: () => { const container = document.getElementById('l-chart'); if (!container) return; const mockLatency = [ { name: 'Mon', latency: 240 }, { name: 'Tue', latency: 198 }, { name: 'Wed', latency: 310 }, { name: 'Thu', latency: 208 }, { name: 'Fri', latency: 250 }, { name: 'Sat', latency: 210 }, { name: 'Sun', latency: 225 }, ]; const max = Math.max(...mockLatency.map(d => d.latency)); container.innerHTML = mockLatency.map((d, i) => `
${d.latency}ms
${d.name}
`).join(''); } };