Fix: Convert 03PovertyMapping from submodule to regular folder
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
/* ============================================================
|
||||
app.js — Main app orchestrator with authentication
|
||||
============================================================ */
|
||||
'use strict';
|
||||
|
||||
// Flag to prevent duplicate initialization
|
||||
let isAppInitialized = false;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Prevent duplicate initialization
|
||||
if (isAppInitialized) {
|
||||
console.warn('App already initialized, skipping duplicate call');
|
||||
return;
|
||||
}
|
||||
|
||||
// First check authentication
|
||||
const isAuthed = await checkAuth();
|
||||
|
||||
if (!isAuthed) {
|
||||
// Not logged in, redirect to login
|
||||
window.location.href = 'login.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize UI based on role
|
||||
initUIByRole();
|
||||
|
||||
// Add logout button to sidebar
|
||||
addLogoutButton();
|
||||
|
||||
// Init map
|
||||
initMap();
|
||||
|
||||
// Init UI
|
||||
initNavTabs();
|
||||
initFilters();
|
||||
initFormTabs();
|
||||
initHelpModal();
|
||||
|
||||
// Load data
|
||||
await loadAllData();
|
||||
|
||||
// Nav tab: activate placement modes
|
||||
hookTabPlacementModes();
|
||||
|
||||
isAppInitialized = true;
|
||||
});
|
||||
|
||||
async function refreshCenters() {
|
||||
const r = await ApiCenters.list();
|
||||
if (r.ok && r.data?.success) {
|
||||
State.centers = r.data.data.centers || [];
|
||||
recountCenterHouseholds(); // Hitung ulang setelah dapat data baru
|
||||
renderCenters();
|
||||
updateLayerCounts();
|
||||
}
|
||||
}
|
||||
|
||||
// Override loadAllData untuk memastikan recount
|
||||
async function loadAllData() {
|
||||
showLoading(true);
|
||||
|
||||
const params = {};
|
||||
if (State.povertyFilter) params.poverty_status = State.povertyFilter;
|
||||
if (State.aidFilter) params.aid_status = State.aidFilter;
|
||||
if (State.searchQuery) params.q = State.searchQuery;
|
||||
if (State.conditionFilter) params.house_condition = State.conditionFilter;
|
||||
params.limit = 500;
|
||||
|
||||
const [centersRes, housesRes] = await Promise.all([
|
||||
ApiCenters.list(),
|
||||
ApiHouses.list(params),
|
||||
loadStats()
|
||||
]);
|
||||
|
||||
if (centersRes.ok && centersRes.data?.success) {
|
||||
State.centers = centersRes.data.data.centers || [];
|
||||
}
|
||||
|
||||
if (housesRes.ok && housesRes.data?.success) {
|
||||
State.houses = housesRes.data.data.households || [];
|
||||
}
|
||||
|
||||
recountCenterHouseholds(); // ⭐ PASTIKAN HITUNG ULANG
|
||||
renderCenters();
|
||||
renderHouses();
|
||||
|
||||
showLoading(false);
|
||||
}
|
||||
|
||||
// Export fungsi refresh
|
||||
window.refreshCenters = refreshCenters;
|
||||
|
||||
async function loadCenters() {
|
||||
const r = await ApiCenters.list();
|
||||
if (r.ok && r.data?.success) {
|
||||
State.centers = r.data.data.centers || [];
|
||||
}
|
||||
renderCenters();
|
||||
}
|
||||
|
||||
async function loadHouses() {
|
||||
const params = {};
|
||||
if (State.povertyFilter) params.poverty_status = State.povertyFilter;
|
||||
if (State.aidFilter) params.aid_status = State.aidFilter;
|
||||
if (State.searchQuery) params.q = State.searchQuery;
|
||||
if (State.conditionFilter) params.house_condition = State.conditionFilter;
|
||||
|
||||
const r = await ApiHouses.list({ ...params, limit: 500 });
|
||||
if (r.ok && r.data?.success) {
|
||||
State.houses = r.data.data.households || [];
|
||||
}
|
||||
renderHouses();
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
const r = await ApiStats.overview();
|
||||
if (!r.ok || !r.data?.success) return;
|
||||
|
||||
const d = r.data.data;
|
||||
State.stats = d;
|
||||
|
||||
animateCount('statCenters', d.centers);
|
||||
animateCount('statHouses', d.households);
|
||||
animateCount('statPopulation', d.population);
|
||||
animateCount('statAided', d.aid_received);
|
||||
animateCount('statPending', d.pending_public ?? 0);
|
||||
}
|
||||
|
||||
// Export for public-reports.js
|
||||
window.loadAllData = loadAllData;
|
||||
window.loadStats = loadStats;
|
||||
|
||||
function animateCount(id, target) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
const start = parseInt(el.textContent.replace(/\D/g,'')) || 0;
|
||||
const diff = target - start;
|
||||
const steps = 20;
|
||||
let step = 0;
|
||||
const interval = setInterval(() => {
|
||||
step++;
|
||||
el.textContent = Math.round(start + (diff * step / steps)).toLocaleString('id-ID');
|
||||
if (step >= steps) clearInterval(interval);
|
||||
}, 30);
|
||||
}
|
||||
|
||||
function initNavTabs() {
|
||||
document.querySelectorAll('.nav-tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const key = tab.dataset.tab;
|
||||
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
if (key === 'dashboard') {
|
||||
openDashboard();
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelector('[data-tab="overview"]').classList.add('active');
|
||||
}, 200);
|
||||
} else if (key === 'overview') {
|
||||
cancelPlacementMode();
|
||||
State.activeFilter = 'all';
|
||||
renderAllLayers();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hookTabPlacementModes() {
|
||||
document.querySelector('[data-tab="centers"]')?.addEventListener('click', () => {
|
||||
setPlacementMode('center');
|
||||
showToast('Klik peta untuk menambah tempat ibadah.', 'success', 3000);
|
||||
});
|
||||
|
||||
document.querySelector('[data-tab="houses"]')?.addEventListener('click', () => {
|
||||
setPlacementMode('house');
|
||||
showToast('Klik peta untuk menambah rumah tangga.', 'success', 3000);
|
||||
});
|
||||
}
|
||||
|
||||
function initFilters() {
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
State.activeFilter = btn.dataset.filter;
|
||||
renderAllLayers();
|
||||
});
|
||||
});
|
||||
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', debounce(async (e) => {
|
||||
State.searchQuery = e.target.value.trim();
|
||||
renderAllLayers();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
let helpStep = 1;
|
||||
const helpTotal = 5;
|
||||
|
||||
function initHelpModal() {
|
||||
document.getElementById('helpBtn')?.addEventListener('click', () => {
|
||||
helpStep = 1;
|
||||
updateHelpStep();
|
||||
openModal('helpModal');
|
||||
});
|
||||
}
|
||||
|
||||
function helpNav(dir) {
|
||||
helpStep = Math.min(helpTotal, Math.max(1, helpStep + dir));
|
||||
updateHelpStep();
|
||||
}
|
||||
|
||||
function updateHelpStep() {
|
||||
document.querySelectorAll('.help-step').forEach(s => s.classList.remove('active'));
|
||||
document.querySelector(`.help-step[data-step="${helpStep}"]`)?.classList.add('active');
|
||||
document.getElementById('helpProgress').textContent = helpStep + ' / ' + helpTotal;
|
||||
document.getElementById('helpPrev').disabled = helpStep === 1;
|
||||
document.getElementById('helpNext').disabled = helpStep === helpTotal;
|
||||
if (helpStep === helpTotal) {
|
||||
document.getElementById('helpNext').textContent = 'Selesai';
|
||||
document.getElementById('helpNext').onclick = () => closeModal('helpModal');
|
||||
} else {
|
||||
document.getElementById('helpNext').textContent = 'Berikutnya →';
|
||||
document.getElementById('helpNext').onclick = () => helpNav(1);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
cancelPlacementMode();
|
||||
document.querySelectorAll('.modal-overlay').forEach(m => {
|
||||
if (m.style.display === 'flex') {
|
||||
m.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'r') {
|
||||
e.preventDefault();
|
||||
loadAllData().then(() => showToast('Data diperbarui.', 'success'));
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user