import { COLORS, SUBTYPE_COLORS } from "./globals.js"; let sequencingTotalChart; let covidLineageFrequencyChart; let influenzaSubtypeFrequencyChart; document.addEventListener("DOMContentLoaded", () => { 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 => { renderSequencingTotalChart(data.trend || []); }); loadCovidLineageFrequency('week', startYear, startWeek, endYear, endWeek) loadInfluenzaSubtypeFrequency('week', startYear, startWeek, endYear, endWeek) const elements = document.querySelectorAll(".report-period"); elements.forEach(el => { el.textContent = 'Week ' + startWeek + ' of ' + startYear + ' to ' + 'Week ' + endWeek + ' of ' + endYear }); }); }); function renderSequencingTotalChart(rows) { const ctx = document.getElementById('sequencingTotalChart')?.getContext('2d'); if (!ctx) return; if (sequencingTotalChart) 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); sequencingTotalChart = new Chart(ctx, { type: 'bar', data: { labels: weeks.map(w => `${w}`), datasets: [{ label: 'Total Samples', data: values, backgroundColor: '#0B8F3C' }] }, options: { maintainAspectRatio: false, plugins: { legend: { display: false }, datalabels: { display: false } }, } }); charts['sequencingTotalChart'] = sequencingTotalChart; } function hexToRGBA(hex, alpha) { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } function loadCovidLineageFrequency(periodType, startYear, startWeek, endYear, endWeek) { fetch(`/api/dashboard/covid-lineage-frequency?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) .then(res => res.json()) .then(data => { // const weeks = [...new Set(data.map(item => item.week))].sort(); const totalYears = endYear - startYear; const useYearlyView = totalYears >= 5; let periods; if (useYearlyView) { periods = [...new Set( data.map(item => item.week.split('-')[0]) )].sort((a, b) => a - b); } else { periods = [...new Set( data.map(item => item.week) )].sort(); } const lineages = [...new Set(data.map(item => item.lineage))]; const datasets = lineages.map((lineage, index) => { // const lineageData = weeks.map(week => { // const found = data.find( // item => item.week === week && item.lineage === lineage // ); // return found ? found.total : 0; // }); const lineageData = periods.map(period => { if (useYearlyView) { return data .filter(item => item.week.split('-')[0] == period && item.lineage === lineage ) .reduce((sum, item) => sum + item.total, 0); } const found = data.find( item => item.week === period && item.lineage === lineage ); return found ? found.total : 0; }); return { label: lineage, data: lineageData, fill: true, tension: 0.4, // borderColor: 'transparent', borderWidth: 0, pointRadius: 0, backgroundColor: hexToRGBA(COLORS[index % COLORS.length], 0.3), stack: 'total' }; }); if (covidLineageFrequencyChart) covidLineageFrequencyChart.destroy(); const ctx = document.getElementById('covidLineageFrequency').getContext('2d'); covidLineageFrequencyChart = new Chart(ctx, { type: 'line', data: { labels: periods, datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false }, datalabels: { display: false } }, scales: { x: { stacked: true, grid: { display: false } }, y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Relative Frequency' }, } } } }); charts['covidLineageFrequency'] = covidLineageFrequencyChart; // ------------------------- // Custom right-side scrollable legend // ------------------------- const legendContainer = document.getElementById('legendContainer'); legendContainer.innerHTML = ''; datasets.forEach((dataset, index) => { const item = document.createElement('div'); item.style.display = 'flex'; item.style.alignItems = 'center'; item.style.marginBottom = '4px'; item.style.fontSize = '11px'; item.style.cursor = 'pointer'; item.innerHTML = ` ${dataset.label} `; item.addEventListener('click', () => { const meta = covidLineageFrequencyChart.getDatasetMeta(index); const allHidden = datasets.every((d, i) => covidLineageFrequencyChart.getDatasetMeta(i).hidden || i === index); if (!allHidden) { datasets.forEach((d, i) => { covidLineageFrequencyChart.getDatasetMeta(i).hidden = true; }); meta.hidden = false; } else { datasets.forEach((d, i) => { covidLineageFrequencyChart.getDatasetMeta(i).hidden = false; }); } covidLineageFrequencyChart.update(); Array.from(legendContainer.children).forEach((child, i) => { const metaItem = covidLineageFrequencyChart.getDatasetMeta(i); child.style.opacity = metaItem.hidden ? 0.5 : 1; }); }); legendContainer.appendChild(item); }); legendContainer.style.maxHeight = '375px'; legendContainer.style.overflowY = 'auto'; legendContainer.style.padding = '8px'; legendContainer.style.borderRadius = '0px'; }); } function loadInfluenzaSubtypeFrequency(periodType, startYear, startWeek, endYear, endWeek) { fetch(`/api/dashboard/influenza-relative-frequency-sequencing?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) .then(res => res.json()) .then(data => { const totalYears = endYear - startYear; const useYearlyView = totalYears >= 5; let periods; if (useYearlyView) { periods = [...new Set( data.map(item => item.week.split('-')[0]) )].sort((a, b) => a - b); } else { periods = [...new Set( data.map(item => item.week) )].sort(); } const lineages = [...new Set( data.map(item => item.lineage) )]; const lineageColors = lineages.map( label => SUBTYPE_COLORS[label] || '#9ca3af' ); const datasets = lineages.map((lineage, index) => { const lineageData = periods.map(period => { if (useYearlyView) { return data .filter(item => item.week.split('-')[0] == period && item.lineage === lineage ) .reduce((sum, item) => sum + item.total, 0); } const found = data.find( item => item.week === period && item.lineage === lineage ); return found ? found.total : 0; }); return { label: lineage, data: lineageData, fill: true, tension: 0.4, borderWidth: 0, pointRadius: 0, backgroundColor: hexToRGBA( lineageColors[index % lineageColors.length], 0.6 ), stack: 'total' }; }); if (influenzaSubtypeFrequencyChart) { influenzaSubtypeFrequencyChart.destroy(); } const ctx = document .getElementById('influenzaSubtypeFrequency') .getContext('2d'); influenzaSubtypeFrequencyChart = new Chart(ctx, { type: 'line', data: { labels: periods, datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false }, datalabels: { display: false } }, scales: { x: { stacked: true, grid: { display: false } }, y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Relative Frequency' } } } } }); charts['influenzaSubtypeFrequency'] = influenzaSubtypeFrequencyChart; /* |-------------------------------------------------------------------------- | CUSTOM LEGEND |-------------------------------------------------------------------------- */ const legendContainer = document.getElementById( 'legendContainerInfluenzaSubtypeFrequency' ); legendContainer.innerHTML = ''; datasets.forEach((dataset, index) => { const item = document.createElement('div'); item.style.display = 'flex'; item.style.alignItems = 'center'; item.style.marginBottom = '4px'; item.style.fontSize = '11px'; item.style.cursor = 'pointer'; item.innerHTML = ` ${dataset.label} `; item.addEventListener('click', () => { const meta = influenzaSubtypeFrequencyChart .getDatasetMeta(index); const allHidden = datasets.every( (d, i) => influenzaSubtypeFrequencyChart .getDatasetMeta(i).hidden || i === index ); if (!allHidden) { datasets.forEach((d, i) => { influenzaSubtypeFrequencyChart .getDatasetMeta(i) .hidden = true; }); meta.hidden = false; } else { datasets.forEach((d, i) => { influenzaSubtypeFrequencyChart .getDatasetMeta(i) .hidden = false; }); } influenzaSubtypeFrequencyChart.update(); Array.from(legendContainer.children) .forEach((child, i) => { const metaItem = influenzaSubtypeFrequencyChart .getDatasetMeta(i); child.style.opacity = metaItem.hidden ? 0.5 : 1; }); }); legendContainer.appendChild(item); }); legendContainer.style.maxHeight = '375px'; legendContainer.style.overflowY = 'auto'; legendContainer.style.padding = '8px'; legendContainer.style.borderRadius = '0px'; }); } // function loadCovidLineageFrequency(periodType, startYear, startWeek, endYear, endWeek) { // fetch(`/api/dashboard/covid-lineage-frequency?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) // .then(res => res.json()) // .then(data => { // const weeks = [...new Set(data.map(item => item.week))].sort(); // const lineages = [...new Set(data.map(item => item.lineage))]; // const datasets = lineages.map((lineage, index) => { // const lineageData = weeks.map(week => { // const found = data.find( // item => item.week === week && item.lineage === lineage // ); // return found ? found.total : 0; // }); // return { // label: lineage, // data: lineageData, // fill: true, // tension: 0.4, // borderColor: 'transparent', // borderWidth: 0, // pointRadius: 0, // backgroundColor: hexToRGBA(colors[index % colors.length], 0.3), // stack: 'total' // }; // }); // if (covidLineageFrequencyChart) covidLineageFrequencyChart.destroy(); // const ctx = document.getElementById('covidLineageFrequency').getContext('2d'); // if (!ctx) return; // covidLineageFrequencyChart = new Chart(ctx, { // type: 'line', // data: { // labels: weeks, // datasets: datasets // }, // options: { // responsive: true, // maintainAspectRatio: false, // interaction: { // mode: 'index', // intersect: false // }, // plugins: { // legend: { // display: false // hide default legend // }, // tooltip: { // mode: 'index', // intersect: false // }, // datalabels: { // display: false // hide labels // } // }, // scales: { // x: { // stacked: true, // title: { // display: true, // text: 'Week' // }, // grid: { // display: false // } // }, // y: { // stacked: true, // beginAtZero: true, // title: { // display: true, // text: 'Relative Frequency' // }, // } // } // } // }); // charts['covidLineageFrequencyChart'] = covidLineageFrequencyChart; // // ------------------------- // // Custom right-side scrollable legend // // ------------------------- // const legendContainer = document.getElementById('legendContainer'); // legendContainer.innerHTML = ''; // datasets.forEach((dataset, index) => { // const item = document.createElement('div'); // item.style.display = 'flex'; // item.style.alignItems = 'center'; // item.style.marginBottom = '4px'; // item.style.fontSize = '11px'; // item.style.cursor = 'pointer'; // item.innerHTML = ` // // ${dataset.label} // `; // item.addEventListener('click', () => { // const meta = covidLineageFrequencyChart.getDatasetMeta(index); // const allHidden = datasets.every((d, i) => covidLineageFrequencyChart.getDatasetMeta(i).hidden || i === index); // if (!allHidden) { // datasets.forEach((d, i) => { // covidLineageFrequencyChart.getDatasetMeta(i).hidden = true; // }); // meta.hidden = false; // } else { // datasets.forEach((d, i) => { // covidLineageFrequencyChart.getDatasetMeta(i).hidden = false; // }); // } // covidLineageFrequencyChart.update(); // Array.from(legendContainer.children).forEach((child, i) => { // const metaItem = covidLineageFrequencyChart.getDatasetMeta(i); // child.style.opacity = metaItem.hidden ? 0.5 : 1; // }); // }); // legendContainer.appendChild(item); // }); // legendContainer.style.maxHeight = '375px'; // legendContainer.style.overflowY = 'auto'; // legendContainer.style.padding = '8px'; // legendContainer.style.borderRadius = '0px'; // }); // } // function loadInfluenzaSubtypeFrequency(periodType, startYear, startWeek, endYear, endWeek) { // fetch(`/api/dashboard/influenza-relative-frequency-sequencing?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`) // .then(res => res.json()) // .then(data => { // const weeks = [...new Set(data.map(item => item.week))].sort(); // const lineages = [...new Set(data.map(item => item.lineage))]; // const datasets = lineages.map((lineage, index) => { // const lineageData = weeks.map(week => { // const found = data.find( // item => item.week === week && item.lineage === lineage // ); // return found ? found.total : 0; // }); // return { // label: lineage, // data: lineageData, // fill: true, // tension: 0.4, // borderColor: 'transparent', // borderWidth: 0, // pointRadius: 0, // backgroundColor: hexToRGBA(colors[(index * 2) % colors.length], 0.8), // stack: 'total' // }; // }); // if (influenzaSubtypeFrequencyChart) influenzaSubtypeFrequencyChart.destroy(); // const ctx = document.getElementById('influenzaSubtypeFrequency').getContext('2d'); // if (!ctx) return; // influenzaSubtypeFrequencyChart = new Chart(ctx, { // type: 'line', // data: { // labels: weeks, // datasets: datasets // }, // options: { // responsive: true, // maintainAspectRatio: false, // interaction: { // mode: 'index', // intersect: false // }, // plugins: { // legend: { // display: false // }, // tooltip: { // mode: 'index', // intersect: false // }, // datalabels: { // display: false // } // }, // scales: { // x: { // stacked: true, // title: { // display: true, // text: 'Week' // }, // grid: { // display: false // } // }, // y: { // stacked: true, // beginAtZero: true, // title: { // display: true, // text: 'Relative Frequency' // }, // } // } // } // }); // charts['influenzaSubtypeFrequencyChart'] = influenzaSubtypeFrequencyChart; // const legendContainer = document.getElementById('legendContainerInfluenzaSubtypeFrequency'); // legendContainer.innerHTML = ''; // datasets.forEach((dataset, index) => { // const item = document.createElement('div'); // item.style.display = 'flex'; // item.style.alignItems = 'center'; // item.style.marginBottom = '4px'; // item.style.fontSize = '11px'; // item.style.cursor = 'pointer'; // item.innerHTML = ` // // ${dataset.label} // `; // item.addEventListener('click', () => { // const meta = influenzaSubtypeFrequencyChart.getDatasetMeta(index); // const allHidden = datasets.every((d, i) => influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden || i === index); // if (!allHidden) { // datasets.forEach((d, i) => { // influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = true; // }); // meta.hidden = false; // } else { // datasets.forEach((d, i) => { // influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = false; // }); // } // influenzaSubtypeFrequencyChart.update(); // Array.from(legendContainer.children).forEach((child, i) => { // const metaItem = influenzaSubtypeFrequencyChart.getDatasetMeta(i); // child.style.opacity = metaItem.hidden ? 0.5 : 1; // }); // }); // legendContainer.appendChild(item); // }); // legendContainer.style.maxHeight = '375px'; // legendContainer.style.overflowY = 'auto'; // legendContainer.style.padding = '8px'; // legendContainer.style.borderRadius = '0px'; // }); // }