let sequencingChart; document.addEventListener("DOMContentLoaded", () => { const canvas = document.getElementById('sequencingChart'); if (!canvas) return; new DashboardFilter((startYear, startWeek, endYear, endWeek) => { fetch(`/api/dashboard/sequencing?surveillance_id=${window.SURVEILLANCE_ID}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) .then(res => res.json()) .then(data => { renderSequencingChart(data.trend || []); renderSequencingCountChart(data.trend || []); renderSequencingPieChart(data.distribution || []); renderSequencingTotalChart(data.trend || []); }); }); }); function renderSequencingCountChart(rows) { const ctx = document.getElementById('sequencingCountChart'); Chart.getChart('sequencingCountChart')?.destroy(); rows = processTopSubtypes(rows); const weeks = [...new Set(rows.map(r => r.period))]; const subtypes = [...new Set(rows.map(r => r.subtype))]; const colors = [ '#2563eb', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#14b8a6', '#f97316', '#84cc16' ]; const datasets = subtypes.map((sub, i) => ({ label: sub, data: weeks.map(w => { const found = rows.find(r => r.period === w && r.subtype === sub); return found ? found.total : 0; }), backgroundColor: colors[i % colors.length] })); new Chart(ctx, { type: 'bar', data: { labels: weeks.map(w => `W${w}`), datasets }, options: { maintainAspectRatio: false, scales: { x: { stacked: true }, y: { stacked: true } }, plugins: { legend: { display: true, position: 'bottom', align: 'center' } } } }); } function renderSequencingPieChart(rows) { const ctx = document.getElementById('sequencingPieChart'); Chart.getChart('sequencingPieChart')?.destroy(); const top = rows.slice(0, 8); const labels = top.map(r => r.subtype); const values = top.map(r => r.total); new Chart(ctx, { type: 'doughnut', data: { labels, datasets: [{ data: values }] }, options: { maintainAspectRatio: false, plugins: { legend: { position: 'bottom' }, } } }); } function renderSequencingTotalChart(rows) { const ctx = document.getElementById('sequencingTotalChart'); Chart.getChart('sequencingTotalChart')?.destroy(); const totals = {}; rows.forEach(r => { totals[r.period] = (totals[r.period] || 0) + Number(r.total); }); const weeks = Object.keys(totals); const values = Object.values(totals); new Chart(ctx, { type: 'bar', data: { labels: weeks.map(w => `W${w}`), datasets: [{ label: 'Total Samples', data: values, backgroundColor: '#0B8F3C' }] }, options: { maintainAspectRatio: false, plugins: { legend: { display: false } }, } }); } function processTopSubtypes(rows) { const totals = {}; rows.forEach(r => { totals[r.subtype] = (totals[r.subtype] || 0) + Number(r.total); }); const sorted = Object.entries(totals) .sort((a, b) => b[1] - a[1]); const top = sorted.slice(0, 8).map(([k]) => k); return rows.map(r => { if (!top.includes(r.subtype)) { return { ...r, subtype: 'Others' }; } return r; }); } function renderSequencingChart(rows) { rows = processTopSubtypes(rows); const ctx = document.getElementById('sequencingChart'); if (sequencingChart) { sequencingChart.destroy(); } const colors = [ '#2563eb', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#14b8a6', '#f97316', '#84cc16', '#6b7280' ]; const aggregated = {}; rows.forEach(r => { const key = `${r.period}_${r.subtype}`; if (!aggregated[key]) { aggregated[key] = { ...r }; } else { aggregated[key].total += Number(r.total); } }); const cleanRows = Object.values(aggregated); const weeks = [...new Set(cleanRows.map(r => r.period))]; const subtypes = [...new Set(cleanRows.map(r => r.subtype))] .sort((a, b) => { const sum = s => cleanRows .filter(r => r.subtype === s) .reduce((t, r) => t + r.total, 0); return sum(b) - sum(a); }); const datasets = subtypes.map((sub, i) => { return { label: sub, data: weeks.map(w => { const weekRows = cleanRows.filter(r => r.period === w); const total = weekRows.reduce((s, r) => s + Number(r.total), 0); const found = weekRows.find(r => r.subtype === sub); return total ? ((found?.total || 0) / total) * 100 : 0; }), fill: true, tension: 0.3, backgroundColor: colors[i % colors.length], borderColor: colors[i % colors.length] }; }); sequencingChart = new Chart(ctx, { type: 'line', data: { labels: weeks.map(w => `W${w}`), datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'bottom', align: 'center' }, datalabels: { display: false } }, scales: { x: { stacked: true }, y: { stacked: true, max: 100, ticks: { callback: v => v + '%' } } } } }); }