Initial commit — WebGIS UAS GeoMiskin
Sistem WebGIS analisis sebaran penduduk miskin dan jangkauan rumah ibadah. Fitur: peta interaktif Leaflet.js, CRUD rumah ibadah & penduduk miskin, auto-kategorisasi haversine, heatmap, export PDF/CSV, dashboard analitik, manajemen pengguna (superadmin/admin), login sistem. Teknologi: PHP, MySQL, Leaflet.js, OpenStreetMap, Nominatim Geocoding, mPDF. Catatan: jalankan `composer install` di folder uas/ untuk install dependencies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$user_id = (int)($_SESSION['user']['id'] ?? 0);
|
||||
$old = $_POST['old_password'] ?? '';
|
||||
$new = $_POST['new_password'] ?? '';
|
||||
|
||||
if (strlen($new) < 6) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Password baru minimal 6 karakter']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$st = mysqli_prepare($conn, "SELECT password FROM users WHERE id = ?");
|
||||
mysqli_stmt_bind_param($st, 'i', $user_id);
|
||||
mysqli_stmt_execute($st);
|
||||
$row = mysqli_fetch_assoc(mysqli_stmt_get_result($st));
|
||||
|
||||
if (!$row || !password_verify($old, $row['password'])) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Password lama tidak sesuai']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$hash = password_hash($new, PASSWORD_DEFAULT);
|
||||
$st2 = mysqli_prepare($conn, "UPDATE users SET password = ? WHERE id = ?");
|
||||
mysqli_stmt_bind_param($st2, 'si', $hash, $user_id);
|
||||
mysqli_stmt_execute($st2);
|
||||
|
||||
$_SESSION['user']['password'] = $hash;
|
||||
|
||||
echo json_encode(['status' => 'success']);
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireSuperAdmin();
|
||||
|
||||
$type = $_POST['type'] ?? '';
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
|
||||
$whitelist = ['ibadah' => 'rumah_ibadah', 'penduduk' => 'penduduk_miskin'];
|
||||
if (!isset($whitelist[$type]) || $id <= 0) {
|
||||
echo json_encode(['status'=>'error','message'=>'Request tidak valid.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$table = $whitelist[$type];
|
||||
if (mysqli_query($conn, "DELETE FROM `{$table}` WHERE id = $id")) {
|
||||
if (mysqli_affected_rows($conn) === 0) {
|
||||
echo json_encode(['status'=>'error','message'=>'Data tidak ditemukan.']);
|
||||
} else {
|
||||
echo json_encode(['status'=>'success']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['status'=>'error','message'=>mysqli_error($conn)]);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$rows = mysqli_fetch_all(mysqli_query($conn,
|
||||
"SELECT p.*, i.nama AS ibadah_nama,
|
||||
DATE_FORMAT(p.tanggal_bantuan,'%d/%m/%Y') AS tgl_fmt
|
||||
FROM penduduk_miskin p
|
||||
LEFT JOIN rumah_ibadah i ON i.id = p.rumah_ibadah_id
|
||||
ORDER BY p.kk_nama"), MYSQLI_ASSOC);
|
||||
|
||||
$date = date('Y-m-d');
|
||||
header('Content-Type: text/csv; charset=UTF-8');
|
||||
header("Content-Disposition: attachment; filename=penduduk_miskin_{$date}.csv");
|
||||
|
||||
$out = fopen('php://output', 'w');
|
||||
// UTF-8 BOM for Excel
|
||||
fwrite($out, "\xEF\xBB\xBF");
|
||||
|
||||
fputcsv($out, ['No','Nama KK','NIK','Alamat','Jumlah Anggota','Rumah Ibadah Terdekat','Jarak (m)','Status Bantuan','Jenis Bantuan','Tanggal Bantuan','Latitude','Longitude']);
|
||||
|
||||
$i = 1;
|
||||
foreach ($rows as $r) {
|
||||
fputcsv($out, [
|
||||
$i++,
|
||||
$r['kk_nama'],
|
||||
$r['nik'],
|
||||
$r['alamat'],
|
||||
$r['jumlah_anggota'],
|
||||
$r['ibadah_nama'] ?: '-',
|
||||
$r['jarak_meter'] ? round($r['jarak_meter']) : '-',
|
||||
$r['status_bantuan'],
|
||||
$r['jenis_bantuan'],
|
||||
$r['tgl_fmt'] ?: '-',
|
||||
$r['lat'],
|
||||
$r['lng'],
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($out);
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
// ── Data ─────────────────────────────────────────────────────────────────────
|
||||
$allIbadah = mysqli_fetch_all(
|
||||
mysqli_query($conn, "SELECT * FROM rumah_ibadah ORDER BY nama"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
|
||||
$allPenduduk = mysqli_fetch_all(
|
||||
mysqli_query($conn,
|
||||
"SELECT p.*, i.nama AS ibadah_nama
|
||||
FROM penduduk_miskin p
|
||||
LEFT JOIN rumah_ibadah i ON i.id = p.rumah_ibadah_id
|
||||
ORDER BY p.kk_nama"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
|
||||
// Anggota count per penduduk
|
||||
$anggotaCounts = [];
|
||||
$res = mysqli_query($conn, "SELECT penduduk_id, COUNT(*) as cnt FROM anggota_keluarga GROUP BY penduduk_id");
|
||||
while ($r = mysqli_fetch_assoc($res)) {
|
||||
$anggotaCounts[$r['penduduk_id']] = (int)$r['cnt'];
|
||||
}
|
||||
|
||||
// ── Statistik ─────────────────────────────────────────────────────────────────
|
||||
$total = count($allPenduduk);
|
||||
$sudah = count(array_filter($allPenduduk, fn($p) => $p['status_bantuan'] === 'sudah_dibantu'));
|
||||
$belum = count(array_filter($allPenduduk, fn($p) => $p['status_bantuan'] === 'belum_dibantu'));
|
||||
$menunggu = count(array_filter($allPenduduk, fn($p) => $p['status_bantuan'] === 'menunggu_verifikasi'));
|
||||
$pctSudah = $total > 0 ? round($sudah / $total * 100, 1) : 0;
|
||||
|
||||
$bulan = ['','Januari','Februari','Maret','April','Mei','Juni',
|
||||
'Juli','Agustus','September','Oktober','November','Desember'];
|
||||
$tgl = date('j') . ' ' . $bulan[(int)date('n')] . ' ' . date('Y');
|
||||
$cetakOleh = htmlspecialchars(currentUser()['nama'] ?? 'Admin');
|
||||
|
||||
// ── Baris tabel ibadah ────────────────────────────────────────────────────────
|
||||
$rowsIbadah = '';
|
||||
foreach ($allIbadah as $i => $ib) {
|
||||
$binaan = count(array_filter($allPenduduk, fn($p) => $p['rumah_ibadah_id'] == $ib['id']));
|
||||
$bg = ($i % 2 === 0) ? '#ffffff' : '#f9fafb';
|
||||
$rowsIbadah .= "
|
||||
<tr style='background:{$bg}'>
|
||||
<td style='text-align:center'>" . ($i + 1) . "</td>
|
||||
<td><b>" . htmlspecialchars($ib['nama']) . "</b></td>
|
||||
<td style='text-align:center'>" . htmlspecialchars($ib['jenis']) . "</td>
|
||||
<td style='text-align:center'>{$ib['radius']} m</td>
|
||||
<td style='text-align:center'><b style='color:#1a7a3e'>{$binaan}</b></td>
|
||||
</tr>";
|
||||
}
|
||||
if (!$rowsIbadah) {
|
||||
$rowsIbadah = "<tr><td colspan='5' style='text-align:center;color:#999'>Belum ada data rumah ibadah.</td></tr>";
|
||||
}
|
||||
|
||||
// ── Baris tabel penduduk ─────────────────────────────────────────────────────
|
||||
$statusLabel = [
|
||||
'belum_dibantu' => 'Belum Dibantu',
|
||||
'sudah_dibantu' => 'Sudah Dibantu',
|
||||
'menunggu_verifikasi'=> 'Menunggu Verifikasi',
|
||||
];
|
||||
$statusColor = [
|
||||
'belum_dibantu' => '#c0392b',
|
||||
'sudah_dibantu' => '#1a7a3e',
|
||||
'menunggu_verifikasi'=> '#856404',
|
||||
];
|
||||
|
||||
$rowsPenduduk = '';
|
||||
foreach ($allPenduduk as $i => $p) {
|
||||
$bg = ($i % 2 === 0) ? '#ffffff' : '#f9fafb';
|
||||
$status = $p['status_bantuan'];
|
||||
$slbl = $statusLabel[$status] ?? $status;
|
||||
$scol = $statusColor[$status] ?? '#333';
|
||||
$jml = $p['jumlah_anggota'] ?? ($anggotaCounts[$p['id']] ?? 1);
|
||||
$jarak = $p['jarak_meter'] ? round($p['jarak_meter']) . ' m' : '-';
|
||||
$tglB = $p['tanggal_bantuan']
|
||||
? date('d/m/Y', strtotime($p['tanggal_bantuan'])) : '-';
|
||||
|
||||
$rowsPenduduk .= "
|
||||
<tr style='background:{$bg}'>
|
||||
<td style='text-align:center'>" . ($i + 1) . "</td>
|
||||
<td><b>" . htmlspecialchars($p['kk_nama']) . "</b>" .
|
||||
($p['nik'] ? "<br><span style='font-size:8px;color:#888'>{$p['nik']}</span>" : '') .
|
||||
"</td>
|
||||
<td style='font-size:9px'>" . htmlspecialchars($p['alamat'] ?? '-') . "</td>
|
||||
<td style='text-align:center'>{$jml}</td>
|
||||
<td style='font-size:9px'>" . htmlspecialchars($p['ibadah_nama'] ?? '-') . "</td>
|
||||
<td style='text-align:center'>{$jarak}</td>
|
||||
<td style='text-align:center;color:{$scol};font-weight:bold'>{$slbl}</td>
|
||||
<td style='font-size:9px'>" . htmlspecialchars($p['jenis_bantuan'] ?? '-') . "</td>
|
||||
<td style='text-align:center'>{$tglB}</td>
|
||||
</tr>";
|
||||
}
|
||||
if (!$rowsPenduduk) {
|
||||
$rowsPenduduk = "<tr><td colspan='9' style='text-align:center;color:#999'>Belum ada data penduduk miskin.</td></tr>";
|
||||
}
|
||||
|
||||
// ── HTML Template ─────────────────────────────────────────────────────────────
|
||||
$html = <<<HTML
|
||||
<style>
|
||||
body { font-family: sans-serif; font-size: 10.5px; color: #1a1a1a; }
|
||||
h1 { font-size: 15px; text-align: center; color: #1e3a5f; margin-bottom: 2px; }
|
||||
.sub { text-align: center; font-size: 9.5px; color: #555; margin-bottom: 14px; }
|
||||
.section { font-size: 11px; font-weight: bold; color: #1e3a5f;
|
||||
border-bottom: 2px solid #1e3a5f; padding-bottom: 3px; margin: 12px 0 7px; }
|
||||
table { width: 100%; border-collapse: collapse; font-size: 9.5px; }
|
||||
th { background: #1e3a5f; color: white; padding: 5px 4px; text-align: center; }
|
||||
td { padding: 4px 5px; border-bottom: 1px solid #e5e7eb; vertical-align: middle; }
|
||||
.sg { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
|
||||
.sc { width: 25%; padding: 8px; text-align: center; border: 1px solid #d1d5db; border-radius: 4px; }
|
||||
.sn { font-size: 20px; font-weight: bold; margin-bottom: 2px; }
|
||||
.sl { font-size: 8.5px; color: #6b7280; }
|
||||
.g { color: #1a7a3e; }
|
||||
.r { color: #c0392b; }
|
||||
.y { color: #856404; }
|
||||
</style>
|
||||
|
||||
<h1>LAPORAN SEBARAN PENDUDUK MISKIN</h1>
|
||||
<div class="sub">
|
||||
Sistem WebGIS GeoMiskin — Universitas Tanjungpura |
|
||||
Dicetak: {$tgl} |
|
||||
Oleh: {$cetakOleh}
|
||||
</div>
|
||||
|
||||
<div class="section">Ringkasan Statistik</div>
|
||||
<table class="sg">
|
||||
<tr>
|
||||
<td class="sc">
|
||||
<div class="sn">{$total}</div>
|
||||
<div class="sl">Total KK Miskin</div>
|
||||
</td>
|
||||
<td class="sc">
|
||||
<div class="sn g">{$sudah}</div>
|
||||
<div class="sl">Sudah Dibantu ({$pctSudah}%)</div>
|
||||
</td>
|
||||
<td class="sc">
|
||||
<div class="sn r">{$belum}</div>
|
||||
<div class="sl">Belum Dibantu</div>
|
||||
</td>
|
||||
<td class="sc">
|
||||
<div class="sn y">{$menunggu}</div>
|
||||
<div class="sl">Menunggu Verifikasi</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="section">Data Rumah Ibadah</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">No</th>
|
||||
<th width="35%">Nama Rumah Ibadah</th>
|
||||
<th width="20%">Jenis</th>
|
||||
<th width="15%">Radius</th>
|
||||
<th width="25%">KK Binaan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{$rowsIbadah}</tbody>
|
||||
</table>
|
||||
|
||||
<div class="section">Daftar Penduduk Miskin</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%">No</th>
|
||||
<th width="14%">Nama KK / NIK</th>
|
||||
<th width="16%">Alamat</th>
|
||||
<th width="5%">Jiwa</th>
|
||||
<th width="14%">Rumah Ibadah</th>
|
||||
<th width="8%">Jarak</th>
|
||||
<th width="13%">Status</th>
|
||||
<th width="14%">Jenis Bantuan</th>
|
||||
<th width="12%">Tgl Bantuan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{$rowsPenduduk}</tbody>
|
||||
</table>
|
||||
HTML;
|
||||
|
||||
// ── Generate PDF ───────────────────────────────────────────────────────────────
|
||||
try {
|
||||
$mpdf = new \Mpdf\Mpdf([
|
||||
'mode' => 'utf-8',
|
||||
'format' => 'A4-L',
|
||||
'margin_top' => 12,
|
||||
'margin_bottom' => 12,
|
||||
'margin_left' => 10,
|
||||
'margin_right' => 10,
|
||||
]);
|
||||
$mpdf->SetTitle('Laporan Sebaran Penduduk Miskin');
|
||||
$mpdf->SetAuthor('GeoMiskin WebGIS');
|
||||
$mpdf->WriteHTML($html);
|
||||
$fname = 'laporan_penduduk_miskin_' . date('Y-m-d') . '.pdf';
|
||||
$mpdf->Output($fname, 'D');
|
||||
} catch (\Mpdf\MpdfException $e) {
|
||||
http_response_code(500);
|
||||
echo '<pre style="font-family:monospace;padding:20px">
|
||||
<b>Gagal membuat PDF:</b> ' . htmlspecialchars($e->getMessage()) . '
|
||||
|
||||
Pastikan extension GD aktif di XAMPP:
|
||||
1. Buka C:\\xampp\\php\\php.ini
|
||||
2. Cari ;extension=gd → hapus tanda titik koma
|
||||
3. Restart Apache di XAMPP Control Panel
|
||||
</pre>';
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
// Rumah Ibadah
|
||||
$ibadah = mysqli_fetch_all(
|
||||
mysqli_query($conn, "SELECT * FROM rumah_ibadah ORDER BY nama"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
|
||||
// Penduduk miskin with anggota count
|
||||
$penduduk_raw = mysqli_fetch_all(
|
||||
mysqli_query($conn,
|
||||
"SELECT p.*, COUNT(a.id) as jumlah_anggota_db
|
||||
FROM penduduk_miskin p
|
||||
LEFT JOIN anggota_keluarga a ON a.penduduk_id = p.id
|
||||
GROUP BY p.id
|
||||
ORDER BY p.kk_nama"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
|
||||
// Attach anggota keluarga to each penduduk
|
||||
$penduduk = [];
|
||||
foreach ($penduduk_raw as $p) {
|
||||
$pid = (int)$p['id'];
|
||||
$anggota = mysqli_fetch_all(
|
||||
mysqli_query($conn, "SELECT * FROM anggota_keluarga WHERE penduduk_id = $pid ORDER BY id"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
// Use jumlah_anggota from table if set, else count from anggota table
|
||||
if (empty($p['jumlah_anggota'])) {
|
||||
$p['jumlah_anggota'] = count($anggota) ?: 1;
|
||||
}
|
||||
$p['anggota'] = $anggota;
|
||||
$penduduk[] = $p;
|
||||
}
|
||||
|
||||
echo json_encode(['ibadah' => $ibadah, 'penduduk' => $penduduk]);
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$nama = mysqli_real_escape_string($conn, trim($_POST['nama'] ?? ''));
|
||||
$jenis = $_POST['jenis'] ?? 'Masjid';
|
||||
$kontak = mysqli_real_escape_string($conn, trim($_POST['kontak'] ?? ''));
|
||||
$alamat = mysqli_real_escape_string($conn, trim($_POST['alamat'] ?? ''));
|
||||
$lat = (float)($_POST['lat'] ?? 0);
|
||||
$lng = (float)($_POST['lng'] ?? 0);
|
||||
$radius = max(50, min(5000, (int)($_POST['radius'] ?? 300)));
|
||||
|
||||
$valid_jenis = ['Masjid','Musholla','Gereja Protestan','Gereja Katolik','Pura','Vihara','Klenteng','Lainnya'];
|
||||
if (!$nama || !$alamat || !$lat || !$lng || !in_array($jenis, $valid_jenis)) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Data tidak lengkap atau tidak valid.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$jenis = mysqli_real_escape_string($conn, $jenis);
|
||||
$q = "INSERT INTO rumah_ibadah (nama, jenis, kontak, alamat, lat, lng, radius)
|
||||
VALUES ('$nama','$jenis','$kontak','$alamat',$lat,$lng,$radius)";
|
||||
|
||||
if (mysqli_query($conn, $q)) {
|
||||
echo json_encode(['status' => 'success', 'id' => mysqli_insert_id($conn)]);
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'message' => mysqli_error($conn)]);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$kk_nama = esc($conn, $_POST['kk_nama'] ?? '');
|
||||
$nik = esc($conn, $_POST['nik'] ?? '');
|
||||
$alamat = esc($conn, $_POST['alamat'] ?? '');
|
||||
$kecamatan = esc($conn, $_POST['kecamatan'] ?? '');
|
||||
$kelurahan = esc($conn, $_POST['kelurahan'] ?? '');
|
||||
$lat = (float)($_POST['lat'] ?? 0);
|
||||
$lng = (float)($_POST['lng'] ?? 0);
|
||||
$jumlah = max(1, (int)($_POST['jumlah_anggota'] ?? 1));
|
||||
$status_allowed = ['belum_dibantu','sudah_dibantu','menunggu_verifikasi'];
|
||||
$status = in_array($_POST['status_bantuan'] ?? '', $status_allowed) ? $_POST['status_bantuan'] : 'belum_dibantu';
|
||||
$jenis_bantuan = esc($conn, $_POST['jenis_bantuan'] ?? '');
|
||||
$tanggal = $_POST['tanggal_bantuan'] ?? '';
|
||||
$tanggal_sql = ($tanggal && preg_match('/^\d{4}-\d{2}-\d{2}$/', $tanggal)) ? "'$tanggal'" : 'NULL';
|
||||
|
||||
if (!$kk_nama || !$alamat || !$lat || !$lng) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Data tidak lengkap.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Find nearest rumah ibadah ---
|
||||
$ibadah_res = mysqli_query($conn, "SELECT id, lat, lng, radius FROM rumah_ibadah");
|
||||
$nearest_id = null;
|
||||
$nearest_dist = PHP_FLOAT_MAX;
|
||||
|
||||
while ($row = mysqli_fetch_assoc($ibadah_res)) {
|
||||
$d = haversine($lat, $lng, (float)$row['lat'], (float)$row['lng']);
|
||||
if ($d <= (float)$row['radius'] && $d < $nearest_dist) {
|
||||
$nearest_dist = $d;
|
||||
$nearest_id = $row['id'];
|
||||
}
|
||||
}
|
||||
// If no ibadah covers, find nearest anyway
|
||||
if (!$nearest_id) {
|
||||
mysqli_data_seek($ibadah_res, 0);
|
||||
while ($row = mysqli_fetch_assoc($ibadah_res)) {
|
||||
$d = haversine($lat, $lng, (float)$row['lat'], (float)$row['lng']);
|
||||
if ($d < $nearest_dist) { $nearest_dist = $d; $nearest_id = $row['id']; }
|
||||
}
|
||||
}
|
||||
$jarak = $nearest_id ? round($nearest_dist, 2) : 'NULL';
|
||||
$ibadah_id_sql = $nearest_id ? $nearest_id : 'NULL';
|
||||
$jarak_sql = is_numeric($jarak) ? $jarak : 'NULL';
|
||||
|
||||
// --- Handle file upload ---
|
||||
$bukti_file = '';
|
||||
if (!empty($_FILES['bukti_file']['name']) && $_FILES['bukti_file']['error'] === UPLOAD_ERR_OK) {
|
||||
$ext = strtolower(pathinfo($_FILES['bukti_file']['name'], PATHINFO_EXTENSION));
|
||||
$allowed = ['jpg','jpeg','png','webp','pdf','doc','docx'];
|
||||
if (in_array($ext, $allowed) && $_FILES['bukti_file']['size'] <= 5 * 1024 * 1024) {
|
||||
$dir = __DIR__ . '/../uploads/bukti/';
|
||||
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
||||
$fname = 'bukti_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
|
||||
if (move_uploaded_file($_FILES['bukti_file']['tmp_name'], $dir . $fname)) {
|
||||
$bukti_file = $fname;
|
||||
}
|
||||
}
|
||||
}
|
||||
$bukti_sql = $bukti_file ? "'".esc($conn,$bukti_file)."'" : 'NULL';
|
||||
|
||||
// --- Insert penduduk ---
|
||||
$q = "INSERT INTO penduduk_miskin (kk_nama,nik,alamat,kecamatan,kelurahan,lat,lng,jumlah_anggota,rumah_ibadah_id,jarak_meter,status_bantuan,jenis_bantuan,tanggal_bantuan,bukti_file)
|
||||
VALUES ('$kk_nama','$nik','$alamat','$kecamatan','$kelurahan',$lat,$lng,$jumlah,$ibadah_id_sql,$jarak_sql,'$status','$jenis_bantuan',$tanggal_sql,$bukti_sql)";
|
||||
|
||||
if (!mysqli_query($conn, $q)) {
|
||||
echo json_encode(['status' => 'error', 'message' => mysqli_error($conn)]);
|
||||
exit;
|
||||
}
|
||||
$pid = mysqli_insert_id($conn);
|
||||
|
||||
// --- Insert anggota keluarga ---
|
||||
$anggota = json_decode($_POST['anggota'] ?? '[]', true) ?: [];
|
||||
foreach ($anggota as $a) {
|
||||
if (empty($a['nama'])) continue;
|
||||
$an = esc($conn,$a['nama']); $ah = esc($conn,$a['hubungan'] ?? '');
|
||||
$au = (int)($a['umur'] ?? 0); $ap = esc($conn,$a['pekerjaan'] ?? ''); $ak = esc($conn,$a['keterangan'] ?? '');
|
||||
mysqli_query($conn, "INSERT INTO anggota_keluarga (penduduk_id,nama,hubungan,umur,pekerjaan,keterangan) VALUES ($pid,'$an','$ah',$au,'$ap','$ak')");
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'success', 'id' => $pid, 'rumah_ibadah_id' => $nearest_id, 'jarak_meter' => $jarak]);
|
||||
|
||||
function haversine($lat1, $lon1, $lat2, $lon2) {
|
||||
$R = 6371000;
|
||||
$d1 = deg2rad($lat1 - $lat2); $d2 = deg2rad($lon1 - $lon2);
|
||||
$a = sin($d1/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($d2/2)**2;
|
||||
return $R * 2 * atan2(sqrt($a), sqrt(1-$a));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) { echo json_encode(['status'=>'error','message'=>'ID tidak valid']); exit; }
|
||||
|
||||
$nama = mysqli_real_escape_string($conn, trim($_POST['nama'] ?? ''));
|
||||
$jenis = $_POST['jenis'] ?? 'Masjid';
|
||||
$kontak = mysqli_real_escape_string($conn, trim($_POST['kontak'] ?? ''));
|
||||
$alamat = mysqli_real_escape_string($conn, trim($_POST['alamat'] ?? ''));
|
||||
$lat = (float)($_POST['lat'] ?? 0);
|
||||
$lng = (float)($_POST['lng'] ?? 0);
|
||||
$radius = max(50, min(5000, (int)($_POST['radius'] ?? 300)));
|
||||
|
||||
$valid_jenis = ['Masjid','Musholla','Gereja Protestan','Gereja Katolik','Pura','Vihara','Klenteng','Lainnya'];
|
||||
if (!$nama || !$alamat || !$lat || !$lng || !in_array($jenis, $valid_jenis)) {
|
||||
echo json_encode(['status'=>'error','message'=>'Data tidak valid']); exit;
|
||||
}
|
||||
$jenis = mysqli_real_escape_string($conn, $jenis);
|
||||
$q = "UPDATE rumah_ibadah SET nama='$nama',jenis='$jenis',kontak='$kontak',alamat='$alamat',lat=$lat,lng=$lng,radius=$radius WHERE id=$id";
|
||||
|
||||
if (mysqli_query($conn, $q)) {
|
||||
// Recalculate nearest ibadah for affected penduduk
|
||||
echo json_encode(['status'=>'success']);
|
||||
} else {
|
||||
echo json_encode(['status'=>'error','message'=>mysqli_error($conn)]);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
function haversine($lat1,$lon1,$lat2,$lon2){$R=6371000;$d1=deg2rad($lat1-$lat2);$d2=deg2rad($lon1-$lon2);$a=sin($d1/2)**2+cos(deg2rad($lat1))*cos(deg2rad($lat2))*sin($d2/2)**2;return $R*2*atan2(sqrt($a),sqrt(1-$a));}
|
||||
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id <= 0) { echo json_encode(['status'=>'error','message'=>'ID tidak valid']); exit; }
|
||||
|
||||
$kk_nama = esc($conn,$_POST['kk_nama'] ?? '');
|
||||
$nik = esc($conn,$_POST['nik'] ?? '');
|
||||
$alamat = esc($conn,$_POST['alamat'] ?? '');
|
||||
$kecamatan = esc($conn,$_POST['kecamatan'] ?? '');
|
||||
$kelurahan = esc($conn,$_POST['kelurahan'] ?? '');
|
||||
$lat = (float)($_POST['lat'] ?? 0);
|
||||
$lng = (float)($_POST['lng'] ?? 0);
|
||||
$jumlah = max(1,(int)($_POST['jumlah_anggota'] ?? 1));
|
||||
$valid_st = ['belum_dibantu','sudah_dibantu','menunggu_verifikasi'];
|
||||
$status = in_array($_POST['status_bantuan']??'',$valid_st) ? $_POST['status_bantuan'] : 'belum_dibantu';
|
||||
$jenis_bantuan = esc($conn,$_POST['jenis_bantuan'] ?? '');
|
||||
$tanggal = $_POST['tanggal_bantuan'] ?? '';
|
||||
$tanggal_sql = ($tanggal && preg_match('/^\d{4}-\d{2}-\d{2}$/',$tanggal)) ? "'$tanggal'" : 'NULL';
|
||||
|
||||
if (!$kk_nama||!$alamat||!$lat||!$lng){echo json_encode(['status'=>'error','message'=>'Data tidak lengkap']);exit;}
|
||||
|
||||
// Find nearest ibadah
|
||||
$ibadah_res = mysqli_query($conn,"SELECT id,lat,lng,radius FROM rumah_ibadah");
|
||||
$nid=null;$ndist=PHP_FLOAT_MAX;
|
||||
while($row=mysqli_fetch_assoc($ibadah_res)){
|
||||
$d=haversine($lat,$lng,(float)$row['lat'],(float)$row['lng']);
|
||||
if($d<=(float)$row['radius']&&$d<$ndist){$ndist=$d;$nid=$row['id'];}
|
||||
}
|
||||
if(!$nid){mysqli_data_seek($ibadah_res,0);while($row=mysqli_fetch_assoc($ibadah_res)){$d=haversine($lat,$lng,(float)$row['lat'],(float)$row['lng']);if($d<$ndist){$ndist=$d;$nid=$row['id'];}}}
|
||||
$jarak=$nid?round($ndist,2):'NULL';$ibadah_sql=$nid?$nid:'NULL';$jarak_sql=is_numeric($jarak)?$jarak:'NULL';
|
||||
|
||||
// Handle file upload
|
||||
$bukti_sql='bukti_file=bukti_file'; // keep existing
|
||||
if(!empty($_FILES['bukti_file']['name'])&&$_FILES['bukti_file']['error']===UPLOAD_ERR_OK){
|
||||
$ext=strtolower(pathinfo($_FILES['bukti_file']['name'],PATHINFO_EXTENSION));
|
||||
$allowed=['jpg','jpeg','png','webp','pdf','doc','docx'];
|
||||
if(in_array($ext,$allowed)&&$_FILES['bukti_file']['size']<=5*1024*1024){
|
||||
$dir=__DIR__.'/../uploads/bukti/';
|
||||
if(!is_dir($dir))mkdir($dir,0755,true);
|
||||
$fname='bukti_'.time().'_'.bin2hex(random_bytes(4)).'.'.$ext;
|
||||
if(move_uploaded_file($_FILES['bukti_file']['tmp_name'],$dir.$fname)){
|
||||
$bukti_sql="bukti_file='".esc($conn,$fname)."'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$q="UPDATE penduduk_miskin SET kk_nama='$kk_nama',nik='$nik',alamat='$alamat',kecamatan='$kecamatan',kelurahan='$kelurahan',lat=$lat,lng=$lng,jumlah_anggota=$jumlah,rumah_ibadah_id=$ibadah_sql,jarak_meter=$jarak_sql,status_bantuan='$status',jenis_bantuan='$jenis_bantuan',tanggal_bantuan=$tanggal_sql,$bukti_sql WHERE id=$id";
|
||||
if(!mysqli_query($conn,$q)){echo json_encode(['status'=>'error','message'=>mysqli_error($conn)]);exit;}
|
||||
|
||||
// Update anggota
|
||||
mysqli_query($conn,"DELETE FROM anggota_keluarga WHERE penduduk_id=$id");
|
||||
$anggota=json_decode($_POST['anggota']??'[]',true)?:[];
|
||||
foreach($anggota as $a){
|
||||
if(empty($a['nama']))continue;
|
||||
$an=esc($conn,$a['nama']);$ah=esc($conn,$a['hubungan']??'');$au=(int)($a['umur']??0);$ap=esc($conn,$a['pekerjaan']??'');$ak=esc($conn,$a['keterangan']??'');
|
||||
mysqli_query($conn,"INSERT INTO anggota_keluarga (penduduk_id,nama,hubungan,umur,pekerjaan,keterangan) VALUES ($id,'$an','$ah',$au,'$ap','$ak')");
|
||||
}
|
||||
|
||||
echo json_encode(['status'=>'success']);
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include '../koneksi.php';
|
||||
include '../auth.php';
|
||||
requireLogin();
|
||||
|
||||
$action = $_POST['action'] ?? $_GET['action'] ?? 'list';
|
||||
|
||||
// ── LIST ────────────────────────────────────────────────────────
|
||||
if ($action === 'list') {
|
||||
$rows = mysqli_fetch_all(
|
||||
mysqli_query($conn, "SELECT id, username, nama, created_at FROM users ORDER BY nama"),
|
||||
MYSQLI_ASSOC
|
||||
);
|
||||
echo json_encode(['status' => 'success', 'data' => $rows]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── ADD ─────────────────────────────────────────────────────────
|
||||
if ($action === 'add') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$nama = trim($_POST['nama'] ?? '');
|
||||
|
||||
if (!$username || !$nama || strlen($password) < 6) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Data tidak lengkap (password minimal 6 karakter)']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$role = in_array($_POST['role'] ?? '', ['admin','superadmin']) ? $_POST['role'] : 'admin';
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$st = mysqli_prepare($conn, "INSERT INTO users (username, password, nama, role) VALUES (?,?,?,?)");
|
||||
mysqli_stmt_bind_param($st, 'ssss', $username, $hash, $nama, $role);
|
||||
if (mysqli_stmt_execute($st)) {
|
||||
echo json_encode(['status' => 'success', 'id' => mysqli_insert_id($conn)]);
|
||||
} else {
|
||||
$err = mysqli_error($conn);
|
||||
$msg = strpos($err, 'Duplicate') !== false ? "Username '{$username}' sudah digunakan" : $err;
|
||||
echo json_encode(['status' => 'error', 'message' => $msg]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── UPDATE ──────────────────────────────────────────────────────
|
||||
if ($action === 'update') {
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$nama = trim($_POST['nama'] ?? '');
|
||||
$pass = $_POST['password'] ?? '';
|
||||
$role = in_array($_POST['role'] ?? '', ['admin','superadmin']) ? $_POST['role'] : 'admin';
|
||||
|
||||
if ($id <= 0 || !$nama) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Data tidak valid']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (strlen($pass) >= 6) {
|
||||
$hash = password_hash($pass, PASSWORD_DEFAULT);
|
||||
$st = mysqli_prepare($conn, "UPDATE users SET nama=?, role=?, password=? WHERE id=?");
|
||||
mysqli_stmt_bind_param($st, 'sssi', $nama, $role, $hash, $id);
|
||||
} else {
|
||||
$st = mysqli_prepare($conn, "UPDATE users SET nama=?, role=? WHERE id=?");
|
||||
mysqli_stmt_bind_param($st, 'ssi', $nama, $role, $id);
|
||||
}
|
||||
|
||||
if (mysqli_stmt_execute($st)) {
|
||||
echo json_encode(['status' => 'success']);
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'message' => mysqli_error($conn)]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── DELETE ──────────────────────────────────────────────────────
|
||||
if ($action === 'delete') {
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$my_id = (int)($_SESSION['user']['id'] ?? 0);
|
||||
|
||||
if ($id <= 0) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'ID tidak valid']);
|
||||
exit;
|
||||
}
|
||||
if ($id === $my_id) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Tidak dapat menghapus akun sendiri']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$st = mysqli_prepare($conn, "DELETE FROM users WHERE id = ?");
|
||||
mysqli_stmt_bind_param($st, 'i', $id);
|
||||
if (mysqli_stmt_execute($st)) {
|
||||
if (mysqli_stmt_affected_rows($st) > 0) {
|
||||
echo json_encode(['status' => 'success']);
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'message' => 'User tidak ditemukan']);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'message' => mysqli_error($conn)]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'error', 'message' => 'Action tidak dikenal']);
|
||||
Reference in New Issue
Block a user