From d45bcee067ddcb54c94acaaa8904f52aba45eed1 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 2 Jun 2026 03:13:49 +0000 Subject: [PATCH] Upload files to "api" --- api/_helpers.php | 44 +++++++++++ api/analisis.php | 102 +++++++++++++++++++++++++ api/bantuan.php | 71 ++++++++++++++++++ api/keluarga.php | 190 +++++++++++++++++++++++++++++++++++++++++++++++ api/musibah.php | 104 ++++++++++++++++++++++++++ 5 files changed, 511 insertions(+) create mode 100644 api/_helpers.php create mode 100644 api/analisis.php create mode 100644 api/bantuan.php create mode 100644 api/keluarga.php create mode 100644 api/musibah.php diff --git a/api/_helpers.php b/api/_helpers.php new file mode 100644 index 0000000..ffb3209 --- /dev/null +++ b/api/_helpers.php @@ -0,0 +1,44 @@ + false, 'message' => "Field {$field} wajib diisi."], 422); + } + } +} + +function clean_string($value): string +{ + return trim((string)($value ?? '')); +} + +function clean_float($value): float +{ + return (float)($value ?? 0); +} + +function clean_int($value): int +{ + return (int)($value ?? 0); +} diff --git a/api/analisis.php b/api/analisis.php new file mode 100644 index 0000000..779a8d9 --- /dev/null +++ b/api/analisis.php @@ -0,0 +1,102 @@ +beginTransaction(); +try { + $rumahStmt = $pdo->query('SELECT * FROM rumah_ibadah WHERE status = "Aktif" ORDER BY nama ASC'); + $rumahIbadah = $rumahStmt->fetchAll(); + + $kelStmt = $pdo->query('SELECT * FROM keluarga ORDER BY FIELD(kerentanan, "Miskin Ekstrem", "Sangat Miskin", "Miskin", "Rentan Miskin"), created_at ASC'); + $keluarga = $kelStmt->fetchAll(); + + $terpakai = []; + foreach ($rumahIbadah as $ri) { + $terpakai[(int)$ri['id']] = 0; + } + + $update = $pdo->prepare('UPDATE keluarga SET status_bantuan = ?, rumah_ibadah_id = ? WHERE id = ?'); + $jumlahDitanggung = 0; + $jumlahBlankSpot = 0; + $jumlahMenunggu = 0; + $jumlahTidakValid = 0; + + foreach ($keluarga as $kel) { + $kelId = (int)$kel['id']; + + if ($kel['status_verifikasi'] === 'Tidak Valid') { + $update->execute(['Tidak Valid', null, $kelId]); + $jumlahTidakValid++; + continue; + } + + if ($kel['status_verifikasi'] !== 'Valid') { + $update->execute(['Menunggu Verifikasi', null, $kelId]); + $jumlahMenunggu++; + continue; + } + + $kandidat = []; + foreach ($rumahIbadah as $ri) { + $riId = (int)$ri['id']; + $kuota = (int)$ri['kuota']; + $radius = (int)$ri['radius']; + + if ($kuota <= 0 || ($terpakai[$riId] ?? 0) >= $kuota) { + continue; + } + + $jarak = haversine_meter((float)$kel['lat'], (float)$kel['lng'], (float)$ri['lat'], (float)$ri['lng']); + if ($jarak <= $radius) { + $kandidat[] = [ + 'id' => $riId, + 'jarak' => $jarak, + 'sisa_kuota' => $kuota - ($terpakai[$riId] ?? 0), + ]; + } + } + + if (!empty($kandidat)) { + usort($kandidat, function ($a, $b) { + if ($a['jarak'] == $b['jarak']) return $b['sisa_kuota'] <=> $a['sisa_kuota']; + return $a['jarak'] <=> $b['jarak']; + }); + + $riId = $kandidat[0]['id']; + $terpakai[$riId]++; + $update->execute(['Ditanggung', $riId, $kelId]); + $jumlahDitanggung++; + } else { + $update->execute(['Blank Spot', null, $kelId]); + $jumlahBlankSpot++; + } + } + + $pdo->commit(); + json_response([ + 'success' => true, + 'message' => 'Analisis tanggungan selesai.', + 'summary' => [ + 'ditanggung' => $jumlahDitanggung, + 'blank_spot' => $jumlahBlankSpot, + 'menunggu_verifikasi' => $jumlahMenunggu, + 'tidak_valid' => $jumlahTidakValid, + ] + ]); +} catch (Throwable $e) { + $pdo->rollBack(); + json_response(['success' => false, 'message' => 'Analisis gagal: ' . $e->getMessage()], 500); +} diff --git a/api/bantuan.php b/api/bantuan.php new file mode 100644 index 0000000..384ceda --- /dev/null +++ b/api/bantuan.php @@ -0,0 +1,71 @@ + 0) { + $where[] = 'rb.keluarga_id = ?'; + $params[] = $keluargaId; + } + + if ($role === 'rumah_ibadah' && !empty($user['rumah_ibadah_id'])) { + $where[] = 'rb.rumah_ibadah_id = ?'; + $params[] = (int)$user['rumah_ibadah_id']; + } + + $sql = 'SELECT rb.*, k.nama_kk, r.nama AS nama_rumah_ibadah + FROM riwayat_bantuan rb + JOIN keluarga k ON k.id = rb.keluarga_id + LEFT JOIN rumah_ibadah r ON r.id = rb.rumah_ibadah_id'; + if ($where) $sql .= ' WHERE ' . implode(' AND ', $where); + $sql .= ' ORDER BY rb.tanggal_bantuan DESC, rb.created_at DESC'; + + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + json_response(['success' => true, 'data' => $stmt->fetchAll()]); +} + +if ($method === 'POST') { + $data = get_json_input(); + required_fields($data, ['keluarga_id','tanggal_bantuan','jenis_bantuan']); + + $keluargaId = clean_int($data['keluarga_id']); + $stmtKel = $pdo->prepare('SELECT rumah_ibadah_id, status_bantuan FROM keluarga WHERE id = ?'); + $stmtKel->execute([$keluargaId]); + $kel = $stmtKel->fetch(); + if (!$kel) json_response(['success' => false, 'message' => 'Data keluarga tidak ditemukan.'], 404); + if ($kel['status_bantuan'] !== 'Ditanggung') { + json_response(['success' => false, 'message' => 'Bantuan hanya dapat dicatat untuk keluarga berstatus Ditanggung.'], 422); + } + + $rumahIbadahId = $kel['rumah_ibadah_id'] ? (int)$kel['rumah_ibadah_id'] : null; + if ($role === 'rumah_ibadah' && $user['rumah_ibadah_id'] && $rumahIbadahId !== (int)$user['rumah_ibadah_id']) { + json_response(['success' => false, 'message' => 'Pengurus rumah ibadah hanya dapat mencatat bantuan untuk keluarga tanggungannya sendiri.'], 403); + } + + $stmt = $pdo->prepare('INSERT INTO riwayat_bantuan + (keluarga_id, rumah_ibadah_id, tanggal_bantuan, jenis_bantuan, nilai_bantuan, catatan, created_by) + VALUES (?, ?, ?, ?, ?, ?, ?)'); + $stmt->execute([ + $keluargaId, + $rumahIbadahId, + clean_string($data['tanggal_bantuan']), + clean_string($data['jenis_bantuan']), + clean_float($data['nilai_bantuan'] ?? 0), + clean_string($data['catatan'] ?? ''), + $user['id'] + ]); + + json_response(['success' => true, 'message' => 'Riwayat bantuan berhasil dicatat.'], 201); +} + +json_response(['success' => false, 'message' => 'Method tidak didukung.'], 405); diff --git a/api/keluarga.php b/api/keluarga.php new file mode 100644 index 0000000..6052851 --- /dev/null +++ b/api/keluarga.php @@ -0,0 +1,190 @@ +prepare($sql); + $stmt->execute($params); + $rows = $stmt->fetchAll(); + + if ($rows) { + $ids = array_map(fn($r) => (int)$r['id'], $rows); + $placeholders = implode(',', array_fill(0, count($ids), '?')); + $anggotaStmt = $pdo->prepare('SELECT * FROM keluarga_anggota WHERE keluarga_id IN (' . $placeholders . ') ORDER BY FIELD(hubungan, "Kepala Keluarga", "Suami", "Istri", "Anak", "Orang Tua", "Mertua", "Famili Lain", "Lainnya"), tanggal_lahir ASC, nama ASC'); + $anggotaStmt->execute($ids); + $anggotaByKeluarga = []; + foreach ($anggotaStmt->fetchAll() as $anggota) { + $anggotaByKeluarga[(int)$anggota['keluarga_id']][] = $anggota; + } + foreach ($rows as &$row) { + $row['anggota'] = $anggotaByKeluarga[(int)$row['id']] ?? []; + } + unset($row); + } + + json_response(['success' => true, 'data' => $rows]); +} + +if ($method === 'POST') { + $data = get_json_input(); + required_fields($data, ['nama_kk','alamat','kerentanan','kebutuhan','lat','lng']); + + $statusVerifikasi = clean_string($data['status_verifikasi'] ?? 'Menunggu Verifikasi'); + if ($role === 'masyarakat' || !role_in(['admin','dinas','relawan'])) { + $statusVerifikasi = 'Menunggu Verifikasi'; + } + + $allowedStatus = ['Menunggu Verifikasi','Valid','Tidak Valid','Perlu Survei Ulang']; + if (!in_array($statusVerifikasi, $allowedStatus, true)) { + $statusVerifikasi = 'Menunggu Verifikasi'; + } + + $anggota = is_array($data['anggota'] ?? null) ? $data['anggota'] : []; + $anggota = array_values(array_filter($anggota, fn($a) => is_array($a) && trim((string)($a['nama'] ?? '')) !== '')); + if (count($anggota) === 0) { + json_response(['success' => false, 'message' => 'Minimal satu anggota keluarga harus diisi.'], 422); + } + + $statusBantuan = $statusVerifikasi === 'Tidak Valid' ? 'Tidak Valid' : 'Menunggu Verifikasi'; + + $pdo->beginTransaction(); + try { + $stmt = $pdo->prepare('INSERT INTO keluarga + (nama_kk, alamat, no_kk, nik, kerentanan, kebutuhan, status_verifikasi, status_bantuan, catatan, lat, lng, created_by) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + + $stmt->execute([ + clean_string($data['nama_kk']), + clean_string($data['alamat']), + clean_string($data['no_kk'] ?? ''), + clean_string($data['nik'] ?? ''), + clean_string($data['kerentanan']), + clean_string($data['kebutuhan']), + $statusVerifikasi, + $statusBantuan, + clean_string($data['catatan'] ?? ''), + clean_float($data['lat']), + clean_float($data['lng']), + $user['id'] + ]); + + $keluargaId = (int)$pdo->lastInsertId(); + $anggotaStmt = $pdo->prepare('INSERT INTO keluarga_anggota + (keluarga_id, nik, nama, hubungan, jenis_kelamin, tempat_lahir, tanggal_lahir, agama, pendidikan, pekerjaan, status_perkawinan, penghasilan, mampu_bekerja, keahlian, status_pemberdayaan, catatan) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + + $allowedHubungan = ['Kepala Keluarga','Istri','Suami','Anak','Orang Tua','Mertua','Famili Lain','Lainnya']; + $allowedGender = ['Laki-laki','Perempuan']; + $allowedWork = ['Ya','Tidak']; + foreach ($anggota as $a) { + $tanggal = clean_string($a['tanggal_lahir'] ?? ''); + $anggotaStmt->execute([ + $keluargaId, + clean_string($a['nik'] ?? ''), + clean_string($a['nama'] ?? ''), + clean_allowed($a['hubungan'] ?? '', $allowedHubungan, 'Lainnya'), + clean_allowed($a['jenis_kelamin'] ?? '', $allowedGender, 'Laki-laki'), + clean_string($a['tempat_lahir'] ?? ''), + $tanggal !== '' ? $tanggal : null, + clean_string($a['agama'] ?? ''), + clean_string($a['pendidikan'] ?? ''), + clean_string($a['pekerjaan'] ?? ''), + clean_string($a['status_perkawinan'] ?? ''), + clean_float($a['penghasilan'] ?? 0), + clean_allowed($a['mampu_bekerja'] ?? 'Tidak', $allowedWork, 'Tidak'), + clean_string($a['keahlian'] ?? ''), + clean_allowed($a['mampu_bekerja'] ?? 'Tidak', $allowedWork, 'Tidak') === 'Ya' ? 'Layak Diberdayakan' : 'Belum Dinilai', + clean_string($a['catatan'] ?? '') + ]); + } + + $pdo->commit(); + json_response(['success' => true, 'message' => 'Data keluarga dan anggota KK berhasil disimpan.', 'id' => $keluargaId], 201); + } catch (Throwable $e) { + $pdo->rollBack(); + json_response(['success' => false, 'message' => 'Data gagal disimpan: ' . $e->getMessage()], 500); + } +} + +if ($method === 'PUT') { + api_require_role(['admin','dinas','relawan']); + $data = get_json_input(); + required_fields($data, ['id']); + + $id = clean_int($data['id']); + $allowedStatus = ['Menunggu Verifikasi','Valid','Tidak Valid','Perlu Survei Ulang']; + $statusVerifikasi = clean_string($data['status_verifikasi'] ?? 'Menunggu Verifikasi'); + if (!in_array($statusVerifikasi, $allowedStatus, true)) { + json_response(['success' => false, 'message' => 'Status verifikasi tidak valid.'], 422); + } + + $statusBantuan = $statusVerifikasi === 'Tidak Valid' ? 'Tidak Valid' : 'Menunggu Verifikasi'; + + $stmt = $pdo->prepare('UPDATE keluarga SET + status_verifikasi = ?, status_bantuan = ?, rumah_ibadah_id = NULL, catatan = CONCAT(COALESCE(catatan, ""), ?) + WHERE id = ?'); + $catatanTambahan = clean_string($data['catatan'] ?? ''); + $catatanTambahan = $catatanTambahan ? "\n[Verifikasi] " . $catatanTambahan : ''; + $stmt->execute([$statusVerifikasi, $statusBantuan, $catatanTambahan, $id]); + + json_response(['success' => true, 'message' => 'Status verifikasi berhasil diperbarui. Jalankan analisis ulang untuk memperbarui status bantuan.']); +} + +if ($method === 'DELETE') { + api_require_role(['admin','dinas']); + $id = clean_int($_GET['id'] ?? 0); + if ($id <= 0) json_response(['success' => false, 'message' => 'ID tidak valid.'], 422); + + $stmt = $pdo->prepare('DELETE FROM keluarga WHERE id = ?'); + $stmt->execute([$id]); + json_response(['success' => true, 'message' => 'Data keluarga berhasil dihapus.']); +} + +json_response(['success' => false, 'message' => 'Method tidak didukung.'], 405); diff --git a/api/musibah.php b/api/musibah.php new file mode 100644 index 0000000..e0f3c9b --- /dev/null +++ b/api/musibah.php @@ -0,0 +1,104 @@ +prepare($sql); + $stmt->execute($params); + json_response(['success' => true, 'data' => $stmt->fetchAll()]); +} + +if ($method === 'POST') { + $data = get_json_input(); + required_fields($data, ['jenis_musibah','tingkat_urgensi','tanggal_kejadian','deskripsi']); + + $keluargaId = clean_int($data['keluarga_id'] ?? 0); + $alamat = clean_string($data['alamat'] ?? ''); + $lat = $data['lat'] ?? ''; + $lng = $data['lng'] ?? ''; + + if ($keluargaId > 0) { + $stmtKel = $pdo->prepare('SELECT id, alamat, lat, lng FROM keluarga WHERE id = ? LIMIT 1'); + $stmtKel->execute([$keluargaId]); + $kel = $stmtKel->fetch(); + if (!$kel) json_response(['success' => false, 'message' => 'Data keluarga tidak ditemukan.'], 404); + if ($alamat === '') $alamat = $kel['alamat']; + if ($lat === '' || $lng === '') { + $lat = $kel['lat']; + $lng = $kel['lng']; + } + } + + if ($alamat === '' || $lat === '' || $lng === '') { + json_response(['success' => false, 'message' => 'Pilih keluarga terdata atau tentukan lokasi kejadian pada peta.'], 422); + } + + $stmt = $pdo->prepare('INSERT INTO laporan_musibah + (keluarga_id, pelapor_id, jenis_musibah, tingkat_urgensi, tanggal_kejadian, alamat, lat, lng, deskripsi, kebutuhan_mendesak, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "Baru")'); + $stmt->execute([ + $keluargaId > 0 ? $keluargaId : null, + $user['id'], + allowed_value($data['jenis_musibah'], ['Kebakaran','Banjir','Sakit Mendesak','Kehilangan Pekerjaan','Kematian Tulang Punggung Keluarga','Rumah Rusak','Lainnya'], 'Lainnya'), + allowed_value($data['tingkat_urgensi'], ['Rendah','Sedang','Tinggi','Darurat'], 'Sedang'), + clean_string($data['tanggal_kejadian']), + $alamat, + clean_float($lat), + clean_float($lng), + clean_string($data['deskripsi']), + clean_string($data['kebutuhan_mendesak'] ?? '') + ]); + + json_response(['success' => true, 'message' => 'Laporan musibah berhasil dikirim dan masuk antrian tindak lanjut.', 'id' => $pdo->lastInsertId()], 201); +} + +if ($method === 'PUT') { + api_require_role(['admin','dinas','relawan','rumah_ibadah']); + $data = get_json_input(); + required_fields($data, ['id','status']); + $id = clean_int($data['id']); + $status = allowed_value($data['status'], ['Baru','Diverifikasi','Diproses','Ditindaklanjuti','Selesai','Ditolak'], 'Baru'); + $tindak = clean_string($data['tindak_lanjut'] ?? ''); + + if ($role === 'rumah_ibadah') { + $stmtCheck = $pdo->prepare('SELECT lm.id FROM laporan_musibah lm LEFT JOIN keluarga k ON k.id = lm.keluarga_id WHERE lm.id = ? AND (k.rumah_ibadah_id = ? OR lm.pelapor_id = ?)'); + $stmtCheck->execute([$id, (int)($user['rumah_ibadah_id'] ?? 0), (int)$user['id']]); + if (!$stmtCheck->fetch()) json_response(['success' => false, 'message' => 'Akses laporan musibah ditolak.'], 403); + } + + $stmt = $pdo->prepare('UPDATE laporan_musibah SET status = ?, tindak_lanjut = CONCAT(COALESCE(tindak_lanjut, ""), ?), handled_by = ?, updated_at = NOW() WHERE id = ?'); + $tambahan = $tindak !== '' ? "\n[" . date('Y-m-d H:i') . "] " . $tindak : ''; + $stmt->execute([$status, $tambahan, $user['id'], $id]); + json_response(['success' => true, 'message' => 'Status laporan musibah berhasil diperbarui.']); +} + +json_response(['success' => false, 'message' => 'Method tidak didukung.'], 405);