finalized overview page

This commit is contained in:
2026-03-19 09:20:42 +07:00
parent c2b820fc6d
commit e80cb128bf
9 changed files with 822 additions and 227 deletions

View File

@@ -1,6 +1,7 @@
const standardPrograms = ['SARI', 'ILI', 'LBM'];
const programCode = (window.PROGRAM_CODE || '').trim().toUpperCase();
let map;
let provinceLayer;
document.addEventListener("DOMContentLoaded", () => {
@@ -15,6 +16,115 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
function renderProvinceHeatmap(rows) {
const totals = {};
rows.forEach(r => {
totals[r.site_province_name] = {
total: Number(r.total),
positive: Number(r.positive)
};
});
if (map) {
map.remove();
}
map = L.map('provinceMap').setView([12.7, 104.9], 7);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
addProvinceLegend();
fetch('/geo/cambodia_provinces.geojson')
.then(r => r.json())
.then(geo => {
function getColor(value) {
if (value > 50) return "#b91c1c";
if (value >= 10) return "#ef4444";
if (value > 0) return "#fecaca";
return "#f3f4f600";
}
provinceLayer = L.geoJSON(geo, {
style: feature => {
const province = feature.properties.ADM1_EN;
const value = totals[province]?.total || 0;
return {
color: "#444",
weight: 1,
fillColor: getColor(value),
fillOpacity: 0.7
};
},
onEachFeature: (feature, layer) => {
const province = feature.properties.ADM1_EN;
const total = totals[province]?.total || 0;
const positive = totals[province]?.positive || 0;
const percent = total ? ((positive / total) * 100).toFixed(1) : 0;
console.log(province, total, positive, percent);
layer.bindTooltip(`
${province}<br>
Total: ${total}<br>
Positivity: ${percent}%
`);
}
}).addTo(map);
});
}
function addProvinceLegend() {
const legend = L.control({ position: "bottomright" });
legend.onAdd = function () {
const div = L.DomUtil.create("div", "map-legend");
div.innerHTML = `
<div style="background:white;padding:10px 12px;border-radius:6px;
box-shadow:0 2px 6px rgba(0,0,0,0.2);font-size:12px;">
<div style="font-weight:600;margin-bottom:6px;">Cases</div>
<div style="display:flex;align-items:center;margin-bottom:4px;">
<span style="width:12px;height:12px;background:#b91c1c;
display:inline-block;margin-right:6px;"></span>
> 50
</div>
<div style="display:flex;align-items:center;margin-bottom:4px;">
<span style="width:12px;height:12px;background:#ef4444;
display:inline-block;margin-right:6px;"></span>
10 50
</div>
<div style="display:flex;align-items:center;margin-bottom:4px;">
<span style="width:12px;height:12px;background:#fecaca;
display:inline-block;margin-right:6px;"></span>
1 9
</div>
<div style="display:flex;align-items:center;">
<span style="width:12px;height:12px;background:#f3f4f6;
display:inline-block;margin-right:6px;"></span>
0
</div>
</div>
`;
return div;
};
legend.addTo(map);
}
function renderTrend(valueId, changeId, current, previous, suffix = '') {
const valueEl = document.getElementById(valueId);
@@ -49,16 +159,17 @@ function renderTrend(valueId, changeId, current, previous, suffix = '') {
function renderProgramTrend(rows) {
rows = rows || [];
const labels = rows.map(r => `W${r.period}`);
const samples = rows.map(r => r.total_samples || 0);
const positivity = rows.map(r => r.positivity_rate || 0);
const fluRate = rows.map(r => r.influenza_rate || 0);
const covidRate = rows.map(r => r.covid_rate || 0);
buildMixedTrendChart(
'trendChart',
labels,
samples,
positivity
fluRate,
covidRate
);
}
function renderSummary(summary) {
@@ -117,26 +228,46 @@ function renderSummary(summary) {
);
}
function renderDashboard(data) {
console.log("SUMMARY:", data.summary);
data = data || {};
renderProgramTrend(data.trend || []);
renderSummary(data.summary || {});
buildChart(
'provinceChart',
'bar',
(data.province_distribution || []).map(r => r.site_province_name),
(data.province_distribution || []).map(r => r.total)
);
renderProvinceHeatmap(data.province_distribution || []);
// buildStackedChart(
// "pathogenChart",
// labels,
// [
// {
// label: "Influenza",
// data: influenza,
// backgroundColor: "#2E7D32"
// },
// {
// label: "SARS-CoV-2",
// data: covid,
// backgroundColor: "#A5D6A7"
// }
// ]
// );
const pathogenRows = (data.pathogen_distribution || [])
.sort((a, b) => b.total - a.total);
const colors = [
'#2563eb', '#10b981', '#f59e0b', '#ef4444',
'#8b5cf6', '#14b8a6', '#f97316', '#84cc16'
];
buildChart(
'pathogenChart',
'bar',
(data.pathogen_distribution || []).map(r => r.pathogen_name),
(data.pathogen_distribution || []).map(r => r.total),
'Positive'
'doughnut',
pathogenRows.map(r => r.pathogen),
pathogenRows.map(r => r.total)
);
charts['pathogenChart'].data.datasets[0].backgroundColor = colors;
charts['pathogenChart'].update();
buildChart(
'ageChart',
@@ -144,6 +275,8 @@ function renderDashboard(data) {
(data.age_distribution || []).map(r => r.age_group),
(data.age_distribution || []).map(r => r.total)
);
charts['ageChart'].data.datasets[0].backgroundColor = colors;
charts['ageChart'].update();
buildChart(
'sexChart',
@@ -151,5 +284,24 @@ function renderDashboard(data) {
(data.sex_distribution || []).map(r => r.patient_sex),
(data.sex_distribution || []).map(r => r.total)
);
charts['sexChart'].data.datasets[0].backgroundColor = colors;
charts['sexChart'].update();
buildChart(
'subtypeChart',
'bar',
(data.subtype_distribution || []).map(r => r.subtype),
(data.subtype_distribution || []).map(r => r.total)
);
charts['subtypeChart'].data.datasets[0].backgroundColor = colors;
charts['subtypeChart'].update();
buildChart(
'sentinelChart',
'pie',
(data.sentinel_sites || []).map(r => r.name),
(data.sentinel_sites || []).map(r => r.total)
);
charts['sentinelChart'].data.datasets[0].backgroundColor = colors;
charts['sentinelChart'].update();
}