// ═══════════════════════════════════════════════════════════════════ // PROGRAMS PAGE — Detail per program with stats and province breakdown // ═══════════════════════════════════════════════════════════════════ let _activeProgTab = 'mbg'; function renderProgramsPage() { renderProgTabs(); renderProgBody(_activeProgTab); } function renderProgTabs() { document.getElementById('prog-tabs').innerHTML = PROGRAMS.map(p => ` `).join(''); } function switchProgTab(id) { _activeProgTab = id; renderProgTabs(); renderProgBody(id); } function renderProgBody(pid) { const p = getProgramById(pid); if (!p) return; const pct = Math.round((p.capaian / p.target) * 100); const budgetPct = Math.round((p.realisasi / p.budget) * 100); const activeIssues = APP_STATE.issues.filter(i => i.programId === pid && i.status !== 'resolved' && i.status !== 'closed'); const critCount = activeIssues.filter(i => i.severity === 'critical').length; const highCount = activeIssues.filter(i => i.severity === 'high').length; // Location data for this program const progLocs = APP_STATE.locs.filter(l => l.programs.includes(pid)); // Generate province stats const provinceData = {}; progLocs.forEach(l => { const prov = l.province; if (!provinceData[prov]) provinceData[prov] = { locs: 0, issues: 0 }; provinceData[prov].locs++; provinceData[prov].issues += l.issueCount; }); const provinceRows = Object.entries(provinceData) .sort((a,b) => b[1].issues - a[1].issues) .slice(0, 10) .map(([prov, data]) => { const locList = progLocs.filter(l => l.province === prov); let prog = 90; // default coverage if (pid === 'mbg') prog = Math.round(locList.reduce((s,l) => s + (l.mbg?.coverage || 0), 0) / locList.length); else if (pid === 'koperasi') prog = 75; return `