From 91fa8729704520f5a0a65ad84496d4db1ee9d2ec Mon Sep 17 00:00:00 2001 From: cygouw Date: Fri, 12 Jun 2026 16:03:35 +0700 Subject: [PATCH] feat(povertymap): make verification tables responsive, add pagination, and support hash routing --- povertymap/index.html | 132 ++++++++++++++++++++++++++++++++---------- povertymap/style.css | 65 ++++++++++++++++++++- 2 files changed, 164 insertions(+), 33 deletions(-) diff --git a/povertymap/index.html b/povertymap/index.html index 6391da0..9170c8d 100644 --- a/povertymap/index.html +++ b/povertymap/index.html @@ -11,7 +11,7 @@ - + @@ -433,6 +433,9 @@ await fetch(API.auth + '?action=logout', { method: 'POST', credentials: 'include' }); currentUser = null; + if (location.hash) { + history.replaceState(null, null, ' '); + } document.getElementById('login-screen').classList.remove('hidden'); document.getElementById('app').classList.add('hidden'); document.getElementById('login-user').value = ''; @@ -473,9 +476,12 @@ buildNav(); window.scrollTo(0, 0); - // Load data dulu, baru render dashboard sekali (mencegah double render) + // Load data dulu await loadAll(); - switchPage('dashboard'); + const hashPage = location.hash.replace('#', ''); + const validPages = getNavItems(currentUser.role).map(item => item.id); + const targetPage = validPages.includes(hashPage) ? hashPage : 'dashboard'; + switchPage(targetPage); } // -- NAV @@ -543,8 +549,23 @@ else cancelAddMode(); renderPage(page); window.scrollTo(0, 0); + + // Update URL hash + if (location.hash !== '#' + page) { + location.hash = page; + } } + // Global listener for SPA back/forward browser buttons + window.addEventListener('hashchange', () => { + if (!currentUser) return; + const hashPage = location.hash.replace('#', ''); + const validPages = getNavItems(currentUser.role).map(item => item.id); + if (validPages.includes(hashPage) && currentPage !== hashPage) { + switchPage(hashPage); + } + }); + // -- RENDER PAGES --------------------------- function renderPage(page) { const area = document.getElementById('page-content'); @@ -1409,6 +1430,8 @@ // -- VERIFY (Table layout) ------------------ let _verifyData = { ibadah: [], warga: [] }; let _verifyTab = 'ibadah'; // active tab: 'ibadah' | 'warga' + let _verifyPage = 1; + let _verifyPerPage = 10; async function renderVerify(area) { area.innerHTML = '

Memuat data pending...

'; @@ -1422,6 +1445,9 @@ // Set default tab ke yang ada datanya if (!_verifyData.ibadah.length && _verifyData.warga.length) _verifyTab = 'warga'; else _verifyTab = 'ibadah'; + + _verifyPage = 1; + if (!_verifyData.ibadah.length && !_verifyData.warga.length) { area.innerHTML = '

Semua data sudah diverifikasi!
Tidak ada data pending.

'; return; @@ -1439,7 +1465,8 @@ -
`; +
+
`; renderVerifyTabs(); renderVerifyTables(); } catch (e) { area.innerHTML = '

Gagal memuat data. Coba refresh.

'; } @@ -1447,6 +1474,7 @@ function switchVerifyTab(tab) { _verifyTab = tab; + _verifyPage = 1; renderVerifyTabs(); renderVerifyTables(); } @@ -1475,21 +1503,43 @@ : new Date(a.created_at) - new Date(b.created_at); const sec = document.getElementById('vt-table-section'); + const pgEl = document.getElementById('vt-pagination'); if (!sec) return; - if (_verifyTab === 'ibadah') { + let filtered = []; + const isIb = (_verifyTab === 'ibadah'); + + if (isIb) { const matchIb = ib => !q || [ib.nama, ib.surveyor_nama, ib.kecamatan, ib.pic, ib.alamat, ib.jenis] .some(v => (v || '').toLowerCase().includes(q)); - const pi = [..._verifyData.ibadah].filter(matchIb).sort(sorter); - const countEl = document.getElementById('vt-count'); - if (countEl) countEl.textContent = pi.length + ' pending'; + filtered = [..._verifyData.ibadah].filter(matchIb).sort(sorter); + } else { + const matchW = w => !q || [w.nama_kk, w.surveyor_nama, w.kecamatan, w.nik, w.keterangan, w.alamat] + .some(v => (v || '').toLowerCase().includes(q)); + filtered = [..._verifyData.warga].filter(matchW).sort(sorter); + } - if (!pi.length) { - sec.innerHTML = '

Tidak ada data yang cocok dengan pencarian.

'; - return; - } + const countEl = document.getElementById('vt-count'); + if (countEl) countEl.textContent = filtered.length + ' pending'; + + if (!filtered.length) { + sec.innerHTML = '

Tidak ada data yang cocok dengan pencarian.

'; + if (pgEl) pgEl.innerHTML = ''; + return; + } + + // Pagination calculation + const total = filtered.length; + const totalPages = Math.max(1, Math.ceil(total / _verifyPerPage)); + if (_verifyPage > totalPages) _verifyPage = totalPages; + if (_verifyPage < 1) _verifyPage = 1; + + const start = (_verifyPage - 1) * _verifyPerPage; + const pageData = filtered.slice(start, start + _verifyPerPage); + + if (isIb) { sec.innerHTML = ` -
+
@@ -1503,7 +1553,7 @@ - ${pi.map(ib => ` + ${pageData.map(ib => ` @@ -1534,21 +1584,9 @@
FotoAksi
${ib.foto_path @@ -1512,7 +1562,7 @@
${h(ib.nama)}
- ${ib.alamat ? `
${h(ib.alamat.substring(0, 45))}${ib.alamat.length > 45 ? ' ' : ''}
` : ''} + ${ib.alamat ? `
${h(ib.alamat.substring(0, 45))}${ib.alamat.length > 45 ? '...' : ''}
` : ''}
${h(ib.jenis)}
${ib.kecamatan ? 'Kec. ' + h(ib.kecamatan) : ' '}
`; - } else { - // Tab Warga Miskin - const matchW = w => !q || [w.nama_kk, w.surveyor_nama, w.kecamatan, w.nik, w.keterangan, w.alamat] - .some(v => (v || '').toLowerCase().includes(q)); - const pw = [..._verifyData.warga].filter(matchW).sort(sorter); - const countEl = document.getElementById('vt-count'); - if (countEl) countEl.textContent = pw.length + ' pending'; - - if (!pw.length) { - sec.innerHTML = '

Tidak ada data yang cocok dengan pencarian.

'; - return; - } sec.innerHTML = ` -
+
@@ -1562,7 +1600,7 @@ - ${pw.map(w => ` + ${pageData.map(w => `
FotoAksi
${w.foto_path @@ -1571,8 +1609,8 @@
${h(w.nama_kk)}
- ${w.alamat ? `
${h(w.alamat.substring(0, 45))}${w.alamat.length > 45 ? ' ' : ''}
` : ''} - ${w.keterangan ? `
${h(w.keterangan.substring(0, 40))}${w.keterangan.length > 40 ? ' ' : ''}
` : ''} + ${w.alamat ? `
${h(w.alamat.substring(0, 45))}${w.alamat.length > 45 ? '...' : ''}
` : ''} + ${w.keterangan ? `
${h(w.keterangan.substring(0, 40))}${w.keterangan.length > 40 ? '...' : ''}
` : ''}
${h(w.nik || ' ')}
@@ -1601,6 +1639,38 @@
`; } + + // Render Pagination Buttons + if (pgEl) { + if (totalPages <= 1) { pgEl.innerHTML = ''; return; } + let pgHtml = ``; + + let pages = []; + if (totalPages <= 7) { + for (let i = 1; i <= totalPages; i++) pages.push(i); + } else { + pages.push(1); + if (_verifyPage > 3) pages.push('...'); + for (let i = Math.max(2, _verifyPage - 1); i <= Math.min(totalPages - 1, _verifyPage + 1); i++) pages.push(i); + if (_verifyPage < totalPages - 2) pages.push('...'); + pages.push(totalPages); + } + + pages.forEach(p => { + if (p === '...') { pgHtml += `...`; } + else { pgHtml += ``; } + }); + + pgHtml += ``; + pgEl.innerHTML = pgHtml; + } + } + + function verifyGoPage(p) { + _verifyPage = p; + renderVerifyTables(); + const sec = document.getElementById('vt-tabs'); + if (sec) sec.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } function openDetailModal(type, id) { diff --git a/povertymap/style.css b/povertymap/style.css index d0d400b..192fd23 100644 --- a/povertymap/style.css +++ b/povertymap/style.css @@ -1627,7 +1627,8 @@ a { background: #fff; border: 1px solid var(--border); border-radius: var(--radius); - overflow: hidden; + overflow-x: auto; + scrollbar-width: thin; margin-bottom: 24px; } @@ -1636,6 +1637,64 @@ a { border-collapse: collapse; } +/* Column sizes for verification tables to prevent squeezing and cutting off */ +.verify-table-wrap th:nth-child(1), +.verify-table-wrap td:nth-child(1) { + width: 52px; + min-width: 52px; +} +.verify-table-wrap th:nth-child(4), +.verify-table-wrap td:nth-child(4) { + min-width: 110px; /* Kecamatan (both tables) */ +} +.verify-table-wrap th:nth-child(7), +.verify-table-wrap td:nth-child(7) { + min-width: 100px; /* Surveyor (both tables) */ +} +.verify-table-wrap th:nth-child(8), +.verify-table-wrap td:nth-child(8) { + min-width: 110px; /* Tanggal (both tables) */ +} +.verify-table-wrap th:last-child, +.verify-table-wrap td:last-child { + width: 180px; + min-width: 180px; + text-align: right; /* Aksi (both tables) */ +} + +/* -- Ibadah table specific columns -- */ +.verify-ibadah-table th:nth-child(3), +.verify-ibadah-table td:nth-child(3) { + min-width: 90px; /* Jenis */ +} +.verify-ibadah-table th:nth-child(5), +.verify-ibadah-table td:nth-child(5) { + min-width: 120px; /* PIC & WA */ +} +.verify-ibadah-table th:nth-child(6), +.verify-ibadah-table td:nth-child(6) { + width: 70px; + min-width: 70px; /* Radius (short, e.g. "500 m") */ +} + +/* -- Warga table specific columns -- */ +.verify-warga-table th:nth-child(3), +.verify-warga-table td:nth-child(3) { + min-width: 130px; /* NIK KK (16 digits) */ +} +.verify-warga-table th:nth-child(5), +.verify-warga-table td:nth-child(5) { + min-width: 80px; /* Jml Anggota (short, e.g. "2 jiwa") */ + text-align: center; +} +.verify-warga-table th:nth-child(5) { + text-align: center; +} +.verify-warga-table th:nth-child(6), +.verify-warga-table td:nth-child(6) { + min-width: 140px; /* Ibadah Terdekat (longer text) */ +} + .verify-table-wrap thead tr { background: var(--border2); border-bottom: 1px solid var(--border); @@ -1649,7 +1708,7 @@ a { letter-spacing: .5px; color: var(--text2); text-align: left; - white-space: nowrap; + white-space: normal; } .verify-table-wrap tbody tr { @@ -1670,6 +1729,8 @@ a { font-size: 13px; color: var(--text); vertical-align: middle; + word-break: break-word; + overflow-wrap: break-word; } .vt-thumb {