70 lines
4.5 KiB
JavaScript
70 lines
4.5 KiB
JavaScript
const Dashboard = (() => {
|
|
async function load() {
|
|
try {
|
|
const d = await Api.get('/dashboard');
|
|
const s = d.statistik;
|
|
sv2('d-miskin', s.total_miskin); sv2('d-rentan', s.total_rentan);
|
|
sv2('d-mampu', s.total_mampu); sv2('d-kk', s.total_kk);
|
|
sv2('d-belum', d.belum_dibantu); sv2('d-darurat', s.laporan_pending);
|
|
sv2('hp-miskin-n', s.total_miskin); sv2('hp-rentan-n', s.total_rentan);
|
|
sv2('hp-mampu-n', s.total_mampu); sv2('hp-lap-n', s.laporan_pending);
|
|
ge('dash-updated').textContent = 'Update: ' + new Date().toLocaleTimeString('id-ID');
|
|
renderPrio(d.prioritas_darurat);
|
|
renderBar('d-kec-chart', d.sebaran_kecamatan, r => ({ label: r.kecamatan||'?', val: +r.miskin+ +r.rentan, color:'#ff4d6d', sub:`${r.miskin} miskin` }));
|
|
renderBar('d-ri-chart', d.beban_per_ri, r => ({ label: r.nama, val: +r.kk_miskin, color:'#4361ee', sub:`${r.jumlah_kk} KK` }));
|
|
renderBar('d-trend-chart', d.trend_bantuan, r => ({ label: r.bulan, val: +r.jumlah, color:'#06d6a0', sub:`${r.jumlah}x` }));
|
|
} catch { loadDemo(); }
|
|
}
|
|
|
|
function loadDemo() {
|
|
sv2('d-miskin',6); sv2('d-rentan',4); sv2('d-mampu',2);
|
|
sv2('d-kk',8); sv2('d-belum',4); sv2('d-darurat',2);
|
|
sv2('hp-miskin-n',6); sv2('hp-rentan-n',4); sv2('hp-mampu-n',2); sv2('hp-lap-n',2);
|
|
ge('dash-updated').textContent = 'Data demo (offline)';
|
|
renderPrio([
|
|
{ nama_lengkap:'Maria Magdalena', umur:54, kondisi_kesehatan:'sakit_berat', kecamatan:'Pontianak Selatan', jumlah_dibantu:1, level_prioritas:'SANGAT TINGGI', nik:'6171010101010006' },
|
|
{ nama_lengkap:'Tan Eng Huat', umur:65, kondisi_kesehatan:'disabilitas', kecamatan:'Pontianak Kota', jumlah_dibantu:1, level_prioritas:'SANGAT TINGGI', nik:'6171010101010014' },
|
|
{ nama_lengkap:'Lim Ah Kow', umur:59, kondisi_kesehatan:'sakit_berat', kecamatan:'Pontianak Kota', jumlah_dibantu:1, level_prioritas:'TINGGI', nik:'6171010101010010' },
|
|
]);
|
|
renderBar('d-kec-chart',[{kecamatan:'Pontianak Timur',miskin:2,rentan:1},{kecamatan:'Pontianak Selatan',miskin:2,rentan:2},{kecamatan:'Pontianak Kota',miskin:2,rentan:1}],
|
|
r=>({label:r.kecamatan,val:+r.miskin+ +r.rentan,color:'#ff4d6d',sub:`${r.miskin} miskin`}));
|
|
renderBar('d-ri-chart',[{nama:"Masjid Jami'",jumlah_kk:3,kk_miskin:2},{nama:'Katedral',jumlah_kk:2,kk_miskin:2},{nama:'Mujahidin',jumlah_kk:1,kk_miskin:1}],
|
|
r=>({label:r.nama,val:+r.kk_miskin,color:'#4361ee',sub:`${r.jumlah_kk} KK`}));
|
|
renderBar('d-trend-chart',[{bulan:'2025-01',jumlah:2},{bulan:'2025-02',jumlah:1},{bulan:'2025-03',jumlah:3},{bulan:'2025-04',jumlah:1}],
|
|
r=>({label:r.bulan,val:+r.jumlah,color:'#06d6a0',sub:`${r.jumlah}x`}));
|
|
}
|
|
|
|
function renderPrio(list) {
|
|
const el = ge('d-prio-list');
|
|
if (!list?.length) { el.innerHTML = '<div class="empty-state"><div class="es-icon">✅</div><p>Tidak ada kasus darurat</p></div>'; return; }
|
|
const cls = { 'SANGAT TINGGI':'pb-sangat-tinggi','TINGGI':'pb-tinggi','SEDANG':'pb-sedang','RENDAH':'pb-rendah' };
|
|
el.innerHTML = list.map(p => `
|
|
<div class="prio-item" onclick="Bantuan.quickSalur('${p.nik}','${p.nama_lengkap}')">
|
|
<span class="prio-badge ${cls[p.level_prioritas]||'pb-rendah'}">${p.level_prioritas}</span>
|
|
<div style="flex:1;min-width:0">
|
|
<div style="font-weight:700;font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${p.nama_lengkap}</div>
|
|
<div style="font-size:11px;color:var(--muted)">${cap(p.kondisi_kesehatan)} · ${p.kecamatan} · dibantu ${p.jumlah_dibantu}x</div>
|
|
</div>
|
|
<span style="font-size:18px;flex-shrink:0">→</span>
|
|
</div>`).join('');
|
|
}
|
|
|
|
function renderBar(elId, data, mapper) {
|
|
const el = ge(elId);
|
|
if (!data?.length) { el.innerHTML = '<p style="color:var(--muted);font-size:12px;padding:12px">Tidak ada data</p>'; return; }
|
|
const mapped = data.map(mapper);
|
|
const max = Math.max(...mapped.map(m => m.val), 1);
|
|
el.innerHTML = mapped.map(m => {
|
|
const pct = Math.round(m.val / max * 100);
|
|
return `<div class="bar-row">
|
|
<div class="bar-lbl" title="${m.label}">${m.label.slice(0,16)}${m.label.length>16?'…':''}</div>
|
|
<div class="bar-track"><div class="bar-fill" style="width:${pct}%;background:${m.color}"></div></div>
|
|
<div class="bar-val">${m.val}<span style="font-size:9px;color:var(--muted);margin-left:3px">${m.sub?.replace(/^\d+ /,'')}</span></div>
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function sv2(id, val) { const el = ge(id); if (el) el.textContent = val ?? '—'; }
|
|
return { load };
|
|
})();
|