Files
Webgis/01/js/main.js
T
2026-06-10 21:59:59 +07:00

912 lines
37 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ========================================
// FILE: js/main.js
// Main JavaScript untuk Pemetaan Kemiskinan
// ========================================
// ========== KONFIGURASI ==========
const MAP_CENTER = [-0.0612, 109.3449]; // Koordinat pusat peta (Pontianak)
const MAP_ZOOM = 12; // Tingkat perbesaran bawaan peta
const API_PATH = 'php/'; // Jalur relatif menuju berkas API PHP
let map;
const markers = {
masyarakat: [],
ibadah: [],
spbu: []
};
const layers = {
jalan: L.layerGroup(),
polygon: L.layerGroup()
};
let current_tab = 'masyarakat';
const all_data = {
masyarakat: [],
ibadah: [],
jalan: [],
polygon: [],
spbu: []
};
let drawnItems;
// ========== ICON MARKER ==========
const iconMasyarakat = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
shadowSize: [41, 41]
});
const iconIbadah = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
shadowSize: [41, 41]
});
// ========== UTILITAS KEAMANAN & FORMAT ==========
/**
* Mengamankan string teks dari potensi serangan injeksi XSS.
* @param {string} str - String mentah yang akan dibersihkan.
* @returns {string} String yang aman untuk direndering ke DOM.
*/
function escapeHTML(str) {
if (str === null || str === undefined) return '-';
return String(str)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Melakukan validasi dasar terhadap respons HTTP Fetch sebelum diproses menjadi JSON.
* @param {Response} response - Objek respons dari Fetch API.
* @returns {Promise} Mengembalikan Promise berupa objek JSON jika sukses.
*/
function handleFetchResponse(response) {
if (!response.ok) {
throw new Error(`Gagal memuat data dari server (Status: ${response.status})`);
}
return response.json();
}
// ========== INISIALISASI ==========
document.addEventListener('DOMContentLoaded', function() {
initMap();
loadData();
attachEventListeners();
// Check initial active tab from landing page
const activeTab = localStorage.getItem('activeTab');
const accs = document.querySelectorAll('.accordion');
if (activeTab === 'fasilitas') {
if (accs.length > 0) {
accs[0].style.display = 'flex'; // Show Fasilitas
if (accs.length > 1) accs[1].style.display = 'none'; // Hide Sosial
if (accs.length > 2) accs[2].style.display = 'none'; // Hide Publik
accs[0].classList.add('active');
accs[0].nextElementSibling.style.display = 'block';
const firstTab = accs[0].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
} else if (activeTab === 'sosial') {
if (accs.length > 1) {
if (accs.length > 0) accs[0].style.display = 'none'; // Hide Fasilitas
accs[1].style.display = 'flex'; // Show Sosial
if (accs.length > 2) accs[2].style.display = 'none'; // Hide Publik
accs[1].classList.add('active');
accs[1].nextElementSibling.style.display = 'block';
const firstTab = accs[1].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
} else if (activeTab === 'publik') {
// Mode Publik: Sembunyikan semua tombol Add dan Form Input
document.querySelectorAll('.btn-primary').forEach(btn => {
if(btn.textContent.includes('+')) btn.style.display = 'none';
});
document.querySelector('.leaflet-draw').style.display = 'none'; // Sembunyikan toolbar Leaflet Draw
// Buka Layanan Publik (Accordion 3)
if (accs.length > 0) accs[0].style.display = 'none';
if (accs.length > 1) accs[1].style.display = 'none';
if (accs.length > 2) {
accs[2].style.display = 'flex';
accs[2].classList.add('active');
accs[2].nextElementSibling.style.display = 'block';
const firstTab = accs[2].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
// Tambahkan badge Portal Publik di sidebar jika belum ada
if (!document.getElementById('badge-publik')) {
const sidebar = document.getElementById('sidebar');
const badge = document.createElement('div');
badge.id = 'badge-publik';
badge.innerHTML = '<div style="background:#2563eb;color:white;padding:10px;text-align:center;border-radius:5px;margin-bottom:15px;font-weight:bold;">Mode Akses Publik</div>';
sidebar.insertBefore(badge, sidebar.children[2]);
}
}
});
/**
* Menginisialisasi objek peta Leaflet dan menambahkan event listener klik.
*/
function initMap() {
map = L.map('map').setView(MAP_CENTER, MAP_ZOOM);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
// LEAFLET DRAW
drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawnItems },
draw: {
polyline: true,
polygon: true,
circle: false,
marker: false,
circlemarker: false,
rectangle: false
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
const type = e.layerType;
const layer = e.layer;
const geojson = JSON.stringify(layer.toGeoJSON());
if (type === 'polyline') {
openModal('modal-form-jalan');
setTimeout(() => { document.getElementById('geojson-jalan').value = geojson; }, 100);
} else if (type === 'polygon') {
openModal('modal-form-polygon');
setTimeout(() => { document.getElementById('geojson-polygon').value = geojson; }, 100);
}
});
// Klik peta untuk memunculkan pilihan tambah data
map.on('click', function(e) {
const lat = e.latlng.lat.toFixed(8);
const lng = e.latlng.lng.toFixed(8);
document.querySelectorAll('input[name="latitude"]').forEach(el => el.value = lat);
document.querySelectorAll('input[name="longitude"]').forEach(el => el.value = lng);
let buttonsHTML = '';
if (current_tab === 'masyarakat') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-masyarakat');" style="background:#4CAF50; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah Warga Miskin</button>`;
} else if (current_tab === 'ibadah') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-ibadah');" style="background:#2196F3; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah Rumah Ibadah</button>`;
} else if (current_tab === 'spbu') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-spbu');" style="background:#ff9800; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah SPBU</button>`;
} else {
buttonsHTML = `<small style="color:#f44336;">Silakan pilih tab yang sesuai (Warga, Ibadah, atau SPBU) di menu kiri terlebih dahulu.</small>`;
}
// Buat konten popup pilihan
const popupContent = `
<div style="text-align: center; min-width: 160px; padding: 5px;">
<b style="font-size: 14px; color: #1e3c72; margin-bottom:5px; display:block;">Pilih Data untuk Ditambah</b>
<small style="color: #666; display:block; margin-bottom:10px;">${lat}, ${lng}</small>
<div style="display: flex; flex-direction: column; gap: 8px;">
${buttonsHTML}
</div>
</div>
`;
L.popup()
.setLatLng(e.latlng)
.setContent(popupContent)
.openOn(map);
});
}
/**
* Memicu proses memuat seluruh data dari API secara paralel.
*/
function loadData() {
loadMasyarakat();
loadRumahIbadah();
loadJalan();
loadPolygon();
loadSPBU();
}
function loadJalan() {
fetch(API_PATH + 'ambil_fasilitas_jalan.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.jalan = result.data || [];
tampilkanDataJalan(all_data.jalan);
}
}).catch(e => console.error(e));
}
function loadPolygon() {
fetch(API_PATH + 'ambil_fasilitas_polygon.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.polygon = result.data || [];
tampilkanDataPolygon(all_data.polygon);
}
}).catch(e => console.error(e));
}
function loadSPBU() {
fetch(API_PATH + 'ambil_fasilitas_spbu.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.spbu = result.data || [];
tampilkanDataSPBU(all_data.spbu);
}
}).catch(e => console.error(e));
}
/**
* Memuat data masyarakat miskin dari server.
*/
function loadMasyarakat() {
fetch(API_PATH + 'ambil_data_masyarakat.php')
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
all_data.masyarakat = result.data || [];
tampilkanDataMasyarakat(all_data.masyarakat);
} else {
console.error('Gagal memuat data masyarakat:', result.message);
}
})
.catch(error => console.error('Error saat fetch data masyarakat:', error));
}
/**
* Memuat data fasilitas rumah ibadah dari server.
*/
function loadRumahIbadah() {
fetch(API_PATH + 'ambil_rumah_ibadah.php')
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
all_data.ibadah = result.data || [];
tampilkanDataIbadah(all_data.ibadah);
} else {
console.error('Gagal memuat data rumah ibadah:', result.message);
}
})
.catch(error => console.error('Error saat fetch data rumah ibadah:', error));
}
/**
* Merender penanda peta dan elemen daftar sidebar untuk kategori masyarakat.
* @param {Array} data - Larik berisi data objek masyarakat.
*/
function tampilkanDataMasyarakat(data) {
markers.masyarakat.forEach(m => map.removeLayer(m));
markers.masyarakat = [];
const list = document.getElementById('list-masyarakat');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const marker = L.marker(
[parseFloat(item.latitude), parseFloat(item.longitude)],
{ icon: iconMasyarakat }
).addTo(map);
const bpjs_class = item.status_bpjs === 'Ya' ? 'status-ya' : (item.status_bpjs === 'Proses' ? 'status-proses' : 'status-tidak');
// Penerapan escapeHTML untuk mencegah kerentanan XSS pada popup
marker.bindPopup(`
<div style="font-size:13px; min-width:250px;">
<b>${escapeHTML(item.nama)}</b><br>
<small style="opacity:0.7;">${escapeHTML(item.alamat)}</small><br>
Status BPJS: <span class="status-label ${bpjs_class}">${escapeHTML(item.status_bpjs)}</span><br>
<button onclick="showDetail('${escapeHTML(item.id)}')" style="width:100%; padding:8px; margin-top:10px; background:#1e3c72; color:white; border:none; border-radius:5px; cursor:pointer;">Lihat Detail</button>
</div>
`);
markers.masyarakat.push(marker);
const li = document.createElement('li');
li.innerHTML = `
<b>${escapeHTML(item.nama)}</b>
<small>${escapeHTML(item.kecamatan)} | BPJS: ${escapeHTML(item.status_bpjs)}</small>
`;
li.onclick = () => {
map.setView([item.latitude, item.longitude], 16);
marker.openPopup();
};
list.appendChild(li);
});
updateMapVisibility();
}
/**
* Merender penanda peta dan elemen daftar sidebar untuk kategori rumah ibadah.
* @param {Array} data - Larik berisi data objek rumah ibadah.
*/
function tampilkanDataIbadah(data) {
markers.ibadah.forEach(m => map.removeLayer(m));
markers.ibadah = [];
const list = document.getElementById('list-ibadah');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const marker = L.marker(
[parseFloat(item.latitude), parseFloat(item.longitude)],
{ icon: iconIbadah }
).addTo(map);
const info_pemimpin = item.nama_pemimpin ? 'Pemimpin: ' + escapeHTML(item.nama_pemimpin) + '<br>' : '';
const info_alamat = item.alamat ? '<small>' + escapeHTML(item.alamat) + '</small>' : '';
marker.bindPopup(`
<div style="font-size:13px; min-width:250px;">
<b>${escapeHTML(item.nama)}</b><br>
<small>${escapeHTML(item.jenis)}</small><br>
${info_pemimpin}
${info_alamat}
</div>
`);
markers.ibadah.push(marker);
const li = document.createElement('li');
li.innerHTML = `
<b>${escapeHTML(item.nama)}</b>
<small>${escapeHTML(item.jenis)}</small>
`;
li.onclick = () => {
map.setView([item.latitude, item.longitude], 16);
marker.openPopup();
};
list.appendChild(li);
});
updateMapVisibility();
}
function tampilkanDataJalan(data) {
if (layers.jalan) map.removeLayer(layers.jalan);
layers.jalan = L.layerGroup().addTo(map);
const list = document.getElementById('list-jalan');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
try {
const geojson = JSON.parse(item.geojson);
const layer = L.geoJSON(geojson, { style: { color: 'blue', weight: 4 } }).addTo(layers.jalan);
layer.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small><br><button onclick="hapusData('jalan', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small>`;
li.onclick = () => { map.fitBounds(layer.getBounds()); layer.openPopup(); };
list.appendChild(li);
} catch(e) { console.error('Invalid GeoJSON', e); }
});
updateMapVisibility();
}
function tampilkanDataPolygon(data) {
if (layers.polygon) map.removeLayer(layers.polygon);
layers.polygon = L.layerGroup().addTo(map);
const list = document.getElementById('list-polygon');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
try {
const geojson = JSON.parse(item.geojson);
const layer = L.geoJSON(geojson, { style: { color: 'purple', fillColor: 'purple', fillOpacity: 0.4 } }).addTo(layers.polygon);
layer.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small><br><button onclick="hapusData('polygon', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small>`;
li.onclick = () => { map.fitBounds(layer.getBounds()); layer.openPopup(); };
list.appendChild(li);
} catch(e) { console.error('Invalid GeoJSON', e); }
});
updateMapVisibility();
}
function tampilkanDataSPBU(data) {
markers.spbu.forEach(m => map.removeLayer(m));
markers.spbu = [];
const list = document.getElementById('list-spbu');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const is24 = item.is_24_jam === 'Ya';
// Reuse iconIbadah for green (24h) and iconMasyarakat for red (not 24h)
const iconSPBU = is24 ? iconIbadah : iconMasyarakat;
const statusText = is24 ? '<span style="color:green;font-weight:bold;">Buka 24 Jam</span>' : '<span style="color:red;font-weight:bold;">Tidak 24 Jam</span>';
const marker = L.marker([parseFloat(item.latitude), parseFloat(item.longitude)], {
icon: iconSPBU,
draggable: true
}).addTo(map);
marker.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.alamat)}</small><br>${statusText}<br><button onclick="hapusData('spbu', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
marker.on('dragend', function(e) {
const newPos = marker.getLatLng();
if (confirm('Apakah Anda yakin ingin memperbarui lokasi SPBU ini?')) {
const fd = new FormData();
fd.append('id', item.id);
fd.append('latitude', newPos.lat.toFixed(8));
fd.append('longitude', newPos.lng.toFixed(8));
fetch(API_PATH + 'update_lokasi_spbu.php', { method: 'POST', body: fd })
.then(handleFetchResponse)
.then(res => {
if (res.status === 'success') {
alert('Lokasi berhasil diperbarui');
item.latitude = newPos.lat;
item.longitude = newPos.lng;
} else {
alert('Gagal: ' + res.message);
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
}
}).catch(err => {
alert('Error: ' + err.message);
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
});
} else {
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
}
});
markers.spbu.push(marker);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.alamat)}</small><br><small>${statusText}</small>`;
li.onclick = () => { map.setView([item.latitude, item.longitude], 16); marker.openPopup(); };
list.appendChild(li);
});
updateMapVisibility();
}
/**
* Memuat detail data spesifik masyarakat miskin termasuk riwayat bantuan dan status BPJS.
* @param {string|number} id - ID unik entitas masyarakat miskin.
*/
function showDetail(id) {
fetch(API_PATH + 'ambil_detail.php?id=' + encodeURIComponent(id))
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
const data = result.data;
const masyarakat = data.masyarakat;
const bantuan = data.bantuan || [];
const bpjs = data.bpjs;
const bpjs_class = masyarakat.status_bpjs === 'Ya' ? 'status-ya' : (masyarakat.status_bpjs === 'Proses' ? 'status-proses' : 'status-tidak');
let html = `
<div class="info-section">
<h3>👤 Data Pribadi</h3>
<div class="info-row"><span class="info-label">Nama</span><span class="info-value">${escapeHTML(masyarakat.nama)}</span></div>
<div class="info-row"><span class="info-label">Tanggal Lahir</span><span class="info-value">${escapeHTML(masyarakat.tanggal_lahir)}</span></div>
<div class="info-row"><span class="info-label">Jenis Kelamin</span><span class="info-value">${escapeHTML(masyarakat.jenis_kelamin)}</span></div>
<div class="info-row"><span class="info-label">NIK</span><span class="info-value">${escapeHTML(masyarakat.nik)}</span></div>
<div class="info-row"><span class="info-label">No. HP</span><span class="info-value">${escapeHTML(masyarakat.no_hp)}</span></div>
</div>
<div class="info-section">
<h3>🏠 Data Alamat</h3>
<div class="info-row"><span class="info-label">Alamat</span><span class="info-value">${escapeHTML(masyarakat.alamat)}</span></div>
<div class="info-row"><span class="info-label">Kelurahan</span><span class="info-value">${escapeHTML(masyarakat.kelurahan)}</span></div>
<div class="info-row"><span class="info-label">Kecamatan</span><span class="info-value">${escapeHTML(masyarakat.kecamatan)}</span></div>
<div class="info-row"><span class="info-label">Status Rumah</span><span class="info-value">${escapeHTML(masyarakat.status_rumah)}</span></div>
</div>
<div class="info-section">
<h3>💼 Data Ekonomi</h3>
<div class="info-row"><span class="info-label">Pekerjaan</span><span class="info-value">${escapeHTML(masyarakat.pekerjaan)}</span></div>
<div class="info-row"><span class="info-label">Pendapatan Bulanan</span><span class="info-value">Rp. ${new Intl.NumberFormat('id-ID').format(masyarakat.pendapatan_bulanan || 0)}</span></div>
<div class="info-row"><span class="info-label">Jumlah Tanggungan</span><span class="info-value">${escapeHTML(masyarakat.jumlah_tanggungan)} orang</span></div>
</div>
<div class="info-section">
<h3>🏥 Status BPJS</h3>
<div class="info-row"><span class="info-label">Status BPJS</span><span class="info-value"><span class="status-label ${bpjs_class}">${escapeHTML(masyarakat.status_bpjs)}</span></span></div>
`;
if (bpjs) {
html += `
<div class="info-row"><span class="info-label">No. Peserta BPJS</span><span class="info-value">${escapeHTML(bpjs.no_peserta)}</span></div>
<div class="info-row"><span class="info-label">Jenis BPJS</span><span class="info-value">${escapeHTML(bpjs.jenis_bpjs)}</span></div>
<div class="info-row"><span class="info-label">Tingkat</span><span class="info-value">${escapeHTML(bpjs.tingkat)}</span></div>
`;
}
html += `</div>`; // Memperbaiki string penutup tag div yang sebelumnya memicu syntax error
if (bantuan.length > 0) {
html += `<div class="info-section"><h3>💰 Riwayat Bantuan</h3>`;
bantuan.forEach(b => {
html += `
<div style="background:white; padding:8px; margin-bottom:8px; border-left:3px solid #ff9800; border-radius:3px; font-size:12px;">
<b>${escapeHTML(b.jenis_bantuan)}</b> (${escapeHTML(b.tanggal_bantuan)})<br>
Nilai: <strong>Rp. ${new Intl.NumberFormat('id-ID').format(b.nilai)}</strong> ${escapeHTML(b.satuan)}
${b.keterangan ? '<br><small>' + escapeHTML(b.keterangan) + '</small>' : ''}
</div>
`;
});
html += `</div>`;
}
html += `
<div class="button-group">
<button class="btn-primary" onclick="editBantuan(${parseInt(id)})"> Tambah Bantuan</button>
<button class="btn-secondary" onclick="hapusData('masyarakat', ${parseInt(id)})">🗑️ Hapus</button>
</div>
`;
const modal_body = document.getElementById('modal-body');
if (modal_body) {
modal_body.innerHTML = html;
openModal('modal-detail');
}
}
})
.catch(error => console.error('Error saat fetch detail data:', error));
}
/**
* Menghubungkan fungsi penangan kejadian (event handler) ke elemen-elemen HTML DOM.
*/
function attachEventListeners() {
// Accordion Logic
document.querySelectorAll('.accordion').forEach(acc => {
acc.addEventListener('click', function() {
// Close other accordions
document.querySelectorAll('.accordion').forEach(otherAcc => {
if (otherAcc !== this) {
otherAcc.classList.remove('active');
if (otherAcc.nextElementSibling) {
otherAcc.nextElementSibling.style.display = "none";
}
}
});
// Toggle this one
this.classList.toggle('active');
const panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
// Optionally auto-click the first tab in this accordion
const firstTab = panel.querySelector('.tab-button');
if (firstTab && !firstTab.classList.contains('active')) {
firstTab.click();
}
}
});
});
document.querySelectorAll('.tab-button').forEach(btn => {
btn.addEventListener('click', function(e) {
const tab = this.dataset.tab;
switchTab(e, tab);
});
});
const search_input = document.getElementById('search');
const f_kecamatan = document.getElementById('filter-kecamatan');
const f_bpjs = document.getElementById('filter-bpjs');
const f_jenis = document.getElementById('filter-jenis');
if (search_input) search_input.addEventListener('input', filterMasyarakat);
if (f_kecamatan) f_kecamatan.addEventListener('change', filterMasyarakat);
if (f_bpjs) f_bpjs.addEventListener('change', filterMasyarakat);
if (f_jenis) f_jenis.addEventListener('change', filterIbadah);
const form_masyarakat = document.getElementById('form-masyarakat');
const form_ibadah = document.getElementById('form-ibadah');
const form_jalan = document.getElementById('form-jalan');
const form_polygon = document.getElementById('form-polygon');
const form_spbu = document.getElementById('form-spbu');
if (form_masyarakat) form_masyarakat.addEventListener('submit', function(e) { e.preventDefault(); submitFormMasyarakat(); });
if (form_ibadah) form_ibadah.addEventListener('submit', function(e) { e.preventDefault(); submitFormIbadah(); });
if (form_jalan) form_jalan.addEventListener('submit', function(e) { e.preventDefault(); submitFormJalan(); });
if (form_polygon) form_polygon.addEventListener('submit', function(e) { e.preventDefault(); submitFormPolygon(); });
if (form_spbu) form_spbu.addEventListener('submit', function(e) { e.preventDefault(); submitFormSPBU(); });
// Shortcut keyboard untuk membuka formulir penambahan data baru
document.addEventListener('keydown', function(e) {
if (e.key === 'n' || e.key === 'N') {
const active_modal = document.querySelector('.modal.active');
if (active_modal) return; // Cegah konflik jika modal lain sudah terbuka
if (current_tab === 'masyarakat') openModal('modal-form-masyarakat');
else if (current_tab === 'ibadah') openModal('modal-form-ibadah');
}
});
}
/**
* Mengubah status visibilitas tab kategori aktif (Masyarakat / Rumah Ibadah).
* @param {Event} e - Objek event klik dari DOM.
* @param {string} tab - Nama tab penanda tujuan ('masyarakat' / 'ibadah').
*/
function switchTab(e, tab) {
current_tab = tab;
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
if (e && e.target) {
e.target.classList.add('active');
}
document.querySelectorAll('.list-container').forEach(container => {
container.classList.remove('active');
});
const target_tab = document.getElementById('tab-' + tab);
if (target_tab) target_tab.classList.add('active');
updateMapVisibility();
}
function updateMapVisibility() {
markers.masyarakat.forEach(m => map.removeLayer(m));
markers.ibadah.forEach(m => map.removeLayer(m));
markers.spbu.forEach(m => map.removeLayer(m));
if (layers.jalan) map.removeLayer(layers.jalan);
if (layers.polygon) map.removeLayer(layers.polygon);
if (current_tab === 'masyarakat' || current_tab === 'info-fasilitas') {
markers.masyarakat.forEach(m => map.addLayer(m));
}
if (current_tab === 'ibadah' || current_tab === 'info-fasilitas') {
markers.ibadah.forEach(m => map.addLayer(m));
}
if (current_tab === 'spbu' || current_tab === 'info-fasilitas') {
markers.spbu.forEach(m => map.addLayer(m));
}
if (current_tab === 'jalan' || current_tab === 'info-fasilitas') {
if (layers.jalan) map.addLayer(layers.jalan);
}
if (current_tab === 'polygon' || current_tab === 'info-fasilitas') {
if (layers.polygon) map.addLayer(layers.polygon);
}
}
/**
* Memfilter kumpulan data masyarakat berdasarkan kata kunci pencarian, kecamatan, dan status BPJS.
*/
function filterMasyarakat() {
const search_el = document.getElementById('search');
const kec_el = document.getElementById('filter-kecamatan');
const bpjs_el = document.getElementById('filter-bpjs');
const keyword = search_el ? search_el.value.toLowerCase() : '';
const kecamatan = kec_el ? kec_el.value : '';
const bpjs = bpjs_el ? bpjs_el.value : '';
const filtered = all_data.masyarakat.filter(item => {
const item_nama = item.nama ? item.nama.toLowerCase() : '';
const item_alamat = item.alamat ? item.alamat.toLowerCase() : '';
const match_keyword = item_nama.includes(keyword) || item_alamat.includes(keyword);
const match_kecamatan = !kecamatan || item.kecamatan === kecamatan;
const match_bpjs = !bpjs || item.status_bpjs === bpjs;
return match_keyword && match_kecamatan && match_bpjs;
});
tampilkanDataMasyarakat(filtered);
}
/**
* Memfilter kumpulan data tempat ibadah berdasarkan jenis kategori tempat ibadah.
*/
function filterIbadah() {
const jenis_el = document.getElementById('filter-jenis');
const jenis = jenis_el ? jenis_el.value : '';
const filtered = all_data.ibadah.filter(item => {
return !jenis || item.jenis === jenis;
});
tampilkanDataIbadah(filtered);
}
/**
* Mengirimkan formulir data masyarakat baru ke berkas PHP pemroses menggunakan Fetch FormData.
*/
function submitFormMasyarakat() {
const form = document.getElementById('form-masyarakat');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_masyarakat.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
showAlert('alert-form', 'Data berhasil disimpan!', 'success');
form.reset();
setTimeout(() => {
closeModal('modal-form-masyarakat');
loadMasyarakat();
}, 1500);
} else {
showAlert('alert-form', result.message || 'Gagal menyimpan data.', 'error');
}
})
.catch(error => {
showAlert('alert-form', 'Terjadi kesalahan sistem: ' + error.message, 'error');
});
}
/**
* Mengirimkan formulir data rumah ibadah baru ke berkas PHP pemroses menggunakan Fetch FormData.
*/
function submitFormIbadah() {
const form = document.getElementById('form-ibadah');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_rumah_ibadah.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
showAlert('alert-form-ibadah', 'Data berhasil disimpan!', 'success');
form.reset();
setTimeout(() => {
closeModal('modal-form-ibadah');
loadRumahIbadah();
}, 1500);
} else {
showAlert('alert-form-ibadah', result.message || 'Gagal menyimpan data.', 'error');
}
})
.catch(error => {
showAlert('alert-form-ibadah', 'Terjadi kesalahan sistem: ' + error.message, 'error');
});
}
function submitFormJalan() {
const form = document.getElementById('form-jalan');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_jalan.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-jalan', 'Berhasil disimpan', 'success'); form.reset(); drawnItems.clearLayers();
setTimeout(() => { closeModal('modal-form-jalan'); loadJalan(); }, 1000);
} else showAlert('alert-form-jalan', result.message, 'error');
}).catch(e => showAlert('alert-form-jalan', e.message, 'error'));
}
function submitFormPolygon() {
const form = document.getElementById('form-polygon');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_polygon.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-polygon', 'Berhasil disimpan', 'success'); form.reset(); drawnItems.clearLayers();
setTimeout(() => { closeModal('modal-form-polygon'); loadPolygon(); }, 1000);
} else showAlert('alert-form-polygon', result.message, 'error');
}).catch(e => showAlert('alert-form-polygon', e.message, 'error'));
}
function submitFormSPBU() {
const form = document.getElementById('form-spbu');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_spbu.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-spbu', 'Berhasil disimpan', 'success'); form.reset();
setTimeout(() => { closeModal('modal-form-spbu'); loadSPBU(); }, 1000);
} else showAlert('alert-form-spbu', result.message, 'error');
}).catch(e => showAlert('alert-form-spbu', e.message, 'error'));
}
/**
* Mengirimkan instruksi penghapusan data spesifik (masyarakat/ibadah) ke sistem basis data backend.
* @param {string} tipe - Jenis kategori data yang akan dihapus ('masyarakat' / 'ibadah').
* @param {string|number} id - ID baris data target.
*/
function hapusData(tipe, id) {
if (!confirm('Apakah Anda yakin ingin menghapus data ini? Tindakan ini tidak dapat dibatalkan.')) {
return;
}
const form_data = new FormData();
form_data.append('tipe', tipe);
form_data.append('id', id);
fetch(API_PATH + 'hapus_data.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
alert('Data berhasil dihapus');
closeModal('modal-detail');
if (tipe === 'masyarakat') loadMasyarakat();
else if (tipe === 'ibadah') loadRumahIbadah();
else if (tipe === 'jalan') loadJalan();
else if (tipe === 'polygon') loadPolygon();
else if (tipe === 'spbu') loadSPBU();
} else {
alert('Gagal menghapus: ' + (result.message || 'Error tidak diketahui'));
}
})
.catch(error => {
alert('Terjadi kesalahan jaringan atau sistem: ' + error.message);
});
}
function editBantuan(id) {
alert('Fitur tambah bantuan dengan ID ' + id + ' akan ditambahkan segera');
}
function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) modal.classList.add('active');
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) modal.classList.remove('active');
}
function showAlert(elementId, message, type) {
const alert_el = document.getElementById(elementId);
if (!alert_el) return;
alert_el.textContent = message;
alert_el.className = 'alert active alert-' + type;
setTimeout(() => {
alert_el.classList.remove('active');
}, 5000);
}
// Menutup jendela modal otomatis jika pengguna mengklik area luar modal (overlay)
document.addEventListener('click', function(e) {
if (e.target && e.target.classList.contains('modal')) {
e.target.classList.remove('active');
}
});