feat: update dashboard recent data

This commit is contained in:
cygouw
2026-06-13 02:32:17 +07:00
parent 760884bdc6
commit 5d25dfdbf7
+86 -32
View File
@@ -231,18 +231,23 @@
<input type="hidden" id="eu-id"> <input type="hidden" id="eu-id">
<div class="form-group" style="margin-bottom:12px;text-align:left"> <div class="form-group" style="margin-bottom:12px;text-align:left">
<label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Username (Tidak dapat diubah)</label> <label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Username (Tidak
<input type="text" id="eu-username" disabled style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--border2);color:var(--text2)"> dapat diubah)</label>
<input type="text" id="eu-username" disabled
style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--border2);color:var(--text2)">
</div> </div>
<div class="form-group" style="margin-bottom:12px;text-align:left"> <div class="form-group" style="margin-bottom:12px;text-align:left">
<label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Nama Lengkap</label> <label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Nama
<input type="text" id="eu-nama" style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm)"> Lengkap</label>
<input type="text" id="eu-nama"
style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm)">
</div> </div>
<div class="form-group" style="margin-bottom:12px;text-align:left"> <div class="form-group" style="margin-bottom:12px;text-align:left">
<label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Role</label> <label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Role</label>
<select id="eu-role" style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm)"> <select id="eu-role"
style="width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius-sm)">
<option value="surveyor">Surveyor</option> <option value="surveyor">Surveyor</option>
<option value="pemangku">Pemangku Keputusan</option> <option value="pemangku">Pemangku Keputusan</option>
<option value="admin">Admin</option> <option value="admin">Admin</option>
@@ -250,16 +255,22 @@
</div> </div>
<div class="form-group" style="margin-bottom:16px;text-align:left"> <div class="form-group" style="margin-bottom:16px;text-align:left">
<label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Password Baru (Kosongkan jika tidak diubah)</label> <label style="display:block;margin-bottom:4px;font-size:12px;font-weight:600;color:var(--text2)">Password Baru
(Kosongkan jika tidak diubah)</label>
<div style="position:relative"> <div style="position:relative">
<input type="password" id="eu-pass" placeholder="min 6 karakter" style="width:100%;padding:8px 36px 8px 8px;border:1px solid var(--border);border-radius:var(--radius-sm)"> <input type="password" id="eu-pass" placeholder="min 6 karakter"
<button type="button" onclick="togglePassword('eu-pass', this)" tabindex="-1" title="Tampilkan/sembunyikan password" style="position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--text3);font-size:13px;padding:2px 4px;display:flex;align-items:center"><i class="fa-solid fa-eye"></i></button> style="width:100%;padding:8px 36px 8px 8px;border:1px solid var(--border);border-radius:var(--radius-sm)">
<button type="button" onclick="togglePassword('eu-pass', this)" tabindex="-1"
title="Tampilkan/sembunyikan password"
style="position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--text3);font-size:13px;padding:2px 4px;display:flex;align-items:center"><i
class="fa-solid fa-eye"></i></button>
</div> </div>
</div> </div>
<div class="modal-btns"> <div class="modal-btns">
<button style="background:var(--bg);color:var(--text2)" onclick="closeEditUserModal()">Batal</button> <button style="background:var(--bg);color:var(--text2)" onclick="closeEditUserModal()">Batal</button>
<button style="background:var(--blue-light);color:var(--blue);font-weight:600" onclick="confirmEditUser()">Simpan</button> <button style="background:var(--blue-light);color:var(--blue);font-weight:600"
onclick="confirmEditUser()">Simpan</button>
</div> </div>
</div> </div>
</div> </div>
@@ -302,9 +313,9 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.heat/0.2.0/leaflet-heat.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.heat/0.2.0/leaflet-heat.js"></script>
<script> <script>
// Global fetch interceptor to append JWT token for tab-session isolation // Global fetch interceptor to append JWT token for tab-session isolation
(function() { (function () {
const originalFetch = window.fetch; const originalFetch = window.fetch;
window.fetch = function(url, options) { window.fetch = function (url, options) {
options = options || {}; options = options || {};
const token = sessionStorage.getItem('user_token'); const token = sessionStorage.getItem('user_token');
if (token) { if (token) {
@@ -711,6 +722,18 @@
</div>`; </div>`;
} }
let recentCardHtml = '';
if (role === 'admin' || role === 'surveyor') {
recentCardHtml = `
<div class="card" style="margin-bottom:16px">
<div class="card-header">
<h3>Data Terbaru</h3>
<button onclick="switchPage('daftar')" style="font-size:11px;font-weight:600;color:var(--blue);border:none;background:none;cursor:pointer;">Lihat Semua</button>
</div>
<div class="card-body" id="dash-recent"></div>
</div>`;
}
if ((role === 'admin' || role === 'pemangku') && dashTab === 'analisis') { if ((role === 'admin' || role === 'pemangku') && dashTab === 'analisis') {
renderDashboardAnalisis(area, tabsHtml, s, pct); renderDashboardAnalisis(area, tabsHtml, s, pct);
return; return;
@@ -845,13 +868,7 @@
</div> </div>
</div> </div>
<div class="card" style="margin-bottom:16px"> ${recentCardHtml}
<div class="card-header">
<h3>${role === 'admin' ? 'Data Terbaru' : 'Rumah Ibadah Terdekat'}</h3>
<button onclick="switchPage('daftar')" style="font-size:11px;font-weight:600;color:var(--blue);border:none;background:none;cursor:pointer;">Lihat Semua </button>
</div>
<div class="card-body" id="dash-recent"></div>
</div>
`; `;
// Persistent map tile & marker di-cache, tidak di-destroy saat navigasi // Persistent map tile & marker di-cache, tidak di-destroy saat navigasi
setTimeout(() => { setTimeout(() => {
@@ -866,17 +883,54 @@
function renderDashRecent() { function renderDashRecent() {
const el = document.getElementById('dash-recent'); const el = document.getElementById('dash-recent');
if (!el) return; if (!el) return;
const items = cachedIbadah.slice(0, 5);
if (!items.length) { el.innerHTML = '<div class="empty-state"><div class="e-icon"><i class="fa-solid fa-place-of-worship"></i></div><p>Belum ada data rumah ibadah</p></div>'; return; } const combined = [
el.innerHTML = items.map(ib => ` ...cachedIbadah.map(item => ({ ...item, type: 'ibadah' })),
<div class="list-item" onclick="flyToOnMap(${ib.lat},${ib.lng})"> ...cachedWarga.map(item => ({ ...item, type: 'warga' }))
<div class="list-icon" style="background:${COLORS[ib.jenis]}22;color:${COLORS[ib.jenis]}"><i class="${ICONS[ib.jenis]}"></i></div> ];
combined.sort((a, b) => {
const timeA = a.created_at ? new Date(a.created_at.replace(/-/g, '/')).getTime() : 0;
const timeB = b.created_at ? new Date(b.created_at.replace(/-/g, '/')).getTime() : 0;
return timeB - timeA;
});
const items = combined.slice(0, 5);
if (!items.length) { el.innerHTML = '<div class="empty-state"><div class="e-icon"><i class="fa-solid fa-database"></i></div><p>Belum ada data terbaru</p></div>'; return; }
el.innerHTML = items.map(item => {
let iconHtml = '';
let titleHtml = '';
let subHtml = '';
if (item.type === 'ibadah') {
iconHtml = `<div class="list-icon" style="background:${COLORS[item.jenis]}22;color:${COLORS[item.jenis]}"><i class="${ICONS[item.jenis]}"></i></div>`;
titleHtml = `<div class="list-name">${h(item.nama)} <span style="font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;background:var(--border2);color:var(--text2);margin-left:6px">Rumah Ibadah</span></div>`;
subHtml = `<div class="list-sub">PIC: ${h(item.pic)} &nbsp;•&nbsp; Radius: ${item.radius}m &nbsp;•&nbsp; ${item.jumlah_kk || 0} KK</div>`;
} else {
const bantuanLabels = {
sembako: 'Sembako Bulanan',
beasiswa: 'Beasiswa Anak Asuh',
modal: 'Modal Pemberdayaan',
tunai: 'Santunan Tunai/Kesehatan',
tidak_ada: 'Belum Menerima Bantuan'
};
const bantuanTxt = bantuanLabels[item.bantuan_diterima] || 'Belum Menerima Bantuan';
iconHtml = `<div class="list-icon" style="background:#3b82f622;color:#3b82f6"><i class="fa-solid fa-people-group"></i></div>`;
titleHtml = `<div class="list-name">${h(item.nama_kk)} <span style="font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;background:var(--border2);color:var(--text2);margin-left:6px">Warga Miskin</span></div>`;
subHtml = `<div class="list-sub">NIK: ${h(item.nik || '-')} &nbsp;•&nbsp; Anggota: ${item.jumlah_anggota} jiwa &nbsp;•&nbsp; Bantuan: ${bantuanTxt}</div>`;
}
return `
<div class="list-item" onclick="flyToOnMap(${item.lat},${item.lng})">
${iconHtml}
<div class="list-body"> <div class="list-body">
<div class="list-name">${h(ib.nama)}</div> ${titleHtml}
<div class="list-sub">PIC: ${h(ib.pic)} Radius: ${ib.radius}m ${ib.jumlah_kk || 0} KK</div> ${subHtml}
</div> </div>
<div class="list-end"><span class="badge badge-${ib.status}">${statusLabel(ib.status)}</span></div> <div class="list-end"><span class="badge badge-${item.status}">${statusLabel(item.status)}</span></div>
</div>`).join(''); </div>`;
}).join('');
} }
// -- PETA PAGE ----------------------------- // -- PETA PAGE -----------------------------
@@ -2698,7 +2752,7 @@
let persistentHeatLayer = null; let persistentHeatLayer = null;
window.updatePersistentHeatmap = function() { window.updatePersistentHeatmap = function () {
const m = persistentMap; const m = persistentMap;
if (!m) return; if (!m) return;
if (persistentHeatLayer) { if (persistentHeatLayer) {
@@ -2725,13 +2779,13 @@
}).addTo(m); }).addTo(m);
}; };
window.updatePersistentHeatRadius = function(val) { window.updatePersistentHeatRadius = function (val) {
if (persistentHeatLayer) { if (persistentHeatLayer) {
persistentHeatLayer.setOptions({ radius: parseInt(val) }); persistentHeatLayer.setOptions({ radius: parseInt(val) });
} }
}; };
window.showHeatmapControls = function(m, show) { window.showHeatmapControls = function (m, show) {
const ctrlEl = document.querySelector('.heatmap-settings-control'); const ctrlEl = document.querySelector('.heatmap-settings-control');
if (ctrlEl) { if (ctrlEl) {
ctrlEl.style.display = show ? 'block' : 'none'; ctrlEl.style.display = show ? 'block' : 'none';
@@ -2871,7 +2925,7 @@
<div class="tooltip-row"> <div class="tooltip-row">
<span>Bantuan Rumah Ibadah</span> <span>Bantuan Rumah Ibadah</span>
<strong style="color:${data.bantuan_diterima && data.bantuan_diterima !== 'tidak_ada' ? 'var(--green)' : 'var(--text3)'}"> <strong style="color:${data.bantuan_diterima && data.bantuan_diterima !== 'tidak_ada' ? 'var(--green)' : 'var(--text3)'}">
${({sembako: 'Sembako Bulanan', beasiswa: 'Beasiswa Anak Asuh', modal: 'Modal Pemberdayaan', tunai: 'Santunan Tunai/Kesehatan', tidak_ada: 'Belum Menerima'}[data.bantuan_diterima] || 'Belum Menerima')} ${({ sembako: 'Sembako Bulanan', beasiswa: 'Beasiswa Anak Asuh', modal: 'Modal Pemberdayaan', tunai: 'Santunan Tunai/Kesehatan', tidak_ada: 'Belum Menerima' }[data.bantuan_diterima] || 'Belum Menerima')}
</strong> </strong>
</div> </div>
${isPemangku ? '' : `<div class="tooltip-row"> ${isPemangku ? '' : `<div class="tooltip-row">
@@ -3737,7 +3791,7 @@
<div class="pb-field"><div class="pb-label">Kecamatan</div><div class="pb-value">${h(w.kecamatan || '-')}</div></div> <div class="pb-field"><div class="pb-label">Kecamatan</div><div class="pb-value">${h(w.kecamatan || '-')}</div></div>
<div class="pb-field"><div class="pb-label">Status Pelayanan</div><div class="pb-value" style="color:${w.ibadah_id ? 'var(--green)' : 'var(--red)'}"><i class="${w.ibadah_id ? 'fa-solid fa-circle-check' : 'fa-solid fa-triangle-exclamation'}" style="margin-right:6px"></i>${w.ibadah_id ? (h(w.ibadah_nama) || 'Terlayani') : 'Belum terlayani'}</div></div> <div class="pb-field"><div class="pb-label">Status Pelayanan</div><div class="pb-value" style="color:${w.ibadah_id ? 'var(--green)' : 'var(--red)'}"><i class="${w.ibadah_id ? 'fa-solid fa-circle-check' : 'fa-solid fa-triangle-exclamation'}" style="margin-right:6px"></i>${w.ibadah_id ? (h(w.ibadah_nama) || 'Terlayani') : 'Belum terlayani'}</div></div>
</div> </div>
<div class="pb-field"><div class="pb-label">Bantuan Rumah Ibadah</div><div class="pb-value" style="font-weight:600;color:${w.bantuan_diterima && w.bantuan_diterima !== 'tidak_ada' ? 'var(--green)' : 'var(--text3)'}">${({sembako: 'Sembako Bulanan', beasiswa: 'Beasiswa Anak Asuh', modal: 'Modal Pemberdayaan', tunai: 'Santunan Tunai/Kesehatan', tidak_ada: 'Belum Menerima Bantuan'}[w.bantuan_diterima] || 'Belum Menerima Bantuan')}</div></div> <div class="pb-field"><div class="pb-label">Bantuan Rumah Ibadah</div><div class="pb-value" style="font-weight:600;color:${w.bantuan_diterima && w.bantuan_diterima !== 'tidak_ada' ? 'var(--green)' : 'var(--text3)'}">${({ sembako: 'Sembako Bulanan', beasiswa: 'Beasiswa Anak Asuh', modal: 'Modal Pemberdayaan', tunai: 'Santunan Tunai/Kesehatan', tidak_ada: 'Belum Menerima Bantuan' }[w.bantuan_diterima] || 'Belum Menerima Bantuan')}</div></div>
<div class="pb-field"><div class="pb-label">Keterangan</div><div class="pb-value">${h(w.keterangan || '-')}</div></div> <div class="pb-field"><div class="pb-label">Keterangan</div><div class="pb-value">${h(w.keterangan || '-')}</div></div>
<div class="pb-field-grid"> <div class="pb-field-grid">
<div class="pb-field"><div class="pb-label">Surveyor</div><div class="pb-value">${h(w.surveyor_nama || '-')}</div></div> <div class="pb-field"><div class="pb-label">Surveyor</div><div class="pb-value">${h(w.surveyor_nama || '-')}</div></div>