let aggregatedChart = null; function initAggregatedChart(data) { const container = document.getElementById('aggregatedChart').parentNode; const oldCanvas = document.getElementById('aggregatedChart'); const newCanvas = document.createElement('canvas'); newCanvas.id = 'aggregatedChart'; newCanvas.height = 300; container.replaceChild(newCanvas, oldCanvas); const ctx = newCanvas.getContext('2d'); const labels = data.map(m => { const d = new Date(m.time_bucket); return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'); }); aggregatedChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [ { label: 'CPU', data: data.map(m => parseFloat(m.avg_cpu)), borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', fill: true, tension: 0.3, pointRadius: 1, }, { label: 'RAM', data: data.map(m => parseFloat(m.avg_ram)), borderColor: '#22c55e', backgroundColor: 'rgba(34, 197, 94, 0.1)', fill: true, tension: 0.3, pointRadius: 1, }, { label: 'Disk', data: data.map(m => parseFloat(m.avg_disk)), borderColor: '#8b5cf6', backgroundColor: 'rgba(139, 92, 246, 0.1)', fill: true, tension: 0.3, pointRadius: 1, }, ], }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index', }, plugins: { legend: { labels: { color: '#9ca3af', font: { family: "'Inter', sans-serif" }, boxWidth: 12, padding: 16, }, }, tooltip: { backgroundColor: '#1a1d2b', borderColor: '#2a2d3d', borderWidth: 1, titleColor: '#e1e4ed', bodyColor: '#9ca3af', padding: 10, cornerRadius: 8, callbacks: { label: function(ctx) { return ctx.dataset.label + ': ' + ctx.parsed.y.toFixed(1) + '%'; }, }, }, }, scales: { x: { ticks: { color: '#6b7280', maxTicksLimit: 12, font: { size: 11 }, }, grid: { color: 'rgba(255,255,255,0.04)', }, }, y: { min: 0, max: 100, ticks: { color: '#6b7280', font: { size: 11 }, callback: function(v) { return v + '%'; }, }, grid: { color: 'rgba(255,255,255,0.04)', }, }, }, }, }); } function refreshAggregatedChart() { fetch('/dashboard/chart-data') .then(r => r.json()) .then(response => { if (response.success && response.data) { const data = response.data; const labels = data.map(m => { const d = new Date(m.time_bucket); return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'); }); if (aggregatedChart) { aggregatedChart.data.labels = labels; aggregatedChart.data.datasets[0].data = data.map(m => parseFloat(m.avg_cpu)); aggregatedChart.data.datasets[1].data = data.map(m => parseFloat(m.avg_ram)); aggregatedChart.data.datasets[2].data = data.map(m => parseFloat(m.avg_disk)); aggregatedChart.update('none'); } else if (data.length > 0) { initAggregatedChart(data); } } }); } if (aggregatedData.length > 0) { initAggregatedChart(aggregatedData); } setInterval(refreshAggregatedChart, 30000);