This commit is contained in:
2026-06-22 08:47:13 +07:00
parent a6f8551ab8
commit f518d7d184
7 changed files with 346 additions and 263 deletions

View File

@@ -93,7 +93,8 @@ function renderTrend(valueId, changeId, current, previous, suffix = '') {
function getSubtypeColor(label, index = 0) {
const specialColors = {
'A/H5N1': '#dc2626'
'A/H5N1': '#dc2626',
'Influenza': '#b90c00'
};
return specialColors[label]
@@ -131,7 +132,10 @@ function renderDashboard(data = {}) {
'doughnut',
(data.pathogen_distribution || [])
.sort((a, b) => b.total - a.total),
'pathogen'
'pathogen',
'total',
getSubtypeColor
);
buildDistributionChart(
@@ -254,6 +258,8 @@ function normalizeProvince(name, validSet) {
return match || null;
}
window.normalizeProvince = normalizeProvince;
function renderProvinceHeatmap(rows = []) {
window.latestProvinceData = rows;
@@ -264,7 +270,7 @@ function renderProvinceHeatmap(rows = []) {
map = L.map('provinceMap')
.setView([12.7, 104.9], 7);
window.map = map;
L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
@@ -593,7 +599,7 @@ function renderProgramTrend(rows = []) {
}),
color: '#1976D2'
color: '#d21919'
},
{
@@ -722,20 +728,33 @@ function renderProgramTrend(rows = []) {
);
}
function renderAFITrend(
rows = [],
data = {},
canvasId,
COLORS,
type = 'trend'
) {
/*
|--------------------------------------------------------------------------
| DATA
|--------------------------------------------------------------------------
*/
const rows =
data.rows || [];
const totals =
data.totals || [];
/*
|--------------------------------------------------------------------------
| EMPTY
|--------------------------------------------------------------------------
*/
if (!rows.length) {
if (!totals.length) {
if (type === 'donut') {
@@ -761,6 +780,57 @@ function renderAFITrend(
return;
}
/*
|--------------------------------------------------------------------------
| LABELS
|--------------------------------------------------------------------------
*/
const labels = [...new Set(
totals.map(r =>
`${r.year}-W${r.period}`
)
)].sort((a, b) => {
const [yearA, weekA] =
a.split('-W').map(Number);
const [yearB, weekB] =
b.split('-W').map(Number);
if (yearA !== yearB) {
return yearA - yearB;
}
return weekA - weekB;
});
/*
|--------------------------------------------------------------------------
| TOTAL CASES (BLUE BARS)
|--------------------------------------------------------------------------
*/
const totalCases = labels.map(label => {
const row = totals.find(r =>
`${r.year}-W${r.period}`
=== label
);
return Number(
row?.total_cases || 0
);
});
/*
|--------------------------------------------------------------------------
| DONUT
@@ -800,16 +870,26 @@ function renderAFITrend(
'total'
);
if (charts[canvasId]) {
/*
|--------------------------------------------------------------------------
| DONUT CENTER TOTAL
|--------------------------------------------------------------------------
|
| MUST MATCH SUM OF BLUE BARS
|
*/
const totalCases = rows.reduce(
(sum, r) =>
sum + Number(r.total_cases || 0),
const donutTotal =
totalCases.reduce(
(a, b) => a + b,
0
);
if (charts[canvasId]) {
charts[canvasId].$afiTotalCases =
totalCases;
donutTotal;
charts[canvasId].update();
@@ -818,90 +898,6 @@ function renderAFITrend(
return;
}
/*
|--------------------------------------------------------------------------
| YEARLY VIEW
|--------------------------------------------------------------------------
*/
const years = [...new Set(
rows.map(r => Number(r.year))
)];
const totalYears =
Math.max(...years) - Math.min(...years);
const useYearlyView =
totalYears >= 5;
/*
|--------------------------------------------------------------------------
| LABELS
|--------------------------------------------------------------------------
*/
let labels;
if (useYearlyView) {
labels = [...new Set(
rows.map(r => String(r.year))
)].sort();
} else {
labels = [...new Set(
rows.map(r =>
`${r.year}-W${r.period}`
)
)].sort((a, b) => {
const [yearA, weekA] =
a.split('-W').map(Number);
const [yearB, weekB] =
b.split('-W').map(Number);
if (yearA !== yearB) {
return yearA - yearB;
}
return weekA - weekB;
});
}
/*
|--------------------------------------------------------------------------
| TOTAL CASES
|--------------------------------------------------------------------------
*/
const totalCases = labels.map(label => {
if (useYearlyView) {
return rows
.filter(r =>
String(r.year) === label
)
.reduce(
(sum, r) =>
sum + Number(r.total_cases || 0),
0
);
}
const row = rows.find(r =>
`${r.year}-W${r.period}` === label
);
return row?.total_cases || 0;
});
/*
|--------------------------------------------------------------------------
| PATHOGENS
@@ -929,26 +925,6 @@ function renderAFITrend(
data: labels.map(label => {
if (useYearlyView) {
const filtered = rows.filter(r =>
String(r.year) === label &&
r.pathogen === pathogen
);
const total =
filtered.reduce(
(sum, r) =>
sum + Number(r.positivity_rate || 0),
0
);
return filtered.length
? total / filtered.length
: 0;
}
const row = rows.find(r =>
`${r.year}-W${r.period}`
@@ -958,7 +934,9 @@ function renderAFITrend(
);
return row?.positivity_rate || 0;
return Number(
row?.positivity_rate || 0
);
}),
@@ -970,6 +948,12 @@ function renderAFITrend(
})
);
/*
|--------------------------------------------------------------------------
| BUILD
|--------------------------------------------------------------------------
*/
buildMixedTrendChart(
canvasId,
labels,
@@ -978,6 +962,8 @@ function renderAFITrend(
);
}
function renderPathogenChart(rows = []) {
buildDistributionChart(
'pathogenChart',