feat: implement core WebGIS mapping features, sidebar UI, and API endpoints for data management
This commit is contained in:
@@ -16,6 +16,7 @@ function loadJalan() {
|
||||
addJalanToMap(item);
|
||||
});
|
||||
}
|
||||
if (window.refreshActivePanel) window.refreshActivePanel();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
// --- Fitur Pemetaan Kemiskinan ---
|
||||
|
||||
// Emoji per jenis ibadah
|
||||
const IBADAH_EMOJI_MAP = {
|
||||
'Masjid': '🕌',
|
||||
'Gereja': '⛪',
|
||||
'Pura': '🛕',
|
||||
'Vihara': '🪷',
|
||||
'Kelenteng': '🏮',
|
||||
};
|
||||
|
||||
// Emoji Bubble Icon builder
|
||||
function makeIbadahIcon() {
|
||||
function makeIbadahIcon(jenis) {
|
||||
const emot = IBADAH_EMOJI_MAP[jenis] || '🕌';
|
||||
return L.divIcon({
|
||||
className: '',
|
||||
html: `<div class="emoji-marker"><div class="bubble ibadah"><span>🕌</span></div></div>`,
|
||||
html: `<div class="emoji-marker"><div class="bubble ibadah"><span>${emot}</span></div></div>`,
|
||||
iconSize: [38, 38],
|
||||
iconAnchor: [19, 38],
|
||||
popupAnchor: [0, -40]
|
||||
@@ -22,10 +32,6 @@ function makeMiskinIcon(inRadius) {
|
||||
});
|
||||
}
|
||||
|
||||
const ibadahIcon = makeIbadahIcon();
|
||||
const miskinMerahIcon = makeMiskinIcon(true);
|
||||
const miskinHijauIcon = makeMiskinIcon(false);
|
||||
|
||||
|
||||
let ibadahDataList = [];
|
||||
let miskinMarkerList = []; // Simpan referensi marker untuk update warna
|
||||
@@ -76,13 +82,14 @@ function loadRumahIbadah() {
|
||||
data.data.forEach(item => {
|
||||
addIbadahMarker(item);
|
||||
});
|
||||
updateSemuaWarnaMiskin(); // Update warna setelah memuat rumah ibadah
|
||||
updateSemuaWarnaMiskin();
|
||||
}
|
||||
if (window.refreshActivePanel) window.refreshActivePanel();
|
||||
});
|
||||
}
|
||||
|
||||
function addIbadahMarker(item) {
|
||||
const marker = L.marker([item.lat, item.lng], { icon: ibadahIcon, draggable: true });
|
||||
const marker = L.marker([item.lat, item.lng], { icon: makeIbadahIcon(item.jenis), draggable: true });
|
||||
|
||||
// Lingkaran radius
|
||||
const circle = L.circle([item.lat, item.lng], {
|
||||
@@ -125,10 +132,12 @@ function addIbadahMarker(item) {
|
||||
marker.circleLayer = circle;
|
||||
|
||||
const d = item;
|
||||
const emot = IBADAH_EMOJI_MAP[d.jenis] || '🕌';
|
||||
const popupContent = `
|
||||
<div style="font-family: Arial, sans-serif; min-width: 150px;">
|
||||
<h4 style="margin:0 0 5px 0;">Rumah Ibadah ${d.nama}</h4>
|
||||
<p style="margin: 0 0 5px 0;"><b>Alamat:</b> ${d.alamat}</p>
|
||||
<div style="font-family: Arial, sans-serif; min-width: 160px;">
|
||||
<h4 style="margin:0 0 5px 0;">${emot} ${d.nama}</h4>
|
||||
<p style="margin: 0 0 3px 0;"><b>Jenis:</b> ${d.jenis || 'Masjid'}</p>
|
||||
<p style="margin: 0 0 3px 0;"><b>Alamat:</b> ${d.alamat}</p>
|
||||
<p style="margin: 0 0 10px 0;"><b>Radius:</b> ${d.radius} m</p>
|
||||
<div style="display:flex; gap:5px;">
|
||||
<button style="padding:4px 8px; background:#007bff; color:white; border:none; border-radius:3px; cursor:pointer;" onclick="openEditIbadahModal(${d.id})">Edit</button>
|
||||
@@ -185,6 +194,16 @@ map.on('click', function(e) {
|
||||
<label>Nama Rumah Ibadah</label>
|
||||
<input type="text" id="modalIbadahNama" placeholder="Nama Rumah Ibadah">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Jenis</label>
|
||||
<select id="modalIbadahJenis">
|
||||
<option value="Masjid">🕌 Masjid</option>
|
||||
<option value="Gereja">⛪ Gereja</option>
|
||||
<option value="Pura">🛕 Pura</option>
|
||||
<option value="Vihara">🪷 Vihara</option>
|
||||
<option value="Kelenteng">🏮 Kelenteng</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Alamat (Auto)</label>
|
||||
<textarea id="modalIbadahAlamat" rows="3">${alamat}</textarea>
|
||||
@@ -196,7 +215,7 @@ map.on('click', function(e) {
|
||||
`;
|
||||
|
||||
openModal("Tambah Rumah Ibadah", bodyHTML, function() {
|
||||
window.saveNewIbadah(lat, lng, 'modalIbadahNama', 'modalIbadahAlamat', 'modalIbadahRadius');
|
||||
window.saveNewIbadah(lat, lng, 'modalIbadahNama', 'modalIbadahJenis', 'modalIbadahAlamat', 'modalIbadahRadius');
|
||||
});
|
||||
|
||||
window.deactivateAddMode();
|
||||
@@ -212,6 +231,16 @@ map.on('click', function(e) {
|
||||
<label>Nama Rumah Ibadah</label>
|
||||
<input type="text" id="modalIbadahNama" placeholder="Nama Rumah Ibadah">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Jenis</label>
|
||||
<select id="modalIbadahJenis">
|
||||
<option value="Masjid">🕌 Masjid</option>
|
||||
<option value="Gereja">⛪ Gereja</option>
|
||||
<option value="Pura">🛕 Pura</option>
|
||||
<option value="Vihara">🪷 Vihara</option>
|
||||
<option value="Kelenteng">🏮 Kelenteng</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Alamat (Manual)</label>
|
||||
<textarea id="modalIbadahAlamat" rows="3" placeholder="Masukkan alamat manual">${alamat}</textarea>
|
||||
@@ -223,7 +252,7 @@ map.on('click', function(e) {
|
||||
`;
|
||||
|
||||
openModal("Tambah Rumah Ibadah", bodyHTML, function() {
|
||||
window.saveNewIbadah(lat, lng, 'modalIbadahNama', 'modalIbadahAlamat', 'modalIbadahRadius');
|
||||
window.saveNewIbadah(lat, lng, 'modalIbadahNama', 'modalIbadahJenis', 'modalIbadahAlamat', 'modalIbadahRadius');
|
||||
});
|
||||
|
||||
window.deactivateAddMode();
|
||||
@@ -231,27 +260,23 @@ map.on('click', function(e) {
|
||||
}
|
||||
});
|
||||
|
||||
window.saveNewIbadah = function(lat, lng, namaId, alamatId, radiusId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
window.saveNewIbadah = function(lat, lng, namaId, jenisId, alamatId, radiusId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
const jenis = document.getElementById(jenisId).value;
|
||||
const alamat = document.getElementById(alamatId).value;
|
||||
const radius = document.getElementById(radiusId).value;
|
||||
|
||||
if (!nama) {
|
||||
alert("Nama Rumah Ibadah harus diisi!");
|
||||
return;
|
||||
}
|
||||
if (!nama) { alert("Nama Rumah Ibadah harus diisi!"); return; }
|
||||
|
||||
fetch('api/rumah_ibadah/create.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ nama, alamat, radius, lat, lng })
|
||||
body: JSON.stringify({ nama, jenis, alamat, radius, lat, lng })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(data.status === 'success') {
|
||||
closeModal();
|
||||
loadRumahIbadah();
|
||||
} else { alert(data.message); }
|
||||
if(data.status === 'success') { closeModal(); loadRumahIbadah(); }
|
||||
else { alert(data.message); }
|
||||
});
|
||||
};
|
||||
|
||||
@@ -262,11 +287,19 @@ window.openEditIbadahModal = function(id) {
|
||||
});
|
||||
if (!d) return;
|
||||
|
||||
const jenisOpts = ['Masjid','Gereja','Pura','Vihara','Kelenteng'].map(j =>
|
||||
`<option value="${j}" ${d.jenis === j ? 'selected' : ''}>${IBADAH_EMOJI_MAP[j] || ''} ${j}</option>`
|
||||
).join('');
|
||||
|
||||
const bodyHTML = `
|
||||
<div class="form-group">
|
||||
<label>Nama Rumah Ibadah</label>
|
||||
<input type="text" id="editIbadahNama" value="${d.nama}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Jenis</label>
|
||||
<select id="editIbadahJenis">${jenisOpts}</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Alamat</label>
|
||||
<textarea id="editIbadahAlamat" rows="2">${d.alamat}</textarea>
|
||||
@@ -278,7 +311,7 @@ window.openEditIbadahModal = function(id) {
|
||||
`;
|
||||
map.closePopup();
|
||||
openModal("Edit Rumah Ibadah", bodyHTML, function() {
|
||||
window.saveEditIbadah(d.id, d.lat, d.lng, 'editIbadahNama', 'editIbadahAlamat', 'editIbadahRadius');
|
||||
window.saveEditIbadah(d.id, d.lat, d.lng, 'editIbadahNama', 'editIbadahJenis', 'editIbadahAlamat', 'editIbadahRadius');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -293,18 +326,19 @@ window.previewRadius = function(id, newRadius) {
|
||||
});
|
||||
};
|
||||
|
||||
window.saveEditIbadah = function(id, lat, lng, namaId, alamatId, radiusId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
window.saveEditIbadah = function(id, lat, lng, namaId, jenisId, alamatId, radiusId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
const jenis = document.getElementById(jenisId).value;
|
||||
const alamat = document.getElementById(alamatId).value;
|
||||
const radius = document.getElementById(radiusId).value;
|
||||
updateIbadah(id, nama, alamat, radius, lat, lng);
|
||||
updateIbadah(id, nama, jenis, alamat, radius, lat, lng);
|
||||
};
|
||||
|
||||
function updateIbadah(id, nama, alamat, radius, lat, lng) {
|
||||
function updateIbadah(id, nama, jenis, alamat, radius, lat, lng) {
|
||||
fetch('api/rumah_ibadah/update.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id, nama, alamat, radius, lat, lng })
|
||||
body: JSON.stringify({ id, nama, jenis, alamat, radius, lat, lng })
|
||||
}).then(res => res.json()).then(data => {
|
||||
if(data.status === 'success') { map.closePopup(); loadRumahIbadah(); }
|
||||
});
|
||||
@@ -336,19 +370,22 @@ function loadPendudukMiskin() {
|
||||
});
|
||||
updateSemuaWarnaMiskin();
|
||||
}
|
||||
if (window.refreshActivePanel) window.refreshActivePanel();
|
||||
});
|
||||
}
|
||||
|
||||
function addMiskinMarker(item) {
|
||||
const marker = L.marker([item.lat, item.lng], { icon: miskinHijauIcon, draggable: true });
|
||||
const marker = L.marker([item.lat, item.lng], { icon: makeMiskinIcon(false), draggable: true });
|
||||
marker.miskinData = item;
|
||||
|
||||
const d = item;
|
||||
const popupContent = `
|
||||
<div style="font-family: Arial, sans-serif; min-width: 150px;">
|
||||
<h4 style="margin:0 0 5px 0;">Penduduk Miskin ${d.nama}</h4>
|
||||
<p style="margin: 0 0 10px 0;"><b>Bantuan:</b> ${d.kategori_bantuan}</p>
|
||||
<div style="display:flex; gap:5px;">
|
||||
<div style="font-family: Arial, sans-serif; min-width: 165px;">
|
||||
<h4 style="margin:0 0 5px 0;">🏠 ${d.nama}</h4>
|
||||
<p style="margin: 0 0 3px 0;"><b>Bantuan:</b> ${d.kategori_bantuan}</p>
|
||||
<p style="margin: 0 0 10px 0;"><b>Jiwa:</b> ${d.jumlah_jiwa || '-'}</p>
|
||||
<div style="display:flex; gap:5px; flex-wrap:wrap;">
|
||||
<button style="padding:4px 8px; background:#6366f1; color:white; border:none; border-radius:3px; cursor:pointer; font-size:11px;" onclick="openLogBantuan(${d.id}, '${d.nama.replace(/'/g, "\\'")}');">📋 Log Bantuan</button>
|
||||
<button style="padding:4px 8px; background:#007bff; color:white; border:none; border-radius:3px; cursor:pointer;" onclick="openEditMiskinModal(${d.id})">Edit</button>
|
||||
<button style="padding:4px 8px; background:#dc3545; color:white; border:none; border-radius:3px; cursor:pointer;" onclick="deleteMiskin(${d.id})">Hapus</button>
|
||||
</div>
|
||||
@@ -374,34 +411,48 @@ function addMiskinMarker(item) {
|
||||
}
|
||||
|
||||
window.formAddMiskin = function(lat, lng) {
|
||||
const popupContent = `
|
||||
<div class="popup-form">
|
||||
<h4>Penduduk Miskin Baru</h4>
|
||||
<input type="text" id="miskinNama" placeholder="Nama Penduduk">
|
||||
map.closePopup();
|
||||
const bodyHTML = `
|
||||
<div class="form-group">
|
||||
<label>Lat: ${lat.toFixed(6)}, Lng: ${lng.toFixed(6)}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nama Penduduk</label>
|
||||
<input type="text" id="miskinNama" placeholder="Nama Lengkap">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Kategori Bantuan</label>
|
||||
<select id="miskinKategori">
|
||||
<option value="Makan">Bantuan Makan</option>
|
||||
<option value="Pemberdayaan">Pemberdayaan</option>
|
||||
</select>
|
||||
<button onclick="saveNewMiskin(${lat}, ${lng})">Simpan</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Jumlah Jiwa</label>
|
||||
<input type="number" id="miskinJiwa" value="1" min="1">
|
||||
</div>
|
||||
`;
|
||||
map.closePopup();
|
||||
L.popup().setLatLng([lat, lng]).setContent(popupContent).openOn(map);
|
||||
openModal("Tambah Penduduk Miskin", bodyHTML, function() {
|
||||
window.saveNewMiskin(lat, lng);
|
||||
});
|
||||
};
|
||||
|
||||
window.saveNewMiskin = function(lat, lng) {
|
||||
const nama = document.getElementById('miskinNama').value;
|
||||
const nama = document.getElementById('miskinNama').value;
|
||||
const kategori_bantuan = document.getElementById('miskinKategori').value;
|
||||
const jumlah_jiwa = document.getElementById('miskinJiwa').value;
|
||||
|
||||
if (!nama) { alert('Nama harus diisi!'); return; }
|
||||
|
||||
fetch('api/penduduk_miskin/create.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ nama, kategori_bantuan, lat, lng })
|
||||
body: JSON.stringify({ nama, kategori_bantuan, jumlah_jiwa, lat, lng })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(data.status === 'success') {
|
||||
map.closePopup();
|
||||
closeModal();
|
||||
loadPendudukMiskin();
|
||||
} else { alert(data.message); }
|
||||
});
|
||||
@@ -420,26 +471,31 @@ window.openEditMiskinModal = function(id) {
|
||||
<input type="text" id="editMiskinNama" value="${d.nama}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Bantuan</label>
|
||||
<label>Kategori Bantuan</label>
|
||||
<select id="editMiskinKategori">
|
||||
<option value="Makan" ${d.kategori_bantuan === 'Makan' ? 'selected' : ''}>Bantuan Makan</option>
|
||||
<option value="Pemberdayaan" ${d.kategori_bantuan === 'Pemberdayaan' ? 'selected' : ''}>Pemberdayaan</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Jumlah Jiwa</label>
|
||||
<input type="number" id="editMiskinJiwa" value="${d.jumlah_jiwa || 1}" min="1">
|
||||
</div>
|
||||
`;
|
||||
map.closePopup();
|
||||
openModal("Edit Penduduk Miskin", bodyHTML, function() {
|
||||
window.saveEditMiskin(d.id, d.lat, d.lng, 'editMiskinNama', 'editMiskinKategori');
|
||||
window.saveEditMiskin(d.id, d.lat, d.lng, 'editMiskinNama', 'editMiskinKategori', 'editMiskinJiwa');
|
||||
});
|
||||
};
|
||||
|
||||
window.saveEditMiskin = function(id, lat, lng, namaId, kategoriId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
window.saveEditMiskin = function(id, lat, lng, namaId, kategoriId, jiwaId) {
|
||||
const nama = document.getElementById(namaId).value;
|
||||
const kategori_bantuan = document.getElementById(kategoriId).value;
|
||||
const jumlah_jiwa = jiwaId ? document.getElementById(jiwaId).value : 1;
|
||||
fetch('api/penduduk_miskin/update.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id, nama, kategori_bantuan, lat, lng })
|
||||
body: JSON.stringify({ id, nama, kategori_bantuan, jumlah_jiwa, lat, lng })
|
||||
}).then(res => res.json()).then(data => {
|
||||
if(data.status === 'success') { closeModal(); loadPendudukMiskin(); }
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ function loadParsil() {
|
||||
addParsilToMap(item);
|
||||
});
|
||||
}
|
||||
if (window.refreshActivePanel) window.refreshActivePanel();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ function loadSpbu() {
|
||||
addSpbuMarker(item);
|
||||
});
|
||||
}
|
||||
if (window.refreshActivePanel) window.refreshActivePanel();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+13
-33
@@ -107,22 +107,19 @@ window.showSearchResults = function(query) {
|
||||
};
|
||||
|
||||
// UI Logic: Custom Layer Control Toggle
|
||||
const layerBtn = document.getElementById('layerBtn');
|
||||
const layerBtn = document.getElementById('layerBtn');
|
||||
const layerPanel = document.getElementById('layerPanel');
|
||||
|
||||
layerBtn.addEventListener('click', function() {
|
||||
if (layerPanel.style.display === 'block') {
|
||||
layerPanel.style.display = 'none';
|
||||
} else {
|
||||
layerPanel.style.display = 'block';
|
||||
}
|
||||
layerPanel.style.display = layerPanel.style.display === 'block' ? 'none' : 'block';
|
||||
});
|
||||
|
||||
// Sembunyikan layer panel saat klik di luar area (pada map)
|
||||
// Sembunyikan layer panel saat klik di peta
|
||||
map.on('click', function() {
|
||||
layerPanel.style.display = 'none';
|
||||
});
|
||||
|
||||
|
||||
// Logic untuk Toggle Layer Visibility
|
||||
document.getElementById('layerSpbu').addEventListener('change', function(e) {
|
||||
e.target.checked ? map.addLayer(spbuLayer) : map.removeLayer(spbuLayer);
|
||||
@@ -248,18 +245,7 @@ window.closeConfirmModal = function() {
|
||||
confirmModal.classList.remove('show');
|
||||
};
|
||||
|
||||
// --- Action Menu Logic ---
|
||||
const actionMenuBtn = document.getElementById('actionMenuBtn');
|
||||
const actionMenu = document.getElementById('actionMenu');
|
||||
|
||||
actionMenuBtn.addEventListener('click', function() {
|
||||
if (actionMenu.style.display === 'flex') {
|
||||
actionMenu.style.display = 'none';
|
||||
window.deactivateAddMode();
|
||||
} else {
|
||||
actionMenu.style.display = 'flex';
|
||||
}
|
||||
});
|
||||
// --- Action Menu Logic (replaced by left sidebar) ---
|
||||
|
||||
window.currentAddMode = null; // 'spbu' atau 'rumah_ibadah'
|
||||
window.currentDrawMode = null; // 'polyline' atau 'polygon'
|
||||
@@ -279,19 +265,14 @@ window.activateAddMode = function(mode) {
|
||||
|
||||
window.currentAddMode = mode;
|
||||
window.currentDrawMode = null;
|
||||
|
||||
// Update button UI
|
||||
document.querySelectorAll('.action-menu button').forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
if (mode === 'spbu') {
|
||||
document.getElementById('btnMenuSpbu').classList.add('active');
|
||||
if (window.cursorTooltip) window.cursorTooltip.textContent = 'Klik untuk Tambah SPBU';
|
||||
}
|
||||
if (mode === 'rumah_ibadah') {
|
||||
document.getElementById('btnMenuIbadah').classList.add('active');
|
||||
if (window.cursorTooltip) window.cursorTooltip.textContent = 'Klik untuk Tambah Rumah Ibadah';
|
||||
}
|
||||
|
||||
|
||||
// Tooltip
|
||||
const tooltips = {
|
||||
'spbu': 'Klik untuk Tambah SPBU',
|
||||
'rumah_ibadah': 'Klik untuk Tambah Rumah Ibadah'
|
||||
};
|
||||
if (window.cursorTooltip && tooltips[mode]) window.cursorTooltip.textContent = tooltips[mode];
|
||||
|
||||
// Ubah kursor map menjadi crosshair
|
||||
document.getElementById('map').style.cursor = 'crosshair';
|
||||
};
|
||||
@@ -303,7 +284,6 @@ window.deactivateAddMode = function() {
|
||||
window.activeDrawHandler.disable();
|
||||
window.activeDrawHandler = null;
|
||||
}
|
||||
document.querySelectorAll('.action-menu button').forEach(btn => btn.classList.remove('active'));
|
||||
document.getElementById('map').style.cursor = '';
|
||||
if (window.cursorTooltip) window.cursorTooltip.style.display = 'none';
|
||||
};
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
// ===== Panel Management =====
|
||||
// Mengelola Left Sidebar Toggle + Right Panel + Layer Control
|
||||
|
||||
(function () {
|
||||
// ---- Konstanta Jenis Ibadah ----
|
||||
const IBADAH_EMOJI = {
|
||||
'Masjid': '🕌',
|
||||
'Gereja': '⛪',
|
||||
'Pura': '🛕',
|
||||
'Vihara': '🪷',
|
||||
'Kelenteng': '🏮',
|
||||
};
|
||||
window.IBADAH_EMOJI = IBADAH_EMOJI;
|
||||
|
||||
// ---- State ----
|
||||
let activePanel = null;
|
||||
|
||||
// ---- Elements ----
|
||||
const sidebarToggleBtn = document.getElementById('sidebarToggleBtn');
|
||||
const sidebarToggleIcon = document.getElementById('sidebarToggleIcon');
|
||||
const leftSidebar = document.getElementById('leftSidebar');
|
||||
const rightPanel = document.getElementById('rightPanel');
|
||||
const rightPanelTitle = document.getElementById('rightPanelTitle');
|
||||
const rightPanelActions = document.getElementById('rightPanelActions');
|
||||
const rightPanelBody = document.getElementById('rightPanelBody');
|
||||
const rightPanelClose = document.getElementById('rightPanelClose');
|
||||
|
||||
// ---- Sidebar Toggle ----
|
||||
sidebarToggleBtn.addEventListener('click', function () {
|
||||
const isOpen = leftSidebar.style.display !== 'none';
|
||||
if (isOpen) {
|
||||
leftSidebar.style.display = 'none';
|
||||
sidebarToggleBtn.classList.remove('open');
|
||||
sidebarToggleIcon.className = 'fas fa-chevron-right';
|
||||
} else {
|
||||
leftSidebar.style.display = 'flex';
|
||||
sidebarToggleBtn.classList.add('open');
|
||||
sidebarToggleIcon.className = 'fas fa-chevron-left';
|
||||
}
|
||||
});
|
||||
|
||||
// ---- Sidebar Buttons ----
|
||||
document.querySelectorAll('.sidebar-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
const panel = this.dataset.panel;
|
||||
if (activePanel === panel) {
|
||||
closePanel();
|
||||
} else {
|
||||
openPanel(panel);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
rightPanelClose.addEventListener('click', closePanel);
|
||||
|
||||
// ---- Open / Close Panel ----
|
||||
function openPanel(panel) {
|
||||
activePanel = panel;
|
||||
rightPanel.classList.add('open');
|
||||
document.querySelectorAll('.sidebar-btn').forEach(b => b.classList.remove('active'));
|
||||
const activeBtn = document.querySelector(`.sidebar-btn[data-panel="${panel}"]`);
|
||||
if (activeBtn) activeBtn.classList.add('active');
|
||||
renderPanel(panel);
|
||||
}
|
||||
|
||||
function closePanel() {
|
||||
activePanel = null;
|
||||
rightPanel.classList.remove('open');
|
||||
document.querySelectorAll('.sidebar-btn').forEach(b => b.classList.remove('active'));
|
||||
}
|
||||
|
||||
window.refreshActivePanel = function () {
|
||||
if (activePanel && activePanel !== 'layer') renderPanel(activePanel);
|
||||
};
|
||||
|
||||
// ---- Render Panel by Type ----
|
||||
function renderPanel(type) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Memuat data...</div>';
|
||||
rightPanelActions.innerHTML = '';
|
||||
|
||||
switch (type) {
|
||||
case 'spbu': renderSpbuPanel(); break;
|
||||
case 'jalan': renderJalanPanel(); break;
|
||||
case 'parsil': renderParsilPanel(); break;
|
||||
case 'ibadah': renderIbadahPanel(); break;
|
||||
case 'miskin': renderMiskinPanel(); break;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// ===== SPBU Panel =========
|
||||
// ==========================
|
||||
function renderSpbuPanel() {
|
||||
rightPanelTitle.textContent = '⛽ SPBU';
|
||||
rightPanelActions.innerHTML = `
|
||||
<button class="btn-panel-add" id="btnPanelAddSpbu">
|
||||
<i class="fas fa-plus"></i> Tambah SPBU
|
||||
</button>`;
|
||||
document.getElementById('btnPanelAddSpbu').addEventListener('click', () => {
|
||||
window.activateAddMode('spbu');
|
||||
});
|
||||
|
||||
const items = [];
|
||||
spbuLayer.eachLayer(l => { if (l.spbuData) items.push(l.spbuData); });
|
||||
|
||||
if (!items.length) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Belum ada data SPBU</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
rightPanelBody.innerHTML = '';
|
||||
items.forEach(d => {
|
||||
const bdg = d.is_24_jam
|
||||
? '<span class="data-card-badge badge-green">24 Jam</span>'
|
||||
: '<span class="data-card-badge badge-red">Tidak 24 Jam</span>';
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon card-icon-spbu">⛽</div>
|
||||
<div class="data-card-info">
|
||||
<div class="data-card-name">${escHtml(d.nama)}</div>
|
||||
<div class="data-card-sub">📞 ${d.no_wa || '-'}</div>
|
||||
${bdg}
|
||||
</div>
|
||||
<div class="data-card-actions">
|
||||
<button class="btn-card-action btn-card-edit" title="Edit"><i class="fas fa-pen"></i></button>
|
||||
<button class="btn-card-action btn-card-delete" title="Hapus"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>`;
|
||||
card.querySelector('.btn-card-edit').addEventListener('click', (e) => { e.stopPropagation(); openEditSpbuModal(d.id); });
|
||||
card.querySelector('.btn-card-delete').addEventListener('click', (e) => { e.stopPropagation(); deleteSpbu(d.id); });
|
||||
card.addEventListener('click', () => flyToSpbu(d.id));
|
||||
rightPanelBody.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function flyToSpbu(id) {
|
||||
spbuLayer.eachLayer(l => {
|
||||
if (l.spbuData && l.spbuData.id == id) { map.setView(l.getLatLng(), 17); l.openPopup(); }
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// ===== Jalan Panel ========
|
||||
// ==========================
|
||||
function renderJalanPanel() {
|
||||
rightPanelTitle.textContent = '🛣️ Jalan';
|
||||
rightPanelActions.innerHTML = `
|
||||
<button class="btn-panel-add" id="btnPanelAddJalan">
|
||||
<i class="fas fa-plus"></i> Tambah Jalan
|
||||
</button>`;
|
||||
document.getElementById('btnPanelAddJalan').addEventListener('click', () => {
|
||||
window.activateDraw('polyline');
|
||||
});
|
||||
|
||||
const items = [];
|
||||
jalanLayer.eachLayer(l => { if (l.jalanData) items.push(l.jalanData); });
|
||||
|
||||
if (!items.length) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Belum ada data jalan</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const statusBadge = {
|
||||
'Nasional' : '<span class="data-card-badge badge-red">Nasional</span>',
|
||||
'Provinsi' : '<span class="data-card-badge badge-blue">Provinsi</span>',
|
||||
'Kabupaten': '<span class="data-card-badge badge-green">Kabupaten</span>',
|
||||
};
|
||||
|
||||
rightPanelBody.innerHTML = '';
|
||||
items.forEach(d => {
|
||||
const km = (d.panjang / 1000).toFixed(2);
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon card-icon-jalan">🛣️</div>
|
||||
<div class="data-card-info">
|
||||
<div class="data-card-name">${escHtml(d.nama)}</div>
|
||||
<div class="data-card-sub">📏 ${km} km</div>
|
||||
${statusBadge[d.status] || ''}
|
||||
</div>
|
||||
<div class="data-card-actions">
|
||||
<button class="btn-card-action btn-card-edit" title="Edit"><i class="fas fa-pen"></i></button>
|
||||
<button class="btn-card-action btn-card-delete" title="Hapus"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>`;
|
||||
card.querySelector('.btn-card-edit').addEventListener('click', (e) => { e.stopPropagation(); openEditJalanModal(d.id); });
|
||||
card.querySelector('.btn-card-delete').addEventListener('click', (e) => { e.stopPropagation(); deleteJalan(d.id); });
|
||||
card.addEventListener('click', () => flyToJalan(d.id));
|
||||
rightPanelBody.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function flyToJalan(id) {
|
||||
jalanLayer.eachLayer(l => {
|
||||
if (l.jalanData && l.jalanData.id == id && l.getBounds) {
|
||||
map.fitBounds(l.getBounds(), { padding: [40, 40] }); l.openPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// ===== Parsil Panel =======
|
||||
// ==========================
|
||||
function renderParsilPanel() {
|
||||
rightPanelTitle.textContent = '🗺️ Parsil Tanah';
|
||||
rightPanelActions.innerHTML = `
|
||||
<button class="btn-panel-add" id="btnPanelAddParsil">
|
||||
<i class="fas fa-plus"></i> Tambah Parsil
|
||||
</button>`;
|
||||
document.getElementById('btnPanelAddParsil').addEventListener('click', () => {
|
||||
window.activateDraw('polygon');
|
||||
});
|
||||
|
||||
const items = [];
|
||||
parsilLayer.eachLayer(l => { if (l.parsilData) items.push(l.parsilData); });
|
||||
|
||||
if (!items.length) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Belum ada data parsil</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const statusBadge = {
|
||||
'SHM': '<span class="data-card-badge badge-green">SHM</span>',
|
||||
'HGB': '<span class="data-card-badge badge-blue">HGB</span>',
|
||||
'HGU': '<span class="data-card-badge badge-yellow">HGU</span>',
|
||||
'HP' : '<span class="data-card-badge badge-orange">HP</span>',
|
||||
};
|
||||
|
||||
rightPanelBody.innerHTML = '';
|
||||
items.forEach(d => {
|
||||
const luas = d.luas >= 10000
|
||||
? (d.luas / 10000).toFixed(2) + ' ha'
|
||||
: d.luas.toFixed(0) + ' m²';
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon card-icon-parsil">🗺️</div>
|
||||
<div class="data-card-info">
|
||||
<div class="data-card-name">${escHtml(d.nama || 'Tanpa Nama')}</div>
|
||||
<div class="data-card-sub">📐 ${luas}</div>
|
||||
${statusBadge[d.status] || ''}
|
||||
</div>
|
||||
<div class="data-card-actions">
|
||||
<button class="btn-card-action btn-card-edit" title="Edit"><i class="fas fa-pen"></i></button>
|
||||
<button class="btn-card-action btn-card-delete" title="Hapus"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>`;
|
||||
card.querySelector('.btn-card-edit').addEventListener('click', (e) => { e.stopPropagation(); openEditParsilModal(d.id); });
|
||||
card.querySelector('.btn-card-delete').addEventListener('click', (e) => { e.stopPropagation(); deleteParsil(d.id); });
|
||||
card.addEventListener('click', () => flyToParsil(d.id));
|
||||
rightPanelBody.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function flyToParsil(id) {
|
||||
parsilLayer.eachLayer(l => {
|
||||
if (l.parsilData && l.parsilData.id == id && l.getBounds) {
|
||||
map.fitBounds(l.getBounds(), { padding: [40, 40] }); l.openPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// ===== Ibadah Panel =======
|
||||
// ==========================
|
||||
function renderIbadahPanel() {
|
||||
rightPanelTitle.textContent = '🕌 Rumah Ibadah';
|
||||
rightPanelActions.innerHTML = `
|
||||
<button class="btn-panel-add" id="btnPanelAddIbadah">
|
||||
<i class="fas fa-plus"></i> Tambah Ibadah
|
||||
</button>`;
|
||||
document.getElementById('btnPanelAddIbadah').addEventListener('click', () => {
|
||||
window.activateAddMode('rumah_ibadah');
|
||||
});
|
||||
|
||||
const items = [];
|
||||
rumahIbadahLayer.eachLayer(l => {
|
||||
if (l.ibadahData && l instanceof L.Marker) items.push(l.ibadahData);
|
||||
});
|
||||
|
||||
if (!items.length) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Belum ada data rumah ibadah</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
rightPanelBody.innerHTML = '';
|
||||
items.forEach(d => {
|
||||
const emoji = IBADAH_EMOJI[d.jenis] || '🕌';
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon card-icon-ibadah">${emoji}</div>
|
||||
<div class="data-card-info">
|
||||
<div class="data-card-name">${escHtml(d.nama)}</div>
|
||||
<div class="data-card-sub">${escHtml(d.jenis || 'Masjid')} • 📡 ${d.radius} m</div>
|
||||
</div>
|
||||
<div class="data-card-actions">
|
||||
<button class="btn-card-action btn-card-edit" title="Edit"><i class="fas fa-pen"></i></button>
|
||||
<button class="btn-card-action btn-card-delete" title="Hapus"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>`;
|
||||
card.querySelector('.btn-card-edit').addEventListener('click', (e) => { e.stopPropagation(); openEditIbadahModal(d.id); });
|
||||
card.querySelector('.btn-card-delete').addEventListener('click', (e) => { e.stopPropagation(); deleteIbadah(d.id); });
|
||||
card.addEventListener('click', () => flyToIbadah(d.id));
|
||||
rightPanelBody.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function flyToIbadah(id) {
|
||||
rumahIbadahLayer.eachLayer(l => {
|
||||
if (l.ibadahData && l.ibadahData.id == id && l instanceof L.Marker) {
|
||||
map.setView(l.getLatLng(), 17); l.openPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// ===== Miskin Panel =======
|
||||
// ==========================
|
||||
function renderMiskinPanel() {
|
||||
rightPanelTitle.textContent = '🏠 Penduduk Miskin';
|
||||
rightPanelActions.innerHTML = `
|
||||
<button class="btn-panel-add" id="btnPanelTambahMiskin">
|
||||
<i class="fas fa-plus"></i> Tambah
|
||||
</button>
|
||||
<button class="btn-panel-import" id="btnPanelImportMiskin">
|
||||
<i class="fas fa-file-import"></i> Impor
|
||||
</button>`;
|
||||
|
||||
// Tombol Tambah: buka form via right-click popup di peta
|
||||
document.getElementById('btnPanelTambahMiskin').addEventListener('click', () => {
|
||||
// Aktifkan mode: klik kiri di peta = tambah miskin
|
||||
window.currentAddMode = 'miskin_click';
|
||||
document.getElementById('map').style.cursor = 'crosshair';
|
||||
if (window.cursorTooltip) {
|
||||
window.cursorTooltip.textContent = 'Klik di peta untuk tentukan lokasi penduduk miskin';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btnPanelImportMiskin').addEventListener('click', () => {
|
||||
alert('Fitur impor CSV akan segera tersedia.');
|
||||
});
|
||||
|
||||
const all = [];
|
||||
pendudukMiskinLayer.eachLayer(l => { if (l.miskinData) all.push(l.miskinData); });
|
||||
|
||||
if (!all.length) {
|
||||
rightPanelBody.innerHTML = '<div class="panel-empty">Belum ada data penduduk miskin</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const inRadius = (d) => {
|
||||
if (typeof ibadahDataList === 'undefined') return false;
|
||||
for (const ib of ibadahDataList) {
|
||||
const dist = L.latLng(d.lat, d.lng).distanceTo(L.latLng(ib.lat, ib.lng));
|
||||
if (dist <= ib.radius) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
rightPanelBody.innerHTML = '';
|
||||
|
||||
const terurusItems = all.filter(d => inRadius(d));
|
||||
const tidakItems = all.filter(d => !inRadius(d));
|
||||
|
||||
if (terurusItems.length) {
|
||||
const h1 = document.createElement('div');
|
||||
h1.className = 'panel-section-title';
|
||||
h1.innerHTML = `✅ Terurus <span style="font-weight:400;text-transform:none;font-size:10px;">(${terurusItems.length})</span>`;
|
||||
rightPanelBody.appendChild(h1);
|
||||
terurusItems.forEach(d => rightPanelBody.appendChild(buildMiskinCard(d, true)));
|
||||
}
|
||||
|
||||
if (tidakItems.length) {
|
||||
const h2 = document.createElement('div');
|
||||
h2.className = 'panel-section-title';
|
||||
h2.innerHTML = `⚠️ Tidak Terurus <span style="font-weight:400;text-transform:none;font-size:10px;">(${tidakItems.length})</span>`;
|
||||
rightPanelBody.appendChild(h2);
|
||||
tidakItems.forEach(d => rightPanelBody.appendChild(buildMiskinCard(d, false)));
|
||||
}
|
||||
}
|
||||
|
||||
function buildMiskinCard(d, terurus) {
|
||||
const iconCls = terurus ? 'card-icon-miskin-in' : 'card-icon-miskin-out';
|
||||
const badge = d.kategori_bantuan === 'Makan'
|
||||
? '<span class="data-card-badge badge-orange">Bantuan Makan</span>'
|
||||
: '<span class="data-card-badge badge-blue">Pemberdayaan</span>';
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.dataset.miskinId = d.id;
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon ${iconCls}">🏠</div>
|
||||
<div class="data-card-info">
|
||||
<div class="data-card-name">${escHtml(d.nama)}</div>
|
||||
<div class="data-card-sub">👨👩👧 ${d.jumlah_jiwa || '-'} jiwa</div>
|
||||
${badge}
|
||||
</div>
|
||||
<div class="data-card-actions">
|
||||
<button class="btn-card-action btn-card-edit" title="Edit"><i class="fas fa-pen"></i></button>
|
||||
<button class="btn-card-action btn-card-delete" title="Hapus"><i class="fas fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-card-expand">
|
||||
<div class="expand-row"><span>Kategori Bantuan</span><span>${escHtml(d.kategori_bantuan)}</span></div>
|
||||
<div class="expand-row"><span>Jumlah Jiwa</span><span>${d.jumlah_jiwa || '-'}</span></div>
|
||||
<div class="expand-row"><span>Status</span><span>${terurus ? 'Terurus ✅' : 'Tidak Terurus ⚠️'}</span></div>
|
||||
<button class="btn-log-bantuan" data-id="${d.id}" data-nama="${escHtml(d.nama)}">
|
||||
<i class="fas fa-history"></i> Lihat Log Bantuan
|
||||
</button>
|
||||
</div>`;
|
||||
|
||||
card.querySelector('.btn-card-edit').addEventListener('click', (e) => { e.stopPropagation(); openEditMiskinModal(d.id); });
|
||||
card.querySelector('.btn-card-delete').addEventListener('click', (e) => { e.stopPropagation(); deleteMiskin(d.id); });
|
||||
card.querySelector('.btn-log-bantuan').addEventListener('click', (e) => { e.stopPropagation(); openLogBantuan(d.id, d.nama); });
|
||||
|
||||
card.querySelector('.data-card-main').addEventListener('click', () => {
|
||||
card.classList.toggle('expanded');
|
||||
flyToMiskin(d.id);
|
||||
});
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function flyToMiskin(id) {
|
||||
pendudukMiskinLayer.eachLayer(l => {
|
||||
if (l.miskinData && l.miskinData.id == id) {
|
||||
map.setView(l.getLatLng(), 17); l.openPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Hook: klik peta saat mode miskin_click ----
|
||||
// Ditangkap oleh kemiskinan.js via contextmenu — mode ini pakai klik kiri
|
||||
map.on('click', function(e) {
|
||||
if (window.currentAddMode === 'miskin_click') {
|
||||
window.currentAddMode = null;
|
||||
document.getElementById('map').style.cursor = '';
|
||||
if (window.cursorTooltip) window.cursorTooltip.style.display = 'none';
|
||||
// Buka form tambah miskin
|
||||
if (typeof window.formAddMiskin === 'function') {
|
||||
window.formAddMiskin(e.latlng.lat, e.latlng.lng);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ==========================
|
||||
// ===== Log Bantuan Modal ==
|
||||
// ==========================
|
||||
const logModal = document.getElementById('logBantuanModal');
|
||||
const logTitle = document.getElementById('logBantuanTitle');
|
||||
const logList = document.getElementById('logBantuanList');
|
||||
const logSaveBtn = document.getElementById('logSaveBtn');
|
||||
const logIbadahSel = document.getElementById('logIbadahId');
|
||||
let _logMiskinId = null;
|
||||
|
||||
window.openLogBantuan = function (miskinId, nama) {
|
||||
_logMiskinId = miskinId;
|
||||
logTitle.textContent = `Log Bantuan — ${nama}`;
|
||||
logModal.classList.add('show');
|
||||
|
||||
// Isi dropdown ibadah
|
||||
logIbadahSel.innerHTML = '<option value="">-- Pilih Rumah Ibadah --</option>';
|
||||
if (typeof ibadahDataList !== 'undefined') {
|
||||
ibadahDataList.forEach(ib => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = ib.id;
|
||||
opt.textContent = `${IBADAH_EMOJI[ib.jenis] || '🕌'} ${ib.nama}`;
|
||||
logIbadahSel.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
// Set tanggal default hari ini
|
||||
document.getElementById('logTanggal').valueAsDate = new Date();
|
||||
fetchLogBantuan(miskinId);
|
||||
};
|
||||
|
||||
window.closeLogBantuanModal = function () {
|
||||
logModal.classList.remove('show');
|
||||
_logMiskinId = null;
|
||||
};
|
||||
|
||||
function fetchLogBantuan(miskinId) {
|
||||
logList.innerHTML = '<div class="panel-empty" style="padding:15px;">Memuat...</div>';
|
||||
fetch(`api/log_bantuan/read.php?miskin_id=${miskinId}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (!data.data || !data.data.length) {
|
||||
logList.innerHTML = '<div class="panel-empty" style="padding:15px;">Belum ada riwayat bantuan</div>';
|
||||
return;
|
||||
}
|
||||
logList.innerHTML = '';
|
||||
data.data.forEach(log => {
|
||||
const tgl = new Date(log.tanggal).toLocaleDateString('id-ID', { day:'2-digit', month:'short', year:'numeric' });
|
||||
const item = document.createElement('div');
|
||||
item.className = 'log-item';
|
||||
item.innerHTML = `
|
||||
<div class="log-dot"></div>
|
||||
<div class="log-info">
|
||||
<div class="log-type">${escHtml(log.tipe_bantuan)}</div>
|
||||
<div class="log-meta">${IBADAH_EMOJI[log.jenis_ibadah] || '🕌'} ${escHtml(log.nama_ibadah)} • ${tgl}</div>
|
||||
${log.keterangan ? `<div class="log-keterangan">${escHtml(log.keterangan)}</div>` : ''}
|
||||
</div>`;
|
||||
logList.appendChild(item);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
logList.innerHTML = '<div class="panel-empty" style="padding:15px; color:#ef4444;">Gagal memuat data</div>';
|
||||
});
|
||||
}
|
||||
|
||||
logSaveBtn.addEventListener('click', function () {
|
||||
const ibadah_id = logIbadahSel.value;
|
||||
const tipe_bantuan = document.getElementById('logTipeBantuan').value;
|
||||
const tanggal = document.getElementById('logTanggal').value;
|
||||
const keterangan = document.getElementById('logKeterangan').value;
|
||||
|
||||
if (!ibadah_id) { alert('Pilih rumah ibadah terlebih dahulu!'); return; }
|
||||
if (!tanggal) { alert('Tanggal harus diisi!'); return; }
|
||||
|
||||
fetch('api/log_bantuan/create.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ miskin_id: _logMiskinId, ibadah_id, tipe_bantuan, tanggal, keterangan })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
document.getElementById('logKeterangan').value = '';
|
||||
fetchLogBantuan(_logMiskinId);
|
||||
} else {
|
||||
alert(data.message || 'Gagal menyimpan');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Utility ----
|
||||
function escHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// ---- Expose ----
|
||||
window.openRightPanel = openPanel;
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user