feat: add data change notifications via 30s polling for all roles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
GuavaPopper
2026-06-04 14:52:25 +07:00
parent f77fc725a8
commit 3ecf7d937c
4 changed files with 119 additions and 0 deletions
+33
View File
@@ -391,6 +391,39 @@
setter(new Chart(ctx, config));
}
// ── NOTIFIKASI PERUBAHAN DATA ────────────────────────────────────────
(function initNotifPolling() {
const POLL_MS = 30000;
const KEY_IBADAH = 'notif_latest_ibadah';
const KEY_MISKIN = 'notif_latest_miskin';
async function pollNotifications() {
try {
const since = localStorage.getItem(KEY_IBADAH) || new Date(0).toISOString();
const res = await fetch(
`{{ url('/api/notifications') }}?since=${encodeURIComponent(since)}`,
{ headers: { 'X-CSRF-TOKEN': csrfToken } }
);
if (!res.ok) return;
const data = await res.json();
if (data.new_ibadah > 0 || data.new_miskin > 0) {
if (data.new_ibadah > 0) showToast(`${data.new_ibadah} rumah ibadah baru ditambahkan`, 'info');
if (data.new_miskin > 0) showToast(`${data.new_miskin} keluarga miskin baru ditambahkan`, 'info');
if (typeof loadDashboard !== 'undefined') loadDashboard();
}
if (data.latest_ibadah) localStorage.setItem(KEY_IBADAH, data.latest_ibadah);
if (data.latest_miskin) localStorage.setItem(KEY_MISKIN, data.latest_miskin);
} catch (e) { /* silent */ }
}
document.addEventListener('DOMContentLoaded', () => {
if (!localStorage.getItem(KEY_IBADAH)) localStorage.setItem(KEY_IBADAH, new Date().toISOString());
if (!localStorage.getItem(KEY_MISKIN)) localStorage.setItem(KEY_MISKIN, new Date().toISOString());
setInterval(pollNotifications, POLL_MS);
});
})();
function showToast(msg, type = 'info') {
const t = document.createElement('div');
const cls = type === 'success' ? 'alert-success' : type === 'error' ? 'alert-error' : 'alert-info';