Files

794 lines
31 KiB
JavaScript

// Globals
let map;
let layerRI, layerPM, layerRadius;
let riData = [];
let pmData = [];
let pickingMode = null; // 'ri' or 'pm'
let tempMarker = null;
let currentView = 'dashboard';
let searchTerm = '';
const rumahIbadahIconConfig = {
Masjid: { icon: 'fa-mosque', className: 'mosque' },
Gereja: { icon: 'fa-church', className: 'church' },
Pura: { icon: 'fa-gopuram', className: 'temple' },
Vihara: { icon: 'fa-vihara', className: 'vihara' },
Klenteng: { icon: 'fa-torii-gate', className: 'klenteng' },
Lainnya: { icon: 'fa-place-of-worship', className: 'other' }
};
function getRumahIbadahIcon(jenis) {
const config = rumahIbadahIconConfig[jenis] || rumahIbadahIconConfig.Lainnya;
return L.divIcon({
className: 'ri-div-icon',
html: `<div class="ri-marker ri-${config.className}" title="${jenis || 'Rumah Ibadah'}"><i class="fas ${config.icon}"></i></div>`,
iconSize: [36, 46],
iconAnchor: [18, 43],
popupAnchor: [0, -43]
});
}
// PM Icons are now circleMarkers generated inline in renderPM
document.addEventListener('DOMContentLoaded', async () => {
await initMap();
initUI();
loadData();
});
async function initMap() {
map = L.map('map', {
zoomControl: false,
preferCanvas: true
}).setView([-0.060547, 109.344902], 13);
window.map = map;
L.control.zoom({ position: 'bottomright' }).addTo(map);
const tileOptions = {
keepBuffer: 6,
updateWhenIdle: false,
updateWhenZooming: false,
updateInterval: 100
};
const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
...tileOptions,
attribution: '&copy; OpenStreetMap contributors'
});
const satelit = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
...tileOptions,
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
});
const darkMap = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
...tileOptions,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
subdomains: 'abcd',
maxZoom: 20
});
osm.addTo(map);
const baseMaps = {
"OSM Standard": osm,
"Citra Satelit": satelit,
"Dark Mode": darkMap
};
layerRadius = L.featureGroup().addTo(map);
layerRI = L.featureGroup().addTo(map);
layerPM = L.featureGroup().addTo(map);
const overlays = {
"Radius Layanan": layerRadius,
"Rumah Ibadah": layerRI,
"Penduduk Miskin": layerPM
};
L.control.layers(baseMaps, overlays, { position: 'topright' }).addTo(map);
// Map Click for Picking Location
map.on('click', async (e) => {
if (!pickingMode) return;
const lat = e.latlng.lat;
const lng = e.latlng.lng;
if (tempMarker) map.removeLayer(tempMarker);
tempMarker = L.marker([lat, lng]).addTo(map);
if (pickingMode === 'ri') {
document.getElementById('ri-lat').value = lat.toFixed(6);
document.getElementById('ri-lng').value = lng.toFixed(6);
document.getElementById('ri-alamat').value = 'Mencari alamat...';
document.getElementById('ri-alamat').value = await reverseGeocode(lat, lng);
document.getElementById('modal-ri').classList.add('show');
} else if (pickingMode === 'pm') {
document.getElementById('pm-lat').value = lat.toFixed(6);
document.getElementById('pm-lng').value = lng.toFixed(6);
document.getElementById('pm-alamat').value = 'Mencari alamat...';
document.getElementById('pm-alamat').value = await reverseGeocode(lat, lng);
document.getElementById('modal-pm').classList.add('show');
}
pickingMode = null;
if(tempMarker) map.removeLayer(tempMarker);
});
}
function initUI() {
// Sidebar Toggle
document.getElementById('toggle-sidebar').addEventListener('click', () => {
const sidebar = document.getElementById('sidebar');
if (window.innerWidth <= 768) {
sidebar.classList.toggle('open');
} else {
sidebar.classList.toggle('collapsed');
}
});
document.querySelectorAll('.menu-item[data-view]').forEach(item => {
item.addEventListener('click', () => switchView(item.dataset.view));
});
// Menus
const openAddRI = () => {
switchView('map');
document.getElementById('form-ri').reset();
document.getElementById('ri-id').value = '';
document.getElementById('modal-ri-title').innerText = 'Tambah Rumah Ibadah';
document.getElementById('modal-ri').classList.add('show');
};
document.getElementById('menu-add-ri').addEventListener('click', openAddRI);
document.getElementById('fab-add-ri')?.addEventListener('click', openAddRI);
const openAddPM = () => {
switchView('map');
document.getElementById('form-pm').reset();
document.getElementById('pm-id').value = '';
document.getElementById('anggota-container').innerHTML = '';
document.getElementById('bantuan-fields').style.display = 'none';
document.getElementById('modal-pm-title').innerText = 'Tambah Penduduk Miskin';
document.getElementById('modal-pm').classList.add('show');
};
document.getElementById('menu-add-pm').addEventListener('click', openAddPM);
document.getElementById('fab-add-pm')?.addEventListener('click', openAddPM);
document.getElementById('dashboard-add-ri')?.addEventListener('click', openAddRI);
document.getElementById('dashboard-add-pm')?.addEventListener('click', openAddPM);
document.getElementById('dashboard-open-map')?.addEventListener('click', () => switchView('map'));
// Filters
document.getElementById('filter-status').addEventListener('change', renderMapLayers);
document.getElementById('filter-jenis').addEventListener('change', renderMapLayers);
document.getElementById('filter-kebutuhan').addEventListener('change', renderPM);
// Search
document.getElementById('search-input').addEventListener('input', () => {
searchTerm = getSearchTerm();
renderMapLayers();
});
document.getElementById('search-input').addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
applyMapSearch(true);
}
});
document.getElementById('search-button').addEventListener('click', () => applyMapSearch(true));
// Location
document.getElementById('btn-my-location').addEventListener('click', () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
map.flyTo([lat, lng], 16);
L.marker([lat, lng]).addTo(map).bindPopup('Lokasi Anda').openPopup();
},
(err) => alert('Gagal mendapatkan lokasi: ' + err.message)
);
} else {
alert('Geolocation tidak didukung browser ini.');
}
});
switchView(currentView);
}
function switchView(view) {
currentView = view;
const mainContent = document.querySelector('.main-content');
document.querySelectorAll('.view-panel').forEach(panel => {
panel.classList.toggle('active', panel.id === `${view}-view`);
});
document.querySelectorAll('.menu-item[data-view]').forEach(item => {
item.classList.toggle('active', item.dataset.view === view);
});
mainContent?.classList.toggle('map-mode', view === 'map');
mainContent?.classList.toggle('dashboard-mode', view !== 'map');
document.querySelector('.search-bar')?.classList.toggle('hidden', view !== 'map');
document.querySelector('.map-header-controls')?.classList.toggle('hidden', view !== 'map');
if (window.innerWidth <= 768) {
document.getElementById('sidebar').classList.remove('open');
}
if (view === 'map' && map) {
refreshMapSize();
}
}
function refreshMapSize() {
[0, 100, 300].forEach(delay => {
setTimeout(() => map.invalidateSize({ pan: false }), delay);
});
}
function getSearchTerm() {
return document.getElementById('search-input').value.trim().toLowerCase();
}
function textIncludes(value, term) {
return String(value || '').toLowerCase().includes(term);
}
function matchesRI(ri) {
if (!searchTerm) return true;
return [
ri.nama,
ri.jenis,
ri.kontak,
ri.alamat
].some(value => textIncludes(value, searchTerm));
}
function matchesPM(pm) {
if (!searchTerm) return true;
const anggotaMatch = Array.isArray(pm.anggota) && pm.anggota.some(anggota => [
anggota.nama,
anggota.hubungan,
anggota.pekerjaan
].some(value => textIncludes(value, searchTerm)));
return anggotaMatch || [
pm.kk_nama,
pm.kk_nik,
pm.alamat,
pm.status_bantuan,
pm.jenis_bantuan,
pm.kategori_kebutuhan,
pm.deskripsi_kebutuhan,
pm.tindak_lanjut,
pm.kondisi_rumah,
pm.rumah_ibadah_nama
].some(value => textIncludes(value, searchTerm));
}
function getFilteredRI() {
const filterJenis = document.getElementById('filter-jenis').value;
return riData.filter(ri => {
if (filterJenis !== 'all' && ri.jenis !== filterJenis) return false;
return matchesRI(ri);
});
}
function getFilteredPM() {
const filterStatus = document.getElementById('filter-status').value;
const filterKebutuhan = document.getElementById('filter-kebutuhan').value;
return pmData.filter(pm => {
if (filterStatus !== 'all' && pm.status_bantuan !== filterStatus) return false;
if (filterKebutuhan !== 'all' && pm.kategori_kebutuhan !== filterKebutuhan) return false;
return matchesPM(pm);
});
}
function renderMapLayers() {
renderRI();
renderPM();
}
function applyMapSearch(focusResult = false) {
searchTerm = getSearchTerm();
renderMapLayers();
if (focusResult) {
switchView('map');
focusSearchResults();
}
}
function focusSearchResults() {
const results = [
...getFilteredRI().map(item => [parseFloat(item.lat), parseFloat(item.lng)]),
...getFilteredPM().map(item => [parseFloat(item.lat), parseFloat(item.lng)])
].filter(([lat, lng]) => Number.isFinite(lat) && Number.isFinite(lng));
if (!searchTerm || results.length === 0) {
alert(searchTerm ? 'Data tidak ditemukan di peta.' : 'Masukkan kata kunci pencarian.');
return;
}
if (results.length === 1) {
map.flyTo(results[0], 16);
return;
}
map.fitBounds(L.latLngBounds(results), { padding: [40, 40], maxZoom: 16 });
}
function pickLocation(mode) {
switchView('map');
pickingMode = mode;
closeModal('modal-' + mode);
}
function closeModal(id) {
document.getElementById(id).classList.remove('show');
}
async function reverseGeocode(lat, lng) {
try {
const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`);
const data = await res.json();
return data.display_name || 'Alamat tidak ditemukan';
} catch (e) {
return 'Gagal memuat alamat';
}
}
function toggleBantuanFields() {
const status = document.getElementById('pm-status').value;
document.getElementById('bantuan-fields').style.display = status === 'Sudah dibantu' ? 'block' : 'none';
}
function addAnggotaField(data = null) {
const container = document.getElementById('anggota-container');
const id = Date.now() + Math.floor(Math.random() * 100);
const html = `
<div class="dynamic-item" id="anggota-${id}">
<button type="button" class="btn-remove" onclick="document.getElementById('anggota-${id}').remove()">X</button>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<div class="form-group">
<label>Nama</label>
<input type="text" class="form-control a-nama" value="${data ? data.nama : ''}" required>
</div>
<div class="form-group">
<label>Hubungan</label>
<input type="text" class="form-control a-hubungan" value="${data ? data.hubungan : ''}" placeholder="Anak/Istri/dll" required>
</div>
<div class="form-group">
<label>Umur</label>
<input type="number" class="form-control a-umur" value="${data ? data.umur : ''}">
</div>
<div class="form-group">
<label>Pekerjaan</label>
<input type="text" class="form-control a-pekerjaan" value="${data && data.pekerjaan ? data.pekerjaan : ''}">
</div>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
}
// Data Fetching and Rendering
async function loadData() {
await loadStats();
await fetchRI();
await fetchPM();
}
async function loadStats() {
try {
const res = await fetch('api/dashboard_stats.php');
const json = await res.json();
if(json.success) {
document.getElementById('stat-ri').innerText = json.data.total_rumah_ibadah;
document.getElementById('stat-pm').innerText = json.data.total_penduduk;
document.getElementById('stat-sudah').innerText = json.data.sudah_dibantu;
document.getElementById('stat-belum').innerText = json.data.belum_dibantu;
document.getElementById('stat-menunggu').innerText = json.data.menunggu_verifikasi || 0;
document.getElementById('stat-prioritas').innerText = json.data.prioritas_tinggi || 0;
document.getElementById('stat-luar-radius').innerText = json.data.luar_radius || 0;
document.getElementById('summary-sudah').innerText = json.data.sudah_dibantu;
document.getElementById('summary-belum').innerText = json.data.belum_dibantu;
document.getElementById('summary-menunggu').innerText = json.data.menunggu_verifikasi || 0;
renderDashboardLists(json.data);
}
} catch(e) { console.error(e); }
}
function renderDashboardLists(stats) {
const priorityList = document.getElementById('priority-list');
const needList = document.getElementById('need-list');
if (priorityList) {
const items = stats.top_prioritas || [];
priorityList.innerHTML = items.length ? items.map(item => `
<div class="priority-item">
<div class="priority-score">${item.skor_prioritas || 0}</div>
<div>
<strong>${item.kk_nama}</strong>
<p>${item.kategori_kebutuhan || 'Kebutuhan belum diisi'} - ${item.tindak_lanjut || 'Belum disurvei'}</p>
</div>
</div>
`).join('') : '<div class="need-item"><p>Belum ada data prioritas.</p></div>';
}
if (needList) {
const items = stats.kebutuhan_terbanyak || [];
needList.innerHTML = items.length ? items.map(item => `
<div class="need-item">
<strong>${item.kategori}</strong>
<p>${item.total} keluarga membutuhkan kategori ini</p>
</div>
`).join('') : '<div class="need-item"><p>Belum ada data kebutuhan.</p></div>';
}
}
async function fetchRI() {
try {
const res = await fetch('api/rumah_ibadah_read.php');
const json = await res.json();
if(json.success) {
riData = json.data;
renderRI();
}
} catch(e) { console.error(e); }
}
async function fetchPM() {
try {
const res = await fetch('api/penduduk_read.php');
const json = await res.json();
if(json.success) {
pmData = json.data;
renderPM();
}
} catch(e) { console.error(e); }
}
function renderRI() {
layerRI.clearLayers();
layerRadius.clearLayers();
getFilteredRI().forEach(ri => {
const lat = parseFloat(ri.lat);
const lng = parseFloat(ri.lng);
const rad = parseInt(ri.radius) || 500;
// Circle
L.circle([lat, lng], {
color: '#4f46e5',
fillColor: '#4f46e5',
fillOpacity: 0.1,
radius: rad,
interactive: false
}).addTo(layerRadius);
// Marker
const marker = L.marker([lat, lng], { icon: getRumahIbadahIcon(ri.jenis) }).addTo(layerRI);
marker.feature = ri;
let popup = `
<div class="popup-header">
<h3>${ri.nama}</h3>
<p>${ri.jenis}</p>
</div>
<div class="popup-body">
<div class="popup-row"><div class="label">Kontak</div><div class="value">${ri.kontak || '-'}</div></div>
<div class="popup-row"><div class="label">Radius</div><div class="value">${rad} m</div></div>
<div class="popup-row" style="flex-direction: column;"><div class="label" style="width:100%">Alamat</div><div class="value" style="font-size:0.8rem; font-weight:400">${ri.alamat}</div></div>
<div class="popup-actions">
<button class="btn-edit" onclick='editRI(${JSON.stringify(ri)})'>Edit</button>
<button class="btn-delete" onclick='deleteRI(${ri.id})'>Hapus</button>
</div>
</div>
`;
marker.bindPopup(popup);
});
layerRadius.eachLayer(circle => circle.bringToBack());
}
function renderPM() {
layerPM.clearLayers();
getFilteredPM().forEach(pm => {
const lat = parseFloat(pm.lat);
const lng = parseFloat(pm.lng);
const anggotaCount = pm.anggota ? pm.anggota.length : 0;
let badgeClass = 'badge-danger';
if(pm.status_bantuan === 'Sudah dibantu') badgeClass = 'badge-success';
else if(pm.status_bantuan === 'Menunggu verifikasi') badgeClass = 'badge-warning';
const isBlankSpot = pm.is_blank_spot == 1;
const pmColor = isBlankSpot ? '#ef4444' : '#10b981'; // Merah (Blank Spot), Hijau (Terjangkau)
const riwayatHtml = Array.isArray(pm.riwayat_bantuan) && pm.riwayat_bantuan.length
? pm.riwayat_bantuan.slice(0, 3).map(r => `${r.tanggal_bantuan}: ${r.jenis_bantuan}`).join('<br>')
: '-';
const buktiHtml = pm.bukti_file
? `<a href="${pm.bukti_file}" target="_blank">Lihat bukti</a>`
: '-';
const marker = L.circleMarker([lat, lng], {
radius: 7,
fillColor: pmColor,
color: '#ffffff',
weight: 1.5,
opacity: 1,
fillOpacity: 0.9
}).addTo(layerPM);
marker.feature = pm;
let popup = `
<div class="popup-header ${pm.status_bantuan === 'Sudah dibantu' ? 'success' : pm.status_bantuan === 'Menunggu verifikasi' ? 'warning' : 'danger'}">
<h3>Keluarga ${pm.kk_nama}</h3>
<span class="badge ${badgeClass}">${pm.status_bantuan}</span>
</div>
<div class="popup-body">
<div class="popup-row"><div class="label">Jumlah Anggota</div><div class="value">${anggotaCount} orang</div></div>
<div class="popup-row"><div class="label">Rumah Ibadah Terdekat</div><div class="value">${pm.rumah_ibadah_nama || '-'}</div></div>
<div class="popup-row"><div class="label">Prioritas</div><div class="value">${pm.skor_prioritas || 0} (${pm.label_prioritas || '-'})</div></div>
<div class="popup-row"><div class="label">Kebutuhan</div><div class="value">${pm.kategori_kebutuhan || '-'}</div></div>
<div class="popup-row"><div class="label">Tindak Lanjut</div><div class="value">${pm.tindak_lanjut || 'Belum disurvei'}</div></div>
<div class="popup-row"><div class="label">Jarak Lurus</div><div class="value">${pm.jarak_m ? pm.jarak_m + ' meter' : '-'} ${isBlankSpot ? '<i style="color:red">(Di luar radius)</i>' : '<i style="color:green">(Terjangkau)</i>'}</div></div>
<div class="popup-row"><div class="label">Jarak Rute</div><div class="value">${pm.jarak_rute_m ? pm.jarak_rute_m + ' meter' : '-'}</div></div>
${pm.status_bantuan === 'Sudah dibantu' ? `<div class="popup-row"><div class="label">Bantuan</div><div class="value">${pm.jenis_bantuan} (${pm.tanggal_bantuan})</div></div>` : ''}
<div class="popup-row"><div class="label">Riwayat</div><div class="value">${riwayatHtml}</div></div>
<div class="popup-row"><div class="label">Bukti</div><div class="value">${buktiHtml}</div></div>
<div class="popup-row" style="flex-direction: column;"><div class="label" style="width:100%">Alamat</div><div class="value" style="font-size:0.8rem; font-weight:400">${pm.alamat}</div></div>
<div class="popup-actions">
<button class="btn-edit" onclick='editPM(${JSON.stringify(pm).replace(/'/g, "\\'")})'>Edit</button>
<button class="btn-edit" onclick='openBantuanModal(${pm.id})'>Bantuan</button>
<button class="btn-delete" onclick='deletePM(${pm.id})'>Hapus</button>
</div>
</div>
`;
marker.bindPopup(popup);
});
layerPM.bringToFront();
}
// CRUD Operations
async function saveRI() {
const id = document.getElementById('ri-id').value;
const data = {
nama: document.getElementById('ri-nama').value,
jenis: document.getElementById('ri-jenis').value,
kontak: document.getElementById('ri-kontak').value,
lat: document.getElementById('ri-lat').value,
lng: document.getElementById('ri-lng').value,
alamat: document.getElementById('ri-alamat').value,
radius: document.getElementById('ri-radius').value
};
if(!data.nama || !data.lat) { alert("Isi form dengan lengkap"); return; }
const url = id ? 'api/rumah_ibadah_update.php' : 'api/rumah_ibadah_create.php';
if(id) data.id = id;
try {
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify(data)
});
const json = await res.json();
if(json.success) {
closeModal('modal-ri');
await fetchRI();
await fetchPM();
await loadStats();
} else {
alert(json.message);
}
} catch(e) { alert('Error saving data'); }
}
async function savePM() {
const id = document.getElementById('pm-id').value;
const data = new FormData();
const anggota = [];
data.append('kk_nama', document.getElementById('pm-nama').value);
data.append('kk_nik', document.getElementById('pm-nik').value);
data.append('lat', document.getElementById('pm-lat').value);
data.append('lng', document.getElementById('pm-lng').value);
data.append('alamat', document.getElementById('pm-alamat').value);
data.append('kategori_kebutuhan', document.getElementById('pm-kategori-kebutuhan').value);
data.append('deskripsi_kebutuhan', document.getElementById('pm-deskripsi-kebutuhan').value);
data.append('kondisi_rumah', document.getElementById('pm-kondisi-rumah').value);
data.append('penghasilan', document.getElementById('pm-penghasilan').value);
data.append('status_bantuan', document.getElementById('pm-status').value);
data.append('jenis_bantuan', document.getElementById('pm-jenis-bantuan').value);
data.append('tanggal_bantuan', document.getElementById('pm-tanggal-bantuan').value);
data.append('tindak_lanjut', document.getElementById('pm-tindak-lanjut').value);
data.append('catatan_survei', document.getElementById('pm-catatan-survei').value);
data.append('existing_bukti_file', document.getElementById('pm-existing-bukti-file').value);
const buktiFile = document.getElementById('pm-bukti-file').files[0];
if (buktiFile) data.append('bukti_file', buktiFile);
if(!data.get('kk_nama') || !data.get('lat')) { alert("Isi form dengan lengkap"); return; }
if (data.get('penghasilan') !== '' && Number(data.get('penghasilan')) < 0) {
alert('Penghasilan tidak boleh bernilai negatif.');
document.getElementById('pm-penghasilan').focus();
return;
}
const items = document.querySelectorAll('.dynamic-item');
items.forEach(el => {
anggota.push({
nama: el.querySelector('.a-nama').value,
hubungan: el.querySelector('.a-hubungan').value,
umur: el.querySelector('.a-umur').value,
pekerjaan: el.querySelector('.a-pekerjaan').value
});
});
data.append('anggota', JSON.stringify(anggota));
const url = id ? 'api/penduduk_update.php' : 'api/penduduk_create.php';
if(id) data.append('id', id);
try {
const res = await fetch(url, {
method: 'POST',
body: data
});
const json = await res.json();
if(json.success) {
closeModal('modal-pm');
await fetchPM();
await loadStats();
if(json.ibadah_id) {
const ri = riData.find(r => r.id == json.ibadah_id);
if(ri) alert(`Warga otomatis dimasukkan ke binaan: ${ri.nama}`);
}
} else {
alert(json.message);
}
} catch(e) { alert('Error saving data'); }
}
function editRI(data) {
document.getElementById('ri-id').value = data.id;
document.getElementById('ri-nama').value = data.nama;
document.getElementById('ri-jenis').value = data.jenis;
document.getElementById('ri-kontak').value = data.kontak;
document.getElementById('ri-lat').value = data.lat;
document.getElementById('ri-lng').value = data.lng;
document.getElementById('ri-alamat').value = data.alamat;
document.getElementById('ri-radius').value = data.radius;
document.getElementById('modal-ri-title').innerText = 'Edit Rumah Ibadah';
document.getElementById('modal-ri').classList.add('show');
}
function editPM(data) {
document.getElementById('pm-id').value = data.id;
document.getElementById('pm-nama').value = data.kk_nama;
document.getElementById('pm-nik').value = data.kk_nik;
document.getElementById('pm-lat').value = data.lat;
document.getElementById('pm-lng').value = data.lng;
document.getElementById('pm-alamat').value = data.alamat;
document.getElementById('pm-kategori-kebutuhan').value = data.kategori_kebutuhan || '';
document.getElementById('pm-deskripsi-kebutuhan').value = data.deskripsi_kebutuhan || '';
document.getElementById('pm-kondisi-rumah').value = data.kondisi_rumah || '';
document.getElementById('pm-penghasilan').value = data.penghasilan || '';
document.getElementById('pm-status').value = data.status_bantuan;
document.getElementById('pm-jenis-bantuan').value = data.jenis_bantuan;
document.getElementById('pm-tanggal-bantuan').value = data.tanggal_bantuan;
document.getElementById('pm-tindak-lanjut').value = data.tindak_lanjut || 'Belum disurvei';
document.getElementById('pm-catatan-survei').value = data.catatan_survei || '';
document.getElementById('pm-existing-bukti-file').value = data.bukti_file || '';
document.getElementById('pm-bukti-file').value = '';
toggleBantuanFields();
document.getElementById('anggota-container').innerHTML = '';
if(data.anggota && data.anggota.length > 0) {
data.anggota.forEach(a => addAnggotaField(a));
}
document.getElementById('modal-pm-title').innerText = 'Edit Penduduk Miskin';
document.getElementById('modal-pm').classList.add('show');
}
function setBantuanAlert(message = '', type = 'error') {
const alertBox = document.getElementById('bantuan-alert');
if (!alertBox) return;
alertBox.textContent = message;
alertBox.hidden = !message;
alertBox.classList.toggle('success', type === 'success');
}
function openBantuanModal(id) {
const penerima = pmData.find(pm => String(pm.id) === String(id));
const form = document.getElementById('form-bantuan');
form.reset();
setBantuanAlert();
document.getElementById('bantuan-penduduk-id').value = id;
document.getElementById('bantuan-penerima').value = penerima ? `Keluarga ${penerima.kk_nama}` : `ID ${id}`;
document.getElementById('bantuan-jenis').value = penerima?.kategori_kebutuhan || '';
document.getElementById('bantuan-tanggal').value = new Date().toISOString().slice(0, 10);
document.getElementById('bantuan-pemberi').value = 'Admin';
document.getElementById('bantuan-bukti-file').value = '';
document.getElementById('btn-save-bantuan').disabled = false;
document.getElementById('modal-bantuan').classList.add('show');
}
async function saveBantuan() {
const id = document.getElementById('bantuan-penduduk-id').value;
const jenis = document.getElementById('bantuan-jenis').value.trim();
const tanggal = document.getElementById('bantuan-tanggal').value;
const nilai = document.getElementById('bantuan-nilai').value;
const pemberi = document.getElementById('bantuan-pemberi').value.trim() || 'Admin';
const catatan = document.getElementById('bantuan-catatan').value.trim();
const buktiFile = document.getElementById('bantuan-bukti-file').files[0];
const saveButton = document.getElementById('btn-save-bantuan');
if (!id || !jenis || !tanggal) {
setBantuanAlert('Keluarga penerima, jenis bantuan, dan tanggal wajib diisi.');
return;
}
const formData = new FormData();
formData.append('penduduk_id', id);
formData.append('jenis_bantuan', jenis);
formData.append('tanggal_bantuan', tanggal);
formData.append('nilai_bantuan', nilai || '');
formData.append('pemberi_bantuan', pemberi);
formData.append('catatan', catatan || '');
if (buktiFile) formData.append('bukti_file', buktiFile);
try {
saveButton.disabled = true;
saveButton.innerText = 'Menyimpan...';
const res = await fetch('api/bantuan_create.php', { method: 'POST', body: formData });
const json = await res.json();
if (json.success) {
closeModal('modal-bantuan');
await fetchPM();
await loadStats();
renderMapLayers();
} else {
setBantuanAlert(json.message || 'Gagal menambah bantuan.');
}
} catch(e) {
setBantuanAlert('Terjadi kesalahan saat menyimpan bantuan.');
} finally {
saveButton.disabled = false;
saveButton.innerText = 'Simpan Bantuan';
}
}
async function deleteRI(id) {
if(!confirm('Hapus rumah ibadah ini?')) return;
try {
const res = await fetch('api/rumah_ibadah_delete.php', { method:'POST', body:JSON.stringify({id}) });
const json = await res.json();
if(json.success) { fetchRI(); fetchPM(); loadStats(); }
} catch(e) { alert('Error'); }
}
async function deletePM(id) {
if(!confirm('Hapus penduduk miskin ini?')) return;
try {
const res = await fetch('api/penduduk_delete.php', { method:'POST', body:JSON.stringify({id}) });
const json = await res.json();
if(json.success) { fetchPM(); loadStats(); }
} catch(e) { alert('Error'); }
}