UAS SIG Proyek WebGis

This commit is contained in:
L4zyL4mb
2026-06-10 21:46:56 +07:00
commit df18a7a99a
35 changed files with 8977 additions and 0 deletions
+308
View File
@@ -0,0 +1,308 @@
<?php
// api/penduduk.php v2.0
// ============================================================
// CRUD API Penduduk Miskin Field diperluas
// ============================================================
require_once __DIR__ . '/../includes/config.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
$method = $_SERVER['REQUEST_METHOD'];
$db = getDB();
// ── Query SELECT lengkap (dipakai di GET & setelah write) ──
$SELECT_SQL = "
SELECT
pm.*,
w.kelurahan, w.kecamatan, w.kota,
ri.nama AS nama_ibadah,
ri.jenis AS jenis_ibadah,
u.nama AS nama_surveyor,
v.nama AS nama_verifikator,
fp.file_path AS foto_utama,
(
SELECT GROUP_CONCAT(jb.kode ORDER BY jb.kode SEPARATOR ',')
FROM bantuan_penduduk bp
JOIN jenis_bantuan jb ON jb.id = bp.bantuan_id
WHERE bp.penduduk_id = pm.id AND bp.status = 'aktif'
) AS kode_bantuan,
(
SELECT COUNT(*) FROM bantuan_penduduk bp
WHERE bp.penduduk_id = pm.id AND bp.status = 'aktif'
) AS jumlah_bantuan
FROM penduduk_miskin pm
LEFT JOIN wilayah w ON w.id = pm.wilayah_id
LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id
LEFT JOIN users u ON u.id = pm.surveyor_id
LEFT JOIN users v ON v.id = pm.verified_by
LEFT JOIN foto_rumah fp ON fp.penduduk_id = pm.id AND fp.is_primary = 1
";
try {
switch ($method) {
// ════════════════════════════════════════════════════════
// GET Ambil data penduduk
// ════════════════════════════════════════════════════════
case 'GET':
$id = $_GET['id'] ?? null;
$kelurahan = $_GET['kelurahan'] ?? null;
$kecamatan = $_GET['kecamatan'] ?? null;
$kategori = $_GET['kategori'] ?? null;
$bantuan = $_GET['bantuan'] ?? null; // 'ada'|'tidak'
$verifikasi = $_GET['verifikasi'] ?? null;
$surveyor = $_GET['surveyor_id']?? null;
$search = $_GET['search'] ?? null;
// GET single
if ($id) {
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch();
if (!$row) jsonResponse(['success' => false, 'error' => 'Data tidak ditemukan'], 404);
// Ambil semua foto
$fotoStmt = $db->prepare("SELECT * FROM foto_rumah WHERE penduduk_id = ? ORDER BY is_primary DESC, created_at ASC");
$fotoStmt->execute([$id]);
$row['fotos'] = $fotoStmt->fetchAll();
// Ambil semua bantuan
$bantuanStmt = $db->prepare("
SELECT bp.*, jb.kode, jb.nama AS nama_bantuan, jb.warna_badge
FROM bantuan_penduduk bp
JOIN jenis_bantuan jb ON jb.id = bp.bantuan_id
WHERE bp.penduduk_id = ?
ORDER BY jb.kode
");
$bantuanStmt->execute([$id]);
$row['bantuans'] = $bantuanStmt->fetchAll();
jsonResponse(['success' => true, 'data' => $row]);
}
// GET list dengan filter
$sql = $SELECT_SQL . " WHERE 1=1";
$params = [];
if ($kelurahan) { $sql .= " AND w.kelurahan = ?"; $params[] = $kelurahan; }
if ($kecamatan) { $sql .= " AND w.kecamatan = ?"; $params[] = $kecamatan; }
if ($kategori) { $sql .= " AND pm.kategori_kemiskinan = ?"; $params[] = $kategori; }
if ($verifikasi) { $sql .= " AND pm.status_verifikasi = ?"; $params[] = $verifikasi; }
if ($surveyor) { $sql .= " AND pm.surveyor_id = ?"; $params[] = $surveyor; }
if ($search) { $sql .= " AND (pm.nama_kk LIKE ? OR pm.nik_kk LIKE ? OR pm.alamat LIKE ?)";
$params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; }
if ($bantuan === 'tidak') {
$sql .= " AND (SELECT COUNT(*) FROM bantuan_penduduk bp WHERE bp.penduduk_id = pm.id AND bp.status='aktif') = 0";
} elseif ($bantuan === 'ada') {
$sql .= " AND (SELECT COUNT(*) FROM bantuan_penduduk bp WHERE bp.penduduk_id = pm.id AND bp.status='aktif') > 0";
}
$sql .= " ORDER BY pm.nama_kk ASC";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll();
// Statistik ringkas
$stats = [
'total' => count($rows),
'total_jiwa' => array_sum(array_column($rows, 'jumlah_anggota')),
'penerima_bantuan' => count(array_filter($rows, fn($r) => $r['jumlah_bantuan'] > 0)),
'belum_verifikasi' => count(array_filter($rows, fn($r) => $r['status_verifikasi'] === 'belum_diverifikasi')),
];
jsonResponse(['success' => true, 'data' => $rows, 'stats' => $stats]);
break;
// ════════════════════════════════════════════════════════
// POST Tambah penduduk baru
// ════════════════════════════════════════════════════════
case 'POST':
$in = getInput();
// Validasi wajib
$required = ['nik_kk', 'nama_kk', 'lat', 'lng'];
foreach ($required as $f) {
if (empty($in[$f])) jsonResponse(['success' => false, 'error' => "Field '$f' wajib diisi"], 400);
}
// Cek NIK duplikat
$cek = $db->prepare("SELECT id FROM penduduk_miskin WHERE nik_kk = ?");
$cek->execute([trim($in['nik_kk'])]);
if ($cek->fetch()) jsonResponse(['success' => false, 'error' => 'NIK sudah terdaftar di sistem'], 409);
// Hitung kategori otomatis
$penghasilan = isset($in['penghasilan_bulanan']) && $in['penghasilan_bulanan'] !== ''
? (float)$in['penghasilan_bulanan'] : null;
$kategori = hitungKategori($penghasilan);
$wilayahId = isset($in['wilayah_id']) && $in['wilayah_id'] !== '' ? (int)$in['wilayah_id'] : null;
$surveyorId = isset($in['surveyor_id']) && $in['surveyor_id'] !== '' ? (int)$in['surveyor_id'] : null;
$statusPekerjaan = $in['status_pekerjaan'] ?? 'tidak_bekerja';
$allowedPekerjaan = ['tidak_bekerja','buruh_harian','petani','nelayan','pedagang','pegawai_swasta','wiraswasta','lainnya'];
if (!in_array($statusPekerjaan, $allowedPekerjaan)) {
$statusPekerjaan = 'lainnya';
}
$db->prepare("
INSERT INTO penduduk_miskin (
nik_kk, nama_kk, no_hp, alamat, wilayah_id, rt, rw, lat, lng,
jumlah_anggota, jumlah_anak_sekolah,
penghasilan_bulanan, sumber_penghasilan, status_pekerjaan,
kategori_kemiskinan, kondisi_rumah, kepemilikan_rumah,
punya_bpjs, jenis_bpjs, status_verifikasi, surveyor_id
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
")->execute([
trim($in['nik_kk']),
trim($in['nama_kk']),
$in['no_hp'] ?? null,
$in['alamat'] ?? null,
$wilayahId,
$in['rt'] ?? null,
$in['rw'] ?? null,
(float)$in['lat'],
(float)$in['lng'],
max(1, (int)($in['jumlah_anggota'] ?? 1)),
max(0, (int)($in['jumlah_anak_sekolah'] ?? 0)),
$penghasilan,
$in['sumber_penghasilan'] ?? null,
$statusPekerjaan,
$kategori,
$in['kondisi_rumah'] ?? 'tidak_layak',
$in['kepemilikan_rumah'] ?? 'milik_sendiri',
(int)($in['punya_bpjs'] ?? 0),
$in['jenis_bpjs'] ?? 'tidak_punya',
'belum_diverifikasi',
$surveyorId,
]);
$newId = (int)$db->lastInsertId();
assignRumahIbadah($newId);
// Catat riwayat & notifikasi tidak block jika gagal (misal user_id tidak ada)
try {
catatRiwayat($newId, (int)($surveyorId ?? 1), 'submit', 'Data baru dari survei');
} catch(\Exception $e) { /* abaikan error audit trail */ }
try {
$db->prepare("
INSERT INTO notifikasi (jenis, judul, pesan, penduduk_id)
VALUES ('belum_verifikasi', 'Data Baru Belum Diverifikasi', ?, ?)
")->execute(["Data KK {$in['nama_kk']} (NIK: {$in['nik_kk']}) menunggu verifikasi admin.", $newId]);
} catch(\Exception $e) { /* abaikan error notifikasi */ }
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$newId]);
jsonResponse(['success' => true, 'data' => $stmt->fetch(), 'message' => 'Data penduduk berhasil ditambahkan']);
break;
// ════════════════════════════════════════════════════════
// PUT Update data penduduk
// ════════════════════════════════════════════════════════
case 'PUT':
$in = getInput();
$id = (int)($in['id'] ?? $_GET['id'] ?? 0);
if (!$id) jsonResponse(['success' => false, 'error' => 'ID diperlukan'], 400);
// Ambil data lama untuk audit
$old = $db->prepare("SELECT * FROM penduduk_miskin WHERE id = ?");
$old->execute([$id]);
$dataLama = $old->fetch();
if (!$dataLama) jsonResponse(['success' => false, 'error' => 'Data tidak ditemukan'], 404);
// Hitung kategori ulang jika penghasilan berubah
$penghasilan = array_key_exists('penghasilan_bulanan', $in)
? (($in['penghasilan_bulanan'] !== '') ? (float)$in['penghasilan_bulanan'] : null)
: (float)$dataLama['penghasilan_bulanan'];
$kategori = hitungKategori($penghasilan);
$allowed = [
'nama_kk','no_hp','alamat','wilayah_id','rt','rw','lat','lng',
'jumlah_anggota','jumlah_anak_sekolah','penghasilan_bulanan',
'sumber_penghasilan','status_pekerjaan','kondisi_rumah',
'kepemilikan_rumah','punya_bpjs','jenis_bpjs',
'status_verifikasi','catatan_verifikasi'
];
$fields = []; $values = []; $posChanged = false;
foreach ($allowed as $f) {
if (!array_key_exists($f, $in)) continue;
$fields[] = "$f = ?";
if ($f === 'lat' || $f === 'lng') { $values[] = (float)$in[$f]; $posChanged = true; }
elseif ($f === 'wilayah_id') { $values[] = (isset($in[$f]) && $in[$f] !== '') ? (int)$in[$f] : null; }
elseif ($f === 'status_pekerjaan') {
$statusPekerjaan = $in[$f] ?? 'tidak_bekerja';
$allowedPekerjaan = ['tidak_bekerja','buruh_harian','petani','nelayan','pedagang','pegawai_swasta','wiraswasta','lainnya'];
$values[] = in_array($statusPekerjaan, $allowedPekerjaan) ? $statusPekerjaan : 'lainnya';
}
elseif (in_array($f, ['jumlah_anggota','jumlah_anak_sekolah','punya_bpjs'])) $values[] = (int)$in[$f];
elseif ($f === 'penghasilan_bulanan') $values[] = $penghasilan;
else $values[] = trim((string)$in[$f]);
}
// Update kategori otomatis
$fields[] = "kategori_kemiskinan = ?";
$values[] = $kategori;
// Jika diverifikasi, catat verifikator
if (isset($in['status_verifikasi']) && $in['status_verifikasi'] === 'terverifikasi') {
$fields[] = "verified_by = ?"; $values[] = $in['verified_by'] ?? 1;
$fields[] = "verified_at = NOW()";
}
if (empty($fields)) jsonResponse(['success' => false, 'error' => 'Tidak ada data untuk diupdate'], 400);
$values[] = $id;
$db->prepare("UPDATE penduduk_miskin SET " . implode(', ', $fields) . " WHERE id = ?")
->execute($values);
if ($posChanged) assignRumahIbadah($id);
$aksi = 'update';
if (isset($in['status_verifikasi'])) {
if ($in['status_verifikasi'] === 'terverifikasi') {
$aksi = 'verifikasi';
} elseif ($in['status_verifikasi'] === 'ditolak') {
$aksi = 'tolak';
}
}
try {
catatRiwayat($id, (int)($in['updated_by'] ?? 1), $aksi, $in['catatan_verifikasi'] ?? null, $dataLama);
} catch(\Exception $e) { /* abaikan error audit trail */ }
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$id]);
jsonResponse(['success' => true, 'data' => $stmt->fetch(), 'message' => 'Data berhasil diperbarui']);
break;
// ════════════════════════════════════════════════════════
// DELETE Hapus penduduk
// ════════════════════════════════════════════════════════
case 'DELETE':
$id = (int)($_GET['id'] ?? getInput()['id'] ?? 0);
if (!$id) jsonResponse(['success' => false, 'error' => 'ID diperlukan'], 400);
// Hapus foto fisik terlebih dahulu
$fotos = $db->prepare("SELECT file_path FROM foto_rumah WHERE penduduk_id = ?");
$fotos->execute([$id]);
foreach ($fotos->fetchAll() as $foto) {
$path = __DIR__ . '/../' . $foto['file_path'];
if (file_exists($path)) @unlink($path);
}
catatRiwayat($id, 1, 'hapus', 'Data dihapus oleh admin');
$db->prepare("DELETE FROM penduduk_miskin WHERE id = ?")->execute([$id]);
jsonResponse(['success' => true, 'message' => 'Data penduduk berhasil dihapus']);
break;
default:
jsonResponse(['success' => false, 'error' => 'Method tidak diizinkan'], 405);
}
} catch (\Throwable $e) {
jsonResponse(['success' => false, 'error' => 'Database error: ' . $e->getMessage()], 500);
}