first commit
This commit is contained in:
+244
@@ -0,0 +1,244 @@
|
||||
/* ============================================================
|
||||
js/anggota.js — Profil individu anggota keluarga v2.1
|
||||
============================================================ */
|
||||
|
||||
window.labelHubungan = {
|
||||
kepala_keluarga:'Kepala Keluarga', istri:'Istri/Suami', anak:'Anak',
|
||||
orang_tua:'Orang Tua', saudara:'Saudara', lainnya:'Lainnya'
|
||||
};
|
||||
window.labelStatusHidup = {
|
||||
hidup:'Hidup', meninggal:'Meninggal', pindah:'Pindah', menikah_keluar:'Menikah/Keluar'
|
||||
};
|
||||
window.labelPendidikan = {
|
||||
belum_sekolah:'Belum Sekolah', paud:'PAUD/TK', sd:'SD', smp:'SMP', sma:'SMA/SMK',
|
||||
kuliah:'Kuliah/S1', pesantren:'Pesantren', pelatihan:'Pelatihan Vokasi',
|
||||
bekerja:'Bekerja', tidak_sekolah:'Tidak Sekolah'
|
||||
};
|
||||
|
||||
window.getSaranPendidikan = function (umur) {
|
||||
if (umur === null || umur < 0) return null;
|
||||
if (umur < 5) return { saran: 'PAUD/TK atau belum sekolah' };
|
||||
if (umur <= 12) return { saran: 'SD (usia ' + umur + ' tahun)' };
|
||||
if (umur <= 15) return { saran: 'SMP (usia ' + umur + ' tahun)' };
|
||||
if (umur <= 18) return { saran: 'SMA/SMK (usia ' + umur + ' tahun)' };
|
||||
if (umur <= 24) return { saran: 'Kuliah / Pelatihan Vokasi (usia ' + umur + ' tahun)' };
|
||||
return { saran: 'Pelatihan / Bekerja (usia ' + umur + ' tahun)' };
|
||||
};
|
||||
|
||||
window.validasiPendidikan = function (umur, pendidikan) {
|
||||
if (umur === null || !pendidikan) return null;
|
||||
var map = {
|
||||
belum_sekolah:[0,5], paud:[3,7], sd:[6,13], smp:[12,16],
|
||||
sma:[15,19], kuliah:[18,30], pesantren:[6,25],
|
||||
pelatihan:[15,99], bekerja:[17,99], tidak_sekolah:[0,99]
|
||||
};
|
||||
var range = map[pendidikan];
|
||||
if (!range) return null;
|
||||
if (umur < range[0] || umur > range[1]) {
|
||||
var saran = window.getSaranPendidikan(umur);
|
||||
return { cocok: false, pesan: 'Umur ' + umur + ' th tidak sesuai dengan "' +
|
||||
(window.labelPendidikan[pendidikan]||pendidikan) + '".' +
|
||||
(saran ? ' Disarankan: ' + saran.saran : '') };
|
||||
}
|
||||
return { cocok: true, pesan: null };
|
||||
};
|
||||
|
||||
window._hitungUmur = function (tglLahir) {
|
||||
if (!tglLahir) return null;
|
||||
var today = new Date(), lahir = new Date(tglLahir);
|
||||
if (isNaN(lahir.getTime())) return null;
|
||||
var u = today.getFullYear() - lahir.getFullYear();
|
||||
var m = today.getMonth() - lahir.getMonth();
|
||||
if (m < 0 || (m === 0 && today.getDate() < lahir.getDate())) u--;
|
||||
return u;
|
||||
};
|
||||
|
||||
/* ── Buka modal tambah / edit ── */
|
||||
window.openTambahAnggotaModal = function (kemiskinanId, namaKK) {
|
||||
document.getElementById('anggota-kem-id').value = kemiskinanId;
|
||||
document.getElementById('anggota-edit-id').value = '';
|
||||
document.getElementById('anggota-nama').value = '';
|
||||
document.getElementById('anggota-tgl-lahir').value = '';
|
||||
document.getElementById('anggota-jk').value = 'laki_laki';
|
||||
document.getElementById('anggota-hubungan').value = 'anak';
|
||||
document.getElementById('anggota-status').value = 'hidup';
|
||||
document.getElementById('anggota-pendidikan').value = 'belum_sekolah';
|
||||
document.getElementById('anggota-catatan').value = '';
|
||||
var s = document.getElementById('anggota-saran');
|
||||
if (s) { s.textContent = ''; s.className = 'saran-box'; s.style.display = 'none'; }
|
||||
document.getElementById('modal-anggota-title').textContent = 'Tambah Anggota — ' + window.escapeHtml(namaKK);
|
||||
|
||||
var modal = document.getElementById('modal-anggota');
|
||||
modal.style.display = 'flex';
|
||||
document.getElementById('anggota-nama').focus();
|
||||
modal.onclick = function (e) { if (e.target === this) this.style.display = 'none'; };
|
||||
|
||||
window._bindAnggotaEvents();
|
||||
document.getElementById('btn-submit-anggota').onclick = window._submitTambahAnggota;
|
||||
};
|
||||
|
||||
window.openEditAnggotaModal = function (a, namaKK) {
|
||||
document.getElementById('anggota-kem-id').value = a.kemiskinan_id;
|
||||
document.getElementById('anggota-edit-id').value = a.id;
|
||||
document.getElementById('anggota-nama').value = a.nama;
|
||||
document.getElementById('anggota-tgl-lahir').value = a.tanggal_lahir || '';
|
||||
document.getElementById('anggota-jk').value = a.jenis_kelamin;
|
||||
document.getElementById('anggota-hubungan').value = a.hubungan_keluarga;
|
||||
document.getElementById('anggota-status').value = a.status_hidup;
|
||||
document.getElementById('anggota-pendidikan').value = a.pendidikan;
|
||||
document.getElementById('anggota-catatan').value = a.catatan_khusus || '';
|
||||
document.getElementById('modal-anggota-title').textContent = 'Edit Anggota — ' + window.escapeHtml(namaKK);
|
||||
|
||||
var modal = document.getElementById('modal-anggota');
|
||||
modal.style.display = 'flex';
|
||||
document.getElementById('anggota-nama').focus();
|
||||
modal.onclick = function (e) { if (e.target === this) this.style.display = 'none'; };
|
||||
|
||||
window._bindAnggotaEvents();
|
||||
window._updateAnggotaSaran();
|
||||
document.getElementById('btn-submit-anggota').onclick = window._submitEditAnggota;
|
||||
};
|
||||
|
||||
window._bindAnggotaEvents = function () {
|
||||
var tgl = document.getElementById('anggota-tgl-lahir');
|
||||
var pend = document.getElementById('anggota-pendidikan');
|
||||
if (!tgl._bound) {
|
||||
tgl.addEventListener('change', window._updateAnggotaSaran);
|
||||
pend.addEventListener('change', window._updateAnggotaSaran);
|
||||
tgl._bound = true;
|
||||
}
|
||||
};
|
||||
|
||||
window._updateAnggotaSaran = function () {
|
||||
var tgl = document.getElementById('anggota-tgl-lahir').value;
|
||||
var pend = document.getElementById('anggota-pendidikan').value;
|
||||
var s = document.getElementById('anggota-saran');
|
||||
if (!s) return;
|
||||
var umur = window._hitungUmur(tgl);
|
||||
if (umur === null) { s.style.display = 'none'; s.textContent = ''; return; }
|
||||
s.style.display = 'block';
|
||||
var v = window.validasiPendidikan(umur, pend);
|
||||
if (!v) { s.className = 'saran-box info'; s.textContent = 'Umur: ' + umur + ' tahun'; return; }
|
||||
if (v.cocok) {
|
||||
var sar = window.getSaranPendidikan(umur);
|
||||
s.className = 'saran-box ok';
|
||||
s.textContent = '✓ Umur ' + umur + ' tahun — pendidikan sesuai.';
|
||||
} else {
|
||||
s.className = 'saran-box warn';
|
||||
s.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> ' + v.pesan;
|
||||
}
|
||||
};
|
||||
|
||||
window._submitTambahAnggota = async function () {
|
||||
var btn = document.getElementById('btn-submit-anggota');
|
||||
btn.disabled = true; btn.textContent = 'Menyimpan...';
|
||||
try {
|
||||
await window.postForm('php/create_anggota.php', window._collectAnggotaForm());
|
||||
document.getElementById('modal-anggota').style.display = 'none';
|
||||
window.showToast('Anggota berhasil ditambahkan.', 'success');
|
||||
if (window._currentDetailItem) window.openDetailPanel(window._currentDetailItem);
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
finally { btn.disabled = false; btn.textContent = 'Simpan'; }
|
||||
};
|
||||
|
||||
window._submitEditAnggota = async function () {
|
||||
var btn = document.getElementById('btn-submit-anggota');
|
||||
btn.disabled = true; btn.textContent = 'Menyimpan...';
|
||||
var payload = window._collectAnggotaForm();
|
||||
payload.id = document.getElementById('anggota-edit-id').value;
|
||||
try {
|
||||
await window.postForm('php/update_anggota.php', payload);
|
||||
document.getElementById('modal-anggota').style.display = 'none';
|
||||
window.showToast('Data anggota diperbarui.', 'success');
|
||||
if (window._currentDetailItem) window.openDetailPanel(window._currentDetailItem);
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
finally { btn.disabled = false; btn.textContent = 'Simpan'; }
|
||||
};
|
||||
|
||||
window._collectAnggotaForm = function () {
|
||||
return {
|
||||
kemiskinan_id: document.getElementById('anggota-kem-id').value,
|
||||
nama: document.getElementById('anggota-nama').value.trim(),
|
||||
tanggal_lahir: document.getElementById('anggota-tgl-lahir').value,
|
||||
jenis_kelamin: document.getElementById('anggota-jk').value,
|
||||
hubungan_keluarga: document.getElementById('anggota-hubungan').value,
|
||||
status_hidup: document.getElementById('anggota-status').value,
|
||||
pendidikan: document.getElementById('anggota-pendidikan').value,
|
||||
catatan_khusus: document.getElementById('anggota-catatan').value.trim()
|
||||
};
|
||||
};
|
||||
|
||||
window.deleteAnggota = async function (id, nama) {
|
||||
if (!confirm('Hapus anggota "' + nama + '"?')) return;
|
||||
try {
|
||||
await window.postForm('php/delete_anggota.php', { id });
|
||||
window.showToast('Anggota dihapus.', 'success');
|
||||
if (window._currentDetailItem) window.openDetailPanel(window._currentDetailItem);
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
};
|
||||
|
||||
/* ── Render tab anggota ── */
|
||||
window.renderTabAnggota = async function (item) {
|
||||
var container = document.getElementById('detail-tab-anggota');
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="loading">Memuat data anggota...</div>';
|
||||
var data = await window.fetchJson('php/read_anggota.php?kemiskinan_id=' + item.id, []);
|
||||
container.innerHTML = '';
|
||||
|
||||
if (window.appAuth.role === 'pengurus_masjid') {
|
||||
var btnAdd = document.createElement('button');
|
||||
btnAdd.className = 'btn btn-add';
|
||||
btnAdd.innerHTML = '+ Tambah Anggota';
|
||||
btnAdd.onclick = function () { window.openTambahAnggotaModal(item.id, item.nama_kepala_keluarga); };
|
||||
container.appendChild(btnAdd);
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'empty';
|
||||
empty.textContent = 'Belum ada data anggota.';
|
||||
container.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(function (a) {
|
||||
var umur = a.umur !== null ? parseInt(a.umur) : null;
|
||||
var umurTxt = umur !== null ? umur + ' tahun' : '—';
|
||||
var tglTxt = a.tgl_lahir_fmt || '—';
|
||||
var val = umur !== null ? window.validasiPendidikan(umur, a.pendidikan) : null;
|
||||
|
||||
var card = document.createElement('div');
|
||||
card.className = 'anggota-card ' + a.status_hidup;
|
||||
|
||||
/* Status chip */
|
||||
var chipColors = { hidup:'chip-green', meninggal:'chip-gray', pindah:'chip-amber', menikah_keluar:'chip-purple' };
|
||||
var chipColor = chipColors[a.status_hidup] || 'chip-gray';
|
||||
|
||||
card.innerHTML =
|
||||
'<div class="ang-head">' +
|
||||
'<span class="ang-name">' + window.escapeHtml(a.nama) + '</span>' +
|
||||
'<span class="chip ' + chipColor + '">' + (window.labelStatusHidup[a.status_hidup] || a.status_hidup) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="ang-row"><span class="ang-lbl">Hubungan</span>' + (window.labelHubungan[a.hubungan_keluarga] || a.hubungan_keluarga) + '</div>' +
|
||||
'<div class="ang-row"><span class="ang-lbl">Lahir</span>' + tglTxt + ' <em class="umur-em">(' + umurTxt + ')</em></div>' +
|
||||
'<div class="ang-row"><span class="ang-lbl">JK</span>' + (a.jenis_kelamin === 'laki_laki' ? 'Laki-laki' : 'Perempuan') + '</div>' +
|
||||
'<div class="ang-row"><span class="ang-lbl">Pendidikan</span>' + (window.labelPendidikan[a.pendidikan] || a.pendidikan) + '</div>' +
|
||||
(val && !val.cocok ? '<div class="ang-warn"><i class="fa-solid fa-triangle-exclamation"></i> ' + window.escapeHtml(val.pesan) + '</div>' : '') +
|
||||
(a.catatan_khusus ? '<div class="ang-row"><span class="ang-lbl">Catatan</span><em>' + window.escapeHtml(a.catatan_khusus) + '</em></div>' : '') +
|
||||
(a.updated_at_fmt ? '<div class="ang-row"><span class="ang-lbl">Update</span><em class="umur-em">' + a.updated_at_fmt + '</em></div>' : '');
|
||||
|
||||
if (window.appAuth.role === 'pengurus_masjid') {
|
||||
var acts = document.createElement('div');
|
||||
acts.className = 'ang-acts';
|
||||
var btnE = document.createElement('button');
|
||||
btnE.className = 'btn btn-edit'; btnE.innerHTML = '<i class="fa-solid fa-pen"></i> Edit';
|
||||
btnE.onclick = (function(aa){ return function(){ window.openEditAnggotaModal(aa, item.nama_kepala_keluarga); }; })(a);
|
||||
var btnH = document.createElement('button');
|
||||
btnH.className = 'btn btn-delete'; btnH.textContent = 'Hapus';
|
||||
btnH.onclick = (function(aa){ return function(){ window.deleteAnggota(aa.id, aa.nama); }; })(a);
|
||||
acts.appendChild(btnE); acts.appendChild(btnH);
|
||||
card.appendChild(acts);
|
||||
}
|
||||
container.appendChild(card);
|
||||
});
|
||||
};
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/* ============================================================
|
||||
js/auth.js — Login / Logout / Role management v2.1
|
||||
============================================================ */
|
||||
|
||||
window.appAuth = { loggedIn: false, nama: '', role: '' };
|
||||
|
||||
window.checkAuthState = async function () {
|
||||
try {
|
||||
var res = await fetch('php/check_auth.php', { cache: 'no-store' });
|
||||
var data = await res.json();
|
||||
window.appAuth.loggedIn = !!data.loggedIn;
|
||||
window.appAuth.nama = data.nama || '';
|
||||
window.appAuth.role = data.role || '';
|
||||
} catch (e) {
|
||||
window.appAuth = { loggedIn: false, nama: '', role: '' };
|
||||
}
|
||||
window.renderAuthBar();
|
||||
if (!window.appAuth.loggedIn) {
|
||||
window.openLoginModal();
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Render auth area di navbar ── */
|
||||
window.renderAuthBar = function () {
|
||||
var area = document.getElementById('auth-area');
|
||||
if (!area) return;
|
||||
area.innerHTML = '';
|
||||
|
||||
/* Clean up old nav buttons */
|
||||
var navMenus = document.getElementById('nav-menus');
|
||||
if (navMenus) {
|
||||
['btn-pesan-nav','btn-laporan-mgr-nav'].forEach(function(id) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.remove();
|
||||
});
|
||||
}
|
||||
|
||||
if (!window.appAuth.loggedIn) {
|
||||
/* Show Masuk button */
|
||||
var btnMasuk = document.createElement('button');
|
||||
btnMasuk.className = 'nav-btn';
|
||||
btnMasuk.id = 'btn-masuk-nav';
|
||||
btnMasuk.innerHTML = '<i class="fa-solid fa-right-to-bracket"></i> Masuk';
|
||||
btnMasuk.onclick = window.openLoginModal;
|
||||
area.appendChild(btnMasuk);
|
||||
} else {
|
||||
/* Role chip */
|
||||
var chip = document.createElement('span');
|
||||
var isPimpinan = window.appAuth.role === 'pimpinan_daerah';
|
||||
chip.className = 'role-chip ' + (isPimpinan ? 'pimpinan' : 'pengurus');
|
||||
chip.innerHTML = isPimpinan
|
||||
? '<i class="fa-solid fa-crown"></i> Pimpinan'
|
||||
: '<i class="fa-solid fa-mosque"></i> Pengurus';
|
||||
area.appendChild(chip);
|
||||
|
||||
var name = document.createElement('span');
|
||||
name.className = 'auth-name';
|
||||
name.textContent = window.appAuth.nama;
|
||||
area.appendChild(name);
|
||||
|
||||
/* Pengurus: add Pesan & Laporan buttons to nav-menus */
|
||||
if (window.appAuth.role === 'pengurus_masjid' && navMenus) {
|
||||
var btnMsg = document.createElement('button');
|
||||
btnMsg.className = 'nav-btn msg-btn';
|
||||
btnMsg.id = 'btn-pesan-nav';
|
||||
btnMsg.innerHTML = '<i class="fa-solid fa-message"></i> Pesan <span class="lumina-badge" id="nav-badge-pesan" style="display:none; background:var(--lumina-primary);"></span>';
|
||||
btnMsg.onclick = window.openPesanPanel;
|
||||
navMenus.appendChild(btnMsg);
|
||||
|
||||
var btnLap = document.createElement('button');
|
||||
btnLap.className = 'nav-btn mgr-btn';
|
||||
btnLap.id = 'btn-laporan-mgr-nav';
|
||||
btnLap.innerHTML = '<i class="fa-solid fa-clipboard-list"></i> Laporan <span class="lumina-badge" id="nav-badge-laporan" style="display:none; background:var(--lumina-error);"></span>';
|
||||
btnLap.onclick = window.openLaporanPanel;
|
||||
navMenus.appendChild(btnLap);
|
||||
}
|
||||
|
||||
var btnLogout = document.createElement('button');
|
||||
btnLogout.className = 'nav-btn logout-btn';
|
||||
btnLogout.id = 'btn-logout-nav';
|
||||
btnLogout.innerHTML = '<i class="fa-solid fa-arrow-right-from-bracket"></i> Keluar';
|
||||
btnLogout.onclick = window.doLogout;
|
||||
area.appendChild(btnLogout);
|
||||
}
|
||||
|
||||
window.updateToolbarForRole();
|
||||
|
||||
if (window.pollLaporanNotification) window.pollLaporanNotification();
|
||||
if (window.pollPesanNotification) window.pollPesanNotification();
|
||||
};
|
||||
|
||||
|
||||
/* ── Role-based toolbar control ── */
|
||||
window.updateToolbarForRole = function () {
|
||||
var editBtn = document.getElementById('luminaModeEdit');
|
||||
var menuKeluarga = document.getElementById('menu-tambah-keluarga');
|
||||
var menuIbadah = document.getElementById('menu-tambah-ibadah');
|
||||
|
||||
var isPengurus = window.appAuth && window.appAuth.loggedIn && window.appAuth.role === 'pengurus_masjid';
|
||||
|
||||
if (isPengurus) {
|
||||
if (editBtn) editBtn.style.display = 'block';
|
||||
if (menuKeluarga) menuKeluarga.style.display = 'flex';
|
||||
if (menuIbadah) menuIbadah.style.display = 'flex';
|
||||
} else {
|
||||
if (editBtn) editBtn.style.display = 'none';
|
||||
if (menuKeluarga) menuKeluarga.style.display = 'none';
|
||||
if (menuIbadah) menuIbadah.style.display = 'none';
|
||||
if (window.appState && window.appState.mode === 'edit') window.setMode('view');
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Open login modal ── */
|
||||
window.openLoginModal = function () {
|
||||
var overlay = document.getElementById('modal-login');
|
||||
if (!overlay) return;
|
||||
document.getElementById('login-username').value = '';
|
||||
document.getElementById('login-password').value = '';
|
||||
document.getElementById('login-error').textContent = '';
|
||||
overlay.style.display = 'flex';
|
||||
document.getElementById('login-username').focus();
|
||||
// Do not allow closing
|
||||
};
|
||||
|
||||
/* ── Login submit ── */
|
||||
window.doLogin = async function (e) {
|
||||
e.preventDefault();
|
||||
var btn = document.getElementById('btn-submit-login');
|
||||
var errEl = document.getElementById('login-error');
|
||||
var uname = document.getElementById('login-username').value.trim();
|
||||
var pass = document.getElementById('login-password').value;
|
||||
|
||||
if (!uname || !pass) {
|
||||
errEl.textContent = 'Username dan password wajib diisi.';
|
||||
errEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
btn.disabled = true; btn.textContent = 'Masuk...';
|
||||
errEl.textContent = '';
|
||||
errEl.style.display = 'none';
|
||||
|
||||
try {
|
||||
var res = await fetch('php/login.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({ username: uname, password: pass })
|
||||
});
|
||||
var data = await res.json();
|
||||
if (data.success) {
|
||||
window.appAuth = { loggedIn: true, nama: data.nama, role: data.role };
|
||||
document.getElementById('modal-login').style.display = 'none';
|
||||
window.renderAuthBar();
|
||||
window.loadAllData();
|
||||
window.showToast('Selamat datang, ' + data.nama + '!', 'success');
|
||||
} else {
|
||||
errEl.textContent = data.error || 'Login gagal.';
|
||||
errEl.style.display = 'block';
|
||||
}
|
||||
} catch (err) {
|
||||
errEl.textContent = 'Koneksi gagal: ' + err.message;
|
||||
errEl.style.display = 'block';
|
||||
} finally {
|
||||
btn.disabled = false; btn.innerHTML = 'Masuk Sekarang <i class="fa-solid fa-arrow-right"></i>';
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Logout ── */
|
||||
window.doLogout = async function () {
|
||||
await fetch('php/logout.php', { cache: 'no-store' });
|
||||
window.appAuth = { loggedIn: false, nama: '', role: '' };
|
||||
window.renderAuthBar();
|
||||
if (window.appState && window.appState.mode === 'edit') window.setMode('view');
|
||||
window.loadAllData();
|
||||
window.openLoginModal();
|
||||
window.showToast('Anda telah keluar.', 'info');
|
||||
};
|
||||
|
||||
/* ── Toggle Password Visibility ── */
|
||||
window.togglePassword = function () {
|
||||
var passInput = document.getElementById('login-password');
|
||||
var eyeIcon = document.getElementById('login-password-eye');
|
||||
if (passInput.type === 'password') {
|
||||
passInput.type = 'text';
|
||||
eyeIcon.classList.remove('fa-eye');
|
||||
eyeIcon.classList.add('fa-eye-slash');
|
||||
} else {
|
||||
passInput.type = 'password';
|
||||
eyeIcon.classList.remove('fa-eye-slash');
|
||||
eyeIcon.classList.add('fa-eye');
|
||||
}
|
||||
};
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/* ============================================================
|
||||
js/bantuan.js — Histori bantuan v2.1
|
||||
============================================================ */
|
||||
|
||||
window.labelJenisBantuan = {
|
||||
sembako:'Sembako', uang:'Uang/Dana', pendidikan:'Pendidikan',
|
||||
kesehatan:'Kesehatan', pakaian:'Pakaian', pelatihan:'Pelatihan', lainnya:'Lainnya'
|
||||
};
|
||||
window.labelStatusBantuan = {
|
||||
tersalur:'Tersalur', belum:'Belum Tersalur', batal:'Dibatalkan'
|
||||
};
|
||||
|
||||
/* Render tab bantuan */
|
||||
window.renderTabBantuan = async function (item) {
|
||||
var container = document.getElementById('detail-tab-bantuan');
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="loading">Memuat histori bantuan...</div>';
|
||||
|
||||
var data = await window.fetchJson('php/read_bantuan.php?kemiskinan_id=' + item.id, []);
|
||||
container.innerHTML = '';
|
||||
|
||||
/* Alert jatuh tempo */
|
||||
var jt = data.filter(function(d){ return parseInt(d.jatuh_tempo) === 1; });
|
||||
if (jt.length) {
|
||||
var alert = document.createElement('div');
|
||||
alert.className = 'bantuan-alert';
|
||||
alert.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> ' + jt.length + ' bantuan sudah jatuh tempo dan belum disalurkan!';
|
||||
container.appendChild(alert);
|
||||
}
|
||||
|
||||
if (window.appAuth.role === 'pengurus_masjid') {
|
||||
var btnAdd = document.createElement('button');
|
||||
btnAdd.className = 'btn btn-add';
|
||||
btnAdd.innerHTML = '+ Catat Bantuan Baru';
|
||||
btnAdd.onclick = function () { window.openTambahBantuanModal(item.id, item.nama_kepala_keluarga); };
|
||||
container.appendChild(btnAdd);
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'empty'; empty.textContent = 'Belum ada histori bantuan.';
|
||||
container.appendChild(empty); return;
|
||||
}
|
||||
|
||||
data.forEach(function (b) {
|
||||
var jmlTxt = b.jumlah_bantuan ? b.jumlah_bantuan + ' ' + (b.satuan || '') : '—';
|
||||
var isJt = parseInt(b.jatuh_tempo) === 1;
|
||||
|
||||
var card = document.createElement('div');
|
||||
card.className = 'b-card ' + b.status_penyaluran + (isJt ? ' jatuh' : '');
|
||||
|
||||
var chipColors = { tersalur:'chip-green', belum:'chip-amber', batal:'chip-gray' };
|
||||
|
||||
card.innerHTML =
|
||||
'<div class="b-head">' +
|
||||
'<span class="b-jenis">' + (window.labelJenisBantuan[b.jenis_bantuan] || b.jenis_bantuan) + '</span>' +
|
||||
'<span class="chip ' + (chipColors[b.status_penyaluran]||'chip-gray') + '">' + (window.labelStatusBantuan[b.status_penyaluran]||b.status_penyaluran) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="b-desc">' + window.escapeHtml(b.deskripsi_bantuan) + '</div>' +
|
||||
'<div class="b-meta">📅 ' + (b.tanggal_fmt||'—') + ' · ' + window.escapeHtml(jmlTxt) + (b.nama_masjid ? ' · ' + window.escapeHtml(b.nama_masjid) : '') + '</div>' +
|
||||
(b.periode_fmt ? '<div class="b-periode' + (isJt ? ' late' : '') + '">Berikutnya: ' + b.periode_fmt + (isJt ? ' — Jatuh Tempo!' : '') + '</div>' : '') +
|
||||
(b.dicatat_oleh ? '<div class="b-meta">Dicatat: ' + window.escapeHtml(b.dicatat_oleh) + '</div>' : '');
|
||||
|
||||
if (window.appAuth.role === 'pengurus_masjid') {
|
||||
var acts = document.createElement('div');
|
||||
acts.className = 'b-acts';
|
||||
|
||||
if (b.status_penyaluran === 'belum') {
|
||||
var btnS = document.createElement('button');
|
||||
btnS.className = 'btn btn-salur'; btnS.textContent = '✓ Tandai Tersalur';
|
||||
btnS.onclick = (function(bid){ return async function(){
|
||||
try {
|
||||
await window.postForm('php/update_bantuan_status.php', { id: bid, status_penyaluran: 'tersalur' });
|
||||
window.showToast('Bantuan ditandai tersalur.', 'success');
|
||||
window.renderTabBantuan(item);
|
||||
} catch(e){ window.showToast('Gagal: ' + e.message, 'error'); }
|
||||
}; })(b.id);
|
||||
acts.appendChild(btnS);
|
||||
|
||||
var btnBt = document.createElement('button');
|
||||
btnBt.className = 'btn btn-delete'; btnBt.textContent = 'Batalkan';
|
||||
btnBt.onclick = (function(bid){ return async function(){
|
||||
if (!confirm('Batalkan bantuan ini?')) return;
|
||||
try {
|
||||
await window.postForm('php/update_bantuan_status.php', { id: bid, status_penyaluran: 'batal' });
|
||||
window.showToast('Bantuan dibatalkan.', 'info');
|
||||
window.renderTabBantuan(item);
|
||||
} catch(e){ window.showToast('Gagal: ' + e.message, 'error'); }
|
||||
}; })(b.id);
|
||||
acts.appendChild(btnBt);
|
||||
}
|
||||
|
||||
var btnH = document.createElement('button');
|
||||
btnH.className = 'btn btn-delete'; btnH.innerHTML = '<i class="fa-solid fa-trash"></i>';
|
||||
btnH.onclick = (function(bid){ return async function(){
|
||||
if (!confirm('Hapus catatan bantuan ini?')) return;
|
||||
try {
|
||||
await window.postForm('php/delete_bantuan.php', { id: bid });
|
||||
window.showToast('Catatan dihapus.', 'success');
|
||||
window.renderTabBantuan(item);
|
||||
} catch(e){ window.showToast('Gagal: ' + e.message, 'error'); }
|
||||
}; })(b.id);
|
||||
acts.appendChild(btnH);
|
||||
card.appendChild(acts);
|
||||
}
|
||||
container.appendChild(card);
|
||||
});
|
||||
};
|
||||
|
||||
/* Modal tambah bantuan */
|
||||
window.openTambahBantuanModal = function (kemiskinanId, namaKK) {
|
||||
document.getElementById('bantuan-kem-id').value = kemiskinanId;
|
||||
document.getElementById('bantuan-jenis').value = 'sembako';
|
||||
document.getElementById('bantuan-deskripsi').value = '';
|
||||
document.getElementById('bantuan-jumlah').value = '';
|
||||
document.getElementById('bantuan-satuan').value = '';
|
||||
document.getElementById('bantuan-tanggal').value = new Date().toISOString().split('T')[0];
|
||||
document.getElementById('bantuan-periode').value = '';
|
||||
document.getElementById('bantuan-status').value = 'belum';
|
||||
document.getElementById('bantuan-dicatat').value = window.appAuth.nama || '';
|
||||
document.getElementById('modal-bantuan-title').textContent = 'Catat Bantuan — ' + namaKK;
|
||||
|
||||
var modal = document.getElementById('modal-bantuan');
|
||||
modal.style.display = 'flex';
|
||||
document.getElementById('bantuan-deskripsi').focus();
|
||||
modal.onclick = function (e) { if (e.target === this) this.style.display = 'none'; };
|
||||
|
||||
document.getElementById('btn-submit-bantuan').onclick = async function () {
|
||||
var btn = this;
|
||||
btn.disabled = true; btn.textContent = 'Menyimpan...';
|
||||
var desc = document.getElementById('bantuan-deskripsi').value.trim();
|
||||
if (!desc) { window.showToast('Deskripsi wajib diisi.', 'error'); btn.disabled=false; btn.textContent='Simpan Bantuan'; return; }
|
||||
var payload = {
|
||||
kemiskinan_id: document.getElementById('bantuan-kem-id').value,
|
||||
jenis_bantuan: document.getElementById('bantuan-jenis').value,
|
||||
deskripsi_bantuan: desc,
|
||||
jumlah_bantuan: document.getElementById('bantuan-jumlah').value,
|
||||
satuan: document.getElementById('bantuan-satuan').value.trim(),
|
||||
tanggal_bantuan: document.getElementById('bantuan-tanggal').value,
|
||||
periode_berikutnya:document.getElementById('bantuan-periode').value,
|
||||
status_penyaluran: document.getElementById('bantuan-status').value,
|
||||
dicatat_oleh: document.getElementById('bantuan-dicatat').value.trim()
|
||||
};
|
||||
try {
|
||||
await window.postForm('php/create_bantuan.php', payload);
|
||||
modal.style.display = 'none';
|
||||
window.showToast('Bantuan berhasil dicatat.', 'success');
|
||||
if (window._currentDetailItem && window._currentDetailItem.id === parseInt(payload.kemiskinan_id)) {
|
||||
window.renderTabBantuan(window._currentDetailItem);
|
||||
}
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
finally { btn.disabled=false; btn.textContent='Simpan Bantuan'; }
|
||||
};
|
||||
};
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
/* ============================================================
|
||||
js/laporan.js — Sistem pelaporan warga v2.2
|
||||
============================================================ */
|
||||
|
||||
window.labelKategoriLaporan = {
|
||||
sakit:'Sakit / Butuh Bantuan Medis', kematian:'Kematian',
|
||||
kelahiran:'Kelahiran Bayi Baru', pernikahan:'Pernikahan',
|
||||
pindah:'Pindah Tempat', darurat_ekonomi:'Darurat Ekonomi', lainnya:'Lainnya'
|
||||
};
|
||||
window.labelStatusLaporan = {
|
||||
menunggu:'Menunggu', diproses:'Diproses', selesai:'Selesai', ditolak:'Ditolak'
|
||||
};
|
||||
|
||||
var _kategoriEmoji = {
|
||||
sakit:'🤒', kematian:'💔', kelahiran:'👶', pernikahan:'💍',
|
||||
pindah:'📦', darurat_ekonomi:'🆘', lainnya:'📣'
|
||||
};
|
||||
|
||||
/* ─────────────────────────────────────────────────────
|
||||
Floating detail box
|
||||
───────────────────────────────────────────────────── */
|
||||
window.openLaporanDetail = function (laporan) {
|
||||
var fl = document.getElementById('laporan-detail-float');
|
||||
if (!fl) return;
|
||||
|
||||
var status = laporan.status_laporan || 'menunggu';
|
||||
var emoji = _kategoriEmoji[laporan.kategori] || '📣';
|
||||
var label = window.labelKategoriLaporan[laporan.kategori] || laporan.kategori;
|
||||
var statusLabel = window.labelStatusLaporan[status] || status;
|
||||
|
||||
/* Status bar colour */
|
||||
var bar = document.getElementById('ldf-status-bar');
|
||||
bar.className = 'ldf-status-bar ' + status;
|
||||
|
||||
/* Icon */
|
||||
var icon = document.getElementById('ldf-icon');
|
||||
icon.textContent = emoji;
|
||||
icon.className = 'ldf-icon ' + status;
|
||||
|
||||
/* Title / subtitle */
|
||||
document.getElementById('ldf-title').textContent = label;
|
||||
document.getElementById('ldf-subtitle').textContent = '● ' + statusLabel;
|
||||
document.getElementById('ldf-subtitle').style.color = {
|
||||
menunggu: 'var(--amber)', diproses: 'var(--blue)',
|
||||
selesai: 'var(--green)', ditolak: 'var(--text-muted)'
|
||||
}[status] || 'var(--text-muted)';
|
||||
|
||||
/* Deskripsi */
|
||||
document.getElementById('ldf-deskripsi').textContent = laporan.deskripsi || '(tidak ada deskripsi)';
|
||||
|
||||
/* KK terkait */
|
||||
var sectionKK = document.getElementById('ldf-section-kk');
|
||||
var badgeKK = document.getElementById('ldf-kk-badge');
|
||||
if (laporan.nama_kepala_keluarga) {
|
||||
badgeKK.innerHTML = '<i class="fa-solid fa-location-dot"></i> ' + laporan.nama_kepala_keluarga;
|
||||
sectionKK.style.display = '';
|
||||
} else {
|
||||
sectionKK.style.display = 'none';
|
||||
}
|
||||
|
||||
/* Info rows */
|
||||
document.getElementById('ldf-pelapor').textContent = laporan.nama_pelapor || '-';
|
||||
|
||||
var rowWA = document.getElementById('ldf-row-wa');
|
||||
if (laporan.nomor_wa) {
|
||||
document.getElementById('ldf-wa').textContent = laporan.nomor_wa;
|
||||
rowWA.style.display = '';
|
||||
} else {
|
||||
rowWA.style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('ldf-waktu').textContent = laporan.created_fmt || '-';
|
||||
|
||||
var rowDitangani = document.getElementById('ldf-row-ditangani');
|
||||
if (laporan.ditangani_oleh) {
|
||||
document.getElementById('ldf-ditangani').textContent = laporan.ditangani_oleh;
|
||||
rowDitangani.style.display = '';
|
||||
} else {
|
||||
rowDitangani.style.display = 'none';
|
||||
}
|
||||
|
||||
/* Action buttons (pengurus only) */
|
||||
var footer = document.getElementById('ldf-footer');
|
||||
footer.innerHTML = '';
|
||||
if (window.appAuth && window.appAuth.role === 'pengurus_masjid' && status !== 'selesai' && status !== 'ditolak') {
|
||||
footer.style.display = '';
|
||||
var statuses = [
|
||||
{ val: 'diproses', label: '<i class="fa-solid fa-rotate"></i> Proses', cls: 'btn-proses' },
|
||||
{ val: 'selesai', label: '<i class="fa-solid fa-check"></i> Selesai', cls: 'btn-selesai' },
|
||||
{ val: 'ditolak', label: '<i class="fa-solid fa-xmark"></i> Tolak', cls: 'btn-tolak' }
|
||||
];
|
||||
statuses.forEach(function (s) {
|
||||
if (s.val === status) return;
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'btn ' + s.cls;
|
||||
btn.innerHTML = s.label;
|
||||
btn.onclick = (function (lid, sv) {
|
||||
return async function () {
|
||||
btn.disabled = true;
|
||||
try {
|
||||
await window.postForm('php/update_laporan_status.php', {
|
||||
id: lid, status_laporan: sv, ditangani_oleh: window.appAuth.nama
|
||||
});
|
||||
window.showToast('Status diperbarui ke: ' + window.labelStatusLaporan[sv], 'success');
|
||||
window.closeLaporanDetail();
|
||||
window.loadLaporan(document.getElementById('laporan-filter-status').value);
|
||||
} catch (e) {
|
||||
window.showToast('Gagal: ' + e.message, 'error');
|
||||
btn.disabled = false;
|
||||
}
|
||||
};
|
||||
})(laporan.id, s.val);
|
||||
footer.appendChild(btn);
|
||||
});
|
||||
} else {
|
||||
footer.style.display = 'none';
|
||||
}
|
||||
|
||||
/* Close button */
|
||||
document.getElementById('ldf-close').onclick = window.closeLaporanDetail;
|
||||
|
||||
/* Show with animation */
|
||||
fl.style.display = 'flex';
|
||||
};
|
||||
|
||||
window.closeLaporanDetail = function () {
|
||||
var fl = document.getElementById('laporan-detail-float');
|
||||
if (fl) fl.style.display = 'none';
|
||||
};
|
||||
|
||||
/* ── Form laporan publik ── */
|
||||
window.openLaporanPublikModal = function (kemiskinanId, namaKK) {
|
||||
var modal = document.getElementById('modal-laporan-publik');
|
||||
document.getElementById('laporan-kem-id').value = kemiskinanId || '';
|
||||
document.getElementById('laporan-nama-pelapor').value = '';
|
||||
document.getElementById('laporan-nomor-wa').value = '';
|
||||
document.getElementById('laporan-kategori').value = 'lainnya';
|
||||
document.getElementById('laporan-deskripsi').value = '';
|
||||
document.getElementById('laporan-judul-kkname').textContent = namaKK
|
||||
? 'Laporkan Kondisi: ' + namaKK
|
||||
: 'Laporkan Kondisi Warga';
|
||||
modal.style.display = 'flex';
|
||||
document.getElementById('laporan-nama-pelapor').focus();
|
||||
modal.onclick = function (e) { if (e.target === this) this.style.display = 'none'; };
|
||||
};
|
||||
|
||||
/* ── Tombol submit laporan dipanggil dari inline HTML ── */
|
||||
window._submitLaporanBtn = async function () {
|
||||
var btn = document.getElementById('btn-submit-laporan');
|
||||
var nama = document.getElementById('laporan-nama-pelapor').value.trim();
|
||||
var desc = document.getElementById('laporan-deskripsi').value.trim();
|
||||
if (!nama || !desc) { window.showToast('Nama dan deskripsi wajib diisi.', 'error'); return; }
|
||||
|
||||
btn.disabled = true; btn.textContent = 'Mengirim...';
|
||||
var payload = {
|
||||
kemiskinan_id: document.getElementById('laporan-kem-id').value,
|
||||
nama_pelapor: nama,
|
||||
nomor_wa: document.getElementById('laporan-nomor-wa').value.trim(),
|
||||
kategori: document.getElementById('laporan-kategori').value,
|
||||
deskripsi: desc
|
||||
};
|
||||
try {
|
||||
var res = await fetch('php/create_laporan.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams(payload)
|
||||
});
|
||||
var text = (await res.text()).trim();
|
||||
if (text === 'success') {
|
||||
document.getElementById('modal-laporan-publik').style.display = 'none';
|
||||
if (window.Swal) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Laporan Terkirim',
|
||||
text: 'Laporan berhasil dikirim. Terima kasih atas informasi Anda!',
|
||||
confirmButtonText: 'Tutup'
|
||||
});
|
||||
} else {
|
||||
window.showToast('Laporan berhasil dikirim. Terima kasih!', 'success');
|
||||
}
|
||||
} else {
|
||||
window.showToast('Gagal: ' + text, 'error');
|
||||
}
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
finally { btn.disabled = false; btn.textContent = 'Kirim Laporan'; }
|
||||
};
|
||||
|
||||
/* ── Panel manajemen (pengurus) ── */
|
||||
window.openLaporanPanel = async function () {
|
||||
var panel = document.getElementById('panel-laporan');
|
||||
if (panel) {
|
||||
var sidebar = document.getElementById('sidebar');
|
||||
if (sidebar) sidebar.style.display = 'none';
|
||||
var pes = document.getElementById('panel-pesan');
|
||||
if (pes) pes.style.display = 'none';
|
||||
|
||||
panel.style.display = 'flex';
|
||||
document.getElementById('laporan-filter-status').value = 'menunggu';
|
||||
window.loadLaporan('menunggu');
|
||||
}
|
||||
};
|
||||
window.closeLaporanPanel = function () {
|
||||
var p = document.getElementById('panel-laporan');
|
||||
if (p) p.style.display = 'none';
|
||||
window.closeLaporanDetail();
|
||||
|
||||
// Auto-open sidebar
|
||||
var sidebar = document.getElementById('sidebar');
|
||||
if (sidebar) sidebar.style.display = 'flex';
|
||||
};
|
||||
|
||||
window.loadLaporan = async function (statusFilter) {
|
||||
var container = document.getElementById('laporan-list-container');
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="loading">Memuat laporan...</div>';
|
||||
|
||||
var url = 'php/read_laporan.php' + (statusFilter ? '?status=' + statusFilter : '');
|
||||
var data = await window.fetchJson(url, []);
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!data.length) {
|
||||
container.innerHTML = '<div class="empty">Tidak ada laporan.</div>'; return;
|
||||
}
|
||||
|
||||
data.forEach(function (l) {
|
||||
var chipColors = { menunggu:'chip-amber', diproses:'chip-blue', selesai:'chip-green', ditolak:'chip-gray' };
|
||||
var emoji = _kategoriEmoji[l.kategori] || '📣';
|
||||
|
||||
var card = document.createElement('div');
|
||||
card.className = 'l-card ' + l.status_laporan;
|
||||
card.title = 'Klik untuk detail lengkap';
|
||||
|
||||
var badgeCls = { menunggu:'chip-amber', diproses:'chip-blue', selesai:'chip-green', ditolak:'chip-gray' }[l.status_laporan] || 'chip-gray';
|
||||
var kkHtml = l.nama_kepala_keluarga
|
||||
? '<div class="l-kk"><i class="fa-solid fa-location-dot"></i> KK: ' + window.escapeHtml(l.nama_kepala_keluarga) + '</div>' : '';
|
||||
|
||||
card.innerHTML =
|
||||
'<div class="l-head">' +
|
||||
'<span class="l-cat">' + emoji + ' ' + (window.labelKategoriLaporan[l.kategori] || l.kategori) + '</span>' +
|
||||
'<span class="chip ' + badgeCls + '">' + window.escapeHtml(window.labelStatusLaporan[l.status_laporan] || l.status_laporan) + '</span>' +
|
||||
'</div>' +
|
||||
kkHtml +
|
||||
'<div class="l-desc">' + window.escapeHtml(l.deskripsi) + '</div>' +
|
||||
'<div class="l-meta"><i class="fa-solid fa-user"></i> ' + window.escapeHtml(l.nama_pelapor) +
|
||||
(l.nomor_wa ? ' · <i class="fa-solid fa-mobile-screen"></i> ' + window.escapeHtml(l.nomor_wa) : '') +
|
||||
' · <i class="fa-regular fa-clock"></i> ' + l.created_fmt + '</div>' +
|
||||
(l.ditangani_oleh ? '<div class="l-meta"><i class="fa-solid fa-check"></i> Ditangani: ' + window.escapeHtml(l.ditangani_oleh) + '</div>' : '');
|
||||
|
||||
/* Klik kartu → buka floating detail */
|
||||
card.addEventListener('click', function (e) {
|
||||
/* Jangan trigger jika yang diklik tombol aksi */
|
||||
if (e.target.closest('button')) return;
|
||||
window.openLaporanDetail(l);
|
||||
});
|
||||
|
||||
if (window.appAuth.role === 'pengurus_masjid' && l.status_laporan !== 'selesai' && l.status_laporan !== 'ditolak') {
|
||||
var acts = document.createElement('div');
|
||||
acts.className = 'l-acts';
|
||||
var statuses = [
|
||||
{ val: 'diproses', label: 'Proses', cls: 'btn-proses' },
|
||||
{ val: 'selesai', label: 'Selesai', cls: 'btn-selesai' },
|
||||
{ val: 'ditolak', label: 'Tolak', cls: 'btn-tolak' }
|
||||
];
|
||||
statuses.forEach(function (s) {
|
||||
if (s.val === l.status_laporan) return;
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'btn ' + s.cls; btn.textContent = s.label;
|
||||
btn.onclick = (function(lid, sv){ return async function(){
|
||||
try {
|
||||
await window.postForm('php/update_laporan_status.php', {
|
||||
id: lid, status_laporan: sv, ditangani_oleh: window.appAuth.nama
|
||||
});
|
||||
window.showToast('Status diperbarui.', 'success');
|
||||
window.closeLaporanDetail();
|
||||
window.loadLaporan(document.getElementById('laporan-filter-status').value);
|
||||
} catch(e){ window.showToast('Gagal: ' + e.message, 'error'); }
|
||||
}; })(l.id, s.val);
|
||||
acts.appendChild(btn);
|
||||
});
|
||||
card.appendChild(acts);
|
||||
}
|
||||
container.appendChild(card);
|
||||
});
|
||||
};
|
||||
|
||||
/* Polling for Laporan Notification */
|
||||
window.pollLaporanNotification = async function() {
|
||||
if (!window.appAuth || window.appAuth.role !== 'pengurus_masjid') {
|
||||
var np = document.getElementById('notif-panel');
|
||||
if (np) np.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var res = await fetch('php/read_laporan.php');
|
||||
var text = await res.text();
|
||||
var data = JSON.parse(text);
|
||||
if(Array.isArray(data)) {
|
||||
var pendingCount = data.filter(function(l) { return l.status_laporan === 'menunggu'; }).length;
|
||||
|
||||
var badge = document.getElementById('nav-badge-laporan');
|
||||
if(badge) {
|
||||
if(pendingCount > 0) {
|
||||
badge.style.display = 'inline-flex';
|
||||
badge.textContent = pendingCount;
|
||||
} else {
|
||||
badge.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
var np = document.getElementById('notif-panel');
|
||||
var nb = document.getElementById('notif-body');
|
||||
if (np && nb) {
|
||||
if(pendingCount > 0) {
|
||||
var isPanelOpen = document.getElementById('panel-laporan') && document.getElementById('panel-laporan').style.display === 'flex';
|
||||
if(!isPanelOpen && (!window._notifDismissedForCount || window._notifDismissedForCount < pendingCount)) {
|
||||
np.style.display = 'block';
|
||||
nb.innerHTML = 'Terdapat <b>' + pendingCount + ' laporan baru</b> dari warga yang belum diproses.<br>Harap segera ditindaklanjuti!';
|
||||
window._notifDismissedForCount = pendingCount;
|
||||
}
|
||||
} else {
|
||||
np.style.display = 'none';
|
||||
window._notifDismissedForCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
// Start polling every 10 seconds
|
||||
setInterval(window.pollLaporanNotification, 10000);
|
||||
+1105
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,377 @@
|
||||
/* ============================================================
|
||||
map.js – WebGIS Pemetaan Kemiskinan & Masjid (SRS v1.0)
|
||||
============================================================ */
|
||||
|
||||
window.appState = {
|
||||
mode: "view",
|
||||
activeType: "kemiskinan",
|
||||
layers: {},
|
||||
layerVisibility: {
|
||||
kemiskinan: true,
|
||||
masjid: true
|
||||
},
|
||||
isHeatmap: false,
|
||||
isRouting: false,
|
||||
routingWaypoints: []
|
||||
};
|
||||
|
||||
/* ── Utility: escape HTML ── */
|
||||
window.escapeHtml = function (value) {
|
||||
return String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
};
|
||||
|
||||
/* ── Utility: SVG pin marker ── */
|
||||
window.createPinIcon = function (bgColor, svgSymbol) {
|
||||
var svg = [
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="42" viewBox="0 0 32 42">',
|
||||
'<defs>',
|
||||
'<filter id="shadow" x="-30%" y="-20%" width="160%" height="160%">',
|
||||
'<feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="rgba(0,0,0,0.35)"/>',
|
||||
'</filter>',
|
||||
'</defs>',
|
||||
/* Pin body */
|
||||
'<path d="M16 2C9.37 2 4 7.37 4 14c0 9 12 26 12 26S28 23 28 14C28 7.37 22.63 2 16 2z"',
|
||||
'fill="' + bgColor + '" filter="url(#shadow)"/>',
|
||||
/* White inner circle */
|
||||
'<circle cx="16" cy="14" r="8" fill="rgba(255,255,255,0.25)"/>',
|
||||
/* Symbol */
|
||||
svgSymbol,
|
||||
'</svg>'
|
||||
].join('');
|
||||
return L.divIcon({
|
||||
className: 'custom-pin-icon',
|
||||
html: svg,
|
||||
iconSize: [32, 42],
|
||||
iconAnchor: [16, 42],
|
||||
popupAnchor: [0, -44]
|
||||
});
|
||||
};
|
||||
|
||||
/* Kemiskinan – merah (tidak terjangkau) */
|
||||
window.redIcon = window.createPinIcon('#dc2626',
|
||||
'<text x="16" y="19" text-anchor="middle" font-size="11" fill="white" font-family="Arial">👪</text>'
|
||||
);
|
||||
/* Kemiskinan – hijau (terjangkau) */
|
||||
window.greenIcon = window.createPinIcon('#16a34a',
|
||||
'<text x="16" y="19" text-anchor="middle" font-size="11" fill="white" font-family="Arial">👪</text>'
|
||||
);
|
||||
/* Masjid – biru */
|
||||
window.blueIcon = window.createPinIcon('#1d4ed8',
|
||||
'<text x="16" y="19" text-anchor="middle" font-size="11" fill="white" font-family="Arial">🕌</text>'
|
||||
);
|
||||
/* Fallback orange (tidak digunakan, tapi tetap didefinisikan) */
|
||||
window.orangeIcon = window.createPinIcon('#f97316',
|
||||
'<text x="16" y="19" text-anchor="middle" font-size="11" fill="white" font-family="Arial">👪</text>'
|
||||
);
|
||||
|
||||
/* ── Utility: POST form-encoded ── */
|
||||
window.postForm = async function (url, payload) {
|
||||
var response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams(payload)
|
||||
});
|
||||
var text = (await response.text()).trim();
|
||||
if (!response.ok || text !== "success") {
|
||||
throw new Error(text || "Permintaan gagal.");
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Navbar hint ── */
|
||||
window.updateMapHint = function () {
|
||||
var hint = document.getElementById("mapHint");
|
||||
if (!hint) { return; }
|
||||
|
||||
if (appState.mode === "view") {
|
||||
hint.textContent = "Mode Lihat aktif — data keluarga prasejahtera & rumah ibadah ditampilkan. Klik kartu untuk navigasi peta.";
|
||||
return;
|
||||
}
|
||||
if (appState.activeType === "kemiskinan") {
|
||||
hint.textContent = "Mode Edit aktif — klik titik di peta untuk menambahkan data keluarga prasejahtera.";
|
||||
return;
|
||||
}
|
||||
hint.textContent = "Mode Edit aktif — klik titik di peta untuk menambahkan data rumah ibadah baru.";
|
||||
};
|
||||
|
||||
/* ── Toolbar state ── */
|
||||
window.updateToolbarState = function () {
|
||||
var states = {
|
||||
modeViewBtn: appState.mode === "view",
|
||||
modeEditBtn: appState.mode === "edit",
|
||||
typeKemiskinanBtn: appState.activeType === "kemiskinan",
|
||||
typeMasjidBtn: appState.activeType === "masjid"
|
||||
};
|
||||
Object.keys(states).forEach(function (id) {
|
||||
var btn = document.getElementById(id);
|
||||
if (!btn) { return; }
|
||||
btn.classList.toggle("active", states[id]);
|
||||
/* Mode buttons get a different active colour */
|
||||
if (id === "modeEditBtn" && states[id]) {
|
||||
btn.classList.add("mode-active");
|
||||
} else {
|
||||
btn.classList.remove("mode-active");
|
||||
}
|
||||
});
|
||||
|
||||
var viewBtn = document.getElementById("luminaModeView");
|
||||
var editBtn = document.getElementById("luminaModeEdit");
|
||||
if (viewBtn && editBtn) {
|
||||
viewBtn.style.opacity = appState.mode === "view" ? "1" : "0.5";
|
||||
viewBtn.style.boxShadow = appState.mode === "view" ? "0 4px 12px rgba(99,102,241,0.25)" : "none";
|
||||
|
||||
editBtn.style.opacity = appState.mode === "edit" ? "1" : "0.5";
|
||||
editBtn.style.boxShadow = appState.mode === "edit" ? "0 4px 12px rgba(20,184,166,0.25)" : "none";
|
||||
}
|
||||
};
|
||||
|
||||
window.setActiveMenu = function(element) {
|
||||
var items = document.querySelectorAll('.lumina-menu-item');
|
||||
items.forEach(function(item) {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
element.classList.add('active');
|
||||
};
|
||||
|
||||
/* ── Layer visibility ── */
|
||||
window.setLayerVisibility = function (key, isVisible) {
|
||||
appState.layerVisibility[key] = !!isVisible;
|
||||
var layer = appState.layers[key];
|
||||
if (!layer || !window.map) { return; }
|
||||
if (isVisible) {
|
||||
if (!map.hasLayer(layer)) { map.addLayer(layer); }
|
||||
} else {
|
||||
if (map.hasLayer(layer)) { map.removeLayer(layer); }
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Mode & type switches ── */
|
||||
window.setMode = function (mode) {
|
||||
appState.mode = mode;
|
||||
window.updateToolbarState();
|
||||
window.updateMapHint();
|
||||
|
||||
var mapEl = document.getElementById("map");
|
||||
if (mapEl) {
|
||||
if (mode === "edit") {
|
||||
mapEl.classList.add("edit-mode");
|
||||
} else {
|
||||
mapEl.classList.remove("edit-mode");
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.loadAllData === "function") {
|
||||
window.loadAllData();
|
||||
}
|
||||
};
|
||||
|
||||
window.setDataType = function (type) {
|
||||
appState.activeType = type;
|
||||
window.updateToolbarState();
|
||||
window.updateMapHint();
|
||||
if (typeof window.filterSidebar === 'function') {
|
||||
window.filterSidebar(type);
|
||||
}
|
||||
};
|
||||
|
||||
window.filterSidebar = function(type) {
|
||||
var secKem = document.getElementById('sec-kemiskinan');
|
||||
var secMsj = document.getElementById('sec-masjid');
|
||||
|
||||
if (secKem) secKem.style.display = (type === 'semua' || type === 'kemiskinan') ? 'block' : 'none';
|
||||
if (secMsj) secMsj.style.display = (type === 'semua' || type === 'masjid') ? 'block' : 'none';
|
||||
|
||||
var btnSemua = document.getElementById('filter-btn-semua');
|
||||
var btnKem = document.getElementById('filter-btn-kemiskinan');
|
||||
var btnMsj = document.getElementById('filter-btn-masjid');
|
||||
|
||||
if (btnSemua) btnSemua.classList.toggle('active', type === 'semua');
|
||||
if (btnKem) btnKem.classList.toggle('active', type === 'kemiskinan');
|
||||
if (btnMsj) btnMsj.classList.toggle('active', type === 'masjid');
|
||||
|
||||
var sidebarSub = document.getElementById('sidebar-sub');
|
||||
if (sidebarSub) {
|
||||
if (type === 'semua') sidebarSub.textContent = 'Menampilkan semua data';
|
||||
else if (type === 'kemiskinan') sidebarSub.textContent = 'Menampilkan list Data Keluarga';
|
||||
else sidebarSub.textContent = 'Menampilkan list Rumah Ibadah';
|
||||
}
|
||||
|
||||
var sidebar = document.getElementById('sidebar');
|
||||
if (sidebar && sidebar.style.display === 'none') {
|
||||
sidebar.style.display = 'flex';
|
||||
var pes = document.getElementById('panel-pesan');
|
||||
if (pes) pes.style.display = 'none';
|
||||
var lap = document.getElementById('panel-laporan');
|
||||
if (lap) lap.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/* ── Format helpers ── */
|
||||
window.formatMeters = function (value) {
|
||||
return Number(value).toLocaleString("id-ID", { maximumFractionDigits: 0 }) + " m";
|
||||
};
|
||||
|
||||
window.isValidCoordinate = function (lat, lng) {
|
||||
return Number.isFinite(Number(lat)) && Number.isFinite(Number(lng));
|
||||
};
|
||||
|
||||
window.isValidRadius = function (radius) {
|
||||
var r = Number(radius);
|
||||
return Number.isFinite(r) && r >= 100 && r <= 5000;
|
||||
};
|
||||
|
||||
/* ─────────────────────────────────────────────
|
||||
DOMContentLoaded – inisialisasi peta (KF-01)
|
||||
───────────────────────────────────────────── */
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
/* Inisialisasi peta terpusat di Pontianak */
|
||||
window.map = L.map("map").setView([-0.0263, 109.3425], 13);
|
||||
|
||||
// --- Base maps ---
|
||||
window.osmBase = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
});
|
||||
window.darkBase = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
|
||||
attribution: '© OpenStreetMap contributors © CARTO'
|
||||
});
|
||||
|
||||
// Add default basemap
|
||||
window.osmBase.addTo(map);
|
||||
|
||||
/* ── Layers ── */
|
||||
appState.layers.kemiskinan = L.layerGroup().addTo(map);
|
||||
appState.layers.masjid = L.layerGroup().addTo(map);
|
||||
appState.layers.heatmap = L.layerGroup(); // Do not add yet
|
||||
|
||||
/* Klik peta – hanya aktif di Mode Edit (KF-03, KF-07) */
|
||||
map.on("click", function (event) {
|
||||
if (appState.mode !== "edit") { return; }
|
||||
|
||||
var lat = event.latlng.lat;
|
||||
var lng = event.latlng.lng;
|
||||
|
||||
if (appState.activeType === "kemiskinan") {
|
||||
if (typeof window.openKemiskinanModal === "function") {
|
||||
window.openKemiskinanModal(lat, lng);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (appState.activeType === "masjid") {
|
||||
if (typeof window.openMasjidModal === "function") {
|
||||
window.openMasjidModal(lat, lng);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Init state awal */
|
||||
window.setMode("view");
|
||||
window.setDataType("kemiskinan");
|
||||
|
||||
setTimeout(function () { map.invalidateSize(); }, 300);
|
||||
|
||||
/* Cek session login, lalu load data */
|
||||
if (typeof window.checkAuthState === "function") {
|
||||
window.checkAuthState().then(function () {
|
||||
if (typeof window.loadAllData === "function") { window.loadAllData(); }
|
||||
});
|
||||
} else if (typeof window.loadAllData === "function") {
|
||||
window.loadAllData();
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Heatmap & Routing Features ── */
|
||||
window.toggleHeatmap = function(btnEl) {
|
||||
if(!window.appState) return;
|
||||
window.appState.isHeatmap = !window.appState.isHeatmap;
|
||||
btnEl.classList.toggle('active', window.appState.isHeatmap);
|
||||
|
||||
if(window.appState.isHeatmap) {
|
||||
document.getElementById('desc-heatmap').style.display = 'block';
|
||||
// Hide kemiskinan markers
|
||||
window.appState.layers.kemiskinan.eachLayer(function(l) { l.setOpacity(0); });
|
||||
// Show heatmap
|
||||
if(!window.map.hasLayer(window.appState.layers.heatmap)) {
|
||||
window.appState.layers.heatmap.addTo(window.map);
|
||||
}
|
||||
// Switch to Dark mode for better visual
|
||||
window.map.removeLayer(window.osmBase);
|
||||
window.darkBase.addTo(window.map);
|
||||
} else {
|
||||
document.getElementById('desc-heatmap').style.display = 'none';
|
||||
// Show kemiskinan markers
|
||||
window.appState.layers.kemiskinan.eachLayer(function(l) { l.setOpacity(1); });
|
||||
// Hide heatmap
|
||||
if(window.map.hasLayer(window.appState.layers.heatmap)) {
|
||||
window.map.removeLayer(window.appState.layers.heatmap);
|
||||
}
|
||||
// Restore map base
|
||||
window.map.removeLayer(window.darkBase);
|
||||
window.osmBase.addTo(window.map);
|
||||
}
|
||||
};
|
||||
|
||||
window.toggleRoutingMode = function(btnEl) {
|
||||
if(!window.appState) return;
|
||||
window.appState.isRouting = true;
|
||||
window.appState.routingWaypoints = [];
|
||||
btnEl.classList.add('active');
|
||||
document.getElementById('desc-routing').style.display = 'block';
|
||||
|
||||
// Show instruction panel
|
||||
document.getElementById('routing-instruction-panel').style.display = 'block';
|
||||
document.getElementById('routing-instruction-text').innerHTML = 'Silakan klik <b>Masjid</b> atau <b>Rumah Warga</b> di peta sebagai titik awal rute.';
|
||||
};
|
||||
|
||||
window.cancelRouting = function() {
|
||||
if(!window.appState) return;
|
||||
window.appState.isRouting = false;
|
||||
window.appState.routingWaypoints = [];
|
||||
document.getElementById('btn-toggle-routing').classList.remove('active');
|
||||
document.getElementById('desc-routing').style.display = 'none';
|
||||
document.getElementById('routing-instruction-panel').style.display = 'none';
|
||||
|
||||
// Remove routing control if exists
|
||||
if(window.routingControl) {
|
||||
window.map.removeControl(window.routingControl);
|
||||
window.routingControl = null;
|
||||
}
|
||||
};
|
||||
|
||||
window.handleRoutingClick = function(lat, lng, name) {
|
||||
window.appState.routingWaypoints.push(L.latLng(lat, lng));
|
||||
var pts = window.appState.routingWaypoints;
|
||||
var inst = document.getElementById('routing-instruction-text');
|
||||
|
||||
if(pts.length === 1) {
|
||||
inst.innerHTML = '<div style="margin-bottom:6px; color:#16a34a;"><i class="fa-solid fa-check"></i> Titik Awal: <b>' + window.escapeHtml(name) + '</b></div>' +
|
||||
'<div style="margin-bottom:6px;"><b>Langkah 2:</b> Pilih Tujuan</div>' +
|
||||
'<span style="color:var(--lumina-text-muted);">Klik rumah/masjid lain di peta sebagai tujuan akhir.</span>';
|
||||
} else if(pts.length === 2) {
|
||||
inst.innerHTML = '<div style="margin-bottom:6px; color:#16a34a;"><i class="fa-solid fa-check"></i> Rute Berhasil Dibuat</div>' +
|
||||
'<span style="color:var(--lumina-text-muted);">Detail arah jalan (navigasi) muncul di layar sebelah kanan peta.</span>';
|
||||
|
||||
// Remove existing control
|
||||
if(window.routingControl) {
|
||||
window.map.removeControl(window.routingControl);
|
||||
}
|
||||
|
||||
window.routingControl = L.Routing.control({
|
||||
waypoints: pts,
|
||||
routeWhileDragging: false,
|
||||
addWaypoints: false,
|
||||
show: true,
|
||||
lineOptions: {
|
||||
styles: [{color: '#2563eb', opacity: 0.8, weight: 6}]
|
||||
}
|
||||
}).addTo(window.map);
|
||||
|
||||
window.appState.isRouting = false; // Turn off click interception after 2 points
|
||||
document.getElementById('btn-toggle-routing').classList.remove('active');
|
||||
}
|
||||
};
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/* ============================================================
|
||||
js/pesan.js — Media komunikasi pengurus v2.2
|
||||
============================================================ */
|
||||
|
||||
window.labelJenisPesan = {
|
||||
permintaan_bantuan: 'Permintaan Bantuan',
|
||||
koordinasi: 'Koordinasi',
|
||||
pengumuman: 'Pengumuman'
|
||||
};
|
||||
|
||||
var _jenisEmoji = {
|
||||
permintaan_bantuan: '🆘',
|
||||
koordinasi: '📋',
|
||||
pengumuman: '📢'
|
||||
};
|
||||
|
||||
/* ─────────────────────────────────────────────────────
|
||||
Floating detail box
|
||||
───────────────────────────────────────────────────── */
|
||||
window.openPesanDetail = async function (p) {
|
||||
var fl = document.getElementById('pesan-detail-float');
|
||||
if (!fl) return;
|
||||
|
||||
var jenis = p.jenis || 'koordinasi';
|
||||
var emoji = _jenisEmoji[jenis] || '💬';
|
||||
var jenisLabel = window.labelJenisPesan[jenis] || jenis;
|
||||
var isBroadcast = !p.penerima_id;
|
||||
var roleLabel = p.role_pengirim === 'pimpinan_daerah' ? '<i class="fa-solid fa-crown"></i> Pimpinan Daerah' : '<i class="fa-solid fa-mosque"></i> Pengurus Masjid';
|
||||
|
||||
/* Accent bar */
|
||||
document.getElementById('pdf-accent-bar').className = 'pdf-accent-bar ' + jenis;
|
||||
|
||||
/* Icon */
|
||||
var icon = document.getElementById('pdf-icon');
|
||||
icon.textContent = emoji;
|
||||
icon.className = 'pdf-icon ' + jenis;
|
||||
|
||||
/* Title (judul pesan) + subtitle (jenis) */
|
||||
document.getElementById('pdf-title').textContent = p.judul || 'Pesan';
|
||||
document.getElementById('pdf-subtitle').textContent = jenisLabel;
|
||||
|
||||
/* Isi */
|
||||
document.getElementById('pdf-isi').textContent = p.isi || '(tidak ada isi)';
|
||||
|
||||
/* Info rows */
|
||||
document.getElementById('pdf-pengirim').textContent = p.nama_pengirim || '-';
|
||||
document.getElementById('pdf-role').innerHTML = roleLabel;
|
||||
document.getElementById('pdf-jenis-label').textContent = emoji + ' ' + jenisLabel;
|
||||
document.getElementById('pdf-waktu').textContent = p.created_fmt || '-';
|
||||
|
||||
/* Broadcast badge */
|
||||
document.getElementById('pdf-row-broadcast').style.display = isBroadcast ? '' : 'none';
|
||||
|
||||
/* Baru badge */
|
||||
document.getElementById('pdf-row-baru').style.display = (p.status === 'baru') ? '' : 'none';
|
||||
|
||||
/* Close */
|
||||
document.getElementById('pdf-close').onclick = window.closePesanDetail;
|
||||
|
||||
/* Show */
|
||||
fl.style.display = 'flex';
|
||||
|
||||
/* Mark as read if it is new and we are not the sender */
|
||||
if (p.status === 'baru' && p.pengirim_id != window.appAuth.id) {
|
||||
try {
|
||||
var formData = new FormData();
|
||||
formData.append('id', p.id);
|
||||
formData.append('status', 'dibaca');
|
||||
await fetch('php/update_pesan_status.php', { method: 'POST', body: formData });
|
||||
|
||||
p.status = 'dibaca';
|
||||
|
||||
// Remove 'baru' indicator from card visually
|
||||
var cardEl = document.querySelector('.p-card[data-id="' + p.id + '"]');
|
||||
if (cardEl) {
|
||||
cardEl.classList.remove('baru');
|
||||
var dot = cardEl.querySelector('.p-new');
|
||||
if (dot) dot.remove();
|
||||
}
|
||||
|
||||
// Immediately update the notification badge
|
||||
if (window.pollPesanNotification) window.pollPesanNotification();
|
||||
} catch (e) {
|
||||
console.error('Failed to update message status', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.closePesanDetail = function () {
|
||||
var fl = document.getElementById('pesan-detail-float');
|
||||
if (fl) fl.style.display = 'none';
|
||||
};
|
||||
|
||||
/* ── Panel open/close ── */
|
||||
window.openPesanPanel = async function () {
|
||||
var panel = document.getElementById('panel-pesan');
|
||||
if (panel) {
|
||||
var sidebar = document.getElementById('sidebar');
|
||||
if (sidebar) sidebar.style.display = 'none';
|
||||
var lap = document.getElementById('panel-laporan');
|
||||
if (lap) lap.style.display = 'none';
|
||||
panel.style.display = 'flex';
|
||||
await window.loadPesan();
|
||||
}
|
||||
};
|
||||
window.closePesanPanel = function () {
|
||||
var p = document.getElementById('panel-pesan');
|
||||
if (p) p.style.display = 'none';
|
||||
window.closePesanDetail();
|
||||
|
||||
// Auto-open sidebar
|
||||
var sidebar = document.getElementById('sidebar');
|
||||
if (sidebar) sidebar.style.display = 'flex';
|
||||
};
|
||||
|
||||
/* ── Load & render pesan ── */
|
||||
window.loadPesan = async function () {
|
||||
var container = document.getElementById('pesan-list-container');
|
||||
if (!container) return;
|
||||
container.innerHTML = '<div class="loading">Memuat pesan...</div>';
|
||||
var data = await window.fetchJson('php/read_pesan.php', []);
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!data.length) {
|
||||
container.innerHTML = '<div class="empty">Belum ada pesan. Mulai komunikasi di bawah.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach(function (p) {
|
||||
var jenisClass = { permintaan_bantuan:'bantuan', koordinasi:'', pengumuman:'pengumuman' }[p.jenis] || '';
|
||||
var chipColors = { permintaan_bantuan:'chip-red', koordinasi:'chip-blue', pengumuman:'chip-amber' };
|
||||
var isBroadcast = !p.penerima_id;
|
||||
var emoji = _jenisEmoji[p.jenis] || '💬';
|
||||
|
||||
var card = document.createElement('div');
|
||||
card.className = 'p-card' + (jenisClass ? ' ' + jenisClass : '') + (p.status === 'baru' ? ' baru' : '');
|
||||
card.title = 'Klik untuk detail lengkap';
|
||||
card.dataset.id = p.id;
|
||||
|
||||
card.innerHTML =
|
||||
'<div class="p-head">' +
|
||||
'<span class="chip ' + (chipColors[p.jenis]||'chip-gray') + '">' + emoji + ' ' + (window.labelJenisPesan[p.jenis]||p.jenis) + '</span>' +
|
||||
(isBroadcast ? '<span class="chip chip-purple">📡 Broadcast</span>' : '') +
|
||||
(p.status === 'baru' ? '<span class="p-new">●</span>' : '') +
|
||||
'</div>' +
|
||||
'<div class="p-judul">' + window.escapeHtml(p.judul) + '</div>' +
|
||||
'<div class="p-isi">' + window.escapeHtml(p.isi) + '</div>' +
|
||||
'<div class="p-meta">👤 ' + window.escapeHtml(p.nama_pengirim) +
|
||||
' (' + (p.role_pengirim === 'pimpinan_daerah' ? 'Pimpinan' : 'Pengurus') + ')' +
|
||||
' · 🕐 ' + p.created_fmt + '</div>';
|
||||
|
||||
/* Klik kartu → buka floating detail */
|
||||
card.addEventListener('click', function () {
|
||||
window.openPesanDetail(p);
|
||||
});
|
||||
|
||||
container.appendChild(card);
|
||||
});
|
||||
};
|
||||
|
||||
/* ── Submit kirim pesan ── */
|
||||
window.submitPesan = async function (e) {
|
||||
e.preventDefault();
|
||||
if (!window.appAuth.loggedIn) { window.showToast('Login diperlukan.', 'error'); return; }
|
||||
|
||||
var btn = document.getElementById('btn-submit-pesan');
|
||||
var judul = document.getElementById('pesan-judul').value.trim();
|
||||
var isi = document.getElementById('pesan-isi').value.trim();
|
||||
var jenis = document.getElementById('pesan-jenis').value;
|
||||
|
||||
if (!judul || !isi) { window.showToast('Judul dan isi wajib diisi.', 'error'); return; }
|
||||
btn.disabled = true; btn.textContent = 'Mengirim...';
|
||||
|
||||
try {
|
||||
await window.postForm('php/send_pesan.php', { judul, isi, jenis });
|
||||
document.getElementById('pesan-judul').value = '';
|
||||
document.getElementById('pesan-isi').value = '';
|
||||
window.showToast('Pesan terkirim.', 'success');
|
||||
await window.loadPesan();
|
||||
} catch (err) { window.showToast('Gagal: ' + err.message, 'error'); }
|
||||
finally { btn.disabled = false; btn.textContent = 'Kirim Pesan'; }
|
||||
};
|
||||
|
||||
/* Polling for Pesan Notification */
|
||||
window.pollPesanNotification = async function() {
|
||||
if (!window.appAuth) return;
|
||||
try {
|
||||
var res = await fetch('php/read_pesan.php');
|
||||
var text = await res.text();
|
||||
var data = JSON.parse(text);
|
||||
if(Array.isArray(data)) {
|
||||
// Count messages from OTHER users that are considered "baru"
|
||||
// Since we don't have a read-status per user in this simple app, we can just count messages from others in the last 24 hours, or just count 'baru'
|
||||
var pendingCount = data.filter(function(m) {
|
||||
return m.status === 'baru' && m.pengirim_id != window.appAuth.id;
|
||||
}).length;
|
||||
|
||||
var badge = document.getElementById('nav-badge-pesan');
|
||||
if(badge) {
|
||||
if(pendingCount > 0) {
|
||||
badge.style.display = 'inline-flex';
|
||||
badge.textContent = pendingCount;
|
||||
} else {
|
||||
badge.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
// Start polling every 15 seconds
|
||||
setInterval(window.pollPesanNotification, 15000);
|
||||
Reference in New Issue
Block a user