connect_error) { die("Koneksi database gagal."); } $conn->set_charset("utf8mb4"); $conn = new mysqli($host, $user, $pass, $db); if ($conn->connect_error) { die("Koneksi gagal: " . $conn->connect_error); } $conn->set_charset("utf8mb4"); function is_logged_in() { return isset($_SESSION['user']); } function current_role() { return $_SESSION['user']['role'] ?? 'masyarakat'; } function current_instansi() { return $_SESSION['user']['instansi'] ?? ''; } function is_admin() { return current_role() === 'admin'; } function is_walikota() { return current_role() === 'walikota'; } function is_dinas() { return current_role() === 'dinas'; } function can_view_dashboard() { return is_admin() || is_walikota() || is_dinas(); } function can_download_data() { return is_admin() || is_walikota() || is_dinas(); } function can_add_histori() { return is_admin() || is_dinas(); } function json_out($arr) { header('Content-Type: application/json; charset=utf-8'); echo json_encode($arr); exit; } function post($key, $default = '') { return trim($_POST[$key] ?? $default); } function ipost($key, $default = 0) { return (int) ($_POST[$key] ?? $default); } function fpost($key, $default = 0) { return (float) ($_POST[$key] ?? $default); } function require_admin() { if (!is_admin()) { json_out([ 'status' => false, 'message' => 'Akses ditolak. Fitur ini hanya untuk admin pengelola.' ]); } } function require_dashboard_access() { if (!can_view_dashboard()) { json_out([ 'status' => false, 'message' => 'Akses ditolak. Silakan login terlebih dahulu.' ]); } } function require_download_access() { if (!can_download_data()) { json_out([ 'status' => false, 'message' => 'Akses download hanya untuk admin, wali kota, atau dinas terkait.' ]); } } function require_histori_write() { if (!can_add_histori()) { json_out([ 'status' => false, 'message' => 'Akses ditolak. Histori bantuan hanya dapat ditambahkan oleh admin atau dinas terkait.' ]); } } function valid_enum($value, $allowed, $default) { return in_array($value, $allowed, true) ? $value : $default; } function allowed_aktor_tujuan() { return [ 'RT/RW', 'Kelurahan', 'Kecamatan', 'Dinas Sosial', 'Dinas Kesehatan', 'Wali Kota' ]; } function dinas_filter_sql($field = 'aktor_tujuan') { if (!is_dinas()) return ['', []]; $instansi = current_instansi() ?: 'Dinas Sosial'; return [" WHERE $field = ? ", [$instansi]]; } function find_matching_penduduk_id($conn, $namaKepalaKeluarga, $nik) { $namaKepalaKeluarga = trim((string) $namaKepalaKeluarga); $nik = trim((string) $nik); if ($nik !== '') { $stmt = $conn->prepare("SELECT id FROM penduduk_kemiskinan WHERE nik = ? LIMIT 1"); $stmt->bind_param("s", $nik); $stmt->execute(); $row = $stmt->get_result()->fetch_assoc(); if ($row) { return (int) $row['id']; } } if ($namaKepalaKeluarga !== '') { $stmt = $conn->prepare("SELECT id FROM penduduk_kemiskinan WHERE LOWER(TRIM(nama_kepala_keluarga)) = LOWER(TRIM(?)) LIMIT 1"); $stmt->bind_param("s", $namaKepalaKeluarga); $stmt->execute(); $row = $stmt->get_result()->fetch_assoc(); if ($row) { return (int) $row['id']; } } return 0; } if (isset($_GET['logout'])) { session_destroy(); header('Location: index.php'); exit; } if (isset($_POST['login'])) { $username = post('username'); $password = $_POST['password'] ?? ''; $stmt = $conn->prepare("SELECT * FROM users WHERE username=? LIMIT 1"); $stmt->bind_param("s", $username); $stmt->execute(); $userRow = $stmt->get_result()->fetch_assoc(); if ($userRow && password_verify($password, $userRow['password'])) { $_SESSION['user'] = [ 'id' => $userRow['id'], 'nama' => $userRow['nama'], 'username' => $userRow['username'], 'role' => $userRow['role'], 'instansi' => $userRow['instansi'] ?? '' ]; header('Location: index.php'); exit; } $login_error = "Username atau password salah."; } /* ========================= DOWNLOAD CSV ========================= */ if (isset($_GET['download'])) { require_download_access(); $type = $_GET['download']; if ($type === 'laporan') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=laporan_masyarakat.csv'); $out = fopen('php://output', 'w'); fputcsv($out, [ 'ID', 'Nama Pelapor', 'No HP', 'Kategori', 'Aktor Tujuan', 'Status', 'Deskripsi Publik', 'Nama Warga Dilaporkan', 'NIK', 'Tanggal Lahir', 'Pendidikan', 'Riwayat Penyakit', 'Jumlah KK', 'Alamat', 'Kecamatan', 'Kelurahan', 'Status Bantuan Awal', 'Latitude', 'Longitude', 'Tanggal' ]); if (is_dinas()) { $instansi = current_instansi() ?: 'Dinas Sosial'; $stmt = $conn->prepare("SELECT * FROM laporan_masyarakat WHERE aktor_tujuan=? ORDER BY id DESC"); $stmt->bind_param("s", $instansi); $stmt->execute(); $result = $stmt->get_result(); } else { $result = $conn->query("SELECT * FROM laporan_masyarakat ORDER BY id DESC"); } while ($row = $result->fetch_assoc()) { fputcsv($out, [ $row['id'], $row['nama_pelapor'], $row['no_hp'], $row['kategori_laporan'], $row['aktor_tujuan'], $row['status_laporan'], $row['deskripsi'], $row['data_nama_kepala_keluarga'] ?? '', $row['data_nik'] ?? '', $row['data_tanggal_lahir'] ?? '', $row['data_pendidikan'] ?? '', $row['data_riwayat_penyakit'] ?? '', $row['data_jumlah_kk'] ?? '', $row['data_alamat'] ?? '', $row['data_kecamatan'] ?? '', $row['data_kelurahan'] ?? '', $row['data_status_bantuan'] ?? '', $row['latitude'], $row['longitude'], $row['created_at'] ]); } exit; } if ($type === 'histori') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=histori_bantuan.csv'); $out = fopen('php://output', 'w'); fputcsv($out, [ 'ID', 'Kepala Keluarga', 'Jenis Bantuan', 'Tanggal', 'Nominal', 'Sumber Bantuan', 'Rumah Ibadah', 'Keterangan' ]); $sql = "SELECT hb.*, pk.nama_kepala_keluarga, m.nama_masjid, m.jenis_rumah_ibadah FROM histori_bantuan hb JOIN penduduk_kemiskinan pk ON pk.id = hb.penduduk_id LEFT JOIN masjid m ON m.id = hb.masjid_id ORDER BY hb.id DESC"; $result = $conn->query($sql); while ($row = $result->fetch_assoc()) { fputcsv($out, [ $row['id'], $row['nama_kepala_keluarga'], $row['jenis_bantuan'], $row['tanggal_bantuan'], $row['nominal'], $row['sumber_bantuan'], $row['nama_masjid'] ?? '', $row['keterangan'] ]); } exit; } if ($type === 'penduduk') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=masyarakat_miskin.csv'); $out = fopen('php://output', 'w'); fputcsv($out, [ 'ID', 'Nama Kepala Keluarga', 'NIK', 'Tanggal Lahir', 'Pendidikan', 'Riwayat Penyakit', 'Jumlah KK', 'Alamat', 'Kecamatan', 'Kelurahan', 'Status Bantuan', 'Latitude', 'Longitude', 'Tanggal Input' ]); $result = $conn->query("SELECT * FROM penduduk_kemiskinan ORDER BY id DESC"); while ($row = $result->fetch_assoc()) { fputcsv($out, [ $row['id'], $row['nama_kepala_keluarga'], $row['nik'], $row['tanggal_lahir'], $row['pendidikan'], $row['riwayat_penyakit'], $row['jumlah_kk'], $row['alamat'], $row['kecamatan'], $row['kelurahan'], $row['status_bantuan'], $row['latitude'], $row['longitude'], $row['created_at'] ]); } exit; } if ($type === 'rumah_ibadah') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=rumah_ibadah.csv'); $out = fopen('php://output', 'w'); fputcsv($out, [ 'ID', 'Jenis', 'Nama Rumah Ibadah', 'PIC', 'Alamat', 'Latitude', 'Longitude', 'Radius Meter', 'Tanggal Input' ]); $result = $conn->query("SELECT * FROM masjid ORDER BY id DESC"); while ($row = $result->fetch_assoc()) { fputcsv($out, [ $row['id'], $row['jenis_rumah_ibadah'], $row['nama_masjid'], $row['pic_masjid'], $row['alamat_masjid'], $row['latitude'], $row['longitude'], $row['radius_meter'], $row['created_at'] ]); } exit; } } /* ========================= API ACTIONS ========================= */ if (isset($_GET['action'])) { $action = $_GET['action']; if ($action === 'get_all') { $data = []; $tables = [ 'penduduk_kemiskinan', 'masjid', 'laporan_masyarakat' ]; foreach ($tables as $table) { $data[$table] = []; if ($table === 'laporan_masyarakat' && is_dinas()) { $instansi = current_instansi() ?: 'Dinas Sosial'; $stmt = $conn->prepare("SELECT * FROM laporan_masyarakat WHERE aktor_tujuan=? ORDER BY id DESC"); $stmt->bind_param("s", $instansi); $stmt->execute(); $result = $stmt->get_result(); } else { $result = $conn->query("SELECT * FROM $table ORDER BY id DESC"); } if ($result) { while ($row = $result->fetch_assoc()) { $data[$table][] = $row; } } } $data['users'] = []; if (is_admin()) { $resultUsers = $conn->query("SELECT id, nama, username, role, instansi, created_at FROM users ORDER BY role, nama"); if ($resultUsers) { while ($row = $resultUsers->fetch_assoc()) { $data['users'][] = $row; } } } $data['histori_bantuan'] = []; $sqlHistori = "SELECT hb.*, pk.nama_kepala_keluarga, m.nama_masjid, m.jenis_rumah_ibadah FROM histori_bantuan hb JOIN penduduk_kemiskinan pk ON pk.id = hb.penduduk_id LEFT JOIN masjid m ON m.id = hb.masjid_id ORDER BY hb.id DESC"; $result = $conn->query($sqlHistori); if ($result) { while ($row = $result->fetch_assoc()) { $data['histori_bantuan'][] = $row; } } json_out($data); } /* ========================= PENDUDUK KEMISKINAN ========================= */ if ($action === 'add_penduduk' || $action === 'update_penduduk') { require_admin(); $id = ipost('id'); $nama = post('nama_kepala_keluarga'); $nik = post('nik'); $tanggalLahir = post('tanggal_lahir') ?: null; $pendidikan = valid_enum(post('pendidikan'), ['Tidak sekolah', 'SD', 'SMP', 'SMA/SMK', 'Diploma', 'S1', 'S2', 'S3', 'Tidak diketahui'], 'Tidak diketahui'); $riwayat = post('riwayat_penyakit'); $jumlahKk = ipost('jumlah_kk', 1); $alamat = post('alamat'); $kecamatan = post('kecamatan'); $kelurahan = post('kelurahan'); $statusBantuan = valid_enum(post('status_bantuan'), ['Belum menerima', 'Sudah menerima', 'Perlu verifikasi'], 'Belum menerima'); $lat = fpost('latitude'); $lng = fpost('longitude'); if ($nama === '' || $jumlahKk <= 0 || $alamat === '' || $lat == 0 || $lng == 0) { json_out(['status' => false, 'message' => 'Data masyarakat prasejahtera belum lengkap']); } if ($action === 'add_penduduk') { $stmt = $conn->prepare("INSERT INTO penduduk_kemiskinan (nama_kepala_keluarga, nik, tanggal_lahir, pendidikan, riwayat_penyakit, jumlah_kk, alamat, kecamatan, kelurahan, status_bantuan, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt->bind_param("sssssissssdd", $nama, $nik, $tanggalLahir, $pendidikan, $riwayat, $jumlahKk, $alamat, $kecamatan, $kelurahan, $statusBantuan, $lat, $lng); } else { if ($id <= 0) json_out(['status' => false, 'message' => 'ID masyarakat prasejahtera tidak valid']); $stmt = $conn->prepare("UPDATE penduduk_kemiskinan SET nama_kepala_keluarga=?, nik=?, tanggal_lahir=?, pendidikan=?, riwayat_penyakit=?, jumlah_kk=?, alamat=?, kecamatan=?, kelurahan=?, status_bantuan=?, latitude=?, longitude=? WHERE id=?"); $stmt->bind_param("sssssissssddi", $nama, $nik, $tanggalLahir, $pendidikan, $riwayat, $jumlahKk, $alamat, $kecamatan, $kelurahan, $statusBantuan, $lat, $lng, $id); } json_out($stmt->execute() ? ['status' => true, 'message' => $action === 'add_penduduk' ? 'Data masyarakat prasejahtera berhasil ditambahkan' : 'Data masyarakat prasejahtera berhasil diupdate'] : ['status' => false, 'message' => 'Gagal menyimpan data masyarakat prasejahtera'] ); } if ($action === 'update_position_penduduk') { require_admin(); $id = ipost('id'); $lat = fpost('latitude'); $lng = fpost('longitude'); $stmt = $conn->prepare("UPDATE penduduk_kemiskinan SET latitude=?, longitude=? WHERE id=?"); $stmt->bind_param("ddi", $lat, $lng, $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Posisi masyarakat prasejahtera berhasil diupdate'] : ['status' => false, 'message' => 'Gagal update posisi masyarakat prasejahtera'] ); } /* ========================= RUMAH IBADAH / MASJID ========================= */ if ($action === 'add_masjid' || $action === 'update_masjid') { require_admin(); $id = ipost('id'); $jenis = valid_enum(post('jenis_rumah_ibadah'), ['Masjid', 'Gereja', 'Vihara', 'Kelenteng', 'Pura'], 'Masjid'); $nama = post('nama_masjid'); $pic = post('pic_masjid'); $alamat = post('alamat_masjid'); $lat = fpost('latitude'); $lng = fpost('longitude'); $radius = max(100, min(5000, ipost('radius_meter', 500))); if ($nama === '' || $pic === '' || $alamat === '' || $lat == 0 || $lng == 0) { json_out(['status' => false, 'message' => 'Data rumah ibadah belum lengkap']); } if ($action === 'add_masjid') { $stmt = $conn->prepare("INSERT INTO masjid (jenis_rumah_ibadah, nama_masjid, pic_masjid, alamat_masjid, latitude, longitude, radius_meter) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->bind_param("ssssddi", $jenis, $nama, $pic, $alamat, $lat, $lng, $radius); } else { if ($id <= 0) json_out(['status' => false, 'message' => 'ID rumah ibadah tidak valid']); $stmt = $conn->prepare("UPDATE masjid SET jenis_rumah_ibadah=?, nama_masjid=?, pic_masjid=?, alamat_masjid=?, latitude=?, longitude=?, radius_meter=? WHERE id=?"); $stmt->bind_param("ssssddii", $jenis, $nama, $pic, $alamat, $lat, $lng, $radius, $id); } json_out($stmt->execute() ? ['status' => true, 'message' => $action === 'add_masjid' ? 'Data rumah ibadah berhasil ditambahkan' : 'Data rumah ibadah berhasil diupdate'] : ['status' => false, 'message' => 'Gagal menyimpan data rumah ibadah'] ); } if ($action === 'update_position_masjid') { require_admin(); $id = ipost('id'); $lat = fpost('latitude'); $lng = fpost('longitude'); $stmt = $conn->prepare("UPDATE masjid SET latitude=?, longitude=? WHERE id=?"); $stmt->bind_param("ddi", $lat, $lng, $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Posisi rumah ibadah berhasil diupdate'] : ['status' => false, 'message' => 'Gagal update posisi rumah ibadah'] ); } /* ========================= LAPORAN MASYARAKAT ========================= */ if ($action === 'add_laporan' || $action === 'update_laporan') { if ($action === 'add_laporan' && is_logged_in()) { json_out([ 'status' => false, 'message' => 'Laporan cepat hanya dibuat dari mode masyarakat umum. Admin cukup memperbarui status laporan.' ]); } if ($action === 'update_laporan') { require_admin(); } $id = ipost('id'); $pendudukIdInput = ipost('penduduk_id'); $pendudukId = $pendudukIdInput > 0 ? $pendudukIdInput : null; // Data publik laporan $nama = post('nama_pelapor'); $hp = post('no_hp'); $kategori = valid_enum(post('kategori_laporan'), ['Data kemiskinan', 'Bantuan belum diterima', 'Kondisi kesehatan', 'Data tidak sesuai', 'Lainnya'], 'Data kemiskinan'); $deskripsi = post('deskripsi'); $aktor = valid_enum(post('aktor_tujuan'), allowed_aktor_tujuan(), 'Dinas Sosial'); $status = valid_enum(post('status_laporan'), ['Masuk', 'Ditinjau', 'Diproses', 'Selesai', 'Dijadikan Data Masyarakat', 'Ditolak'], 'Masuk'); // Data lengkap calon masyarakat prasejahtera dari laporan $dataNamaKK = post('data_nama_kepala_keluarga'); $dataNik = post('data_nik'); $dataTanggalLahir = post('data_tanggal_lahir') ?: null; $dataPendidikan = valid_enum(post('data_pendidikan'), ['Tidak sekolah', 'SD', 'SMP', 'SMA/SMK', 'Diploma', 'S1', 'S2', 'S3', 'Tidak diketahui'], 'Tidak diketahui'); $dataRiwayat = post('data_riwayat_penyakit'); $dataJumlahKk = max(1, ipost('data_jumlah_kk', 1)); $dataAlamat = post('data_alamat'); $dataKecamatan = post('data_kecamatan'); $dataKelurahan = post('data_kelurahan'); $dataStatusBantuan = valid_enum(post('data_status_bantuan'), ['Belum menerima', 'Sudah menerima', 'Perlu verifikasi'], 'Belum menerima'); // Logika auto-terhubung: // Jika NIK sama dengan data masyarakat yang sudah ada, sistem langsung menghubungkan laporan ke data tersebut. // Jika NIK kosong, sistem mencoba mencocokkan dari nama kepala keluarga/warga yang dilaporkan. // Status laporan tetap Masuk agar kebutuhan bantuan terbaru tetap terlihat di dashboard. if (!$pendudukId) { $matchedPendudukId = find_matching_penduduk_id($conn, $dataNamaKK, $dataNik); $pendudukId = $matchedPendudukId > 0 ? $matchedPendudukId : null; } $lat = fpost('latitude'); $lng = fpost('longitude'); if ($nama === '' || $hp === '' || $deskripsi === '' || $dataNamaKK === '' || $dataAlamat === '' || $lat == 0 || $lng == 0) { json_out(['status' => false, 'message' => 'Data laporan belum lengkap. Isi data pelapor, data warga yang dilaporkan, alamat, deskripsi, dan titik lokasi.']); } if ($action === 'add_laporan') { $stmt = $conn->prepare("INSERT INTO laporan_masyarakat (penduduk_id, nama_pelapor, no_hp, kategori_laporan, deskripsi, aktor_tujuan, status_laporan, data_nama_kepala_keluarga, data_nik, data_tanggal_lahir, data_pendidikan, data_riwayat_penyakit, data_jumlah_kk, data_alamat, data_kecamatan, data_kelurahan, data_status_bantuan, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, 'Masuk', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); if (!$stmt) { json_out(['status' => false, 'message' => 'Gagal menyiapkan query laporan: ' . $conn->error]); } $stmt->bind_param("issssssssssissssdd", $pendudukId, $nama, $hp, $kategori, $deskripsi, $aktor, $dataNamaKK, $dataNik, $dataTanggalLahir, $dataPendidikan, $dataRiwayat, $dataJumlahKk, $dataAlamat, $dataKecamatan, $dataKelurahan, $dataStatusBantuan, $lat, $lng); } else { if ($id <= 0) json_out(['status' => false, 'message' => 'ID laporan tidak valid']); $stmt = $conn->prepare("UPDATE laporan_masyarakat SET penduduk_id=?, nama_pelapor=?, no_hp=?, kategori_laporan=?, deskripsi=?, aktor_tujuan=?, status_laporan=?, data_nama_kepala_keluarga=?, data_nik=?, data_tanggal_lahir=?, data_pendidikan=?, data_riwayat_penyakit=?, data_jumlah_kk=?, data_alamat=?, data_kecamatan=?, data_kelurahan=?, data_status_bantuan=?, latitude=?, longitude=? WHERE id=?"); if (!$stmt) { json_out(['status' => false, 'message' => 'Gagal menyiapkan query update laporan: ' . $conn->error]); } $stmt->bind_param("isssssssssssissssddi", $pendudukId, $nama, $hp, $kategori, $deskripsi, $aktor, $status, $dataNamaKK, $dataNik, $dataTanggalLahir, $dataPendidikan, $dataRiwayat, $dataJumlahKk, $dataAlamat, $dataKecamatan, $dataKelurahan, $dataStatusBantuan, $lat, $lng, $id); } if ($stmt->execute()) { $linkedText = $pendudukId ? ' Sistem mendeteksi nama/NIK yang sama, sehingga laporan otomatis terhubung ke data masyarakat prasejahtera yang sudah ada.' : ''; json_out([ 'status' => true, 'message' => ($action === 'add_laporan' ? 'Laporan berhasil dikirim dan menunggu tindak lanjut admin.' : 'Laporan berhasil diupdate.') . $linkedText ]); } json_out([ 'status' => false, 'message' => 'Gagal menyimpan laporan: ' . ($stmt ? $stmt->error : $conn->error) ]); } if ($action === 'update_position_laporan') { require_admin(); $id = ipost('id'); $lat = fpost('latitude'); $lng = fpost('longitude'); $stmt = $conn->prepare("UPDATE laporan_masyarakat SET latitude=?, longitude=? WHERE id=?"); $stmt->bind_param("ddi", $lat, $lng, $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Posisi laporan berhasil diupdate'] : ['status' => false, 'message' => 'Gagal update posisi laporan'] ); } if ($action === 'update_status_laporan') { require_admin(); $id = ipost('id'); $status = valid_enum(post('status_laporan'), ['Masuk', 'Ditinjau', 'Diproses', 'Selesai', 'Dijadikan Data Masyarakat', 'Ditolak'], 'Masuk'); $stmt = $conn->prepare("UPDATE laporan_masyarakat SET status_laporan=? WHERE id=?"); $stmt->bind_param("si", $status, $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Status laporan berhasil diperbarui'] : ['status' => false, 'message' => 'Gagal update status laporan'] ); } /* ========================= KONVERSI LAPORAN MENJADI DATA MASYARAKAT PRASEJAHTERA ========================= */ if ($action === 'convert_laporan_to_penduduk') { require_admin(); $id = ipost('id'); if ($id <= 0) { json_out(['status' => false, 'message' => 'ID laporan tidak valid']); } $stmt = $conn->prepare("SELECT * FROM laporan_masyarakat WHERE id=? LIMIT 1"); $stmt->bind_param("i", $id); $stmt->execute(); $laporan = $stmt->get_result()->fetch_assoc(); if (!$laporan) { json_out(['status' => false, 'message' => 'Laporan tidak ditemukan']); } if (($laporan['status_laporan'] ?? '') !== 'Selesai') { json_out([ 'status' => false, 'message' => 'Laporan belum bisa dijadikan data masyarakat. Ubah status laporan menjadi Selesai setelah laporan benar-benar valid.' ]); } if (!empty($laporan['penduduk_id'])) { $update = $conn->prepare("UPDATE laporan_masyarakat SET status_laporan='Dijadikan Data Masyarakat' WHERE id=?"); $update->bind_param("i", $id); $update->execute(); json_out([ 'status' => true, 'message' => 'Laporan ini sudah terhubung dengan data masyarakat prasejahtera.' ]); } $namaKK = trim($laporan['data_nama_kepala_keluarga'] ?? ''); $alamat = trim($laporan['data_alamat'] ?? ''); $matchedPendudukId = find_matching_penduduk_id($conn, $namaKK, $laporan['data_nik'] ?? ''); if ($matchedPendudukId > 0) { $update = $conn->prepare("UPDATE laporan_masyarakat SET penduduk_id=?, status_laporan='Dijadikan Data Masyarakat' WHERE id=?"); $update->bind_param("ii", $matchedPendudukId, $id); $update->execute(); json_out([ 'status' => true, 'message' => 'Nama/NIK pada laporan cocok dengan data masyarakat yang sudah ada. Laporan otomatis dihubungkan, sehingga tidak membuat data masyarakat ganda.' ]); } if ($namaKK === '' || $alamat === '') { json_out([ 'status' => false, 'message' => 'Data warga pada laporan belum lengkap. Isi nama kepala keluarga/warga dan alamat terlebih dahulu.' ]); } $nik = $laporan['data_nik'] ?? null; $tanggalLahir = $laporan['data_tanggal_lahir'] ?: null; $pendidikan = $laporan['data_pendidikan'] ?: 'Tidak diketahui'; $riwayat = $laporan['data_riwayat_penyakit'] ?? ''; $jumlahKk = (int)($laporan['data_jumlah_kk'] ?? 1); if ($jumlahKk <= 0) $jumlahKk = 1; $kecamatan = $laporan['data_kecamatan'] ?? ''; $kelurahan = $laporan['data_kelurahan'] ?? ''; $statusBantuan = $laporan['data_status_bantuan'] ?: 'Belum menerima'; $lat = (float)$laporan['latitude']; $lng = (float)$laporan['longitude']; $insert = $conn->prepare("INSERT INTO penduduk_kemiskinan (nama_kepala_keluarga, nik, tanggal_lahir, pendidikan, riwayat_penyakit, jumlah_kk, alamat, kecamatan, kelurahan, status_bantuan, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $insert->bind_param("sssssissssdd", $namaKK, $nik, $tanggalLahir, $pendidikan, $riwayat, $jumlahKk, $alamat, $kecamatan, $kelurahan, $statusBantuan, $lat, $lng); if (!$insert->execute()) { json_out(['status' => false, 'message' => 'Gagal menjadikan laporan sebagai data masyarakat prasejahtera']); } $pendudukIdBaru = $conn->insert_id; $update = $conn->prepare("UPDATE laporan_masyarakat SET penduduk_id=?, status_laporan='Dijadikan Data Masyarakat' WHERE id=?"); $update->bind_param("ii", $pendudukIdBaru, $id); $update->execute(); json_out([ 'status' => true, 'message' => 'Laporan berhasil dijadikan data masyarakat prasejahtera. Bubble laporan akan disembunyikan dari peta, tetapi tetap tersimpan di dashboard/statistik.' ]); } /* ========================= HUBUNGKAN LAPORAN DENGAN DATA MASYARAKAT YANG SUDAH ADA ========================= */ if ($action === 'link_laporan_to_penduduk') { require_admin(); $id = ipost('id'); $pendudukId = ipost('penduduk_id'); if ($id <= 0 || $pendudukId <= 0) { json_out(['status' => false, 'message' => 'Pilih laporan dan data masyarakat yang valid']); } $cekLaporan = $conn->prepare("SELECT id, status_laporan, penduduk_id FROM laporan_masyarakat WHERE id=? LIMIT 1"); $cekLaporan->bind_param("i", $id); $cekLaporan->execute(); $laporan = $cekLaporan->get_result()->fetch_assoc(); if (!$laporan) { json_out(['status' => false, 'message' => 'Laporan tidak ditemukan']); } if (($laporan['status_laporan'] ?? '') !== 'Selesai') { json_out([ 'status' => false, 'message' => 'Laporan belum bisa dihubungkan. Ubah status laporan menjadi Selesai setelah laporan benar-benar valid.' ]); } if (!empty($laporan['penduduk_id'])) { json_out([ 'status' => true, 'message' => 'Laporan ini sudah terhubung dengan data masyarakat prasejahtera.' ]); } $cekPenduduk = $conn->prepare("SELECT id FROM penduduk_kemiskinan WHERE id=? LIMIT 1"); $cekPenduduk->bind_param("i", $pendudukId); $cekPenduduk->execute(); if (!$cekPenduduk->get_result()->fetch_assoc()) { json_out(['status' => false, 'message' => 'Data masyarakat prasejahtera tidak ditemukan']); } $stmt = $conn->prepare("UPDATE laporan_masyarakat SET penduduk_id=?, status_laporan='Dijadikan Data Masyarakat' WHERE id=?"); $stmt->bind_param("ii", $pendudukId, $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Laporan berhasil dihubungkan ke data masyarakat yang sudah ada. Laporan tetap tersimpan untuk arsip/statistik, tetapi bubble laporan disembunyikan dari peta.'] : ['status' => false, 'message' => 'Gagal menghubungkan laporan ke data masyarakat'] ); } /* ========================= HISTORI BANTUAN ========================= */ if ($action === 'add_histori') { require_histori_write(); $pendudukId = ipost('penduduk_id'); $masjidIdInput = ipost('masjid_id'); $masjidId = $masjidIdInput > 0 ? $masjidIdInput : null; $jenis = post('jenis_bantuan'); $tanggal = post('tanggal_bantuan'); $nominal = fpost('nominal'); $sumber = post('sumber_bantuan'); if ($sumber === '' && $masjidId) { $stmtMasjid = $conn->prepare("SELECT nama_masjid FROM masjid WHERE id=? LIMIT 1"); $stmtMasjid->bind_param("i", $masjidId); $stmtMasjid->execute(); $rowMasjid = $stmtMasjid->get_result()->fetch_assoc(); if ($rowMasjid) { $sumber = $rowMasjid['nama_masjid']; } } if ($sumber === '') { $sumber = is_dinas() ? (current_instansi() ?: 'Dinas Terkait') : 'Pemkot Pontianak / Rumah Ibadah / Donatur'; } $keterangan = post('keterangan'); if ($pendudukId <= 0 || $jenis === '' || $tanggal === '') { json_out(['status' => false, 'message' => 'Data histori bantuan belum lengkap']); } $stmt = $conn->prepare("INSERT INTO histori_bantuan (penduduk_id, masjid_id, jenis_bantuan, tanggal_bantuan, nominal, sumber_bantuan, keterangan) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->bind_param("iissdss", $pendudukId, $masjidId, $jenis, $tanggal, $nominal, $sumber, $keterangan); if ($stmt->execute()) { $updateStatus = $conn->prepare("UPDATE penduduk_kemiskinan SET status_bantuan='Sudah menerima' WHERE id=?"); $updateStatus->bind_param("i", $pendudukId); $updateStatus->execute(); json_out([ 'status' => true, 'message' => 'Histori bantuan berhasil ditambahkan dan status bantuan warga menjadi Sudah menerima' ]); } json_out([ 'status' => false, 'message' => 'Gagal menyimpan histori bantuan' ]); } /* ========================= KELOLA AKUN ROLE ========================= */ if ($action === 'add_user') { require_admin(); $nama = post('nama'); $username = post('username'); $password = $_POST['password'] ?? ''; $role = valid_enum(post('role'), ['admin', 'walikota', 'dinas'], 'dinas'); $instansi = post('instansi'); if ($nama === '' || $username === '' || $password === '') { json_out(['status' => false, 'message' => 'Nama, username, dan password wajib diisi']); } if ($role === 'walikota') { $instansi = 'Wali Kota'; } elseif ($role === 'admin') { $instansi = 'Admin Pengelola'; } elseif ($instansi === '') { $instansi = 'Dinas Sosial'; } $hash = password_hash($password, PASSWORD_DEFAULT); $stmt = $conn->prepare("INSERT INTO users (nama, username, password, role, instansi) VALUES (?, ?, ?, ?, ?)"); $stmt->bind_param("sssss", $nama, $username, $hash, $role, $instansi); json_out($stmt->execute() ? ['status' => true, 'message' => 'Akun berhasil ditambahkan'] : ['status' => false, 'message' => 'Gagal menambahkan akun. Username mungkin sudah digunakan.'] ); } if ($action === 'delete_user') { require_admin(); $id = ipost('id'); if ($id <= 0) json_out(['status' => false, 'message' => 'ID akun tidak valid']); if ($id === (int)($_SESSION['user']['id'] ?? 0)) { json_out(['status' => false, 'message' => 'Akun yang sedang login tidak boleh dihapus']); } $stmt = $conn->prepare("DELETE FROM users WHERE id=?"); $stmt->bind_param("i", $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Akun berhasil dihapus'] : ['status' => false, 'message' => 'Gagal menghapus akun'] ); } /* ========================= DELETE GENERIC ========================= */ if ($action === 'delete') { require_admin(); $allowed = [ 'penduduk_kemiskinan', 'masjid', 'laporan_masyarakat', 'histori_bantuan' ]; $table = post('table'); $id = ipost('id'); if (!in_array($table, $allowed, true)) { json_out(['status' => false, 'message' => 'Tabel tidak valid']); } $stmt = $conn->prepare("DELETE FROM $table WHERE id=?"); $stmt->bind_param("i", $id); json_out($stmt->execute() ? ['status' => true, 'message' => 'Data berhasil dihapus'] : ['status' => false, 'message' => 'Gagal menghapus data'] ); } json_out(['status' => false, 'message' => 'Action tidak dikenal']); } $admin = is_admin(); $loggedIn = is_logged_in(); $canViewDashboard = can_view_dashboard(); $roleLabel = ['admin' => 'Admin Pengelola', 'walikota' => 'Wali Kota', 'dinas' => 'Dinas Terkait'][$_SESSION['user']['role'] ?? ''] ?? 'Masyarakat Umum'; $instansiLabel = current_instansi(); ?> WebGIS Prasejahtera Pontianak

Sistem Informasi Geografis

WebGIS Prasejahtera Pontianak

Pemetaan masyarakat prasejahtera, rumah ibadah, laporan cepat, dan histori bantuan berbasis lokasi.

Peta WebGIS Poverty Mapping

Pilih mode, klik titik pada peta, lalu isi form pada panel kanan sesuai kebutuhan data.

Luar radius Dalam radius