Files

430 lines
22 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ================================================================
// Admin: CRUD Penduduk Miskin
// ================================================================
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/functions.php';
requireLogin();
$pageTitle = 'Penduduk Miskin';
require_once __DIR__ . '/../includes/header.php';
$db = getDB();
$perPage = 15;
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $perPage;
$search = trim($_GET['search'] ?? '');
$filterSt = $_GET['status'] ?? '';
$filterKec = $_GET['kecamatan'] ?? '';
$filterIb = (int)($_GET['ibadah_id'] ?? 0);
$where = ['1=1'];
$params = [];
if ($search) { $where[] = '(pm.kk_nama LIKE ? OR pm.nik LIKE ? OR pm.alamat LIKE ?)'; $q="%$search%"; $params=array_merge($params,[$q,$q,$q]); }
if ($filterSt) { $where[] = 'pm.status_bantuan = ?'; $params[] = $filterSt; }
if ($filterKec){ $where[] = 'pm.kecamatan = ?'; $params[] = $filterKec; }
if ($filterIb) { $where[] = 'pm.rumah_ibadah_id = ?'; $params[] = $filterIb; }
$ws = implode(' AND ', $where);
$stmtCnt = $db->prepare("SELECT COUNT(*) FROM penduduk_miskin pm WHERE $ws");
$stmtCnt->execute($params);
$total = (int)$stmtCnt->fetchColumn();
$totalPages = max(1, ceil($total / $perPage));
$stmt = $db->prepare("
SELECT pm.*, ri.nama AS ibadah_nama, ri.jenis AS ibadah_jenis
FROM penduduk_miskin pm
LEFT JOIN rumah_ibadah ri ON pm.rumah_ibadah_id = ri.id
WHERE $ws ORDER BY pm.created_at DESC LIMIT $perPage OFFSET $offset
");
$stmt->execute($params);
$rows = $stmt->fetchAll();
$kecList = $db->query("SELECT DISTINCT kecamatan FROM penduduk_miskin WHERE kecamatan IS NOT NULL AND kecamatan != '' ORDER BY kecamatan")->fetchAll(PDO::FETCH_COLUMN);
$ibadahList = $db->query("SELECT id, nama, jenis FROM rumah_ibadah ORDER BY nama")->fetchAll();
?>
<!-- ── Page Header ─────────────────────────────────────────── -->
<div class="page-header">
<div class="page-title-block">
<h1>👤 Penduduk Miskin</h1>
<div class="page-subtitle">Total <?= $total ?> KK terdaftar</div>
</div>
<div class="page-actions">
<a href="<?= BASE_URL ?>/?mode=miskin" target="_blank" class="btn btn-ghost btn-sm">
<i class="bi bi-map"></i> Tambah di Peta
</a>
<button class="btn btn-success btn-sm" onclick="openMiskinModal()">
<i class="bi bi-plus-lg"></i> Tambah Manual
</button>
</div>
</div>
<!-- ── Filter ─────────────────────────────────────────────── -->
<div class="card" style="margin-bottom:16px;">
<div class="card-body" style="padding:14px;">
<form method="GET" style="display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;">
<div class="search-bar" style="flex:1;min-width:200px;">
<i class="bi bi-search search-ico"></i>
<input type="text" class="form-control" name="search" placeholder="Cari nama, NIK, alamat..." value="<?= htmlspecialchars($search) ?>">
</div>
<div>
<select class="form-control" name="status">
<option value="">Semua Status</option>
<option value="belum" <?= $filterSt==='belum'?'selected':'' ?>>❌ Belum Dibantu</option>
<option value="menunggu" <?= $filterSt==='menunggu'?'selected':'' ?>>⏳ Menunggu</option>
<option value="sudah" <?= $filterSt==='sudah'?'selected':'' ?>>✅ Sudah Dibantu</option>
<option value="darurat" <?= $filterSt==='darurat'?'selected':'' ?>>🚨 Bantuan Darurat</option>
</select>
</div>
<div>
<select class="form-control" name="kecamatan">
<option value="">Semua Kecamatan</option>
<?php foreach ($kecList as $k): ?>
<option value="<?= htmlspecialchars($k) ?>" <?= $filterKec===$k?'selected':'' ?>><?= htmlspecialchars($k) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-search"></i> Filter</button>
<a href="<?= BASE_URL ?>/admin/penduduk_miskin.php" class="btn btn-ghost btn-sm"><i class="bi bi-x"></i> Reset</a>
</form>
</div>
</div>
<!-- ── Table ───────────────────────────────────────────────── -->
<div class="card">
<div class="table-responsive">
<table class="admin-table">
<thead>
<tr>
<th>#</th>
<th>Nama KK</th>
<th>NIK</th>
<th>Anggota</th>
<th>Kecamatan</th>
<th>Status</th>
<th>Rumah Ibadah</th>
<th>Jarak</th>
<th>Bantuan</th>
<th>Nominal</th>
<th>Terdaftar</th>
<th class="td-actions">Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $i => $row): ?>
<tr>
<td style="color:var(--text3);font-size:11px;"><?= $offset+$i+1 ?></td>
<td style="font-weight:700;"><?= htmlspecialchars($row['kk_nama']) ?></td>
<td style="font-size:11px;color:var(--text3);font-family:monospace;"><?= htmlspecialchars($row['nik']?: '—') ?></td>
<td style="text-align:center;font-weight:700;"><?= $row['jumlah_anggota'] ?></td>
<td style="font-size:12px;color:var(--text2);"><?= htmlspecialchars($row['kecamatan']?: '—') ?></td>
<td><span class="badge-status badge-<?= $row['status_bantuan'] ?>"><?= ucfirst($row['status_bantuan']) ?></span></td>
<td style="font-size:11px;color:var(--text2);"><?= htmlspecialchars($row['ibadah_nama'] ?: '—') ?></td>
<td style="font-size:11px;color:var(--accent);"><?= $row['jarak_ke_ibadah'] ? round($row['jarak_ke_ibadah']).' m' : '—' ?></td>
<td style="font-size:11px;color:var(--text2);"><?= htmlspecialchars($row['jenis_bantuan'] ?: '—') ?></td>
<td style="font-size:11px;color:var(--green);font-family:monospace;"><?= $row['nominal_bantuan'] ? 'Rp '.number_format($row['nominal_bantuan'],0,',','.') : '—' ?></td>
<td style="font-size:11px;color:var(--text3);"><?= formatTanggal($row['created_at']) ?></td>
<td class="td-actions">
<button class="btn btn-ghost btn-sm" onclick='editMiskinRow(<?= htmlspecialchars(json_encode($row)) ?>)' title="Edit">
<i class="bi bi-pencil-fill"></i>
</button>
<?php if ($row['bukti_file']): ?>
<a href="<?= BASE_URL ?>/assets/uploads/<?= htmlspecialchars($row['bukti_file']) ?>" target="_blank" class="btn btn-ghost btn-sm" title="Lihat Bukti">
<i class="bi bi-paperclip"></i>
</a>
<?php endif; ?>
<button class="btn btn-danger btn-sm" onclick="deleteMiskin(<?= $row['id'] ?>, '<?= addslashes($row['kk_nama']) ?>')" title="Hapus">
<i class="bi bi-trash-fill"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($rows)): ?>
<tr><td colspan="11" style="text-align:center;padding:32px;color:var(--text3);">
<div style="font-size:28px;margin-bottom:6px;">👤</div>Tidak ada data ditemukan
</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($totalPages > 1): ?>
<div style="padding:14px 18px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;">
<div style="font-size:12px;color:var(--text2);">Menampilkan <?= $offset+1 ?><?= min($offset+$perPage,$total) ?> dari <?= $total ?> data</div>
<div class="pagination">
<?php
$base = BASE_URL . '/admin/penduduk_miskin.php?' . http_build_query(array_filter(['search'=>$search,'status'=>$filterSt,'kecamatan'=>$filterKec]));
for ($p = 1; $p <= $totalPages; $p++):
?>
<a href="<?= $base ?>&page=<?= $p ?>" class="page-btn <?= $p===$page?'active':'' ?>"><?= $p ?></a>
<?php endfor; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- ── Modal Tambah/Edit Penduduk ─────────────────────────── -->
<div class="modal fade" id="modal-miskin" tabindex="-1">
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="miskin-modal-title">👤 Tambah Penduduk Miskin</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="m-edit-id">
<div class="section-label">📍 Lokasi</div>
<div class="form-row" style="margin-bottom:12px;">
<div class="form-group">
<label class="form-label">Latitude <span style="color:var(--red)">*</span></label>
<input class="form-control" id="m-lat" type="number" step="any" placeholder="-0.0263">
</div>
<div class="form-group">
<label class="form-label">Longitude <span style="color:var(--red)">*</span></label>
<input class="form-control" id="m-lng" type="number" step="any" placeholder="109.3425">
</div>
</div>
<div class="form-row" style="margin-bottom:12px;">
<div class="form-group"><label class="form-label">Kelurahan</label><input class="form-control" id="m-kelurahan" placeholder="Kelurahan"></div>
<div class="form-group"><label class="form-label">Kecamatan</label><input class="form-control" id="m-kecamatan" placeholder="Kecamatan"></div>
</div>
<div class="form-group" style="margin-bottom:14px;">
<label class="form-label">Alamat Lengkap</label>
<input class="form-control" id="m-alamat" placeholder="Jl. xxx No. xxx">
</div>
<div class="section-label">👨‍👩‍👧 Data Keluarga</div>
<div class="form-row" style="margin-bottom:12px;">
<div class="form-group"><label class="form-label">Nama KK <span style="color:var(--red)">*</span></label><input class="form-control" id="m-kk-nama" placeholder="Nama kepala keluarga"></div>
<div class="form-group"><label class="form-label">NIK KK</label><input class="form-control" id="m-nik" placeholder="16 digit" maxlength="16"></div>
</div>
<div class="form-group" style="margin-bottom:14px;">
<label class="form-label">Jumlah Anggota Keluarga</label>
<input class="form-control" id="m-jumlah" type="number" min="1" value="1" style="max-width:160px;">
</div>
<div class="section-label">👥 Detail Anggota</div>
<div id="m-anggota-container"></div>
<button type="button" class="btn btn-outline-accent btn-sm" onclick="addMAnggota()" style="width:100%;margin-bottom:14px;">
<i class="bi bi-plus-circle"></i> Tambah Anggota
</button>
<div class="section-label">🤝 Status Bantuan</div>
<div class="form-group">
<label class="form-label">Status <span style="color:var(--red)">*</span></label>
<select class="form-control" id="m-status" onchange="toggleMBantuan()">
<option value="belum">❌ Belum Dibantu</option>
<option value="menunggu">⏳ Menunggu Verifikasi</option>
<option value="sudah">✅ Sudah Dibantu</option>
<option value="darurat">🚨 Bantuan Darurat (Musibah)</option>
</select>
</div>
<div id="m-bantuan-fields" style="display:none;">
<div class="form-row" style="margin-bottom:12px;">
<div class="form-group"><label class="form-label">Jenis Bantuan</label><input class="form-control" id="m-jenis-bantuan" placeholder="Mis. Sembako"></div>
<div class="form-group"><label class="form-label">Tanggal Bantuan</label><input class="form-control" id="m-tanggal" type="date"></div>
</div>
<div class="form-group">
<label class="form-label">💰 Nominal Bantuan (Rupiah)</label>
<div style="position:relative;">
<span style="position:absolute;left:12px;top:50%;transform:translateY(-50%);font-size:12px;color:var(--text2);font-weight:700;pointer-events:none;">Rp</span>
<input class="form-control" id="m-nominal"
type="text" inputmode="numeric"
placeholder="0"
style="padding-left:36px;font-family:'JetBrains Mono',monospace;"
oninput="formatNominalInput(this)">
</div>
<div style="font-size:10px;color:var(--text3);margin-top:4px;">Kosongkan jika belum ditentukan nominal</div>
</div>
</div>
<div class="section-label">📎 Bukti (Opsional)</div>
<div class="form-group">
<input type="file" class="form-control" id="m-bukti" accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx">
<div class="form-hint">JPG, PNG, PDF, DOC maks 10 MB. Biarkan kosong jika tidak diubah.</div>
<div id="m-bukti-info" style="margin-top:6px;font-size:11px;color:var(--text2);"></div>
</div>
<div class="section-label">🏛️ Rumah Ibadah (opsional override)</div>
<div class="form-group">
<label class="form-label">Rumah Ibadah Terdekat (akan dipilih otomatis)</label>
<select class="form-control" id="m-ibadah-id">
<option value="">— Otomatis dari koordinat —</option>
<?php foreach ($ibadahList as $ib): ?>
<option value="<?= $ib['id'] ?>"><?= htmlspecialchars($ib['nama']) ?> (<?= $ib['jenis'] ?>)</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-ghost" data-bs-dismiss="modal"><i class="bi bi-x-lg"></i> Batal</button>
<button type="button" class="btn btn-success" id="btn-save-m"><i class="bi bi-floppy2-fill"></i> Simpan</button>
</div>
</div>
</div>
</div>
<script>
const BASE = '<?= BASE_URL ?>';
let mAIdx = 0;
function formatNominalInput(el) {
let raw = el.value.replace(/[^0-9]/g, '');
el.value = raw.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}
function openMiskinModal() { fillMModal(null); }
function editMiskinRow(data) { fillMModal(data); }
function fillMModal(data) {
const isEdit = data !== null;
document.getElementById('miskin-modal-title').textContent = isEdit ? '✏️ Edit Penduduk Miskin' : '👤 Tambah Penduduk Miskin';
document.getElementById('m-edit-id').value = isEdit ? data.id : '';
document.getElementById('m-kk-nama').value = isEdit ? data.kk_nama : '';
document.getElementById('m-nik').value = isEdit ? (data.nik||'') : '';
document.getElementById('m-jumlah').value = isEdit ? (data.jumlah_anggota||1) : 1;
document.getElementById('m-lat').value = isEdit ? data.lat : '';
document.getElementById('m-lng').value = isEdit ? data.lng : '';
document.getElementById('m-kelurahan').value = isEdit ? (data.kelurahan||'') : '';
document.getElementById('m-kecamatan').value = isEdit ? (data.kecamatan||'') : '';
document.getElementById('m-alamat').value = isEdit ? (data.alamat||'') : '';
document.getElementById('m-status').value = isEdit ? data.status_bantuan : 'belum';
document.getElementById('m-jenis-bantuan').value = isEdit ? (data.jenis_bantuan||'') : '';
document.getElementById('m-tanggal').value = isEdit ? (data.tanggal_bantuan||'') : '';
// Set nominal bantuan
const nomEl = document.getElementById('m-nominal');
if (nomEl) {
const rawNom = isEdit && data.nominal_bantuan ? parseFloat(data.nominal_bantuan) : '';
nomEl.value = rawNom !== '' ? rawNom.toLocaleString('id-ID', {maximumFractionDigits:0}) : '';
}
document.getElementById('m-ibadah-id').value = isEdit ? (data.rumah_ibadah_id||'') : '';
document.getElementById('m-bukti').value = '';
document.getElementById('m-bukti-info').innerHTML = isEdit && data.bukti_file
? `📎 File saat ini: <a href="${BASE}/assets/uploads/${data.bukti_file}" target="_blank" style="color:var(--accent)">${data.bukti_file}</a>`
: '';
document.getElementById('m-anggota-container').innerHTML = '';
mAIdx = 0;
if (isEdit && data.id) loadMAnggota(data.id);
toggleMBantuan();
new bootstrap.Modal(document.getElementById('modal-miskin')).show();
}
function toggleMBantuan() {
const statusVal = document.getElementById('m-status').value;
document.getElementById('m-bantuan-fields').style.display =
(statusVal === 'sudah' || statusVal === 'menunggu' || statusVal === 'darurat') ? 'block' : 'none';
}
async function loadMAnggota(pid) {
try {
const r = await fetch(`${BASE}/api/anggota_keluarga.php?penduduk_id=${pid}`);
const js = await r.json();
if (js.success) js.data.forEach(a => addMAnggota(a));
} catch{}
}
function addMAnggota(data = null) {
mAIdx++;
const c = document.getElementById('m-anggota-container');
const d = document.createElement('div');
d.className = 'anggota-row'; d.id = `ma-row-${mAIdx}`;
d.innerHTML = `
<div class="anggota-row-header">
<span class="anggota-num">Anggota ${mAIdx}</span>
<button class="btn-remove-anggota" type="button" onclick="document.getElementById('ma-row-${mAIdx}').remove()">✕</button>
</div>
<div class="form-row">
<div class="form-group" style="margin-bottom:0"><label class="form-label">Nama *</label><input class="form-control ma-nama" id="ma-nama-${mAIdx}" value="${data?.nama||''}"></div>
<div class="form-group" style="margin-bottom:0"><label class="form-label">Hubungan</label><input class="form-control" id="ma-hub-${mAIdx}" value="${data?.hubungan||''}"></div>
</div>
<div class="form-row" style="margin-top:8px">
<div class="form-group" style="margin-bottom:0"><label class="form-label">Umur</label><input class="form-control" id="ma-umur-${mAIdx}" type="number" min="0" value="${data?.umur||''}"></div>
<div class="form-group" style="margin-bottom:0"><label class="form-label">Pekerjaan</label><input class="form-control" id="ma-pkj-${mAIdx}" value="${data?.pekerjaan||''}"></div>
</div>
<div class="form-group" style="margin-top:8px;margin-bottom:0">
<label class="form-label">Keterangan</label>
<input class="form-control" id="ma-ket-${mAIdx}" placeholder="Catatan tambahan (opsional)" value="${data?.keterangan||''}">
</div>`;
c.appendChild(d);
}
function collectMAnggota() {
const rows = document.querySelectorAll('.ma-nama');
return Array.from(rows).map(el => {
const idx = el.id.replace('ma-nama-','');
return {
nama: el.value.trim(),
hubungan: document.getElementById(`ma-hub-${idx}`)?.value || '',
umur: document.getElementById(`ma-umur-${idx}`)?.value || '',
pekerjaan: document.getElementById(`ma-pkj-${idx}`)?.value || '',
keterangan: document.getElementById(`ma-ket-${idx}`)?.value || ''
};
}).filter(a => a.nama);
}
document.getElementById('btn-save-m').addEventListener('click', async function() {
const nama = document.getElementById('m-kk-nama').value.trim();
const lat = document.getElementById('m-lat').value;
const lng = document.getElementById('m-lng').value;
if (!nama || !lat || !lng) { Swal.fire('Error','Nama KK, Lat, dan Lng wajib diisi!','error'); return; }
const editId = document.getElementById('m-edit-id').value;
const fd = new FormData();
fd.append('kk_nama', nama);
fd.append('nik', document.getElementById('m-nik').value);
fd.append('jumlah_anggota', document.getElementById('m-jumlah').value||1);
fd.append('lat', lat); fd.append('lng', lng);
fd.append('kelurahan', document.getElementById('m-kelurahan').value);
fd.append('kecamatan', document.getElementById('m-kecamatan').value);
fd.append('alamat', document.getElementById('m-alamat').value);
fd.append('status_bantuan', document.getElementById('m-status').value);
fd.append('jenis_bantuan', document.getElementById('m-jenis-bantuan').value);
fd.append('tanggal_bantuan', document.getElementById('m-tanggal').value);
// Nominal bantuan: hapus titik pemisah ribuan sebelum kirim
const nomRaw = (document.getElementById('m-nominal')?.value || '').replace(/\./g,'');
fd.append('nominal_bantuan', nomRaw);
fd.append('anggota', JSON.stringify(collectMAnggota()));
const buktiInput = document.getElementById('m-bukti');
if (buktiInput.files[0]) fd.append('bukti_file', buktiInput.files[0]);
this.disabled = true; this.textContent = '⏳ Menyimpan...';
try {
const url = editId ? `${BASE}/api/penduduk_miskin.php?id=${editId}` : `${BASE}/api/penduduk_miskin.php`;
const resp = await fetch(url, { method: 'POST', body: fd });
const json = await resp.json();
if (json.success) { location.reload(); } else { Swal.fire('Error', json.message, 'error'); }
} catch { Swal.fire('Error','Gagal menyimpan','error'); }
finally { this.disabled = false; this.innerHTML = '<i class="bi bi-floppy2-fill"></i> Simpan'; }
});
async function deleteMiskin(id, nama) {
const res = await Swal.fire({
title: 'Hapus Penduduk?', html: `Data <b>${nama}</b> akan dihapus permanen.`,
icon: 'warning', showCancelButton: true, confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal', confirmButtonColor: '#ef4444',
background: '#161b27', color: '#e8ecf4'
});
if (!res.isConfirmed) return;
try {
const r = await fetch(`${BASE}/api/penduduk_miskin.php?id=${id}`, { method:'DELETE' });
const js = await r.json();
if (js.success) { Swal.fire({title:'Terhapus!',icon:'success',timer:1500,background:'#161b27',color:'#e8ecf4'}).then(()=>location.reload()); }
else Swal.fire('Error', js.message, 'error');
} catch { Swal.fire('Error','Gagal menghapus','error'); }
}
</script>
<?php require_once __DIR__ . '/../includes/footer.php'; ?>