167 lines
5.6 KiB
PHP
167 lines
5.6 KiB
PHP
<?php
|
|
// ============================================================
|
|
// API: Laporan Masyarakat
|
|
// POST: publik (tanpa login, rate-limited) | GET: admin+walikota | PUT: admin
|
|
// ============================================================
|
|
require_once __DIR__ . '/config.php';
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
// ── POST: Submit laporan baru (tanpa auth, dengan rate limit) ─
|
|
if ($method === 'POST') {
|
|
// Rate limit: maks 5 submit per jam per IP
|
|
$ip = getClientIP();
|
|
if (!checkRateLimit($pdo, $ip)) {
|
|
jsonResponse(null, 'Anda telah mencapai batas maksimal 5 laporan per jam. Silakan coba lagi nanti.', false, 429);
|
|
}
|
|
|
|
// Ambil data dari form multipart (karena ada file upload)
|
|
$nama_terlapor = clean($_POST['nama_terlapor'] ?? '');
|
|
$nik_terlapor = clean($_POST['nik_terlapor'] ?? '');
|
|
$nama_pelapor = clean($_POST['nama_pelapor'] ?? '');
|
|
$no_hp = clean($_POST['no_hp'] ?? '');
|
|
$alamat_terlapor = clean($_POST['alamat_terlapor'] ?? '');
|
|
$lat = (float)($_POST['lat'] ?? 0);
|
|
$lng = (float)($_POST['lng'] ?? 0);
|
|
$deskripsi = clean($_POST['deskripsi'] ?? '');
|
|
|
|
if (!$alamat_terlapor || !$lat || !$lng || !$deskripsi) {
|
|
jsonResponse(null, 'Alamat, koordinat, dan deskripsi wajib diisi.', false, 400);
|
|
}
|
|
|
|
// Validasi NIK jika diisi (harus 16 digit angka)
|
|
if ($nik_terlapor && !preg_match('/^\d{16}$/', $nik_terlapor)) {
|
|
jsonResponse(null, 'NIK harus 16 digit angka.', false, 400);
|
|
}
|
|
|
|
// Handle upload foto (opsional)
|
|
$fotoPath = handleUpload('foto');
|
|
|
|
// Simpan laporan
|
|
$stmt = $pdo->prepare("
|
|
INSERT INTO laporan_masyarakat
|
|
(nama_terlapor, nik_terlapor, nama_pelapor, no_hp, alamat_terlapor, lat, lng, deskripsi, foto, status)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'Baru')
|
|
");
|
|
$stmt->execute([
|
|
$nama_terlapor ?: null,
|
|
$nik_terlapor ?: null,
|
|
$nama_pelapor ?: null,
|
|
$no_hp ?: null,
|
|
$alamat_terlapor,
|
|
$lat, $lng,
|
|
$deskripsi,
|
|
$fotoPath
|
|
]);
|
|
|
|
$newId = (int)$pdo->lastInsertId();
|
|
|
|
jsonResponse(['id' => $newId], 'Laporan berhasil dikirim. Terima kasih atas partisipasi Anda.');
|
|
}
|
|
|
|
// ── GET: Daftar laporan (admin & walikota) ───────────────────
|
|
if ($method === 'GET') {
|
|
$user = requireRole(['admin', 'walikota']);
|
|
|
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : null;
|
|
|
|
if ($id) {
|
|
$stmt = $pdo->prepare("
|
|
SELECT lm.*,
|
|
u.nama AS nama_penangan,
|
|
kk.nama_kepala_keluarga AS nama_kk_converted
|
|
FROM laporan_masyarakat lm
|
|
LEFT JOIN users u ON lm.ditangani_oleh = u.id
|
|
LEFT JOIN keluarga_miskin kk ON lm.converted_to_kk_id = kk.id
|
|
WHERE lm.id = ?
|
|
");
|
|
$stmt->execute([$id]);
|
|
$row = $stmt->fetch();
|
|
|
|
if (!$row) {
|
|
jsonResponse(null, 'Laporan tidak ditemukan.', false, 404);
|
|
}
|
|
|
|
jsonResponse($row, 'Detail laporan.');
|
|
}
|
|
|
|
// Filter status
|
|
$where = [];
|
|
$params = [];
|
|
|
|
if (!empty($_GET['status'])) {
|
|
$where[] = 'lm.status = ?';
|
|
$params[] = clean($_GET['status']);
|
|
}
|
|
|
|
$sql = "
|
|
SELECT lm.*,
|
|
u.nama AS nama_penangan,
|
|
kk.nama_kepala_keluarga AS nama_kk_converted
|
|
FROM laporan_masyarakat lm
|
|
LEFT JOIN users u ON lm.ditangani_oleh = u.id
|
|
LEFT JOIN keluarga_miskin kk ON lm.converted_to_kk_id = kk.id
|
|
";
|
|
if (!empty($where)) {
|
|
$sql .= ' WHERE ' . implode(' AND ', $where);
|
|
}
|
|
$sql .= ' ORDER BY lm.created_at DESC';
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$rows = $stmt->fetchAll();
|
|
|
|
jsonResponse($rows, 'Daftar laporan masyarakat.');
|
|
}
|
|
|
|
// ── PUT: Update status laporan (admin only) ──────────────────
|
|
if ($method === 'PUT') {
|
|
$user = requireRole(['admin']);
|
|
$body = json_decode(file_get_contents('php://input'), true) ?? [];
|
|
|
|
$id = (int)($body['id'] ?? 0);
|
|
$status = clean($body['status'] ?? '');
|
|
$alasan_tolak = clean($body['alasan_tolak'] ?? '');
|
|
$converted_to_kk = isset($body['converted_to_kk_id']) ? (int)$body['converted_to_kk_id'] : null;
|
|
|
|
if (!$id || !$status) {
|
|
jsonResponse(null, 'ID dan status wajib diisi.', false, 400);
|
|
}
|
|
|
|
$validStatus = ['Baru', 'Diverifikasi', 'Ditolak'];
|
|
if (!in_array($status, $validStatus)) {
|
|
jsonResponse(null, 'Status tidak valid. Pilih: Baru, Diverifikasi, atau Ditolak.', false, 400);
|
|
}
|
|
|
|
// Jika ditolak, wajib ada alasan
|
|
if ($status === 'Ditolak' && empty($alasan_tolak)) {
|
|
jsonResponse(null, 'Alasan penolakan wajib diisi.', false, 400);
|
|
}
|
|
|
|
// Update laporan
|
|
$stmt = $pdo->prepare("
|
|
UPDATE laporan_masyarakat SET
|
|
status = ?,
|
|
alasan_tolak = ?,
|
|
ditangani_oleh = ?,
|
|
converted_to_kk_id = ?
|
|
WHERE id = ?
|
|
");
|
|
$stmt->execute([
|
|
$status,
|
|
$status === 'Ditolak' ? $alasan_tolak : null,
|
|
$user['id'],
|
|
$status === 'Diverifikasi' ? $converted_to_kk : null,
|
|
$id
|
|
]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
jsonResponse(null, 'Laporan tidak ditemukan.', false, 404);
|
|
}
|
|
|
|
jsonResponse(null, 'Status laporan berhasil diperbarui ke: ' . $status);
|
|
}
|
|
|
|
// ── Method tidak didukung ────────────────────────────────────
|
|
jsonResponse(null, 'Method tidak didukung.', false, 405);
|