Files
2026-06-11 18:14:21 +07:00

196 lines
12 KiB
JavaScript

// modules/kebutuhan.js — Manajemen Kebutuhan per Warga Miskin (F14)
(function () {
const KATEGORI_ICON = {
'Sembako' : 'shopping-cart',
'Biaya Sekolah' : 'graduation-cap',
'Biaya Kesehatan' : 'heart-pulse',
'Modal Usaha' : 'briefcase',
'Renovasi Rumah' : 'home',
'Perlengkapan Rumah': 'armchair',
'Pakaian' : 'shirt',
'Lainnya' : 'clipboard-list',
};
const STATUS_COLOR = {
'Belum Terpenuhi': '#ef4444',
'Dalam Proses' : '#e3b341',
'Terpenuhi' : '#3fb950',
};
const KATEGORI_LIST = [
'Sembako','Biaya Sekolah','Biaya Kesehatan','Modal Usaha',
'Renovasi Rumah','Perlengkapan Rumah','Pakaian','Lainnya',
];
function isOp() { return window.APP_USER && (window.APP_USER.isOp || window.APP_USER.isAdmin); }
function esc(str) {
const d = document.createElement('div');
d.appendChild(document.createTextNode(str ?? ''));
return d.innerHTML;
}
// ── Bangun seksi kebutuhan untuk popup penduduk ───────────────────────────
window._kebutuhanBuildSection = function (pendudukId, kebutuhanOpen, statusVerifikasi) {
const count = parseInt(kebutuhanOpen) || 0;
const canMutate = isOp() && statusVerifikasi === 'Terverifikasi';
const badgeHtml = count > 0
? `<span id="kebutuhanBadge_${pendudukId}"
style="background:#ef444422;color:#ef4444;border:1px solid #ef444444;
font-size:10px;font-weight:700;padding:2px 7px;border-radius:20px;
margin-left:4px;">${count} Belum Terpenuhi</span>`
: `<span id="kebutuhanBadge_${pendudukId}"></span>`;
const formHtml = canMutate ? `
<div id="kebutuhanForm_${pendudukId}" style="display:none;margin-top:8px;padding:8px;
background:rgba(255,255,255,.03);border-radius:6px;border:1px solid var(--c-border);">
<select id="kf_kat_${pendudukId}"
style="width:100%;padding:5px 8px;background:var(--c-surface2);border:1px solid var(--c-border);
border-radius:6px;color:var(--c-text);font-size:12px;margin-bottom:6px;">
${KATEGORI_LIST.map(k => `<option value="${esc(k)}">${esc(k)}</option>`).join('')}
</select>
<input type="text" id="kf_desc_${pendudukId}"
placeholder="Deskripsi opsional (maks 300 karakter)" maxlength="300"
style="width:100%;padding:5px 8px;background:var(--c-surface2);border:1px solid var(--c-border);
<div style="display:flex;gap:6px;">
<button onclick="window._kebutuhanSimpan(${pendudukId})"
style="flex:1;padding:5px;font-size:11px;background:rgba(63,185,80,.1);color:#3fb950;
border:1px solid rgba(63,185,80,.3);border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:4px;">
<i data-lucide="save" style="width:12px;height:12px;"></i> Simpan
</button>
<button onclick="document.getElementById('kebutuhanForm_${pendudukId}').style.display='none'"
style="padding:5px 10px;font-size:11px;background:transparent;color:var(--c-muted);
border:1px solid var(--c-border);border-radius:6px;cursor:pointer;">
Batal
</button>
</div>
</div>
<button id="btnTambahKebutuhan_${pendudukId}"
onclick="document.getElementById('kebutuhanForm_${pendudukId}').style.display='';
document.getElementById('btnTambahKebutuhan_${pendudukId}').style.display='none';
if(typeof lucide !== 'undefined') lucide.createIcons();"
style="font-size:11px;padding:3px 8px;margin-top:6px;border-radius:4px;cursor:pointer;
border:1px solid rgba(63,185,80,.3);background:transparent;color:#3fb950;">
+ Tambah Kebutuhan
</button>` : (isOp() ? `<div style="font-size:11px;color:var(--c-muted);font-weight:600;margin-top:6px;">Menunggu verifikasi admin</div>` : '');
return `<div class="info-row" style="align-items:flex-start;border-top:1px solid var(--c-border);padding-top:8px;margin-top:4px;">
<div class="info-row-icon"><i data-lucide="clipboard-list"></i></div>
<div style="flex:1;">
<div class="info-row-label">Kebutuhan ${badgeHtml}</div>
<div id="kebutuhanBody_${pendudukId}" data-verifikasi="${esc(statusVerifikasi || 'Pending')}" style="margin-top:4px;">
<button onclick="window._kebutuhanLoad(${pendudukId})"
style="font-size:11px;padding:2px 7px;border-radius:4px;cursor:pointer;
border:1px solid var(--c-border);background:transparent;color:var(--c-muted);">
Muat Kebutuhan
</button>
</div>
${formHtml}
</div>
</div>`;
};
// ── Muat dan render daftar kebutuhan ─────────────────────────────────────
window._kebutuhanLoad = function (pendudukId) {
const body = document.getElementById('kebutuhanBody_' + pendudukId);
if (!body) return;
const statusVerifikasi = body.dataset.verifikasi || 'Pending';
const canMutate = isOp() && statusVerifikasi === 'Terverifikasi';
body.innerHTML = '<span style="font-size:11px;color:var(--c-muted);display:inline-flex;align-items:center;gap:4px;"><i data-lucide="loader-2" class="lucide animate-spin" style="width:12px;height:12px;"></i> Memuat...</span>';
if (typeof lucide !== 'undefined') lucide.createIcons();
fetch('api/kebutuhan/ambil.php?penduduk_id=' + pendudukId + '&_=' + Date.now(), { cache: 'no-store' })
.then(r => r.json())
.then(j => {
if (j.status !== 'success') throw new Error(j.message);
const rows = j.data;
const openCount = rows.filter(r => r.status === 'Belum Terpenuhi').length;
if (typeof window._pendudukRefreshKebutuhanCount === 'function') {
window._pendudukRefreshKebutuhanCount(pendudukId, openCount);
}
if (rows.length === 0) {
body.innerHTML = '<span style="font-size:11px;color:var(--c-muted);">Belum ada kebutuhan tercatat.</span>';
return;
}
body.innerHTML = rows.map(r => {
const sc = STATUS_COLOR[r.status] || '#8b949e';
const icon = KATEGORI_ICON[r.kategori] || 'clipboard-list';
const iconHtml = `<i data-lucide="${icon}" class="lucide-inline" style="width:14px;height:14px;vertical-align:middle;margin-right:4px;"></i>`;
const statusBtns = canMutate
? ['Belum Terpenuhi', 'Dalam Proses', 'Terpenuhi'].map(s =>
`<button onclick="window._kebutuhanUpdateStatus(${r.id},'${s}',${pendudukId})"
style="font-size:10px;padding:2px 6px;border-radius:4px;cursor:pointer;
border:1px solid ${STATUS_COLOR[s]};
background:${s === r.status ? STATUS_COLOR[s] + '33' : 'transparent'};
color:${STATUS_COLOR[s]};font-weight:${s === r.status ? '700' : '400'};">${s}</button>`
).join('')
: `<span style="font-size:11px;color:${sc};font-weight:700;">${esc(r.status)}</span>`;
return `<div style="padding:5px 0;border-bottom:1px solid rgba(255,255,255,.06);">
<div style="font-size:12px;font-weight:600;color:var(--c-text);display:flex;align-items:center;gap:2px;">
${iconHtml} ${esc(r.kategori)}
</div>
${r.deskripsi ? `<div style="font-size:11px;color:var(--c-muted);margin:2px 0;">${esc(r.deskripsi)}</div>` : ''}
<div style="display:flex;gap:4px;margin-top:4px;flex-wrap:wrap;">${statusBtns}</div>
</div>`;
}).join('');
if (typeof lucide !== 'undefined') lucide.createIcons();
})
.catch(err => {
const b = document.getElementById('kebutuhanBody_' + pendudukId);
if (b) {
b.innerHTML = `<span style="font-size:11px;color:#ef4444;display:inline-flex;align-items:center;gap:4px;"><i data-lucide="x-circle" style="width:12px;height:12px;"></i> Gagal: ${esc(err.message)}</span>`;
if (typeof lucide !== 'undefined') lucide.createIcons();
}
});
};
// ── Simpan kebutuhan baru ─────────────────────────────────────────────────
window._kebutuhanSimpan = function (pendudukId) {
const kat = document.getElementById('kf_kat_' + pendudukId)?.value;
const desc = document.getElementById('kf_desc_' + pendudukId)?.value?.trim() || '';
if (!kat) return;
const fd = new FormData();
fd.append('penduduk_id', pendudukId);
fd.append('kategori', kat);
fd.append('deskripsi', desc);
fetch('api/kebutuhan/simpan.php', { method: 'POST', body: appendCsrf(fd) })
.then(r => r.json())
.then(j => {
if (j.status !== 'success') throw new Error(j.message);
const formEl = document.getElementById('kebutuhanForm_' + pendudukId);
const btnEl = document.getElementById('btnTambahKebutuhan_' + pendudukId);
const descEl = document.getElementById('kf_desc_' + pendudukId);
if (formEl) formEl.style.display = 'none';
if (btnEl) btnEl.style.display = '';
if (descEl) descEl.value = '';
window._kebutuhanLoad(pendudukId);
if (typeof window.dispatchDataChanged === 'function') {
window.dispatchDataChanged('kebutuhan', { pendudukId, mutation: 'create' });
}
showToast('Kebutuhan berhasil disimpan.');
})
.catch(err => showToast(err.message || 'Gagal menyimpan kebutuhan.', 'error'));
};
// ── Update status kebutuhan ───────────────────────────────────────────────
window._kebutuhanUpdateStatus = function (kebutuhanId, status, pendudukId) {
const catatan = prompt('Catatan perubahan kebutuhan (opsional):', '');
if (catatan === null) return;
const fd = new FormData();
fd.append('id', kebutuhanId);
fd.append('status', status);
fd.append('catatan', catatan);
fetch('api/kebutuhan/update_status.php', { method: 'POST', body: appendCsrf(fd) })
.then(r => r.json())
.then(j => {
if (j.status !== 'success') throw new Error(j.message);
window._kebutuhanLoad(pendudukId);
if (typeof window.dispatchDataChanged === 'function') {
window.dispatchDataChanged('kebutuhan', { pendudukId, kebutuhanId, mutation: 'status' });
}
showToast('Status kebutuhan diperbarui.');
})
.catch(err => showToast(err.message || 'Gagal memperbarui status.', 'error'));
};
})();