commit 846ce944738ba7e2c516f76eceef229f64051986 Author: NASYA KHAIRA ADINIA Date: Thu Jun 11 21:20:59 2026 +0700 first commit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api/auth.php b/api/auth.php new file mode 100644 index 0000000..af2d3b3 --- /dev/null +++ b/api/auth.php @@ -0,0 +1,51 @@ +prepare("SELECT * FROM users WHERE username = ? AND aktif = 1 LIMIT 1"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if (!$user || !password_verify($password, $user['password'])) { + jsonError('Username atau password salah.', 401); + } + + $_SESSION['user'] = [ + 'id' => $user['id'], + 'username' => $user['username'], + 'role' => $user['role'], + 'jabatan' => $user['jabatan'], + 'can_edit' => (bool)$user['can_edit'], + 'is_admin' => (bool)$user['is_admin'], + 'is_wali' => (bool)$user['is_wali'], + ]; + + jsonOk($_SESSION['user'], 'Login berhasil'); +} + +if ($method === 'POST' && $action === 'logout') { + session_destroy(); + jsonOk(null, 'Logout berhasil'); +} + +if ($method === 'GET' && $action === 'check') { + if (!empty($_SESSION['user'])) { + jsonOk($_SESSION['user'], 'Session aktif'); + } + jsonError('Tidak terautentikasi', 401); +} + +jsonError('Method tidak valid', 405); diff --git a/api/bantuan.php b/api/bantuan.php new file mode 100644 index 0000000..799b258 --- /dev/null +++ b/api/bantuan.php @@ -0,0 +1,64 @@ +prepare( + "SELECT id, tanggal, jenis, nominal, sumber_dana AS sumber, keterangan + FROM histori_bantuan + WHERE warga_id = ? + ORDER BY tanggal DESC" + ); + $stmt->execute([$wargaId]); + jsonOk($stmt->fetchAll()); +} + +// ── POST: Tambah bantuan baru ──────────────────────────────── +if ($method === 'POST') { + requireEdit(); + $d = body(); + + if (empty($d['warga_id'])) jsonError('ID warga wajib diisi.'); + if (empty($d['tanggal'])) jsonError('Tanggal wajib diisi.'); + if (empty($d['jenis'])) jsonError('Program bantuan wajib dipilih.'); + + $stmt = $db->prepare( + "INSERT INTO histori_bantuan (warga_id, tanggal, jenis, nominal, sumber_dana, keterangan, created_by) + VALUES (?, ?, ?, ?, ?, ?, ?)" + ); + $stmt->execute([ + $d['warga_id'], + $d['tanggal'], + $d['jenis'], + $d['nominal'] ?? 0, + $d['sumber_dana'] ?? '', + $d['keterangan'] ?? '', + $user['id'] + ]); + + jsonOk(['id' => $db->lastInsertId()], 'Data bantuan berhasil ditambahkan'); +} + +// ── DELETE: Hapus bantuan ──────────────────────────────────── +if ($method === 'DELETE') { + requireEdit(); + $id = getParam('id'); + if (!$id) jsonError('ID bantuan wajib disertakan.'); + + $db->prepare("DELETE FROM histori_bantuan WHERE id = ?")->execute([$id]); + jsonOk(null, 'Data bantuan berhasil dihapus'); +} + +jsonError('Method tidak valid', 405); diff --git a/api/config.php b/api/config.php new file mode 100644 index 0000000..abe53df --- /dev/null +++ b/api/config.php @@ -0,0 +1,86 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, + ]); + } catch (PDOException $e) { + jsonError('Koneksi database gagal: ' . $e->getMessage(), 500); + } + } + return $pdo; +} + +// Response helpers — bersihkan output buffer dulu agar tidak ada PHP warning yang merusak JSON +function jsonOk($data = null, string $msg = 'OK'): void { + if (ob_get_level()) ob_clean(); + echo json_encode(['success' => true, 'message' => $msg, 'data' => $data], JSON_UNESCAPED_UNICODE); + exit(); +} +function jsonError(string $msg, int $code = 400): void { + if (ob_get_level()) ob_clean(); + http_response_code($code); + echo json_encode(['success' => false, 'message' => $msg], JSON_UNESCAPED_UNICODE); + exit(); +} + +// Auth check +function requireAuth(): array { + if (empty($_SESSION['user'])) { + jsonError('Tidak terautentikasi. Silakan login.', 401); + } + return $_SESSION['user']; +} +function requireEdit(): array { + $user = requireAuth(); + if (!$user['can_edit']) jsonError('Anda tidak memiliki hak akses edit.', 403); + return $user; +} + +// Input helper +function body(): array { + $raw = file_get_contents('php://input'); + return json_decode($raw, true) ?? []; +} +function getParam(string $key, $default = null) { + return $_GET[$key] ?? $default; +} diff --git a/api/dashboard.php b/api/dashboard.php new file mode 100644 index 0000000..ef60e35 --- /dev/null +++ b/api/dashboard.php @@ -0,0 +1,152 @@ +query("SELECT COUNT(*) FROM warga WHERE status = 'aktif'")->fetchColumn(); + + // Keluarga Sangat Miskin (aktif) + $kpiSM = $db->query("SELECT COUNT(*) FROM warga WHERE status = 'aktif' AND kategori = 'sangat_miskin'")->fetchColumn(); + + // Total Penyaluran Bantuan & Nominal + $bantuanStats = $db->query( + "SELECT COUNT(*) AS total_penyaluran, COALESCE(SUM(nominal), 0) AS total_nominal + FROM histori_bantuan" + )->fetch(); + $kpiBn = $bantuanStats['total_penyaluran']; + $kpiBnNom = $bantuanStats['total_nominal']; + + // Penyakit Terbanyak + $kpiPyRow = $db->query( + "SELECT penyakit, COUNT(*) AS jumlah + FROM riwayat_penyakit + GROUP BY penyakit + ORDER BY jumlah DESC + LIMIT 1" + )->fetch(); + $kpiPy = $kpiPyRow ? $kpiPyRow['penyakit'] : 'Tidak Ada'; + + // ── 2. CHART 1: KK Miskin per Kelurahan (Top 10) ────────── + $chartKelRows = $db->query( + "SELECT k.nama AS kelurahan, COUNT(w.id) AS jumlah + FROM warga w + LEFT JOIN kelurahan k ON w.kelurahan_id = k.id + WHERE w.status = 'aktif' + GROUP BY w.kelurahan_id + ORDER BY jumlah DESC + LIMIT 10" + )->fetchAll(); + + $chartKel = [ + 'labels' => array_column($chartKelRows, 'kelurahan'), + 'data' => array_map('intval', array_column($chartKelRows, 'jumlah')) + ]; + + // ── 3. CHART 2: Distribusi Kategori Kemiskinan ──────────── + $chartKatRows = $db->query( + "SELECT kategori, COUNT(*) AS jumlah + FROM warga + WHERE status = 'aktif' + GROUP BY kategori" + )->fetchAll(); + + $katCounts = ['sangat_miskin' => 0, 'miskin' => 0, 'hampir_miskin' => 0]; + foreach ($chartKatRows as $row) { + $katCounts[$row['kategori']] = (int)$row['jumlah']; + } + $chartKat = [ + 'labels' => ['Sangat Miskin', 'Miskin', 'Hampir Miskin'], + 'data' => [$katCounts['sangat_miskin'], $katCounts['miskin'], $katCounts['hampir_miskin']] + ]; + + // ── 4. CHART 3: Penyaluran per Program Bantuan ──────────── + $chartBnRows = $db->query( + "SELECT jenis, COUNT(*) AS jumlah + FROM histori_bantuan + GROUP BY jenis + ORDER BY jumlah DESC" + )->fetchAll(); + + $chartBn = [ + 'labels' => array_column($chartBnRows, 'jenis'), + 'data' => array_map('intval', array_column($chartBnRows, 'jumlah')) + ]; + + // ── 5. CHART 4: Prevalensi Penyakit ─────────────────────── + $chartPyRows = $db->query( + "SELECT penyakit, COUNT(*) AS jumlah + FROM riwayat_penyakit + GROUP BY penyakit + ORDER BY jumlah DESC" + )->fetchAll(); + + $chartPy = [ + 'labels' => array_column($chartPyRows, 'penyakit'), + 'data' => array_map('intval', array_column($chartPyRows, 'jumlah')) + ]; + + // ── 6. TABEL RANKING KELURAHAN LENGKAP ──────────────────── + $rankingRows = $db->query( + "SELECT + k.nama AS kelurahan, + k.kecamatan, + COUNT(CASE WHEN w.kategori = 'sangat_miskin' AND w.status = 'aktif' THEN 1 END) AS sangat_miskin, + COUNT(CASE WHEN w.kategori = 'miskin' AND w.status = 'aktif' THEN 1 END) AS miskin, + COUNT(CASE WHEN w.kategori = 'hampir_miskin' AND w.status = 'aktif' THEN 1 END) AS hampir_miskin, + COUNT(CASE WHEN w.status = 'aktif' THEN 1 END) AS total, + COALESCE(SUM(hb.nominal), 0) AS total_bantuan, + AVG(CASE WHEN w.status = 'aktif' AND w.lat IS NOT NULL AND w.lat != 0 THEN w.lat END) AS avg_lat, + AVG(CASE WHEN w.status = 'aktif' AND w.lng IS NOT NULL AND w.lng != 0 THEN w.lng END) AS avg_lng + FROM kelurahan k + LEFT JOIN warga w ON w.kelurahan_id = k.id + LEFT JOIN histori_bantuan hb ON hb.warga_id = w.id + GROUP BY k.id + ORDER BY total DESC, kelurahan ASC" + )->fetchAll(); + + $ranking = []; + foreach ($rankingRows as $r) { + $ranking[] = [ + 'kelurahan' => $r['kelurahan'], + 'kecamatan' => $r['kecamatan'], + 'sangat_miskin' => (int)$r['sangat_miskin'], + 'miskin' => (int)$r['miskin'], + 'hampir_miskin' => (int)$r['hampir_miskin'], + 'total' => (int)$r['total'], + 'total_bantuan' => (float)$r['total_bantuan'], + 'lat' => $r['avg_lat'] ? (float)$r['avg_lat'] : null, + 'lng' => $r['avg_lng'] ? (float)$r['avg_lng'] : null, + ]; + } + + // Gabungkan hasil statistik + $dashboardData = [ + 'kpis' => [ + 'total_kk' => $kpiKK, + 'sangat_miskin' => $kpiSM, + 'total_penyaluran' => $kpiBn, + 'total_nominal' => $kpiBnNom, + 'penyakit_top' => $kpiPy + ], + 'charts' => [ + 'kelurahan' => $chartKel, + 'kategori' => $chartKat, + 'bantuan' => $chartBn, + 'penyakit' => $chartPy + ], + 'ranking' => $ranking + ]; + + jsonOk($dashboardData); +} + +jsonError('Method tidak valid', 405); diff --git a/api/fasilitas_umum.php b/api/fasilitas_umum.php new file mode 100644 index 0000000..6c50f86 --- /dev/null +++ b/api/fasilitas_umum.php @@ -0,0 +1,23 @@ +prepare("SELECT * FROM fasilitas_umum WHERE kategori = ? ORDER BY nama"); + $stmt->execute([$kategori]); + $rows = $stmt->fetchAll(); + } else { + $rows = $db->query("SELECT * FROM fasilitas_umum ORDER BY kategori, nama")->fetchAll(); + } + jsonOk($rows); +} + +jsonError('Method tidak valid', 405); diff --git a/api/kelurahan.php b/api/kelurahan.php new file mode 100644 index 0000000..afbf44e --- /dev/null +++ b/api/kelurahan.php @@ -0,0 +1,9 @@ +query("SELECT id, nama, kecamatan FROM kelurahan ORDER BY kecamatan, nama")->fetchAll(); +jsonOk($rows); diff --git a/api/laporan.php b/api/laporan.php new file mode 100644 index 0000000..9ec0df3 --- /dev/null +++ b/api/laporan.php @@ -0,0 +1,150 @@ +prepare("SELECT COUNT(*) FROM laporan WHERE YEAR(created_at) = ?"); + $stmtCount->execute([$year]); + $count = $stmtCount->fetchColumn(); + $nextNo = $count + 1; + $noLaporan = "RPT-" . $year . "-" . str_pad($nextNo, 3, '0', STR_PAD_LEFT); + + // Pastikan no_laporan benar-benar unik + $stmtCheck = $db->prepare("SELECT COUNT(*) FROM laporan WHERE no_laporan = ?"); + $stmtCheck->execute([$noLaporan]); + if ($stmtCheck->fetchColumn() > 0) { + $noLaporan = "RPT-" . $year . "-" . str_pad($nextNo + 1, 3, '0', STR_PAD_LEFT); + } + + $stmt = $db->prepare( + "INSERT INTO laporan (no_laporan, nama_pelapor, hp_pelapor, nama_dilaporkan, nik, + kelurahan_id, kategori, jumlah_anggota, penghasilan, keterangan, + lat, lng, status, tgl_laporan) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'menunggu', CURRENT_DATE())" + ); + $stmt->execute([ + $noLaporan, + $d['pelapor'] ?? null, + $d['hp'] ?? null, + $d['nama'], + $d['nik'] ?? null, + $d['kelurahan_id'], + $d['kategori'] ?? 'miskin', + $d['jumlah_anggota'] ?? 1, + $d['penghasilan'] ?? 0, + $d['keterangan'], + $d['lat'] ?? null, + $d['lng'] ?? null + ]); + + $lapId = $db->lastInsertId(); + jsonOk(['id' => $lapId, 'no' => $noLaporan], 'Laporan berhasil dikirim'); +} + +// Laporan read/update butuh autentikasi +$user = requireAuth(); + +// ── GET: List semua laporan ────────────────────────────────── +if ($method === 'GET') { + $rows = $db->query( + "SELECT l.id, l.no_laporan AS no, l.nama_pelapor AS pelapor, l.hp_pelapor AS hp, + l.nama_dilaporkan AS nama, l.nik, l.kategori, l.jumlah_anggota AS jml, + l.penghasilan AS ph, l.keterangan AS ket, l.lat, l.lng, l.status, + l.tgl_laporan AS tgl, l.created_at, k.id AS kelurahan_id, k.nama AS kel, k.kecamatan + FROM laporan l + LEFT JOIN kelurahan k ON l.kelurahan_id = k.id + ORDER BY l.created_at DESC" + )->fetchAll(); + + jsonOk($rows); +} + +// ── PUT: Approve atau Reject laporan ────────────────────────── +if ($method === 'PUT') { + requireEdit(); + $id = getParam('id'); + $action = getParam('action'); // 'approve' atau 'reject' + + if (!$id) jsonError('ID laporan wajib disertakan.'); + if (!in_array($action, ['approve', 'reject'])) jsonError('Aksi tidak valid.'); + + $db->beginTransaction(); + try { + // Ambil data laporan + $stmtLap = $db->prepare("SELECT * FROM laporan WHERE id = ?"); + $stmtLap->execute([$id]); + $lap = $stmtLap->fetch(); + + if (!$lap) jsonError('Laporan tidak ditemukan.'); + if ($lap['status'] !== 'menunggu') jsonError('Laporan sudah diproses sebelumnya.'); + + if ($action === 'reject') { + $stmtUp = $db->prepare("UPDATE laporan SET status = 'ditolak', tgl_proses = CURRENT_TIMESTAMP() WHERE id = ?"); + $stmtUp->execute([$id]); + $db->commit(); + jsonOk(null, 'Laporan ditolak'); + } + + if ($action === 'approve') { + // 1. Tambahkan ke tabel warga + $stmtWarga = $db->prepare( + "INSERT INTO warga (nama, nik, kelurahan_id, lat, lng, kategori, jumlah_anggota, + penghasilan, tgl_lahir, pendidikan, keterangan, status, pelapor, no_laporan) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, '', ?, 'aktif', ?, ?)" + ); + $stmtWarga->execute([ + $lap['nama_dilaporkan'], + $lap['nik'], + $lap['kelurahan_id'], + $lap['lat'] ?? -0.0230, + $lap['lng'] ?? 109.3420, + $lap['kategori'], + $lap['jumlah_anggota'] ?? 1, + $lap['penghasilan'] ?? 0, + $lap['keterangan'] . ' (Laporan: ' . $lap['no_laporan'] . ')', + $lap['nama_pelapor'], + $lap['no_laporan'] + ]); + $wargaId = $db->lastInsertId(); + + // 2. Tambahkan KK sebagai anggota keluarga pertama + $stmtAnggota = $db->prepare( + "INSERT INTO anggota_keluarga (warga_id, nama, usia, hubungan) + VALUES (?, ?, 0, 'Kepala Keluarga')" + ); + $stmtAnggota->execute([$wargaId, $lap['nama_dilaporkan']]); + + // 3. Update status laporan menjadi disetujui & tautkan warga_id + $stmtUp = $db->prepare( + "UPDATE laporan + SET status = 'disetujui', warga_id = ?, approved_by = ?, tgl_proses = CURRENT_TIMESTAMP() + WHERE id = ?" + ); + $stmtUp->execute([$wargaId, $user['id'], $id]); + + $db->commit(); + jsonOk(['warga_id' => $wargaId], 'Laporan disetujui dan warga berhasil ditambahkan'); + } + } catch (Exception $e) { + $db->rollBack(); + jsonError('Gagal memproses laporan: ' . $e->getMessage(), 500); + } +} + +jsonError('Method tidak valid', 405); diff --git a/api/rumah_ibadah.php b/api/rumah_ibadah.php new file mode 100644 index 0000000..62a1b3a --- /dev/null +++ b/api/rumah_ibadah.php @@ -0,0 +1,71 @@ +query( + "SELECT ri.*, k.nama AS kelurahan, k.kecamatan + FROM rumah_ibadah ri + LEFT JOIN kelurahan k ON ri.kelurahan_id = k.id + ORDER BY ri.tipe, ri.nama" + )->fetchAll(); + jsonOk($rows); +} + +if ($method === 'POST') { + requireEdit(); + $d = body(); + if (empty($d['nama'])) jsonError('Nama wajib diisi.'); + if (!isset($d['lat'], $d['lng'])) jsonError('Koordinat wajib diisi.'); + $ikonMap = ['masjid'=>'🕌','gereja'=>'⛪','pura'=>'🛕','vihara'=>'🏛️','klenteng'=>'🏯']; + $tipe = $d['tipe'] ?? 'masjid'; + $stmt = $db->prepare( + "INSERT INTO rumah_ibadah (nama, tipe, ikon, lat, lng, kapasitas, kelurahan_id, alamat) + VALUES (?,?,?,?,?,?,?,?)" + ); + $stmt->execute([ + $d['nama'], $tipe, $ikonMap[$tipe] ?? '🛕', + $d['lat'], $d['lng'], + $d['kapasitas'] ?? 0, + !empty($d['kelurahan_id']) ? $d['kelurahan_id'] : null, + $d['alamat'] ?? '' + ]); + jsonOk(['id' => $db->lastInsertId()], 'Rumah ibadah berhasil ditambahkan'); +} + +if ($method === 'PUT') { + requireEdit(); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + $d = body(); + if (empty($d['nama'])) jsonError('Nama wajib diisi.'); + if (!isset($d['lat'], $d['lng'])) jsonError('Koordinat wajib diisi.'); + $ikonMap = ['masjid'=>'🕌','gereja'=>'⛪','pura'=>'🛕','vihara'=>'🏛️','klenteng'=>'🏯']; + $tipe = $d['tipe'] ?? 'masjid'; + $stmt = $db->prepare( + "UPDATE rumah_ibadah + SET nama=?, tipe=?, ikon=?, lat=?, lng=?, kapasitas=?, kelurahan_id=?, alamat=? + WHERE id=?" + ); + $stmt->execute([ + $d['nama'], $tipe, $ikonMap[$tipe] ?? '🛕', + $d['lat'], $d['lng'], + $d['kapasitas'] ?? 0, + !empty($d['kelurahan_id']) ? $d['kelurahan_id'] : null, + $d['alamat'] ?? '', + $id + ]); + jsonOk(null, 'Rumah ibadah berhasil diperbarui'); +} + +if ($method === 'DELETE') { + requireEdit(); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + $db->prepare("DELETE FROM rumah_ibadah WHERE id=?")->execute([$id]); + jsonOk(null, 'Rumah ibadah berhasil dihapus'); +} + +jsonError('Method tidak valid', 405); diff --git a/api/users.php b/api/users.php new file mode 100644 index 0000000..4fe5e86 --- /dev/null +++ b/api/users.php @@ -0,0 +1,103 @@ +query( + "SELECT id, username, role, jabatan, can_edit, is_admin, is_wali, aktif, created_at + FROM users ORDER BY id" + )->fetchAll(); + // Hapus password dari hasil + jsonOk($rows); +} + +// ── PUT: update data pengguna ─────────────────────────────── +if ($method === 'PUT') { + $user = requireAuth(); + if (!$user['is_admin']) jsonError('Hanya admin yang dapat mengubah data pengguna.', 403); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + $d = body(); + + $fields = []; + $params = []; + + if (isset($d['jabatan'])) { $fields[] = 'jabatan=?'; $params[] = $d['jabatan']; } + if (isset($d['role'])) { $fields[] = 'role=?'; $params[] = $d['role']; + $params_extra = []; + $isAdmin = $d['role'] === 'admin' ? 1 : 0; + $isWali = $d['role'] === 'walikota' ? 1 : 0; + $fields[] = 'is_admin=?'; $params[] = $isAdmin; + $fields[] = 'is_wali=?'; $params[] = $isWali; } + if (isset($d['can_edit'])) { $fields[] = 'can_edit=?'; $params[] = $d['can_edit'] ? 1 : 0; } + if (isset($d['aktif'])) { $fields[] = 'aktif=?'; $params[] = $d['aktif'] ? 1 : 0; } + if (isset($d['password']) && strlen($d['password']) >= 6) { + $fields[] = 'password=?'; + $params[] = password_hash($d['password'], PASSWORD_BCRYPT); + } + + if (empty($fields)) jsonError('Tidak ada data yang diubah.'); + + $params[] = $id; + $db->prepare("UPDATE users SET " . implode(', ', $fields) . " WHERE id=?")->execute($params); + jsonOk(null, 'Data pengguna berhasil diperbarui'); +} + +// ── POST: tambah pengguna baru ────────────────────────────── +if ($method === 'POST') { + $user = requireAuth(); + if (!$user['is_admin']) jsonError('Hanya admin yang dapat menambah pengguna.', 403); + $d = body(); + if (empty($d['username'])) jsonError('Username wajib diisi.'); + if (empty($d['password']) || strlen($d['password']) < 6) jsonError('Password minimal 6 karakter.'); + + // Cek username sudah ada + $chk = $db->prepare("SELECT id FROM users WHERE username=?"); + $chk->execute([$d['username']]); + if ($chk->fetch()) jsonError('Username sudah digunakan.'); + + $role = $d['role'] ?? 'viewer'; + $isAdmin = $role === 'admin' ? 1 : 0; + $isWali = $role === 'walikota' ? 1 : 0; + $canEdit = in_array($role, ['admin', 'petugas']) ? 1 : 0; + if (isset($d['can_edit'])) $canEdit = $d['can_edit'] ? 1 : 0; + + $stmt = $db->prepare( + "INSERT INTO users (username, password, role, jabatan, can_edit, is_admin, is_wali, aktif) + VALUES (?,?,?,?,?,?,?,1)" + ); + $stmt->execute([ + $d['username'], + password_hash($d['password'], PASSWORD_BCRYPT), + $role, + $d['jabatan'] ?? '', + $canEdit, + $isAdmin, + $isWali, + ]); + jsonOk(['id' => $db->lastInsertId()], 'Pengguna berhasil ditambahkan'); +} + +// ── DELETE: nonaktifkan pengguna ──────────────────────────── +if ($method === 'DELETE') { + $user = requireAuth(); + if (!$user['is_admin']) jsonError('Hanya admin yang dapat menghapus pengguna.', 403); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + // Jangan hapus diri sendiri + if ($id == $user['id']) jsonError('Tidak dapat menghapus akun sendiri.'); + $db->prepare("DELETE FROM users WHERE id=?")->execute([$id]); + jsonOk(null, 'Pengguna berhasil dihapus'); +} + +jsonError('Method tidak valid', 405); diff --git a/api/warga.php b/api/warga.php new file mode 100644 index 0000000..728aeec --- /dev/null +++ b/api/warga.php @@ -0,0 +1,260 @@ +prepare($sql); + $stmt->execute($params); + $wargaList = $stmt->fetchAll(); + + // Ambil anggota & penyakit untuk setiap warga + foreach ($wargaList as &$w) { + $stmt2 = $db->prepare( + "SELECT a.id, a.nama, a.usia, a.hubungan, + GROUP_CONCAT(rp.penyakit SEPARATOR ',') AS penyakit_csv + FROM anggota_keluarga a + LEFT JOIN riwayat_penyakit rp ON rp.anggota_id = a.id + WHERE a.warga_id = ? + GROUP BY a.id" + ); + $stmt2->execute([$w['id']]); + $anggota = $stmt2->fetchAll(); + foreach ($anggota as &$a) { + $a['penyakit'] = $a['penyakit_csv'] ? explode(',', $a['penyakit_csv']) : []; + unset($a['penyakit_csv']); + } + $w['anggota'] = $anggota; + + // Riwayat penyakit KK (dari anggota dengan nama sama / index 0) + $rp_kk = []; + if (!empty($anggota)) { + $rp_kk = $anggota[0]['penyakit']; + } + $w['riwayat_penyakit'] = $rp_kk; + + // Histori bantuan + $stmt3 = $db->prepare( + "SELECT id, tanggal, jenis, nominal, sumber_dana AS sumber, keterangan + FROM histori_bantuan WHERE warga_id = ? ORDER BY tanggal DESC" + ); + $stmt3->execute([$w['id']]); + $w['histori_bantuan'] = $stmt3->fetchAll(); + } + + jsonOk($wargaList); +} + +// ── POST: tambah warga ────────────────────────────────────── +if ($method === 'POST') { + requireEdit(); + $d = body(); + + if (empty($d['nama'])) jsonError('Nama wajib diisi.'); + if (!isset($d['lat'], $d['lng'])) jsonError('Koordinat lokasi wajib diisi.'); + if (empty($d['kelurahan_id'])) jsonError('Kelurahan wajib dipilih.'); + + $db->beginTransaction(); + try { + $stmt = $db->prepare( + "INSERT INTO warga (nama, nik, kelurahan_id, lat, lng, kategori, jumlah_anggota, + penghasilan, tgl_lahir, pendidikan, keterangan, pelapor, no_laporan) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)" + ); + $stmt->execute([ + $d['nama'], + $d['nik'] ?? null, + $d['kelurahan_id'], + $d['lat'], + $d['lng'], + $d['kategori'] ?? 'miskin', + $d['jumlah_anggota'] ?? 1, + $d['penghasilan'] ?? 0, + !empty($d['tgl_lahir']) ? $d['tgl_lahir'] : null, + $d['pendidikan'] ?? '', + $d['keterangan'] ?? '', + $d['pelapor'] ?? null, + $d['no_laporan'] ?? null, + ]); + $wargaId = $db->lastInsertId(); + + // 1. Simpan Kepala Keluarga sebagai anggota keluarga pertama + $usiaKk = 0; + if (!empty($d['tgl_lahir'])) { + try { + $birthDate = new DateTime($d['tgl_lahir']); + $today = new DateTime(); + $usiaKk = $today->diff($birthDate)->y; + } catch (Exception $e) { + $usiaKk = 0; + } + } + $stmtKk = $db->prepare( + "INSERT INTO anggota_keluarga (warga_id, nama, usia, hubungan) VALUES (?, ?, ?, 'Kepala Keluarga')" + ); + $stmtKk->execute([$wargaId, $d['nama'], $usiaKk]); + $kkAnggotaId = $db->lastInsertId(); + + $kkPenyakit = $d['rp'] ?? $d['riwayat_penyakit'] ?? []; + foreach ($kkPenyakit as $py) { + if (!$py) continue; + $db->prepare("INSERT INTO riwayat_penyakit (anggota_id, penyakit) VALUES (?, ?)") + ->execute([$kkAnggotaId, $py]); + } + + // 2. Simpan anggota keluarga lainnya + penyakit mereka + foreach (($d['anggota'] ?? []) as $a) { + if (empty($a['nama'])) continue; + + // Hindari duplikasi jika KK dikirim lagi di list anggota + if (strtolower(trim($a['nama'])) === strtolower(trim($d['nama'])) && ($a['hub'] ?? $a['hubungan'] ?? '') === 'Kepala Keluarga') { + continue; + } + + $stmtA = $db->prepare( + "INSERT INTO anggota_keluarga (warga_id, nama, usia, hubungan) VALUES (?,?,?,?)" + ); + $stmtA->execute([$wargaId, $a['nama'], $a['usia'] ?? 0, $a['hub'] ?? $a['hubungan'] ?? 'Anak']); + $anggotaId = $db->lastInsertId(); + + foreach (($a['penyakit'] ?? $a['py'] ?? []) as $py) { + if (!$py) continue; + $db->prepare("INSERT INTO riwayat_penyakit (anggota_id, penyakit) VALUES (?,?)") + ->execute([$anggotaId, $py]); + } + } + + $db->commit(); + jsonOk(['id' => $wargaId], 'Data warga berhasil ditambahkan'); + } catch (Exception $e) { + $db->rollBack(); + jsonError('Gagal menyimpan: ' . $e->getMessage(), 500); + } +} + +// ── PUT: update data warga ───────────────────────────────── +if ($method === 'PUT') { + requireEdit(); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + $d = body(); + if (empty($d['nama'])) jsonError('Nama wajib diisi.'); + if (!isset($d['lat'], $d['lng'])) jsonError('Koordinat lokasi wajib diisi.'); + if (empty($d['kelurahan_id'])) jsonError('Kelurahan wajib dipilih.'); + + $db->beginTransaction(); + try { + // 1. Update data kepala keluarga + $stmt = $db->prepare( + "UPDATE warga SET nama=?, nik=?, kelurahan_id=?, lat=?, lng=?, + kategori=?, jumlah_anggota=?, penghasilan=?, + tgl_lahir=?, pendidikan=?, keterangan=? + WHERE id=?" + ); + $stmt->execute([ + $d['nama'], + $d['nik'] ?? null, + $d['kelurahan_id'], + $d['lat'], + $d['lng'], + $d['kategori'] ?? 'miskin', + $d['jumlah_anggota'] ?? 1, + $d['penghasilan'] ?? 0, + !empty($d['tgl_lahir']) ? $d['tgl_lahir'] : null, + $d['pendidikan'] ?? '', + $d['keterangan'] ?? '', + $id + ]); + + // 2. Hapus anggota lama beserta riwayat penyakitnya + $oldAnggota = $db->prepare("SELECT id FROM anggota_keluarga WHERE warga_id=?"); + $oldAnggota->execute([$id]); + foreach ($oldAnggota->fetchAll() as $oa) { + $db->prepare("DELETE FROM riwayat_penyakit WHERE anggota_id=?")->execute([$oa['id']]); + } + $db->prepare("DELETE FROM anggota_keluarga WHERE warga_id=?")->execute([$id]); + + // 3. Simpan KK sebagai anggota keluarga pertama + $usiaKk = 0; + if (!empty($d['tgl_lahir'])) { + try { + $birthDate = new DateTime($d['tgl_lahir']); + $today = new DateTime(); + $usiaKk = $today->diff($birthDate)->y; + } catch (Exception $e) { $usiaKk = 0; } + } + $stmtKk = $db->prepare( + "INSERT INTO anggota_keluarga (warga_id, nama, usia, hubungan) VALUES (?, ?, ?, 'Kepala Keluarga')" + ); + $stmtKk->execute([$id, $d['nama'], $usiaKk]); + $kkAnggotaId = $db->lastInsertId(); + + $kkPenyakit = $d['rp'] ?? $d['riwayat_penyakit'] ?? []; + foreach ($kkPenyakit as $py) { + if (!$py) continue; + $db->prepare("INSERT INTO riwayat_penyakit (anggota_id, penyakit) VALUES (?, ?)") + ->execute([$kkAnggotaId, $py]); + } + + // 4. Simpan anggota keluarga lainnya + foreach (($d['anggota'] ?? []) as $a) { + if (empty($a['nama'])) continue; + if (strtolower(trim($a['nama'])) === strtolower(trim($d['nama'])) && + ($a['hub'] ?? $a['hubungan'] ?? '') === 'Kepala Keluarga') { + continue; + } + $stmtA = $db->prepare( + "INSERT INTO anggota_keluarga (warga_id, nama, usia, hubungan) VALUES (?,?,?,?)" + ); + $stmtA->execute([$id, $a['nama'], $a['usia'] ?? 0, $a['hub'] ?? $a['hubungan'] ?? 'Anak']); + $anggotaId = $db->lastInsertId(); + foreach (($a['penyakit'] ?? $a['py'] ?? []) as $py) { + if (!$py) continue; + $db->prepare("INSERT INTO riwayat_penyakit (anggota_id, penyakit) VALUES (?,?)") + ->execute([$anggotaId, $py]); + } + } + + $db->commit(); + jsonOk(null, 'Data warga berhasil diperbarui'); + } catch (Exception $e) { + $db->rollBack(); + jsonError('Gagal memperbarui: ' . $e->getMessage(), 500); + } +} + +// ── DELETE: hapus warga ──────────────────────────────────── +if ($method === 'DELETE') { + requireEdit(); + $id = getParam('id'); + if (!$id) jsonError('ID wajib disertakan.'); + $db->prepare("UPDATE warga SET status='nonaktif' WHERE id=?")->execute([$id]); + jsonOk(null, 'Data warga berhasil dihapus'); +} + +jsonError('Method tidak valid', 405); diff --git a/database/geososial.sql b/database/geososial.sql new file mode 100644 index 0000000..2e993e9 --- /dev/null +++ b/database/geososial.sql @@ -0,0 +1,264 @@ +-- ============================================================ +-- GeoSosial Database Schema +-- Sistem Informasi Sosial Kota Pontianak +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS geososial CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE geososial; + +-- ============================================================ +-- TABEL: users (autentikasi) +-- ============================================================ +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + role ENUM('admin','petugas','walikota','viewer') NOT NULL DEFAULT 'viewer', + jabatan VARCHAR(100), + can_edit TINYINT(1) NOT NULL DEFAULT 0, + is_admin TINYINT(1) NOT NULL DEFAULT 0, + is_wali TINYINT(1) NOT NULL DEFAULT 0, + aktif TINYINT(1) NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: kelurahan (master data) +-- ============================================================ +CREATE TABLE IF NOT EXISTS kelurahan ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama VARCHAR(100) NOT NULL, + kecamatan VARCHAR(100) NOT NULL +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: rumah_ibadah +-- ============================================================ +CREATE TABLE IF NOT EXISTS rumah_ibadah ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama VARCHAR(150) NOT NULL, + tipe ENUM('masjid','gereja','pura','vihara','klenteng') NOT NULL, + ikon VARCHAR(10) DEFAULT '🕌', + lat DECIMAL(10,7) NOT NULL, + lng DECIMAL(10,7) NOT NULL, + kapasitas INT DEFAULT 0, + kelurahan_id INT, + alamat TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (kelurahan_id) REFERENCES kelurahan(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: warga (data kepala keluarga) +-- ============================================================ +CREATE TABLE IF NOT EXISTS warga ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama VARCHAR(150) NOT NULL, + nik VARCHAR(16), + kelurahan_id INT, + lat DECIMAL(10,7) NOT NULL, + lng DECIMAL(10,7) NOT NULL, + kategori ENUM('sangat_miskin','miskin','hampir_miskin') NOT NULL, + jumlah_anggota INT DEFAULT 1, + penghasilan BIGINT DEFAULT 0, + tgl_lahir DATE, + pendidikan ENUM('tidak_sekolah','sd','smp','sma','d3','s1','s2','') DEFAULT '', + keterangan TEXT, + status ENUM('aktif','nonaktif') DEFAULT 'aktif', + pelapor VARCHAR(150), + no_laporan VARCHAR(20), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (kelurahan_id) REFERENCES kelurahan(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: anggota_keluarga +-- ============================================================ +CREATE TABLE IF NOT EXISTS anggota_keluarga ( + id INT AUTO_INCREMENT PRIMARY KEY, + warga_id INT NOT NULL, + nama VARCHAR(150) NOT NULL, + usia INT DEFAULT 0, + hubungan VARCHAR(50) DEFAULT 'Anak', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (warga_id) REFERENCES warga(id) ON DELETE CASCADE +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: riwayat_penyakit (per anggota keluarga) +-- ============================================================ +CREATE TABLE IF NOT EXISTS riwayat_penyakit ( + id INT AUTO_INCREMENT PRIMARY KEY, + anggota_id INT NOT NULL, + penyakit ENUM('hipertensi','diabetes','tbc','asma','stroke','jantung','odgj','stunting','kanker') NOT NULL, + FOREIGN KEY (anggota_id) REFERENCES anggota_keluarga(id) ON DELETE CASCADE +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: histori_bantuan +-- ============================================================ +CREATE TABLE IF NOT EXISTS histori_bantuan ( + id INT AUTO_INCREMENT PRIMARY KEY, + warga_id INT NOT NULL, + tanggal DATE NOT NULL, + jenis VARCHAR(50) NOT NULL, + nominal BIGINT DEFAULT 0, + sumber_dana VARCHAR(100), + keterangan TEXT, + created_by INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (warga_id) REFERENCES warga(id) ON DELETE CASCADE, + FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ============================================================ +-- TABEL: laporan (dari masyarakat) +-- ============================================================ +CREATE TABLE IF NOT EXISTS laporan ( + id INT AUTO_INCREMENT PRIMARY KEY, + no_laporan VARCHAR(20) NOT NULL UNIQUE, + nama_pelapor VARCHAR(100), + hp_pelapor VARCHAR(20), + nama_dilaporkan VARCHAR(150) NOT NULL, + nik VARCHAR(16), + kelurahan_id INT, + kategori ENUM('sangat_miskin','miskin','hampir_miskin') NOT NULL, + jumlah_anggota INT DEFAULT 0, + penghasilan BIGINT DEFAULT 0, + keterangan TEXT NOT NULL, + lat DECIMAL(10,7), + lng DECIMAL(10,7), + status ENUM('menunggu','disetujui','ditolak') DEFAULT 'menunggu', + warga_id INT COMMENT 'warga_id jika laporan disetujui', + approved_by INT, + tgl_laporan DATE NOT NULL, + tgl_proses TIMESTAMP NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (kelurahan_id) REFERENCES kelurahan(id) ON DELETE SET NULL, + FOREIGN KEY (warga_id) REFERENCES warga(id) ON DELETE SET NULL, + FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ============================================================ +-- SEED DATA: users +-- ============================================================ +INSERT INTO users (username, password, role, jabatan, can_edit, is_admin, is_wali) VALUES +('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin', 'Kadis Sosial', 1, 1, 0), +('petugas', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'petugas', 'Staf Verifikasi', 1, 0, 0), +('walikota', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'walikota', 'Walikota Pontianak', 0, 0, 1), +('viewer', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'viewer', 'Pemangku Kepentingan', 0, 0, 0); + +-- NOTE: Password hash di atas adalah hash bcrypt dari kata "password" +-- Ubah password melalui phpMyAdmin atau script update_password.php +-- Password asli: admin=admin123, petugas=petugas123, walikota=wali2024, viewer=viewer123 +-- (Hash akan diupdate otomatis saat pertama login via generate_passwords.php) + +-- ============================================================ +-- SEED DATA: kelurahan (6 kecamatan, 24 kelurahan) +-- ============================================================ +INSERT INTO kelurahan (nama, kecamatan) VALUES +-- Pontianak Kota +('Kel. Tengah', 'Pontianak Kota'), +('Kel. Darat Sekip', 'Pontianak Kota'), +('Kel. Sungai Bangkong', 'Pontianak Kota'), +('Kel. Mariana', 'Pontianak Kota'), +('Kel. Sungai Jawi', 'Pontianak Kota'), +-- Pontianak Selatan +('Kel. Parit Tokaya', 'Pontianak Selatan'), +('Kel. Benua Melayu Darat', 'Pontianak Selatan'), +('Kel. Benua Melayu Laut', 'Pontianak Selatan'), +('Kel. Kota Baru', 'Pontianak Selatan'), +-- Pontianak Barat +('Kel. Sungai Beliung', 'Pontianak Barat'), +('Kel. Pal Lima', 'Pontianak Barat'), +('Kel. Akcaya', 'Pontianak Barat'), +('Kel. Sungai Jawi Luar', 'Pontianak Barat'), +-- Pontianak Utara +('Kel. Siantan Hulu', 'Pontianak Utara'), +('Kel. Batu Layang', 'Pontianak Utara'), +('Kel. Saigon', 'Pontianak Utara'), +('Kel. Siantan Hilir', 'Pontianak Utara'), +-- Pontianak Timur +('Kel. Tanjung Hulu', 'Pontianak Timur'), +('Kel. Banjar Serasan', 'Pontianak Timur'), +('Kel. Dalam Bugis', 'Pontianak Timur'), +-- Pontianak Tenggara +('Kel. Bangka Belitung Darat', 'Pontianak Tenggara'), +('Kel. Bangka Belitung Laut', 'Pontianak Tenggara'), +('Kel. Bansir Laut', 'Pontianak Tenggara'), +('Kel. Bansir Darat', 'Pontianak Tenggara'); + +-- ============================================================ +-- SEED DATA: rumah_ibadah +-- ============================================================ +INSERT INTO rumah_ibadah (nama, tipe, ikon, lat, lng, kapasitas, kelurahan_id, alamat) VALUES +('Masjid Al-Ikhlas', 'masjid', '🕌', -0.0180000, 109.3320000, 150, 4, 'Jl. Mariana, Pontianak Kota'), +('Masjid Raya Mujahidin', 'masjid', '🕌', -0.0428000, 109.3373000, 500, 12, 'Jl. Jend. Ahmad Yani, Pontianak Selatan'), +('Masjid Jami Sultan', 'masjid', '🕌', -0.0400000, 109.3440000, 300, 20, 'Jl. Tanjungpura, Pontianak Timur'), +('Masjid Nurul Hidayah', 'masjid', '🕌', -0.0100000, 109.3360000, 100, 14, 'Jl. Siantan Hulu, Pontianak Utara'), +('Gereja GPIB Immanuel', 'gereja', '⛪', -0.0130000, 109.3480000, 200, 14, 'Jl. Sisingamangaraja, Pontianak Utara'), +('Gereja Bethel Indonesia','gereja', '⛪', -0.0300000, 109.3370000, 180, 6, 'Jl. Parit Tokaya, Pontianak Selatan'), +('Gereja Katedral', 'gereja', '⛪', -0.0350000, 109.3620000, 350, 15, 'Jl. Diponegoro, Pontianak Utara'), +('Pura Girinatha', 'pura', '🛕', -0.0220000, 109.3550000, 80, 3, 'Jl. Sungai Bangkong, Pontianak Kota'), +('Vihara Bodhisatva', 'vihara', '🏛️', -0.0080000, 109.3400000, 60, 14, 'Jl. Siantan, Pontianak Utara'), +('Klenteng Tua Pek Kong', 'klenteng', '🏯', -0.0260000, 109.3420000, 70, 1, 'Jl. Diponegoro, Pontianak Kota'); + +-- ============================================================ +-- VIEWS berguna +-- ============================================================ +CREATE OR REPLACE VIEW v_warga_lengkap AS +SELECT + w.id, w.nama, w.nik, w.lat, w.lng, w.kategori, + w.jumlah_anggota, w.penghasilan, w.tgl_lahir, w.pendidikan, + w.keterangan, w.status, w.pelapor, w.no_laporan, w.created_at, + k.nama AS kelurahan, k.kecamatan, + (SELECT COUNT(*) FROM histori_bantuan hb WHERE hb.warga_id = w.id) AS total_bantuan, + (SELECT COALESCE(SUM(hb2.nominal),0) FROM histori_bantuan hb2 WHERE hb2.warga_id = w.id) AS total_nominal_bantuan +FROM warga w +LEFT JOIN kelurahan k ON w.kelurahan_id = k.id; + +CREATE OR REPLACE VIEW v_laporan_lengkap AS +SELECT + l.id, l.no_laporan, l.nama_pelapor, l.hp_pelapor, + l.nama_dilaporkan, l.nik, l.kategori, l.jumlah_anggota, + l.penghasilan, l.keterangan, l.lat, l.lng, l.status, + l.tgl_laporan, l.created_at, + k.nama AS kelurahan, k.kecamatan +FROM laporan l +LEFT JOIN kelurahan k ON l.kelurahan_id = k.id; + +-- ============================================================ +-- TABEL: fasilitas_umum (opsional) +-- ============================================================ +CREATE TABLE IF NOT EXISTS fasilitas_umum ( + id INT AUTO_INCREMENT PRIMARY KEY, + nama VARCHAR(150) NOT NULL, + kategori VARCHAR(50) NOT NULL, + lat DECIMAL(10,7) NOT NULL, + lng DECIMAL(10,7) NOT NULL, + alamat TEXT, + keterangan TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB; + +-- ============================================================ +-- SEED DATA: fasilitas_umum +-- ============================================================ +INSERT INTO fasilitas_umum (nama, kategori, lat, lng, alamat, keterangan) VALUES +-- Kesehatan +('RSUD Dr. Soedarso', 'kesehatan', -0.0461000, 109.3458000, 'Jl. Dr. Soedarso, Pontianak Tenggara', 'Rumah Sakit Umum Daerah Provinsi Kalbar'), +('Puskesmas Parit Tokaya', 'kesehatan', -0.0396000, 109.3364000, 'Jl. Paris II, Pontianak Selatan', 'Pelayanan Kesehatan Tingkat Pertama'), +('RS Katolik Antonius', 'kesehatan', -0.0336000, 109.3242000, 'Jl. K.H. Wahid Hasyim, Pontianak Kota', 'Rumah Sakit Swasta Tipe B'), +-- Pendidikan +('Universitas Tanjungpura', 'pendidikan', -0.0622000, 109.3468000, 'Jl. Prof. Dr. H. Hadari Nawawi, Pontianak Tenggara', 'Perguruan Tinggi Negeri Utama Kalbar'), +('SMA Negeri 1 Pontianak', 'pendidikan', -0.0354000, 109.3328000, 'Jl. Anjungan, Pontianak Kota', 'Sekolah Menengah Atas Negeri'), +('SMP Negeri 1 Pontianak', 'pendidikan', -0.0248000, 109.3344000, 'Jl. Jend. Urip Sumoharjo, Pontianak Kota', 'Sekolah Menengah Pertama Negeri'), +-- Pasar Tradisional +('Pasar Flamboyan', 'pasar', -0.0381000, 109.3394000, 'Jl. Pahlawan, Pontianak Selatan', 'Pasar Tradisional Terbesar di Kota Pontianak'), +('Pasar Mawar', 'pasar', -0.0279000, 109.3387000, 'Jl. Cokroaminoto, Pontianak Kota', 'Pasar Tradisional Sayur & Kebutuhan Pokok'), +('Pasar Kemuning', 'pasar', -0.0456000, 109.3218000, 'Jl. Prof. M. Yamin, Pontianak Kota', 'Pasar Rakyat Modern Kelurahan Kota Baru'), +-- Kantor Kelurahan +('Kantor Lurah Akcaya', 'kelurahan', -0.0352000, 109.3236000, 'Jl. Sutan Syahrir, Pontianak Selatan', 'Pelayanan Publik Kelurahan Akcaya'), +('Kantor Lurah Parit Tokaya', 'kelurahan', -0.0478000, 109.3415000, 'Jl. Paris I, Pontianak Selatan', 'Pelayanan Publik Kelurahan Parit Tokaya'), +('Kantor Lurah Benua Melayu Darat', 'kelurahan', -0.0345000, 109.3444000, 'Jl. Gajah Mada, Pontianak Selatan', 'Pelayanan Publik Kelurahan BMD'); diff --git a/index.php b/index.php new file mode 100644 index 0000000..e545957 --- /dev/null +++ b/index.php @@ -0,0 +1,876 @@ + + + + + + + GeoSosial — Sistem Informasi Kemiskinan Kota Pontianak + + + + + + + + + + +
+ +
+
📍
+
+
GeoSosial
+
Kota Pontianak
+
+
+ + +
+ + +
+
+
Kelurahan
+
24
+
+
+
Total Warga
+
+
+
+
Sangat Miskin
+
+
+
+
Total Bantuan
+
+
+
+
+ + + + + + + + + + + diff --git a/profile.html b/profile.html new file mode 100644 index 0000000..1bf453e --- /dev/null +++ b/profile.html @@ -0,0 +1,687 @@ + + + + + + Profil — GeoSosial Pontianak + + + + + + + + + + + + +
+ + +
+
+
+ AD +
+
+
+
+
+
+ + ● Online +
+
+
+
+ + +
+
+
🔐
+
Hak Akses
+
+
+
+
🗓
+
Bergabung
+
+
+
+
+
Login Terakhir
+
+
+
+ + +
+ + +
+
+
+
🪪
+
+
Informasi Akun
+
Data profil dan pengaturan akun
+
+
+ +
+
+
+
+
Username
+
@
+
+
+
Nama Lengkap / Jabatan
+
+ +
+
+
+
+
Role
+
+ +
+
+
Status Akun
+
● Aktif
+
+
+
+
+
Sesi Aktif
+
● Online
+
+
+
Login Terakhir
+
+
+
+
+
+ + +
+
+ + +
+
+
+
🔑
+
+
Ganti Password
+
Perbarui kata sandi akun Anda
+
+
+
+
+
+ +
+ 🔒 + + +
+
+
+ +
+ 🔑 + + +
+
+
+
+
+ +
+ + + +
+
+
+ +
+
+
+ +
+
+ +
+ + + + diff --git a/sig1.html b/sig1.html new file mode 100644 index 0000000..a30e8da --- /dev/null +++ b/sig1.html @@ -0,0 +1,5261 @@ + + + + + + + GeoSosial — Sistem Informasi Sosial Kota Pontianak + + + + + + + + + + + +
+
+
📍
+
+
GeoSosial
+
Kota Pontianak
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+ +
Selamat Datang
+
Masuk untuk mengakses sistem informasi geografis kemiskinan dan bantuan sosial Kota Pontianak. +
+
Username
+ +
Password
+ + +
⚠ Username atau password salah.
+ +
Demo Akun:
Admin: admin/admin123  |  Petugas: + petugas/petugas123
Walikota: walikota/wali2024  |  Viewer: + viewer/viewer123
+
+
+ + +
+
+ + +
LAPORAN PUBLIK
+
+
+
+
+
📢 Laporkan Kondisi Kemiskinan
+
Bantu kami mengidentifikasi warga yang membutuhkan bantuan. Data akan diverifikasi + petugas Dinas Sosial. Laporan boleh anonim.
+
Data Pelapor (Opsional)
+
+
+
Nama Pelapor
+
+
+
No. HP / WhatsApp
+
+
+
Data Warga yang Dilaporkan
+
+
+
Nama Kepala Keluarga *
+
+
+
NIK (jika diketahui)
+
+
+
+
+
Kelurahan *
+
+
+
Kategori Kondisi *
+ +
+
+
+
+
Jumlah Anggota KK
+
+
+
Perkiraan Penghasilan/Bln
+
+
+
Keterangan Kondisi *
+ +
Lokasi Rumah (Klik peta)
+
+
🖱 Klik untuk memilih lokasi
+
+
+ +
+
+
+
Laporan Berhasil Dikirim!
+
Laporan Anda diterima dan akan diverifikasi petugas Dinas Sosial Kota + Pontianak.
Simpan nomor laporan berikut:
+
RPT-2026-XXX
+ + +
+
+
+
+ + +
+ + + +
+
+
+
+
Filter Peta
+
Pemetaan spasial kemiskinan Pontianak
+
+
+
Kategori
+
+
+
Kecamatan
+
+
+
Kelurahan
+
+
+
+ + +
+
+
+ + +
+
500m
+
+
+
+
+
+
👥
+
+
0
+
Total KK Miskin
+
+
+
+
🕌
+
+
0
+
Rumah Ibadah
+
+
+
+
+
+
+
Blank Spot
+
+
+
+
📡
+
+
+
Cakupan Radar
+
+
+
+ +
+
+
+ Tematik Utama + +
+
+ + +
+
+
+
+ Opsional + +
+ +
+
+
+
Legenda
+
+
Sangat Miskin +
+
+
Miskin +
+
+
Hampir Miskin +
+
+
Rumah Ibadah +
+
+
Blank Spot +
+
+
+
Rumah Ibadah — Kapasitas Terpakai
+
+
+
+ + +
+
+
📊 Dashboard Eksekutif
+
Ringkasan Data Kemiskinan & Bantuan Sosial — Kota Pontianak
+
+
+
+
+
👥
+
0
+
Total KK Terdaftar
+
+
+
+
🏠
+
0
+
Keluarga Sangat Miskin
+
+
+
💰
+
0
+
Total Bantuan Disalurkan
+
+
+
+
🏥
+
+
Penyakit Terbanyak
+
+
+
+
+
KK Miskin per Kelurahan
+
10 kelurahan tertinggi
+
+
+
+
Distribusi Kategori
+
Proporsi tingkat kemiskinan
+
+
+
+
+
+
Program Bantuan Disalurkan
+
Jumlah penyaluran per program
+
+
+
+
Prevalensi Penyakit
+
Distribusi riwayat penyakit seluruh anggota
+
+
+
+
+
+
+
🏆 Ranking Kelurahan — Kemiskinan Tertinggi
+
+
+
Data terkini seluruh kelurahan di Kota Pontianak
+ + + + + + + + + + + + + + +
#KelurahanKecamatanSangat MiskinMiskinHampir MiskinTotalTotal Bantuan
+
+
+
+
+ + +
+
+
+
📋 Manajemen Data Warga
+
+
+
+ + + + +
0 data
+ +
+
+ + + + + + + + + + + + + + + + +
#Nama KKKelurahanKategoriTgl LahirPendidikanPenghasilanPenyakitBantuanAksi
+
+
+
+ + +
+
+
+
📩 Laporan Masuk dari Masyarakat
+
+ +
+
+
+
+
+
0
+
Menunggu Verifikasi
+
+
+
+
+
+
0
+
Disetujui
+
+
+
+
+
+
0
+
Ditolak
+
+
+
+
+ + + + + + + + + + + + + + +
No. LaporanTanggalPelaporNama DilaporkanKelurahanKategoriStatusAksi
+
+
+ + +
+
+
👤 Manajemen Pengguna
+ +
+
+ + + + + + + + + + + + + +
#UsernameJabatanRoleHak AksesStatusAksi
+
+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + + + \ No newline at end of file diff --git a/test.html b/test.html new file mode 100644 index 0000000..784b92c --- /dev/null +++ b/test.html @@ -0,0 +1,48 @@ + + +Test Login + +

🔧 Test Login GeoSosial

+

Username:

+

Password:

+ +

+
+
+