feat: implement full authentication system and CRUD APIs for users, rumah ibadah, and penduduk miskin with database updates.
This commit is contained in:
+113
-220
@@ -83,203 +83,30 @@
|
||||
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 isAdmin = !!(window.currentUser && window.currentUser.role === 'admin');
|
||||
if (isAdmin) {
|
||||
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');
|
||||
});
|
||||
} else {
|
||||
rightPanelActions.innerHTML = '';
|
||||
}
|
||||
|
||||
const items = [];
|
||||
rumahIbadahLayer.eachLayer(l => {
|
||||
@@ -296,6 +123,15 @@
|
||||
const emoji = IBADAH_EMOJI[d.jenis] || '🕌';
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
let actionButtons = '';
|
||||
if (isAdmin) {
|
||||
actionButtons = `
|
||||
<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>
|
||||
`;
|
||||
}
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon card-icon-ibadah">${emoji}</div>
|
||||
@@ -303,13 +139,12 @@
|
||||
<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>
|
||||
${actionButtons}
|
||||
</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); });
|
||||
if (isAdmin) {
|
||||
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);
|
||||
});
|
||||
@@ -328,31 +163,34 @@
|
||||
// ==========================
|
||||
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>`;
|
||||
const canManage = !!(window.currentUser && (window.currentUser.role === 'admin' || window.currentUser.role === 'pengelola'));
|
||||
if (canManage) {
|
||||
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('btnPanelTambahMiskin').addEventListener('click', () => {
|
||||
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', () => {
|
||||
if (typeof window.openImportMiskinModal === 'function') {
|
||||
window.openImportMiskinModal();
|
||||
} else {
|
||||
alert('Fitur impor belum siap.');
|
||||
}
|
||||
});
|
||||
document.getElementById('btnPanelImportMiskin').addEventListener('click', () => {
|
||||
if (typeof window.openImportMiskinModal === 'function') {
|
||||
window.openImportMiskinModal();
|
||||
} else {
|
||||
alert('Fitur impor belum siap.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
rightPanelActions.innerHTML = '';
|
||||
}
|
||||
|
||||
const all = [];
|
||||
pendudukMiskinLayer.eachLayer(l => { if (l.miskinData) all.push(l.miskinData); });
|
||||
@@ -394,6 +232,7 @@
|
||||
}
|
||||
|
||||
function buildMiskinCard(d, terurus) {
|
||||
const canManage = !!(window.currentUser && (window.currentUser.role === 'admin' || window.currentUser.role === 'pengelola'));
|
||||
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>'
|
||||
@@ -402,6 +241,41 @@
|
||||
const card = document.createElement('div');
|
||||
card.className = 'data-card';
|
||||
card.dataset.miskinId = d.id;
|
||||
|
||||
let actionButtons = '';
|
||||
if (canManage) {
|
||||
actionButtons = `
|
||||
<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>
|
||||
`;
|
||||
}
|
||||
|
||||
let fotoHtml = '';
|
||||
if (d.foto_rumah || d.foto_kk) {
|
||||
fotoHtml += `<div class="expand-row-photos" style="display:flex; gap:10px; margin-top:8px; justify-content: space-around;">`;
|
||||
if (d.foto_rumah) {
|
||||
fotoHtml += `
|
||||
<div style="text-align:center;">
|
||||
<span style="font-size:10px; display:block; font-weight:500; color:#555;">Foto Rumah</span>
|
||||
<a href="uploads/${d.foto_rumah}" target="_blank">
|
||||
<img src="uploads/${d.foto_rumah}" style="width:80px; height:60px; object-fit:cover; border-radius:4px; border:1px solid #ddd; margin-top:3px;" />
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
if (d.foto_kk) {
|
||||
fotoHtml += `
|
||||
<div style="text-align:center;">
|
||||
<span style="font-size:10px; display:block; font-weight:500; color:#555;">Foto KK</span>
|
||||
<a href="uploads/${d.foto_kk}" target="_blank">
|
||||
<img src="uploads/${d.foto_kk}" style="width:80px; height:60px; object-fit:cover; border-radius:4px; border:1px solid #ddd; margin-top:3px;" />
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
fotoHtml += `</div>`;
|
||||
}
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="data-card-main">
|
||||
<div class="data-card-icon ${iconCls}">🏠</div>
|
||||
@@ -410,22 +284,22 @@
|
||||
<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>
|
||||
${actionButtons}
|
||||
</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>
|
||||
${fotoHtml}
|
||||
<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); });
|
||||
if (canManage) {
|
||||
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', () => {
|
||||
@@ -473,6 +347,18 @@
|
||||
logTitle.textContent = `Log Bantuan — ${nama}`;
|
||||
logModal.classList.add('show');
|
||||
|
||||
const isAuth = !!window.currentUser;
|
||||
const formContainer = document.querySelector('#logBantuanModal .modal-body > div:last-child');
|
||||
const modalFooter = document.querySelector('#logBantuanModal .modal-footer');
|
||||
|
||||
if (isAuth) {
|
||||
if (formContainer) formContainer.style.display = 'block';
|
||||
if (modalFooter) modalFooter.style.display = 'flex';
|
||||
} else {
|
||||
if (formContainer) formContainer.style.display = 'none';
|
||||
if (modalFooter) modalFooter.style.display = 'none';
|
||||
}
|
||||
|
||||
// Isi dropdown ibadah
|
||||
logIbadahSel.innerHTML = '<option value="">-- Pilih Rumah Ibadah --</option>';
|
||||
if (typeof ibadahDataList !== 'undefined') {
|
||||
@@ -484,6 +370,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (window.currentUser && window.currentUser.role === 'pengelola') {
|
||||
logIbadahSel.value = window.currentUser.ibadah_id;
|
||||
logIbadahSel.disabled = true;
|
||||
} else {
|
||||
logIbadahSel.disabled = false;
|
||||
}
|
||||
|
||||
// Set tanggal default hari ini
|
||||
document.getElementById('logTanggal').valueAsDate = new Date();
|
||||
fetchLogBantuan(miskinId);
|
||||
|
||||
Reference in New Issue
Block a user