initiall commit

This commit is contained in:
unknown
2026-06-10 21:59:59 +07:00
commit cfd55d0dd0
28 changed files with 3442 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# Development files
.vscode/
.idea/
# Local XAMPP/PHP Server files
error_log
access_log
*.log
# Environment specific configuration
# (Note: we leave koneksi.php tracked for this template,
# but usually it should be ignored in a real production repo)
# php/koneksi.php
# OS generated files
.DS_Store
Thumbs.db
+193
View File
@@ -0,0 +1,193 @@
# ⚡ QUICK START - Mulai Cepat dalam 5 Menit
## 🎯 Langkah 1: Ekstrak & Tempatkan File
1. **Extract ZIP** ke folder web Anda:
- Windows: `C:\xampp\htdocs\pemetaan-kemiskinan`
- Linux: `/var/www/html/pemetaan-kemiskinan`
2. **Pastikan XAMPP/Web Server JALAN**
- Buka XAMPP Control Panel
- Klik **"Start"** untuk Apache
- Klik **"Start"** untuk MySQL
---
## 🎯 Langkah 2: Setup Database (3 Langkah Mudah)
### Cara A: Menggunakan phpMyAdmin (PALING MUDAH)
1. **Buka browser** → ketik: `http://localhost/phpmyadmin`
2. **Klik menu "Import"** di bagian atas
3. **Klik "Choose File"** → pilih file `database.sql` dari folder pemetaan-kemiskinan
4. **Klik tombol "Import"** → SELESAI ✅
Tunggu 5-10 detik, database akan terbuat otomatis dengan semua tabel yang diperlukan.
### Cara B: Manual (Jika Cara A tidak berhasil)
1. Buka phpMyAdmin → `http://localhost/phpmyadmin`
2. Di sebelah kiri, cari **"Create new database"**
3. Ketik nama: `pemetaan_kemiskinan`
4. Klik **"Create"**
5. Pilih database yang baru dibuat
6. Klik tab **"SQL"**
7. Copy-paste semua kode dari file `database.sql`
8. Klik **"Go"**
---
## 🎯 Langkah 3: Verifikasi Koneksi
1. **Buka file**: `php/koneksi.php`
2. **Periksa konfigurasi** (biasanya tidak perlu diubah):
```php
$host = "localhost";
$username = "root";
$password = ""; // Kosongkan jika XAMPP default
$database = "pemetaan_kemiskinan";
```
3. **Jika ada password MySQL**, ubah baris:
```php
$password = "password_anda_di_sini";
```
---
## 🎯 Langkah 4: Buka Aplikasi
**Buka browser dan ketik:**
```
http://localhost/pemetaan-kemiskinan/
```
✅ **SELESAI! Aplikasi siap digunakan!**
---
## 🎮 Test Aplikasi
### Test Input Masyarakat Miskin:
1. Map akan muncul dengan area Pontianak
2. Klik tombol di sidebar → Tab "Masyarakat"
3. **Klik di peta** untuk set koordinat lokasi
4. Isi form dengan data:
```
Nama: Siti Nur Azizah
Tanggal Lahir: 1985-03-15
Jenis Kelamin: Perempuan
Alamat: Jl. Mahakam No. 25
Kelurahan: Sungai Jawi
Kecamatan: Pontianak Kota
No HP: 081234567890
Status BPJS: Ya
(Koordinat sudah otomatis dari klik peta)
```
5. Klik **"Simpan Data"** → Data berhasil disimpan ✅
6. Lihat marker merah di peta, dan nama muncul di sidebar list
### Test Input Rumah Ibadah:
1. Klik tab **"Rumah Ibadah"** di sidebar
2. **Klik di peta** untuk koordinat
3. Isi form:
```
Nama: Masjid Al-Hidayah
Jenis: Masjid
Alamat: Jl. Diponegoro No. 45
Nama Pemimpin: Ustaz Ahmad
```
4. Klik **"Simpan Data"** → Marker hijau muncul ✅
---
## 📱 Interface Penjelasan
```
┌─────────────────────────────────────────┐
│ SIDEBAR (300px) │ MAP (Sisa) │
│ │ │
│ 📍 Pemetaan Kemiskinan │ Marker Merah │
│ [Masyarakat] [Ibadah] │ Masyarakat │
│ │ │
│ [Search Box] │ Marker Hijau │
│ [Filter Dropdown] │ Rumah Ibadah │
│ │ │
│ - Nama 1 │ │
│ - Nama 2 │ Klik di peta │
│ - Nama 3 │ = Set Koordinat
│ │ │
│ [Tombol Hapus] │ Klik Marker │
│ │ = Lihat Detail
└─────────────────────────────────────────┘
```
---
## ❓ FAQ
**Q: Koordinat tidak ketangkap saat klik peta?**
A: Pastikan modal form sudah terbuka (berwarna putih). Klik dulu field nama atau di peta, baru akan bisa set koordinat otomatis.
**Q: Data tidak tampil di peta?**
A: Cek browser console (F12), atau pastikan latitude longitude valid (antara -90 hingga 90 untuk lat, -180 hingga 180 untuk lng).
**Q: Database tidak terbuat?**
A: Cek di phpMyAdmin apakah database `pemetaan_kemiskinan` sudah ada. Jika belum, buat manual atau coba import ulang file `database.sql`.
**Q: Error "Access denied" saat jalankan?**
A: Password MySQL tidak cocok. Edit file `php/koneksi.php` dan ubah password sesuai konfigurasi MySQL Anda.
**Q: Peta tidak muncul?**
A: Periksa koneksi internet (butuh untuk CDN Leaflet & OpenStreetMap). Atau cek browser console untuk error.
---
## 🔧 Struktur File Penting
```
pemetaan-kemiskinan/
├── index.html ← Buka file ini di browser
├── database.sql ← File untuk import database
├── config.php ← Konfigurasi umum
├── php/
│ ├── koneksi.php ← EDIT INI jika ada password MySQL
│ ├── simpan_masyarakat.php
│ ├── simpan_rumah_ibadah.php
│ ├── ambil_data_masyarakat.php
│ └── ...
├── js/
│ └── main.js ← Logic aplikasi
└── docs/
└── PANDUAN_PHPMY_ADMIN.md ← Dokumentasi detail
```
---
## 📞 Jika Ada Masalah
1. **Cek Browser Console** (Tekan F12):
- Lihat tab "Console"
- Ada error merah? Catat error message-nya
- Refresh halaman (Ctrl+F5)
2. **Cek phpMyAdmin**:
- Database ada?
- Tabel ada? (masyarakat_miskin, rumah_ibadah, dll)
- Ada data sample?
3. **Cek File koneksi.php**:
- Username & password MySQL cocok?
- Nama database benar? (`pemetaan_kemiskinan`)
4. **Restart Web Server**:
- Tutup XAMPP
- Buka kembali XAMPP
- Start Apache & MySQL
---
**✅ Selesai! Nikmati aplikasi pemetaan kemiskinan Anda!**
*Untuk dokumentasi lengkap, baca file `README.md`*
+64
View File
@@ -0,0 +1,64 @@
# WebGIS Poverty Mapping - Pontianak
Sistem Informasi Geografis (GIS) Terpadu untuk pemetaan kemiskinan, fasilitas tata kota, dan pelaporan masyarakat di Kota Pontianak. Dibangun dengan HTML, CSS, JavaScript (Leaflet.js), dan PHP (MySQL).
## 🚀 Fitur Utama
1. **Fasilitas & Tata Kota**: Pemetaan jalan raya (Polyline), batas area taman/perumahan (Polygon), dan titik lokasi SPBU (Marker).
2. **Sosial & Kemasyarakatan**: Pemetaan data warga miskin dengan detail bantuan, status BPJS, serta pemetaan lokasi rumah ibadah.
3. **Portal Publik (Read-Only Mode)**: Akses bagi warga untuk melihat data fasilitas terdekat tanpa kemampuan merubah atau menghapus data.
## 🛠️ Persiapan Lingkungan Lokal (Local Development)
### Syarat Sistem
- XAMPP (Apache & MySQL)
- PHP 7.4 atau lebih baru (Rekomendasi PHP 8+)
### Langkah Instalasi
1. Nyalakan modul **Apache** dan **MySQL** di XAMPP Control Panel Anda.
2. Buka `phpMyAdmin` (`http://localhost/phpmyadmin`).
3. Buat database baru bernama `pemetaan_kemiskinan`.
4. Import file `database.sql` yang ada di direktori utama ke dalam database tersebut. File ini sudah berisi skema tabel dan beberapa *dummy data* (titik awal).
5. Akses portal di `http://localhost/web_miskin/01` (sesuaikan port jika diperlukan).
## 📤 Git Push & Kolaborasi
Proyek ini telah dilengkapi dengan `.gitignore`.
Untuk mengunggah kode ke GitHub/GitLab:
```bash
git init
git add .
git commit -m "Initial commit WebGIS"
git branch -M main
git remote add origin https://github.com/username-anda/repo-anda.git
git push -u origin main
```
## 🌐 Panduan Deployment (cPanel Hosting)
Untuk mendeploy proyek ini ke internet (contoh menggunakan Niagahoster, Hostinger, atau cPanel standar):
1. **Siapkan Database di Server**
- Masuk ke cPanel hosting Anda, buka menu **MySQL Databases**.
- Buat database baru (contoh: `u12345_pemetaan`).
- Buat user database baru dan hubungkan user tersebut ke database dengan semua hak akses (*All Privileges*).
- Buka **phpMyAdmin** dari cPanel, pilih database baru Anda, lalu menu **Import**.
- Unggah file `database.sql` dari komputer Anda.
2. **Upload File Web**
- Di cPanel, buka **File Manager** -> masuk ke direktori `public_html`.
- Jika Anda memiliki banyak project, buat folder baru (misal `webgis`). Jika ini aplikasi utama, biarkan di root `public_html`.
- *Zip* seluruh isi folder `01` dari PC Anda (jangan Zip folder `01`-nya, tapi isi di dalamnya: `index.html`, `dashboard.html`, folder `css`, `js`, `php`).
- Upload file *Zip* tersebut ke File Manager dan klik *Extract*.
3. **Konfigurasi Koneksi PHP**
- Cari dan edit file `php/koneksi.php` yang sudah di-*upload*.
- Sesuaikan bagian ini dengan kredensial dari cPanel Anda:
```php
$host = "localhost";
$username = "u12345_userdb"; // User dari cPanel
$password = "PasswordKuat123!"; // Password user DB
$database = "u12345_pemetaan"; // Nama DB dari cPanel
```
Selesai! Aplikasi WebGIS Anda sekarang sudah online.
+47
View File
@@ -0,0 +1,47 @@
# Analisis Kode: Sistem Pemetaan Kemiskinan
Proyek ini adalah aplikasi web bernama **Sistem Pemetaan Kemiskinan** yang digunakan untuk memetakan lokasi masyarakat miskin dan rumah ibadah di suatu wilayah (secara bawaan diset ke daerah Pontianak). Aplikasi ini dibangun dengan menggunakan arsitektur *client-server* sederhana yang memisahkan sisi tampilan (frontend) dan sisi server (backend).
## Struktur Direktori dan File
Berikut adalah hasil analisis berdasarkan penataan folder dan file yang ada di dalam proyek ini:
### 1. Root Direktori (`/01/`)
Direktori utama ini menyimpan file-file inti, konfigurasi, dokumentasi, dan titik masuk aplikasi (*entry point*).
- **`index.html`**: File utama antarmuka pengguna. Memuat kerangka HTML, tata letak sidebar, kontainer peta, gaya desain CSS internal, dan kerangka *form modal* untuk menambahkan data. File ini juga mengintegrasikan pustaka eksternal **Leaflet.js** untuk merender peta interaktif.
- **`config.php`**: File konfigurasi utama di sisi server. Berisi definisi konstanta penting seperti konfigurasi database MySQL, pengaturan awal tampilan peta (*latitude*, *longitude*, *zoom level*), serta lingkungan aplikasi (*development*).
- **`database.sql`**: File skrip SQL yang memuat skema struktur database, berisi tabel `masyarakat_miskin`, `rumah_ibadah`, `bantuan_riwayat`, `bpjs_record`, dan tabel `users` untuk sistem login. Terdapat juga beberapa data sampel (*dummy*) untuk keperluan testing.
- **`README.md` & `QUICK_START.md`**: Dokumentasi proyek yang menjelaskan instruksi instalasi, penggunaan aplikasi, serta fitur-fiturnya.
- **`{php,js,css,icons,docs}`**: Folder kosong yang sepertinya terbuat karena ketidaksengajaan (*typo* pada *command-line* saat membuat *multiple directories* sekaligus).
### 2. Folder `js/` (Logika Frontend / Interaksi)
Folder ini menangani segala logika yang berjalan di sisi *browser* (klien).
- **`main.js`**: Skrip JavaScript tunggal yang mengontrol aplikasi. Bertanggung jawab atas:
- Menginisialisasi peta dari `index.html`.
- Mengambil data spasial dari backend menggunakan *Fetch API* secara asinkron (*AJAX*).
- Meletakkan *marker* (titik lokasi) untuk masyarakat miskin dan rumah ibadah di peta.
- Memproses pencarian (*search*) dan filter di bagian sidebar.
- Menangani aksi pengiriman form, tombol klik, serta memberikan *feedback* sukses/error kepada pengguna (*alert*).
### 3. Folder `php/` (Logika Backend / API)
Berisi sekumpulan skrip PHP yang berfungsi sebagai perantara antara antarmuka pengguna dan *database*. Endpoint ini menerima input dan mengembalikan format **JSON**.
- **`koneksi.php`**: Pusat koneksi database menggunakan `mysqli`. Menangani perizinan *CORS Headers* agar API bisa diakses dari frontend dan mengatur proteksi anti-XSS melalui fungsi *sanitasi input*.
- **Endpoint Pengambilan Data (`ambil_*.php`)**:
- `ambil_data_masyarakat.php` & `ambil_rumah_ibadah.php`: Merespons permintaan dengan daftar titik-titik lokasi dari tabel terkait di database.
- `ambil_detail.php`: Menampilkan data terperinci masyarakat miskin, mencakup data BPJS dan riwayat bantuan.
- **Endpoint Penyimpanan Data (`simpan_*.php`)**:
- `simpan_masyarakat.php`, `simpan_rumah_ibadah.php`: Mengamankan data yang masuk dari POST *Request* form, memvalidasi dan memasukkannya ke database. Terdapat file lain seperti `simpan_bantuan.php` dan `simpan_bpjs.php` untuk relasi tabel sekunder.
- **`hapus_data.php`**: Endpoint untuk menghapus data berdasarkan ID.
### 4. Folder `codeanalysis/`
- **`codeanalyisis.md`**: File dokumen *Markdown* tempat tersimpannya hasil analisis kode yang menguraikan arsitektur dan struktur proyek ini.
### 5. Folder `docs/`
- **`PANDUAN_PHPMY_ADMIN.md`**: Dokumen petunjuk teknis sekunder terkait pengelolaan basis data menggunakan *phpMyAdmin*.
---
## Kesimpulan Arsitektur Sistem
Proyek ini mengadopsi pola perancangan **API-driven (Vanilla)**.
1. **Frontend** dibuat reaktif tanpa membebani server, di mana `index.html` dan `main.js` merender visual serta mengirimkan permintaan *asinkron*.
2. **Backend (PHP)** bertindak hanya sebagai API pengelola data yang merespons dalam format JSON (bukan sebagai perender HTML). Hal ini membuat kode lebih terisolasi, bersih (*clean code*), dan mudah dirawat ke depannya.
+36
View File
@@ -0,0 +1,36 @@
<?php
// ========================================
// FILE: config.php
// Konfigurasi Umum Aplikasi
// ========================================
// DATABASE CONFIG
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', ''); // Kosongkan jika XAMPP default, atau masukkan password
define('DB_NAME', 'pemetaan_kemiskinan');
// API CONFIG
define('API_URL', 'php/');
define('APP_NAME', 'Sistem Pemetaan Kemiskinan');
define('APP_VERSION', '1.0.0');
// MAP CONFIG
define('MAP_CENTER_LAT', -0.0612); // Pontianak
define('MAP_CENTER_LNG', 109.3449);
define('MAP_DEFAULT_ZOOM', 12);
define('MAP_MAX_ZOOM', 19);
// TIMEZONE
date_default_timezone_set('Asia/Jakarta');
// ERROR REPORTING
if (getenv('APP_ENV') === 'development') {
error_reporting(E_ALL);
ini_set('display_errors', 1);
} else {
error_reporting(0);
ini_set('display_errors', 0);
}
?>
+865
View File
@@ -0,0 +1,865 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sistem Pemetaan Kemiskinan</title>
<!-- LEAFLET -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- LEAFLET DRAW -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw/dist/leaflet.draw.css"/>
<script src="https://unpkg.com/leaflet-draw/dist/leaflet.draw.js"></script>
<!-- LEAFLET GEOMETRY UTIL -->
<script src="https://unpkg.com/leaflet-geometryutil"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: 'Segoe UI', Tahoma, sans-serif;
background: #f1f3f6;
}
.container {
display: flex;
height: 100vh;
}
/* SIDEBAR */
#sidebar {
width: 320px;
background: linear-gradient(180deg, #1e3c72, #2a5298);
color: white;
padding: 20px;
overflow-y: auto;
box-shadow: 2px 0 10px rgba(0,0,0,0.2);
z-index: 100;
}
#sidebar h2 {
font-size: 18px;
margin-bottom: 15px;
border-bottom: 2px solid rgba(255,255,255,0.3);
padding-bottom: 10px;
}
.tab-buttons {
display: flex;
gap: 5px;
margin-bottom: 15px;
}
.tab-button {
flex: 1;
padding: 8px;
background: rgba(255,255,255,0.2);
border: none;
color: white;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
transition: 0.3s;
}
.tab-button.active {
background: rgba(255,255,255,0.5);
font-weight: bold;
}
.tab-button:hover {
background: rgba(255,255,255,0.3);
}
#search, #filter {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border-radius: 8px;
border: none;
outline: none;
font-size: 13px;
}
.list-container {
display: none;
max-height: 450px;
overflow-y: auto;
}
.list-container.active {
display: block;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
ul li {
padding: 12px;
margin-bottom: 8px;
background: rgba(255,255,255,0.1);
border-radius: 8px;
cursor: pointer;
transition: 0.3s;
font-size: 13px;
border-left: 3px solid transparent;
}
ul li:hover {
background: rgba(255,255,255,0.2);
transform: translateX(5px);
border-left-color: #4CAF50;
}
ul li small {
opacity: 0.8;
display: block;
font-size: 11px;
}
/* ACCORDION */
.accordion {
background-color: rgba(255,255,255,0.1);
color: white;
cursor: pointer;
padding: 12px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
font-weight: bold;
border-radius: 5px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
}
.accordion.active, .accordion:hover {
background-color: rgba(255,255,255,0.3);
}
.panel {
padding: 0 10px;
background-color: transparent;
display: none;
overflow: hidden;
margin-bottom: 10px;
}
/* MAP */
#map {
flex: 1;
position: relative;
}
/* MODAL */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal.active {
display: block;
}
.modal-content {
background-color: #fefefe;
margin: 5% auto;
padding: 20px;
border: 1px solid #888;
width: 90%;
max-width: 600px;
border-radius: 10px;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 2px solid #1e3c72;
padding-bottom: 10px;
}
.modal-header h2 {
color: #1e3c72;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.close-btn:hover {
color: #333;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-group input, .form-group select, .form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 13px;
font-family: inherit;
}
.form-group textarea {
resize: vertical;
min-height: 60px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
transition: 0.3s;
}
.btn-primary {
background: #4CAF50;
color: white;
flex: 1;
}
.btn-primary:hover {
background: #45a049;
}
.btn-secondary {
background: #f44336;
color: white;
flex: 1;
}
.btn-secondary:hover {
background: #da190b;
}
.status-label {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.status-ya {
background: #4CAF50;
color: white;
}
.status-tidak {
background: #f44336;
color: white;
}
.status-proses {
background: #ff9800;
color: white;
}
.info-section {
background: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 15px;
border-left: 4px solid #1e3c72;
}
.info-section h3 {
color: #1e3c72;
margin-bottom: 10px;
font-size: 14px;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 5px 0;
font-size: 13px;
border-bottom: 1px solid #eee;
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
font-weight: bold;
color: #666;
}
.info-value {
color: #333;
}
/* ALERT */
.alert {
padding: 12px;
margin-bottom: 15px;
border-radius: 5px;
display: none;
}
.alert.active {
display: block;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* LOADING */
.loading {
display: none;
text-align: center;
padding: 20px;
color: #666;
}
.loading.active {
display: block;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #1e3c72;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<!-- SIDEBAR -->
<div id="sidebar">
<a href="index.html" style="display:inline-block; margin-bottom:15px; color:#fff; text-decoration:none; font-size:12px; background:rgba(255,255,255,0.2); padding:5px 10px; border-radius:5px;">&larr; Kembali ke Portal</a>
<h2>📍 Portal Pemetaan Terpadu</h2>
<!-- ACCORDION: FASILITAS KOTA -->
<button class="accordion">🏗️ Fasilitas Kota <span></span></button>
<div class="panel">
<div class="tab-buttons" style="flex-wrap: wrap;">
<button class="tab-button" data-tab="jalan" style="margin-bottom:5px;">Jalan</button>
<button class="tab-button" data-tab="polygon" style="margin-bottom:5px;">Area</button>
<button class="tab-button" data-tab="spbu" style="margin-bottom:5px;">SPBU</button>
</div>
<div style="display:flex;gap:5px;margin-bottom:10px;">
<button class="btn-primary" onclick="alert('Untuk menambahkan data Jalan, gunakan ikon menggambar Garis (Polyline) di pojok kiri peta.')" style="font-size:11px;padding:5px;background:#555;">+ Jalan</button>
<button class="btn-primary" onclick="alert('Untuk menambahkan Area, gunakan ikon menggambar Polygon di pojok kiri peta.')" style="font-size:11px;padding:5px;background:#555;">+ Area</button>
<button class="btn-primary" onclick="openModal('modal-form-spbu')" style="font-size:11px;padding:5px;">+ SPBU</button>
</div>
<!-- TAB: JALAN -->
<div id="tab-jalan" class="list-container">
<ul id="list-jalan"></ul>
</div>
<!-- TAB: POLYGON -->
<div id="tab-polygon" class="list-container">
<ul id="list-polygon"></ul>
</div>
<!-- TAB: SPBU -->
<div id="tab-spbu" class="list-container">
<ul id="list-spbu"></ul>
</div>
</div>
<!-- ACCORDION: SOSIALISASI KEMASYARAKATAN -->
<button class="accordion">👥 Sosialisasi Kemasyarakatan <span></span></button>
<div class="panel">
<div class="tab-buttons">
<button class="tab-button" data-tab="masyarakat">Masyarakat</button>
<button class="tab-button" data-tab="ibadah">Rumah Ibadah</button>
</div>
<div style="display:flex;gap:5px;margin-bottom:10px;">
<button class="btn-primary" onclick="openModal('modal-form-masyarakat')" style="font-size:11px;padding:5px;">+ Masyarakat</button>
<button class="btn-primary" onclick="openModal('modal-form-ibadah')" style="font-size:11px;padding:5px;">+ Rumah Ibadah</button>
</div>
<!-- TAB: MASYARAKAT MISKIN -->
<div id="tab-masyarakat" class="list-container">
<input type="text" id="search" placeholder="Cari nama/alamat...">
<select id="filter-kecamatan">
<option value="">- Pilih Kecamatan -</option>
<option value="Pontianak Kota">Pontianak Kota</option>
<option value="Pontianak Utara">Pontianak Utara</option>
<option value="Pontianak Barat">Pontianak Barat</option>
<option value="Pontianak Selatan">Pontianak Selatan</option>
<option value="Pontianak Tenggara">Pontianak Tenggara</option>
</select>
<select id="filter-bpjs">
<option value="">- Status BPJS -</option>
<option value="Ya">BPJS Aktif</option>
<option value="Tidak">Tidak BPJS</option>
<option value="Proses">Proses</option>
</select>
<ul id="list-masyarakat"></ul>
</div>
<!-- TAB: RUMAH IBADAH -->
<div id="tab-ibadah" class="list-container">
<select id="filter-jenis">
<option value="">- Semua Jenis -</option>
<option value="Masjid">Masjid</option>
<option value="Gereja">Gereja</option>
<option value="Vihara">Vihara</option>
<option value="Kelenteng">Kelenteng</option>
<option value="Lain">Lain-lain</option>
</select>
<ul id="list-ibadah"></ul>
</div>
</div>
<!-- ACCORDION: PORTAL PUBLIK -->
<button class="accordion" id="acc-publik">📢 Layanan Publik & Laporan <span></span></button>
<div class="panel">
<div class="tab-buttons">
<button class="tab-button" data-tab="pelaporan">Lapor Darurat</button>
<button class="tab-button" data-tab="info-fasilitas">Info Terdekat</button>
</div>
<!-- TAB: PELAPORAN DARURAT -->
<div id="tab-pelaporan" class="list-container">
<div style="background:#fee2e2; padding:15px; border-radius:8px; margin-bottom:15px; border-left:4px solid #ef4444;">
<h3 style="color:#b91c1c; margin:0 0 5px 0; font-size:14px;"><i class="fa-solid fa-triangle-exclamation"></i> Darurat/Bencana</h3>
<p style="font-size:12px; color:#7f1d1d; margin:0 0 10px 0;">Laporkan segera jika terjadi kebakaran, banjir, darurat medis, atau jalan rusak parah.</p>
<button class="btn-primary" onclick="alert('Formulir Laporan Terkirim ke BPBD / Kepolisian Resort Kota Pontianak!')" style="width:100%; background:#ef4444; border:none; padding:10px;">🚨 Panggil Bantuan</button>
</div>
</div>
<!-- TAB: INFO FASILITAS -->
<div id="tab-info-fasilitas" class="list-container">
<p style="font-size:12px; color:#cbf4c9; background:#166534; padding:10px; border-radius:5px;">Klik icon lokasi pada peta untuk melihat Detail Fasilitas, SPBU terdekat, maupun Rumah Ibadah di area sekitar Anda.</p>
<ul class="data-list" id="list-info-terdekat">
<li style="text-align:center; color:#94a3b8; padding:20px; font-style:italic;">Silakan berinteraksi dengan peta</li>
</ul>
</div>
</div>
</div>
<!-- MAP -->
<div id="map"></div>
</div>
<!-- MODAL: DETAIL MASYARAKAT -->
<div id="modal-detail" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Detail Masyarakat</h2>
<button class="close-btn" onclick="closeModal('modal-detail')">&times;</button>
</div>
<div id="modal-body"></div>
</div>
</div>
<!-- MODAL: FORM INPUT MASYARAKAT -->
<div id="modal-form-masyarakat" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Input Masyarakat Miskin</h2>
<button class="close-btn" onclick="closeModal('modal-form-masyarakat')">&times;</button>
</div>
<div id="alert-form" class="alert"></div>
<form id="form-masyarakat">
<div style="
background:#e8f4ff;
padding:10px;
margin-bottom:10px;
border-radius:5px;
font-size:12px;">
📍 Klik lokasi rumah warga pada peta untuk mengisi Latitude dan Longitude secara otomatis.
</div>
<div class="form-group">
<label>Nama Lengkap *</label>
<input type="text" name="nama" required>
</div>
<div class="form-row">
<div class="form-group">
<label>Tanggal Lahir</label>
<input type="date" name="tanggal_lahir">
</div>
<div class="form-group">
<label>Jenis Kelamin</label>
<select name="jenis_kelamin">
<option value="Laki-laki">Laki-laki</option>
<option value="Perempuan">Perempuan</option>
</select>
</div>
</div>
<div class="form-group">
<label>NIK</label>
<input type="text" name="nik" placeholder="16 digit">
</div>
<div class="form-group">
<label>Alamat *</label>
<textarea name="alamat" required></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label>Kelurahan</label>
<input type="text" name="kelurahan">
</div>
<div class="form-group">
<label>Kecamatan</label>
<select name="kecamatan">
<option value="">Pilih Kecamatan</option>
<option value="Pontianak Kota">Pontianak Kota</option>
<option value="Pontianak Utara">Pontianak Utara</option>
<option value="Pontianak Barat">Pontianak Barat</option>
<option value="Pontianak Selatan">Pontianak Selatan</option>
<option value="Pontianak Tenggara">Pontianak Tenggara</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Latitude *</label>
<input type="number" name="latitude" step="0.00000001" required readonly>
</div>
<div class="form-group">
<label>Longitude *</label>
<input type="number" name="longitude" step="0.00000001" required readonly>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>No. HP</label>
<input type="text" name="no_hp" placeholder="Cth: 081234567890">
</div>
<div class="form-group">
<label>Status BPJS *</label>
<select name="status_bpjs" required>
<option value="Tidak">Tidak Memiliki</option>
<option value="Ya">Sudah Memiliki</option>
<option value="Proses">Proses Pendaftaran</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Pekerjaan</label>
<input type="text" name="pekerjaan" placeholder="Cth: Buruh, Petani, dll">
</div>
<div class="form-group">
<label>Pendapatan Bulanan (Rp)</label>
<input type="number" name="pendapatan_bulanan">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Jumlah Tanggungan</label>
<input type="number" name="jumlah_tanggungan" min="0">
</div>
<div class="form-group">
<label>Status Rumah</label>
<select name="status_rumah">
<option value="Menumpang">Menumpang</option>
<option value="Sewa">Sewa</option>
<option value="Milik Sendiri">Milik Sendiri</option>
</select>
</div>
</div>
<div class="button-group">
<button type="submit" class="btn-primary">Simpan Data</button>
<button type="button" class="btn-secondary" onclick="closeModal('modal-form-masyarakat')">Batal</button>
</div>
</form>
</div>
</div>
<!-- MODAL: FORM INPUT RUMAH IBADAH -->
<div id="modal-form-ibadah" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Input Rumah Ibadah</h2>
<button class="close-btn" onclick="closeModal('modal-form-ibadah')">&times;</button>
</div>
<div id="alert-form-ibadah" class="alert"></div>
<form id="form-ibadah">
<div class="form-group">
<label>Nama Tempat Ibadah *</label>
<input type="text" name="nama" required>
</div>
<div class="form-group">
<label>Jenis Tempat Ibadah *</label>
<select name="jenis" required>
<option value="">Pilih Jenis</option>
<option value="Masjid">Masjid</option>
<option value="Gereja">Gereja</option>
<option value="Vihara">Vihara</option>
<option value="Kelenteng">Kelenteng</option>
<option value="Sinagog">Sinagog</option>
<option value="Lain">Lain-lain</option>
</select>
</div>
<div class="form-group">
<label>Alamat *</label>
<textarea name="alamat" required></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label>Latitude *</label>
<input type="number" name="latitude" step="0.00000001" required readonly>
</div>
<div class="form-group">
<label>Longitude *</label>
<input type="number" name="longitude" step="0.00000001" required readonly>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Nama Pemimpin</label>
<input type="text" name="nama_pemimpin">
</div>
<div class="form-group">
<label>No. HP Pemimpin</label>
<input type="text" name="no_hp_pemimpin">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Kapasitas (orang)</label>
<input type="number" name="kapasitas" min="0">
</div>
<div class="form-group">
<label>Tahun Didirikan</label>
<input type="number" name="tahun_didirikan" min="1900" max="2099">
</div>
</div>
<div class="form-group">
<label>Keterangan</label>
<textarea name="keterangan"></textarea>
</div>
<div class="button-group">
<button type="submit" class="btn-primary">Simpan Data</button>
<button type="button" class="btn-secondary" onclick="closeModal('modal-form-ibadah')">Batal</button>
</div>
</form>
</div>
</div>
<!-- MODAL: FORM INPUT JALAN -->
<div id="modal-form-jalan" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Input Fasilitas Jalan</h2>
<button class="close-btn" onclick="closeModal('modal-form-jalan')">&times;</button>
</div>
<div id="alert-form-jalan" class="alert"></div>
<form id="form-jalan">
<div style="background:#e8f4ff; padding:10px; margin-bottom:10px; border-radius:5px; font-size:12px;">
📍 Gunakan tombol <strong>Polyline</strong> di peta untuk menggambar rute jalan, data GeoJSON akan otomatis terisi di bawah.
</div>
<div class="form-group">
<label>Nama Jalan *</label>
<input type="text" name="nama" required>
</div>
<div class="form-group">
<label>Tipe Jalan</label>
<select name="tipe">
<option value="Jalan Utama">Jalan Utama</option>
<option value="Jalan Lingkungan">Jalan Lingkungan</option>
<option value="Jalan Tol">Jalan Tol</option>
</select>
</div>
<div class="form-group">
<label>Data GeoJSON *</label>
<textarea name="geojson" id="geojson-jalan" required readonly placeholder="Gambar garis di peta untuk mengisi ini"></textarea>
</div>
<div class="form-group">
<label>Keterangan</label>
<textarea name="keterangan"></textarea>
</div>
<div class="button-group">
<button type="submit" class="btn-primary">Simpan Data</button>
<button type="button" class="btn-secondary" onclick="closeModal('modal-form-jalan')">Batal</button>
</div>
</form>
</div>
</div>
<!-- MODAL: FORM INPUT POLYGON -->
<div id="modal-form-polygon" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Input Fasilitas Area (Polygon)</h2>
<button class="close-btn" onclick="closeModal('modal-form-polygon')">&times;</button>
</div>
<div id="alert-form-polygon" class="alert"></div>
<form id="form-polygon">
<div style="background:#e8f4ff; padding:10px; margin-bottom:10px; border-radius:5px; font-size:12px;">
📍 Gunakan tombol <strong>Polygon</strong> di peta untuk menggambar area, data GeoJSON akan otomatis terisi di bawah.
</div>
<div class="form-group">
<label>Nama Area *</label>
<input type="text" name="nama" required>
</div>
<div class="form-group">
<label>Tipe Area</label>
<select name="tipe">
<option value="Taman">Taman Kota</option>
<option value="Pasar">Pasar / Pusat Perbelanjaan</option>
<option value="Perumahan">Kawasan Perumahan</option>
<option value="Lainnya">Lainnya</option>
</select>
</div>
<div class="form-group">
<label>Data GeoJSON *</label>
<textarea name="geojson" id="geojson-polygon" required readonly placeholder="Gambar polygon di peta untuk mengisi ini"></textarea>
</div>
<div class="form-group">
<label>Keterangan</label>
<textarea name="keterangan"></textarea>
</div>
<div class="button-group">
<button type="submit" class="btn-primary">Simpan Data</button>
<button type="button" class="btn-secondary" onclick="closeModal('modal-form-polygon')">Batal</button>
</div>
</form>
</div>
</div>
<!-- MODAL: FORM INPUT SPBU -->
<div id="modal-form-spbu" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Input SPBU</h2>
<button class="close-btn" onclick="closeModal('modal-form-spbu')">&times;</button>
</div>
<div id="alert-form-spbu" class="alert"></div>
<form id="form-spbu">
<div style="background:#e8f4ff; padding:10px; margin-bottom:10px; border-radius:5px; font-size:12px;">
📍 Klik lokasi di peta untuk mengisi Latitude dan Longitude secara otomatis.
</div>
<div class="form-group">
<label>Nama SPBU *</label>
<input type="text" name="nama" required>
</div>
<div class="form-row">
<div class="form-group">
<label>Latitude *</label>
<input type="number" name="latitude" id="lat-spbu" step="0.00000001" required readonly>
</div>
<div class="form-group">
<label>Longitude *</label>
<input type="number" name="longitude" id="lng-spbu" step="0.00000001" required readonly>
</div>
</div>
<div class="form-group">
<label>Alamat Lengkap</label>
<textarea name="alamat"></textarea>
</div>
<div class="form-group">
<label>Buka 24 Jam?</label>
<select name="is_24_jam" required>
<option value="Tidak">Tidak</option>
<option value="Ya">Ya, 24 Jam</option>
</select>
</div>
<div class="button-group">
<button type="submit" class="btn-primary">Simpan Data</button>
<button type="button" class="btn-secondary" onclick="closeModal('modal-form-spbu')">Batal</button>
</div>
</form>
</div>
</div>
<script src="js/main.js"></script>
</body>
</html>
+166
View File
@@ -0,0 +1,166 @@
-- ============================================
-- DATABASE: pemetaan_kemiskinan
-- DIBUAT UNTUK: Sistem Pemetaan Kemiskinan
-- ============================================
-- CREATE DATABASE
CREATE DATABASE IF NOT EXISTS `pemetaan_kemiskinan` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE `pemetaan_kemiskinan`;
-- ============================================
-- TABLE 1: masyarakat_miskin
-- ============================================
CREATE TABLE IF NOT EXISTS `masyarakat_miskin` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(100) NOT NULL,
`tanggal_lahir` date NOT NULL,
`jenis_kelamin` enum('Laki-laki','Perempuan') NOT NULL,
`nik` varchar(20),
`alamat` text NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`status_bpjs` enum('Ya','Tidak','Proses') DEFAULT 'Tidak',
`no_hp` varchar(15),
`pekerjaan` varchar(100),
`pendapatan_bulanan` int(11),
`jumlah_tanggungan` int(11),
`status_rumah` enum('Milik Sendiri','Sewa','Menumpang') DEFAULT 'Menumpang',
`kelurahan` varchar(100),
`kecamatan` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_nama` (`nama`),
INDEX `idx_status_bpjs` (`status_bpjs`),
INDEX `idx_kecamatan` (`kecamatan`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 2: rumah_ibadah
-- ============================================
CREATE TABLE IF NOT EXISTS `rumah_ibadah` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(100) NOT NULL,
`jenis` enum('Masjid','Gereja','Vihara','Kelenteng','Sinagog','Lain') NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`alamat` text NOT NULL,
`nama_pemimpin` varchar(100),
`no_hp_pemimpin` varchar(15),
`kapasitas` int(11),
`tahun_didirikan` year,
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_nama` (`nama`),
INDEX `idx_jenis` (`jenis`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 3: bantuan_riwayat
-- ============================================
CREATE TABLE IF NOT EXISTS `bantuan_riwayat` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_masyarakat` int(11) NOT NULL,
`jenis_bantuan` varchar(100) NOT NULL,
`tanggal_bantuan` date NOT NULL,
`nilai` int(11) NOT NULL,
`satuan` varchar(50),
`keterangan` text,
`nama_pemberi` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`id_masyarakat`) REFERENCES `masyarakat_miskin`(`id`) ON DELETE CASCADE,
INDEX `idx_id_masyarakat` (`id_masyarakat`),
INDEX `idx_tanggal` (`tanggal_bantuan`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 4: bpjs_record
-- ============================================
CREATE TABLE IF NOT EXISTS `bpjs_record` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_masyarakat` int(11) NOT NULL,
`no_peserta` varchar(20) NOT NULL UNIQUE,
`jenis_bpjs` enum('Kesehatan','Ketenagakerjaan','Jaminan Hari Tua') DEFAULT 'Kesehatan',
`tingkat` enum('Penerima Bantuan Iuran (PBI)','Pekerja Penerima Upah','Pekerja Bukan Penerima Upah','Peserta Mandiri') DEFAULT 'Penerima Bantuan Iuran (PBI)',
`tanggal_daftar` date NOT NULL,
`status_aktif` enum('Aktif','Nonaktif','Suspended') DEFAULT 'Aktif',
`faskes_tingkat_1` varchar(100),
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`id_masyarakat`) REFERENCES `masyarakat_miskin`(`id`) ON DELETE CASCADE,
INDEX `idx_id_masyarakat` (`id_masyarakat`),
INDEX `idx_no_peserta` (`no_peserta`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 5: users (untuk login admin)
-- ============================================
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` varchar(50) NOT NULL UNIQUE,
`password` varchar(255) NOT NULL,
`nama_lengkap` varchar(100) NOT NULL,
`role` enum('Admin','Surveyor','Viewer') DEFAULT 'Surveyor',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Insert default admin user (password: admin123)
INSERT INTO `users` (`username`, `password`, `nama_lengkap`, `role`) VALUES
('admin', '5f4dcc3b5aa765d61d8327deb882cf99', 'Administrator', 'Admin');
-- ============================================
-- TABLE 6: fasilitas_jalan
-- ============================================
CREATE TABLE IF NOT EXISTS `fasilitas_jalan` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`geojson` text NOT NULL,
`tipe` varchar(50),
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 7: fasilitas_polygon
-- ============================================
CREATE TABLE IF NOT EXISTS `fasilitas_polygon` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`geojson` text NOT NULL,
`tipe` varchar(50),
`keterangan` text,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- TABLE 8: fasilitas_spbu
-- ============================================
CREATE TABLE IF NOT EXISTS `fasilitas_spbu` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`nama` varchar(150) NOT NULL,
`latitude` decimal(10,8) NOT NULL,
`longitude` decimal(11,8) NOT NULL,
`alamat` text,
`is_24_jam` enum('Ya','Tidak') DEFAULT 'Tidak',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============================================
-- SAMPLE DATA (Opsional - untuk testing)
-- ============================================
-- Sample Masyarakat Miskin
INSERT INTO `masyarakat_miskin` (`nama`, `tanggal_lahir`, `jenis_kelamin`, `nik`, `alamat`, `latitude`, `longitude`, `status_bpjs`, `no_hp`, `pekerjaan`, `pendapatan_bulanan`, `jumlah_tanggungan`, `status_rumah`, `kelurahan`, `kecamatan`) VALUES
('Siti Nur Azizah', '1985-03-15', 'Perempuan', '6101234567890123', 'Jl. Mahakam No. 25', -0.0612, 109.3445, 'Ya', '081234567890', 'Buruh Harian', 1500000, 4, 'Menumpang', 'Sungai Jawi', 'Pontianak Kota');
-- Sample Rumah Ibadah
INSERT INTO `rumah_ibadah` (`nama`, `jenis`, `latitude`, `longitude`, `alamat`, `nama_pemimpin`, `no_hp_pemimpin`, `kapasitas`, `tahun_didirikan`) VALUES
('Masjid Al-Hidayah', 'Masjid', -0.0615, 109.3450, 'Jl. Diponegoro No. 45', 'Ustaz Ahmad', '081234567890', 500, 2005),
('Gereja Hati Kudus', 'Gereja', -0.0610, 109.3440, 'Jl. Jend Sudirman No. 12', 'Pdt. Bonaventura', '081234567891', 300, 1995);
-- ============================================
-- SELESAI
-- ============================================
+134
View File
@@ -0,0 +1,134 @@
# 📖 Panduan Lengkap: Membuat Database di phpMyAdmin
## STEP 1: Akses phpMyAdmin
1. Buka browser dan akses: `http://localhost/phpmyadmin`
- Username: `root`
- Password: *(kosongkan atau sesuai konfigurasi)*
2. Tekan tombol **Login**
---
## STEP 2: Buat Database Baru
### Cara Manual:
1. **Di halaman utama phpMyAdmin**, cari kolom **"Create new database"** di sebelah kiri
2. Isikan nama database: `pemetaan_kemiskinan`
3. Pilih **Collation**: `utf8mb4_general_ci`
4. Klik tombol **Create**
### Atau menggunakan Import SQL:
1. Di menu atas, pilih tab **"Import"**
2. Klik **"Choose File"** dan pilih file `database.sql`
3. Pastikan encoding: `utf-8`
4. Klik **Import** (database akan terbuat otomatis)
✅ Database berhasil dibuat
---
## STEP 3: Verifikasi Tabel-Tabel
Setelah import atau membuat manual, klik pada database `pemetaan_kemiskinan` di sebelah kiri.
**Anda seharusnya melihat tabel-tabel berikut:**
```
✓ masyarakat_miskin
✓ rumah_ibadah
✓ bantuan_riwayat
✓ bpjs_record
```
---
## STEP 4: Struktur Tabel (REFERENSI)
### Tabel: `masyarakat_miskin`
| Field | Type | Keterangan |
|-------|------|-----------|
| id | INT (Primary Key) | ID unik |
| nama | VARCHAR(100) | Nama lengkap |
| tanggal_lahir | DATE | Tgl lahir (YYYY-MM-DD) |
| jenis_kelamin | ENUM('Laki-laki','Perempuan') | - |
| alamat | TEXT | Alamat rumah |
| latitude | DECIMAL(10,8) | Koordinat lat |
| longitude | DECIMAL(11,8) | Koordinat lng |
| status_bpjs | ENUM('Ya','Tidak','Proses') | Status BPJS |
| no_hp | VARCHAR(15) | Nomor HP |
| created_at | TIMESTAMP | Waktu input |
### Tabel: `rumah_ibadah`
| Field | Type | Keterangan |
|-------|------|-----------|
| id | INT (Primary Key) | ID unik |
| nama | VARCHAR(100) | Nama tempat ibadah |
| jenis | ENUM('Masjid','Gereja','Vihara','Kelenteng','Lain') | Tipe |
| latitude | DECIMAL(10,8) | Koordinat lat |
| longitude | DECIMAL(11,8) | Koordinat lng |
| alamat | TEXT | Alamat |
| keterangan | TEXT | Info tambahan |
| created_at | TIMESTAMP | Waktu input |
### Tabel: `bantuan_riwayat`
| Field | Type | Keterangan |
|-------|------|-----------|
| id | INT (Primary Key) | ID unik |
| id_masyarakat | INT | ID masyarakat (foreign key) |
| jenis_bantuan | VARCHAR(100) | Tipe bantuan |
| tanggal_bantuan | DATE | Tanggal pemberian |
| nilai | INT | Nominal (Rp) |
| keterangan | TEXT | Catatan |
### Tabel: `bpjs_record`
| Field | Type | Keterangan |
|-------|------|-----------|
| id | INT (Primary Key) | ID unik |
| id_masyarakat | INT | ID masyarakat |
| no_peserta | VARCHAR(20) | Nomor peserta BPJS |
| tingkat | ENUM('Keluarga','Mandiri','Penerima Bantuan Iuran') | Tipe BPJS |
| tanggal_daftar | DATE | Tgl pendaftaran |
---
## STEP 5: Cek Koneksi Database (Opsional)
Jika ingin test koneksi manual:
1. Buat file `test_koneksi.php` di folder project
2. Isi dengan kode ini:
```php
<?php
$conn = new mysqli("localhost", "root", "", "pemetaan_kemiskinan");
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
echo "✅ Koneksi berhasil!";
?>
```
3. Akses: `http://localhost/pemetaan-kemiskinan/test_koneksi.php`
4. Jika muncul pesan hijau = Database siap digunakan ✅
---
## TROUBLESHOOTING
### ❌ "Access denied for user 'root'@'localhost'"
- Buka file `config/koneksi.php`
- Ubah password sesuai konfigurasi MySQL Anda
- Jika menggunakan XAMPP default, kosongkan saja password
### ❌ "Database pemetaan_kemiskinan tidak ditemukan"
- Pastikan sudah run file `database.sql` melalui Import
- Atau buat manual: **Create new database** → nama: `pemetaan_kemiskinan`
### ❌ File upload tidak berfungsi
- Cek folder upload di `_uploads/` sudah ada & permission 755
- Atau buat manual: klik kanan folder → Properties → Permission → 755
---
**Selesai! Database siap digunakan untuk website pemetaan kemiskinan**
+280
View File
@@ -0,0 +1,280 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WebGIS Poverty Mapping</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
:root {
--primary: #00f2fe;
--secondary: #4facfe;
--accent: #ff0844;
--glass-bg: rgba(15, 23, 42, 0.6);
--glass-border: rgba(255, 255, 255, 0.1);
}
body, html {
margin: 0;
padding: 0;
font-family: 'Outfit', sans-serif;
height: 100%;
overflow-x: hidden;
background: #0f172a;
}
#bg-map {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, rgba(15,23,42,0.9) 0%, rgba(15,23,42,0.6) 100%);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
z-index: 2;
}
.content-wrapper {
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 2rem;
color: white;
}
.header {
text-align: center;
margin-bottom: 4rem;
animation: fadeInDown 1s ease-out;
}
.header h1 {
font-size: 4rem;
font-weight: 900;
margin: 0 0 10px 0;
background: linear-gradient(to right, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-transform: uppercase;
letter-spacing: 2px;
text-shadow: 0 0 30px rgba(79, 172, 254, 0.3);
}
.header p {
font-size: 1.2rem;
color: #94a3b8;
font-weight: 300;
letter-spacing: 1px;
margin: 0;
}
.cards-container {
display: flex;
gap: 2rem;
flex-wrap: wrap;
justify-content: center;
max-width: 1200px;
width: 100%;
}
.card {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: 24px;
padding: 2.5rem 2rem;
width: 320px;
text-align: center;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, transparent 100%);
opacity: 0;
transition: opacity 0.4s ease;
}
.card:hover {
transform: translateY(-15px) scale(1.02);
border-color: rgba(255, 255, 255, 0.3);
box-shadow: 0 30px 60px -10px rgba(0, 242, 254, 0.2);
}
.card:hover::before {
opacity: 1;
}
.icon-container {
font-size: 3.5rem;
margin-bottom: 1.5rem;
position: relative;
z-index: 2;
}
.card-1 .icon-container { color: #10b981; text-shadow: 0 0 20px rgba(16,185,129,0.5); }
.card-2 .icon-container { color: #f43f5e; text-shadow: 0 0 20px rgba(244,63,94,0.5); }
.card-3 .icon-container { color: #3b82f6; text-shadow: 0 0 20px rgba(59,130,246,0.5); }
.card h2 {
font-size: 1.5rem;
color: #f8fafc;
margin: 0 0 1rem 0;
font-weight: 700;
position: relative;
z-index: 2;
}
.card p {
font-size: 0.95rem;
color: #cbd5e1;
line-height: 1.6;
margin: 0 0 2rem 0;
font-weight: 300;
position: relative;
z-index: 2;
}
.btn {
display: inline-block;
padding: 0.8rem 2rem;
border-radius: 50px;
font-size: 0.95rem;
font-weight: 600;
text-decoration: none;
color: white;
transition: all 0.3s ease;
position: relative;
z-index: 2;
border: 1px solid transparent;
width: 70%;
}
.btn:hover {
transform: scale(1.05);
letter-spacing: 1px;
}
.btn-green {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
box-shadow: 0 10px 20px -5px rgba(16,185,129,0.4);
}
.btn-pink {
background: linear-gradient(135deg, #f43f5e 0%, #e11d48 100%);
box-shadow: 0 10px 20px -5px rgba(244,63,94,0.4);
}
.btn-blue {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
box-shadow: 0 10px 20px -5px rgba(59,130,246,0.4);
}
/* Animations */
@keyframes fadeInDown {
from { opacity: 0; transform: translateY(-30px); }
to { opacity: 1; transform: translateY(0); }
}
.card-1 { animation: fadeInUp 0.8s ease-out 0.2s both; }
.card-2 { animation: fadeInUp 0.8s ease-out 0.4s both; }
.card-3 { animation: fadeInUp 0.8s ease-out 0.6s both; }
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.header h1 { font-size: 2.5rem; }
.card { width: 100%; max-width: 350px; }
}
</style>
</head>
<body>
<!-- Background Map -->
<div id="bg-map"></div>
<div class="overlay"></div>
<!-- Content -->
<div class="content-wrapper">
<div class="header">
<h1>WebGIS Pontianak</h1>
<p>Pusat Integrasi Data Geospasial & Kemiskinan</p>
</div>
<div class="cards-container">
<!-- Fasilitas & Tata Kota -->
<div class="card card-1">
<div class="icon-container"><i class="fa-solid fa-layer-group"></i></div>
<h2>Fasilitas & Tata Kota</h2>
<p>Manajemen pemetaan jalan, fasilitas publik, batas area, dan inventarisasi tata ruang kota Pontianak.</p>
<a href="dashboard.html" class="btn btn-green" onclick="localStorage.setItem('activeTab', 'fasilitas')">Masuk Sistem &rarr;</a>
</div>
<!-- Sosial & Kemasyarakatan -->
<div class="card card-2">
<div class="icon-container"><i class="fa-solid fa-users-viewfinder"></i></div>
<h2>Sosial Kemasyarakatan</h2>
<p>Sistem informasi geografis kawasan rentan, sebaran warga miskin, dan pemantauan rumah ibadah terpadu.</p>
<a href="dashboard.html" class="btn btn-pink" onclick="localStorage.setItem('activeTab', 'sosial')">Masuk Sistem &rarr;</a>
</div>
<!-- Portal Publik -->
<div class="card card-3">
<div class="icon-container"><i class="fa-solid fa-tower-broadcast"></i></div>
<h2>Portal Publik</h2>
<p>Akses peta interaktif secara transparan untuk masyarakat umum dan layanan pelaporan darurat.</p>
<a href="dashboard.html" class="btn btn-blue" onclick="localStorage.setItem('activeTab', 'publik')">Akses Warga &rarr;</a>
</div>
</div>
</div>
<script>
// Initialize an interactive background map
const map = L.map('bg-map', {
zoomControl: false,
dragging: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
keyboard: false
}).setView([-0.0227, 109.3333], 13); // Pontianak coordinates
// Dark theme map tiles for better integration with the dark overlay
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
attribution: '&copy; OpenStreetMap contributors &copy; CARTO'
}).addTo(map);
// Add a slow continuous pan effect
setInterval(() => {
map.panBy([1, 0.5], {animate: true, duration: 1});
}, 100);
</script>
</body>
</html>
+912
View File
@@ -0,0 +1,912 @@
// ========================================
// FILE: js/main.js
// Main JavaScript untuk Pemetaan Kemiskinan
// ========================================
// ========== KONFIGURASI ==========
const MAP_CENTER = [-0.0612, 109.3449]; // Koordinat pusat peta (Pontianak)
const MAP_ZOOM = 12; // Tingkat perbesaran bawaan peta
const API_PATH = 'php/'; // Jalur relatif menuju berkas API PHP
let map;
const markers = {
masyarakat: [],
ibadah: [],
spbu: []
};
const layers = {
jalan: L.layerGroup(),
polygon: L.layerGroup()
};
let current_tab = 'masyarakat';
const all_data = {
masyarakat: [],
ibadah: [],
jalan: [],
polygon: [],
spbu: []
};
let drawnItems;
// ========== ICON MARKER ==========
const iconMasyarakat = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
shadowSize: [41, 41]
});
const iconIbadah = L.icon({
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
shadowSize: [41, 41]
});
// ========== UTILITAS KEAMANAN & FORMAT ==========
/**
* Mengamankan string teks dari potensi serangan injeksi XSS.
* @param {string} str - String mentah yang akan dibersihkan.
* @returns {string} String yang aman untuk direndering ke DOM.
*/
function escapeHTML(str) {
if (str === null || str === undefined) return '-';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Melakukan validasi dasar terhadap respons HTTP Fetch sebelum diproses menjadi JSON.
* @param {Response} response - Objek respons dari Fetch API.
* @returns {Promise} Mengembalikan Promise berupa objek JSON jika sukses.
*/
function handleFetchResponse(response) {
if (!response.ok) {
throw new Error(`Gagal memuat data dari server (Status: ${response.status})`);
}
return response.json();
}
// ========== INISIALISASI ==========
document.addEventListener('DOMContentLoaded', function() {
initMap();
loadData();
attachEventListeners();
// Check initial active tab from landing page
const activeTab = localStorage.getItem('activeTab');
const accs = document.querySelectorAll('.accordion');
if (activeTab === 'fasilitas') {
if (accs.length > 0) {
accs[0].style.display = 'flex'; // Show Fasilitas
if (accs.length > 1) accs[1].style.display = 'none'; // Hide Sosial
if (accs.length > 2) accs[2].style.display = 'none'; // Hide Publik
accs[0].classList.add('active');
accs[0].nextElementSibling.style.display = 'block';
const firstTab = accs[0].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
} else if (activeTab === 'sosial') {
if (accs.length > 1) {
if (accs.length > 0) accs[0].style.display = 'none'; // Hide Fasilitas
accs[1].style.display = 'flex'; // Show Sosial
if (accs.length > 2) accs[2].style.display = 'none'; // Hide Publik
accs[1].classList.add('active');
accs[1].nextElementSibling.style.display = 'block';
const firstTab = accs[1].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
} else if (activeTab === 'publik') {
// Mode Publik: Sembunyikan semua tombol Add dan Form Input
document.querySelectorAll('.btn-primary').forEach(btn => {
if(btn.textContent.includes('+')) btn.style.display = 'none';
});
document.querySelector('.leaflet-draw').style.display = 'none'; // Sembunyikan toolbar Leaflet Draw
// Buka Layanan Publik (Accordion 3)
if (accs.length > 0) accs[0].style.display = 'none';
if (accs.length > 1) accs[1].style.display = 'none';
if (accs.length > 2) {
accs[2].style.display = 'flex';
accs[2].classList.add('active');
accs[2].nextElementSibling.style.display = 'block';
const firstTab = accs[2].nextElementSibling.querySelector('.tab-button');
if (firstTab) firstTab.click();
}
// Tambahkan badge Portal Publik di sidebar jika belum ada
if (!document.getElementById('badge-publik')) {
const sidebar = document.getElementById('sidebar');
const badge = document.createElement('div');
badge.id = 'badge-publik';
badge.innerHTML = '<div style="background:#2563eb;color:white;padding:10px;text-align:center;border-radius:5px;margin-bottom:15px;font-weight:bold;">Mode Akses Publik</div>';
sidebar.insertBefore(badge, sidebar.children[2]);
}
}
});
/**
* Menginisialisasi objek peta Leaflet dan menambahkan event listener klik.
*/
function initMap() {
map = L.map('map').setView(MAP_CENTER, MAP_ZOOM);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
// LEAFLET DRAW
drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawnItems },
draw: {
polyline: true,
polygon: true,
circle: false,
marker: false,
circlemarker: false,
rectangle: false
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
const type = e.layerType;
const layer = e.layer;
const geojson = JSON.stringify(layer.toGeoJSON());
if (type === 'polyline') {
openModal('modal-form-jalan');
setTimeout(() => { document.getElementById('geojson-jalan').value = geojson; }, 100);
} else if (type === 'polygon') {
openModal('modal-form-polygon');
setTimeout(() => { document.getElementById('geojson-polygon').value = geojson; }, 100);
}
});
// Klik peta untuk memunculkan pilihan tambah data
map.on('click', function(e) {
const lat = e.latlng.lat.toFixed(8);
const lng = e.latlng.lng.toFixed(8);
document.querySelectorAll('input[name="latitude"]').forEach(el => el.value = lat);
document.querySelectorAll('input[name="longitude"]').forEach(el => el.value = lng);
let buttonsHTML = '';
if (current_tab === 'masyarakat') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-masyarakat');" style="background:#4CAF50; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah Warga Miskin</button>`;
} else if (current_tab === 'ibadah') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-ibadah');" style="background:#2196F3; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah Rumah Ibadah</button>`;
} else if (current_tab === 'spbu') {
buttonsHTML = `<button onclick="map.closePopup(); openModal('modal-form-spbu');" style="background:#ff9800; color:white; border:none; padding:8px; border-radius:4px; cursor:pointer; font-size:12px; font-weight:bold; transition:0.2s;">+ Tambah SPBU</button>`;
} else {
buttonsHTML = `<small style="color:#f44336;">Silakan pilih tab yang sesuai (Warga, Ibadah, atau SPBU) di menu kiri terlebih dahulu.</small>`;
}
// Buat konten popup pilihan
const popupContent = `
<div style="text-align: center; min-width: 160px; padding: 5px;">
<b style="font-size: 14px; color: #1e3c72; margin-bottom:5px; display:block;">Pilih Data untuk Ditambah</b>
<small style="color: #666; display:block; margin-bottom:10px;">${lat}, ${lng}</small>
<div style="display: flex; flex-direction: column; gap: 8px;">
${buttonsHTML}
</div>
</div>
`;
L.popup()
.setLatLng(e.latlng)
.setContent(popupContent)
.openOn(map);
});
}
/**
* Memicu proses memuat seluruh data dari API secara paralel.
*/
function loadData() {
loadMasyarakat();
loadRumahIbadah();
loadJalan();
loadPolygon();
loadSPBU();
}
function loadJalan() {
fetch(API_PATH + 'ambil_fasilitas_jalan.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.jalan = result.data || [];
tampilkanDataJalan(all_data.jalan);
}
}).catch(e => console.error(e));
}
function loadPolygon() {
fetch(API_PATH + 'ambil_fasilitas_polygon.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.polygon = result.data || [];
tampilkanDataPolygon(all_data.polygon);
}
}).catch(e => console.error(e));
}
function loadSPBU() {
fetch(API_PATH + 'ambil_fasilitas_spbu.php').then(handleFetchResponse).then(result => {
if (result.status === 'success') {
all_data.spbu = result.data || [];
tampilkanDataSPBU(all_data.spbu);
}
}).catch(e => console.error(e));
}
/**
* Memuat data masyarakat miskin dari server.
*/
function loadMasyarakat() {
fetch(API_PATH + 'ambil_data_masyarakat.php')
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
all_data.masyarakat = result.data || [];
tampilkanDataMasyarakat(all_data.masyarakat);
} else {
console.error('Gagal memuat data masyarakat:', result.message);
}
})
.catch(error => console.error('Error saat fetch data masyarakat:', error));
}
/**
* Memuat data fasilitas rumah ibadah dari server.
*/
function loadRumahIbadah() {
fetch(API_PATH + 'ambil_rumah_ibadah.php')
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
all_data.ibadah = result.data || [];
tampilkanDataIbadah(all_data.ibadah);
} else {
console.error('Gagal memuat data rumah ibadah:', result.message);
}
})
.catch(error => console.error('Error saat fetch data rumah ibadah:', error));
}
/**
* Merender penanda peta dan elemen daftar sidebar untuk kategori masyarakat.
* @param {Array} data - Larik berisi data objek masyarakat.
*/
function tampilkanDataMasyarakat(data) {
markers.masyarakat.forEach(m => map.removeLayer(m));
markers.masyarakat = [];
const list = document.getElementById('list-masyarakat');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const marker = L.marker(
[parseFloat(item.latitude), parseFloat(item.longitude)],
{ icon: iconMasyarakat }
).addTo(map);
const bpjs_class = item.status_bpjs === 'Ya' ? 'status-ya' : (item.status_bpjs === 'Proses' ? 'status-proses' : 'status-tidak');
// Penerapan escapeHTML untuk mencegah kerentanan XSS pada popup
marker.bindPopup(`
<div style="font-size:13px; min-width:250px;">
<b>${escapeHTML(item.nama)}</b><br>
<small style="opacity:0.7;">${escapeHTML(item.alamat)}</small><br>
Status BPJS: <span class="status-label ${bpjs_class}">${escapeHTML(item.status_bpjs)}</span><br>
<button onclick="showDetail('${escapeHTML(item.id)}')" style="width:100%; padding:8px; margin-top:10px; background:#1e3c72; color:white; border:none; border-radius:5px; cursor:pointer;">Lihat Detail</button>
</div>
`);
markers.masyarakat.push(marker);
const li = document.createElement('li');
li.innerHTML = `
<b>${escapeHTML(item.nama)}</b>
<small>${escapeHTML(item.kecamatan)} | BPJS: ${escapeHTML(item.status_bpjs)}</small>
`;
li.onclick = () => {
map.setView([item.latitude, item.longitude], 16);
marker.openPopup();
};
list.appendChild(li);
});
updateMapVisibility();
}
/**
* Merender penanda peta dan elemen daftar sidebar untuk kategori rumah ibadah.
* @param {Array} data - Larik berisi data objek rumah ibadah.
*/
function tampilkanDataIbadah(data) {
markers.ibadah.forEach(m => map.removeLayer(m));
markers.ibadah = [];
const list = document.getElementById('list-ibadah');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const marker = L.marker(
[parseFloat(item.latitude), parseFloat(item.longitude)],
{ icon: iconIbadah }
).addTo(map);
const info_pemimpin = item.nama_pemimpin ? 'Pemimpin: ' + escapeHTML(item.nama_pemimpin) + '<br>' : '';
const info_alamat = item.alamat ? '<small>' + escapeHTML(item.alamat) + '</small>' : '';
marker.bindPopup(`
<div style="font-size:13px; min-width:250px;">
<b>${escapeHTML(item.nama)}</b><br>
<small>${escapeHTML(item.jenis)}</small><br>
${info_pemimpin}
${info_alamat}
</div>
`);
markers.ibadah.push(marker);
const li = document.createElement('li');
li.innerHTML = `
<b>${escapeHTML(item.nama)}</b>
<small>${escapeHTML(item.jenis)}</small>
`;
li.onclick = () => {
map.setView([item.latitude, item.longitude], 16);
marker.openPopup();
};
list.appendChild(li);
});
updateMapVisibility();
}
function tampilkanDataJalan(data) {
if (layers.jalan) map.removeLayer(layers.jalan);
layers.jalan = L.layerGroup().addTo(map);
const list = document.getElementById('list-jalan');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
try {
const geojson = JSON.parse(item.geojson);
const layer = L.geoJSON(geojson, { style: { color: 'blue', weight: 4 } }).addTo(layers.jalan);
layer.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small><br><button onclick="hapusData('jalan', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small>`;
li.onclick = () => { map.fitBounds(layer.getBounds()); layer.openPopup(); };
list.appendChild(li);
} catch(e) { console.error('Invalid GeoJSON', e); }
});
updateMapVisibility();
}
function tampilkanDataPolygon(data) {
if (layers.polygon) map.removeLayer(layers.polygon);
layers.polygon = L.layerGroup().addTo(map);
const list = document.getElementById('list-polygon');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
try {
const geojson = JSON.parse(item.geojson);
const layer = L.geoJSON(geojson, { style: { color: 'purple', fillColor: 'purple', fillOpacity: 0.4 } }).addTo(layers.polygon);
layer.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small><br><button onclick="hapusData('polygon', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.tipe)}</small>`;
li.onclick = () => { map.fitBounds(layer.getBounds()); layer.openPopup(); };
list.appendChild(li);
} catch(e) { console.error('Invalid GeoJSON', e); }
});
updateMapVisibility();
}
function tampilkanDataSPBU(data) {
markers.spbu.forEach(m => map.removeLayer(m));
markers.spbu = [];
const list = document.getElementById('list-spbu');
if (!list) return;
list.innerHTML = '';
data.forEach(item => {
const is24 = item.is_24_jam === 'Ya';
// Reuse iconIbadah for green (24h) and iconMasyarakat for red (not 24h)
const iconSPBU = is24 ? iconIbadah : iconMasyarakat;
const statusText = is24 ? '<span style="color:green;font-weight:bold;">Buka 24 Jam</span>' : '<span style="color:red;font-weight:bold;">Tidak 24 Jam</span>';
const marker = L.marker([parseFloat(item.latitude), parseFloat(item.longitude)], {
icon: iconSPBU,
draggable: true
}).addTo(map);
marker.bindPopup(`<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.alamat)}</small><br>${statusText}<br><button onclick="hapusData('spbu', ${item.id})" style="margin-top:5px;cursor:pointer;background:#f44336;color:white;border:none;padding:5px;border-radius:3px;">Hapus</button>`);
marker.on('dragend', function(e) {
const newPos = marker.getLatLng();
if (confirm('Apakah Anda yakin ingin memperbarui lokasi SPBU ini?')) {
const fd = new FormData();
fd.append('id', item.id);
fd.append('latitude', newPos.lat.toFixed(8));
fd.append('longitude', newPos.lng.toFixed(8));
fetch(API_PATH + 'update_lokasi_spbu.php', { method: 'POST', body: fd })
.then(handleFetchResponse)
.then(res => {
if (res.status === 'success') {
alert('Lokasi berhasil diperbarui');
item.latitude = newPos.lat;
item.longitude = newPos.lng;
} else {
alert('Gagal: ' + res.message);
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
}
}).catch(err => {
alert('Error: ' + err.message);
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
});
} else {
marker.setLatLng([parseFloat(item.latitude), parseFloat(item.longitude)]);
}
});
markers.spbu.push(marker);
const li = document.createElement('li');
li.innerHTML = `<b>${escapeHTML(item.nama)}</b><br><small>${escapeHTML(item.alamat)}</small><br><small>${statusText}</small>`;
li.onclick = () => { map.setView([item.latitude, item.longitude], 16); marker.openPopup(); };
list.appendChild(li);
});
updateMapVisibility();
}
/**
* Memuat detail data spesifik masyarakat miskin termasuk riwayat bantuan dan status BPJS.
* @param {string|number} id - ID unik entitas masyarakat miskin.
*/
function showDetail(id) {
fetch(API_PATH + 'ambil_detail.php?id=' + encodeURIComponent(id))
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
const data = result.data;
const masyarakat = data.masyarakat;
const bantuan = data.bantuan || [];
const bpjs = data.bpjs;
const bpjs_class = masyarakat.status_bpjs === 'Ya' ? 'status-ya' : (masyarakat.status_bpjs === 'Proses' ? 'status-proses' : 'status-tidak');
let html = `
<div class="info-section">
<h3>👤 Data Pribadi</h3>
<div class="info-row"><span class="info-label">Nama</span><span class="info-value">${escapeHTML(masyarakat.nama)}</span></div>
<div class="info-row"><span class="info-label">Tanggal Lahir</span><span class="info-value">${escapeHTML(masyarakat.tanggal_lahir)}</span></div>
<div class="info-row"><span class="info-label">Jenis Kelamin</span><span class="info-value">${escapeHTML(masyarakat.jenis_kelamin)}</span></div>
<div class="info-row"><span class="info-label">NIK</span><span class="info-value">${escapeHTML(masyarakat.nik)}</span></div>
<div class="info-row"><span class="info-label">No. HP</span><span class="info-value">${escapeHTML(masyarakat.no_hp)}</span></div>
</div>
<div class="info-section">
<h3>🏠 Data Alamat</h3>
<div class="info-row"><span class="info-label">Alamat</span><span class="info-value">${escapeHTML(masyarakat.alamat)}</span></div>
<div class="info-row"><span class="info-label">Kelurahan</span><span class="info-value">${escapeHTML(masyarakat.kelurahan)}</span></div>
<div class="info-row"><span class="info-label">Kecamatan</span><span class="info-value">${escapeHTML(masyarakat.kecamatan)}</span></div>
<div class="info-row"><span class="info-label">Status Rumah</span><span class="info-value">${escapeHTML(masyarakat.status_rumah)}</span></div>
</div>
<div class="info-section">
<h3>💼 Data Ekonomi</h3>
<div class="info-row"><span class="info-label">Pekerjaan</span><span class="info-value">${escapeHTML(masyarakat.pekerjaan)}</span></div>
<div class="info-row"><span class="info-label">Pendapatan Bulanan</span><span class="info-value">Rp. ${new Intl.NumberFormat('id-ID').format(masyarakat.pendapatan_bulanan || 0)}</span></div>
<div class="info-row"><span class="info-label">Jumlah Tanggungan</span><span class="info-value">${escapeHTML(masyarakat.jumlah_tanggungan)} orang</span></div>
</div>
<div class="info-section">
<h3>🏥 Status BPJS</h3>
<div class="info-row"><span class="info-label">Status BPJS</span><span class="info-value"><span class="status-label ${bpjs_class}">${escapeHTML(masyarakat.status_bpjs)}</span></span></div>
`;
if (bpjs) {
html += `
<div class="info-row"><span class="info-label">No. Peserta BPJS</span><span class="info-value">${escapeHTML(bpjs.no_peserta)}</span></div>
<div class="info-row"><span class="info-label">Jenis BPJS</span><span class="info-value">${escapeHTML(bpjs.jenis_bpjs)}</span></div>
<div class="info-row"><span class="info-label">Tingkat</span><span class="info-value">${escapeHTML(bpjs.tingkat)}</span></div>
`;
}
html += `</div>`; // Memperbaiki string penutup tag div yang sebelumnya memicu syntax error
if (bantuan.length > 0) {
html += `<div class="info-section"><h3>💰 Riwayat Bantuan</h3>`;
bantuan.forEach(b => {
html += `
<div style="background:white; padding:8px; margin-bottom:8px; border-left:3px solid #ff9800; border-radius:3px; font-size:12px;">
<b>${escapeHTML(b.jenis_bantuan)}</b> (${escapeHTML(b.tanggal_bantuan)})<br>
Nilai: <strong>Rp. ${new Intl.NumberFormat('id-ID').format(b.nilai)}</strong> ${escapeHTML(b.satuan)}
${b.keterangan ? '<br><small>' + escapeHTML(b.keterangan) + '</small>' : ''}
</div>
`;
});
html += `</div>`;
}
html += `
<div class="button-group">
<button class="btn-primary" onclick="editBantuan(${parseInt(id)})"> Tambah Bantuan</button>
<button class="btn-secondary" onclick="hapusData('masyarakat', ${parseInt(id)})">🗑️ Hapus</button>
</div>
`;
const modal_body = document.getElementById('modal-body');
if (modal_body) {
modal_body.innerHTML = html;
openModal('modal-detail');
}
}
})
.catch(error => console.error('Error saat fetch detail data:', error));
}
/**
* Menghubungkan fungsi penangan kejadian (event handler) ke elemen-elemen HTML DOM.
*/
function attachEventListeners() {
// Accordion Logic
document.querySelectorAll('.accordion').forEach(acc => {
acc.addEventListener('click', function() {
// Close other accordions
document.querySelectorAll('.accordion').forEach(otherAcc => {
if (otherAcc !== this) {
otherAcc.classList.remove('active');
if (otherAcc.nextElementSibling) {
otherAcc.nextElementSibling.style.display = "none";
}
}
});
// Toggle this one
this.classList.toggle('active');
const panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
// Optionally auto-click the first tab in this accordion
const firstTab = panel.querySelector('.tab-button');
if (firstTab && !firstTab.classList.contains('active')) {
firstTab.click();
}
}
});
});
document.querySelectorAll('.tab-button').forEach(btn => {
btn.addEventListener('click', function(e) {
const tab = this.dataset.tab;
switchTab(e, tab);
});
});
const search_input = document.getElementById('search');
const f_kecamatan = document.getElementById('filter-kecamatan');
const f_bpjs = document.getElementById('filter-bpjs');
const f_jenis = document.getElementById('filter-jenis');
if (search_input) search_input.addEventListener('input', filterMasyarakat);
if (f_kecamatan) f_kecamatan.addEventListener('change', filterMasyarakat);
if (f_bpjs) f_bpjs.addEventListener('change', filterMasyarakat);
if (f_jenis) f_jenis.addEventListener('change', filterIbadah);
const form_masyarakat = document.getElementById('form-masyarakat');
const form_ibadah = document.getElementById('form-ibadah');
const form_jalan = document.getElementById('form-jalan');
const form_polygon = document.getElementById('form-polygon');
const form_spbu = document.getElementById('form-spbu');
if (form_masyarakat) form_masyarakat.addEventListener('submit', function(e) { e.preventDefault(); submitFormMasyarakat(); });
if (form_ibadah) form_ibadah.addEventListener('submit', function(e) { e.preventDefault(); submitFormIbadah(); });
if (form_jalan) form_jalan.addEventListener('submit', function(e) { e.preventDefault(); submitFormJalan(); });
if (form_polygon) form_polygon.addEventListener('submit', function(e) { e.preventDefault(); submitFormPolygon(); });
if (form_spbu) form_spbu.addEventListener('submit', function(e) { e.preventDefault(); submitFormSPBU(); });
// Shortcut keyboard untuk membuka formulir penambahan data baru
document.addEventListener('keydown', function(e) {
if (e.key === 'n' || e.key === 'N') {
const active_modal = document.querySelector('.modal.active');
if (active_modal) return; // Cegah konflik jika modal lain sudah terbuka
if (current_tab === 'masyarakat') openModal('modal-form-masyarakat');
else if (current_tab === 'ibadah') openModal('modal-form-ibadah');
}
});
}
/**
* Mengubah status visibilitas tab kategori aktif (Masyarakat / Rumah Ibadah).
* @param {Event} e - Objek event klik dari DOM.
* @param {string} tab - Nama tab penanda tujuan ('masyarakat' / 'ibadah').
*/
function switchTab(e, tab) {
current_tab = tab;
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
if (e && e.target) {
e.target.classList.add('active');
}
document.querySelectorAll('.list-container').forEach(container => {
container.classList.remove('active');
});
const target_tab = document.getElementById('tab-' + tab);
if (target_tab) target_tab.classList.add('active');
updateMapVisibility();
}
function updateMapVisibility() {
markers.masyarakat.forEach(m => map.removeLayer(m));
markers.ibadah.forEach(m => map.removeLayer(m));
markers.spbu.forEach(m => map.removeLayer(m));
if (layers.jalan) map.removeLayer(layers.jalan);
if (layers.polygon) map.removeLayer(layers.polygon);
if (current_tab === 'masyarakat' || current_tab === 'info-fasilitas') {
markers.masyarakat.forEach(m => map.addLayer(m));
}
if (current_tab === 'ibadah' || current_tab === 'info-fasilitas') {
markers.ibadah.forEach(m => map.addLayer(m));
}
if (current_tab === 'spbu' || current_tab === 'info-fasilitas') {
markers.spbu.forEach(m => map.addLayer(m));
}
if (current_tab === 'jalan' || current_tab === 'info-fasilitas') {
if (layers.jalan) map.addLayer(layers.jalan);
}
if (current_tab === 'polygon' || current_tab === 'info-fasilitas') {
if (layers.polygon) map.addLayer(layers.polygon);
}
}
/**
* Memfilter kumpulan data masyarakat berdasarkan kata kunci pencarian, kecamatan, dan status BPJS.
*/
function filterMasyarakat() {
const search_el = document.getElementById('search');
const kec_el = document.getElementById('filter-kecamatan');
const bpjs_el = document.getElementById('filter-bpjs');
const keyword = search_el ? search_el.value.toLowerCase() : '';
const kecamatan = kec_el ? kec_el.value : '';
const bpjs = bpjs_el ? bpjs_el.value : '';
const filtered = all_data.masyarakat.filter(item => {
const item_nama = item.nama ? item.nama.toLowerCase() : '';
const item_alamat = item.alamat ? item.alamat.toLowerCase() : '';
const match_keyword = item_nama.includes(keyword) || item_alamat.includes(keyword);
const match_kecamatan = !kecamatan || item.kecamatan === kecamatan;
const match_bpjs = !bpjs || item.status_bpjs === bpjs;
return match_keyword && match_kecamatan && match_bpjs;
});
tampilkanDataMasyarakat(filtered);
}
/**
* Memfilter kumpulan data tempat ibadah berdasarkan jenis kategori tempat ibadah.
*/
function filterIbadah() {
const jenis_el = document.getElementById('filter-jenis');
const jenis = jenis_el ? jenis_el.value : '';
const filtered = all_data.ibadah.filter(item => {
return !jenis || item.jenis === jenis;
});
tampilkanDataIbadah(filtered);
}
/**
* Mengirimkan formulir data masyarakat baru ke berkas PHP pemroses menggunakan Fetch FormData.
*/
function submitFormMasyarakat() {
const form = document.getElementById('form-masyarakat');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_masyarakat.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
showAlert('alert-form', 'Data berhasil disimpan!', 'success');
form.reset();
setTimeout(() => {
closeModal('modal-form-masyarakat');
loadMasyarakat();
}, 1500);
} else {
showAlert('alert-form', result.message || 'Gagal menyimpan data.', 'error');
}
})
.catch(error => {
showAlert('alert-form', 'Terjadi kesalahan sistem: ' + error.message, 'error');
});
}
/**
* Mengirimkan formulir data rumah ibadah baru ke berkas PHP pemroses menggunakan Fetch FormData.
*/
function submitFormIbadah() {
const form = document.getElementById('form-ibadah');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_rumah_ibadah.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
showAlert('alert-form-ibadah', 'Data berhasil disimpan!', 'success');
form.reset();
setTimeout(() => {
closeModal('modal-form-ibadah');
loadRumahIbadah();
}, 1500);
} else {
showAlert('alert-form-ibadah', result.message || 'Gagal menyimpan data.', 'error');
}
})
.catch(error => {
showAlert('alert-form-ibadah', 'Terjadi kesalahan sistem: ' + error.message, 'error');
});
}
function submitFormJalan() {
const form = document.getElementById('form-jalan');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_jalan.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-jalan', 'Berhasil disimpan', 'success'); form.reset(); drawnItems.clearLayers();
setTimeout(() => { closeModal('modal-form-jalan'); loadJalan(); }, 1000);
} else showAlert('alert-form-jalan', result.message, 'error');
}).catch(e => showAlert('alert-form-jalan', e.message, 'error'));
}
function submitFormPolygon() {
const form = document.getElementById('form-polygon');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_polygon.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-polygon', 'Berhasil disimpan', 'success'); form.reset(); drawnItems.clearLayers();
setTimeout(() => { closeModal('modal-form-polygon'); loadPolygon(); }, 1000);
} else showAlert('alert-form-polygon', result.message, 'error');
}).catch(e => showAlert('alert-form-polygon', e.message, 'error'));
}
function submitFormSPBU() {
const form = document.getElementById('form-spbu');
if (!form) return;
const form_data = new FormData(form);
fetch(API_PATH + 'simpan_fasilitas_spbu.php', { method: 'POST', body: form_data })
.then(handleFetchResponse).then(result => {
if (result.status === 'success') {
showAlert('alert-form-spbu', 'Berhasil disimpan', 'success'); form.reset();
setTimeout(() => { closeModal('modal-form-spbu'); loadSPBU(); }, 1000);
} else showAlert('alert-form-spbu', result.message, 'error');
}).catch(e => showAlert('alert-form-spbu', e.message, 'error'));
}
/**
* Mengirimkan instruksi penghapusan data spesifik (masyarakat/ibadah) ke sistem basis data backend.
* @param {string} tipe - Jenis kategori data yang akan dihapus ('masyarakat' / 'ibadah').
* @param {string|number} id - ID baris data target.
*/
function hapusData(tipe, id) {
if (!confirm('Apakah Anda yakin ingin menghapus data ini? Tindakan ini tidak dapat dibatalkan.')) {
return;
}
const form_data = new FormData();
form_data.append('tipe', tipe);
form_data.append('id', id);
fetch(API_PATH + 'hapus_data.php', {
method: 'POST',
body: form_data
})
.then(handleFetchResponse)
.then(result => {
if (result.status === 'success') {
alert('Data berhasil dihapus');
closeModal('modal-detail');
if (tipe === 'masyarakat') loadMasyarakat();
else if (tipe === 'ibadah') loadRumahIbadah();
else if (tipe === 'jalan') loadJalan();
else if (tipe === 'polygon') loadPolygon();
else if (tipe === 'spbu') loadSPBU();
} else {
alert('Gagal menghapus: ' + (result.message || 'Error tidak diketahui'));
}
})
.catch(error => {
alert('Terjadi kesalahan jaringan atau sistem: ' + error.message);
});
}
function editBantuan(id) {
alert('Fitur tambah bantuan dengan ID ' + id + ' akan ditambahkan segera');
}
function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) modal.classList.add('active');
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) modal.classList.remove('active');
}
function showAlert(elementId, message, type) {
const alert_el = document.getElementById(elementId);
if (!alert_el) return;
alert_el.textContent = message;
alert_el.className = 'alert active alert-' + type;
setTimeout(() => {
alert_el.classList.remove('active');
}, 5000);
}
// Menutup jendela modal otomatis jika pengguna mengklik area luar modal (overlay)
document.addEventListener('click', function(e) {
if (e.target && e.target.classList.contains('modal')) {
e.target.classList.remove('active');
}
});
+67
View File
@@ -0,0 +1,67 @@
<?php
// ========================================
// FILE: php/ambil_data_masyarakat.php
// Mengambil data masyarakat miskin
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Kumpulkan filter dulu SEBELUM query dibuat
$filters = [];
$params = [];
$types = "";
if (!empty($_GET['kecamatan'])) {
$filters[] = "kecamatan = ?";
$params[] = $_GET['kecamatan'];
$types .= "s";
}
if (!empty($_GET['status_bpjs'])) {
$filters[] = "status_bpjs = ?";
$params[] = $_GET['status_bpjs'];
$types .= "s";
}
if (!empty($_GET['search'])) {
$filters[] = "(nama LIKE ? OR alamat LIKE ?)";
$searchParam = "%" . $_GET['search'] . "%";
$params[] = $searchParam;
$params[] = $searchParam;
$types .= "ss";
}
// Susun query dengan urutan benar: WHERE dulu, baru ORDER BY
$query = "SELECT * FROM masyarakat_miskin";
if (!empty($filters)) {
$query .= " WHERE " . implode(" AND ", $filters);
}
$query .= " ORDER BY created_at DESC";
$stmt = $conn->prepare($query);
if ($stmt === false) {
sendJSON('error', 'Query prepare gagal: ' . $conn->error);
}
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
if (!$stmt->execute()) {
sendJSON('error', 'Query gagal dieksekusi: ' . $stmt->error);
}
$result = $stmt->get_result();
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
sendJSON('success', 'Data berhasil diambil', $data);
}
?>
+52
View File
@@ -0,0 +1,52 @@
<?php
// ========================================
// FILE: php/ambil_detail.php
// Mengambil detail lengkap data masyarakat + bantuan + BPJS
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$id = intval($_GET['id'] ?? 0);
if ($id == 0) {
sendJSON('error', 'ID tidak ditemukan');
}
// AMBIL DATA MASYARAKAT
$query_masyarakat = "SELECT * FROM masyarakat_miskin WHERE id = $id";
$result_masyarakat = $conn->query($query_masyarakat);
if ($result_masyarakat->num_rows === 0) {
sendJSON('error', 'Data masyarakat tidak ditemukan');
}
$masyarakat = $result_masyarakat->fetch_assoc();
// AMBIL DATA BANTUAN
$query_bantuan = "SELECT * FROM bantuan_riwayat WHERE id_masyarakat = $id ORDER BY tanggal_bantuan DESC";
$result_bantuan = $conn->query($query_bantuan);
$bantuan_list = [];
while ($row = $result_bantuan->fetch_assoc()) {
$bantuan_list[] = $row;
}
// AMBIL DATA BPJS
$query_bpjs = "SELECT * FROM bpjs_record WHERE id_masyarakat = $id";
$result_bpjs = $conn->query($query_bpjs);
$bpjs = null;
if ($result_bpjs->num_rows > 0) {
$bpjs = $result_bpjs->fetch_assoc();
}
$data = [
'masyarakat' => $masyarakat,
'bantuan' => $bantuan_list,
'bpjs' => $bpjs
];
sendJSON('success', 'Detail data berhasil diambil', $data);
}
?>
+16
View File
@@ -0,0 +1,16 @@
<?php
include 'koneksi.php';
$query = "SELECT * FROM fasilitas_jalan ORDER BY nama ASC";
$result = $conn->query($query);
$data = [];
if ($result) {
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
sendJSON('success', 'Data jalan berhasil diambil', $data);
} else {
sendJSON('error', 'Gagal mengambil data: ' . $conn->error);
}
?>
+16
View File
@@ -0,0 +1,16 @@
<?php
include 'koneksi.php';
$query = "SELECT * FROM fasilitas_polygon ORDER BY nama ASC";
$result = $conn->query($query);
$data = [];
if ($result) {
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
sendJSON('success', 'Data polygon berhasil diambil', $data);
} else {
sendJSON('error', 'Gagal mengambil data: ' . $conn->error);
}
?>
+16
View File
@@ -0,0 +1,16 @@
<?php
include 'koneksi.php';
$query = "SELECT * FROM fasilitas_spbu ORDER BY nama ASC";
$result = $conn->query($query);
$data = [];
if ($result) {
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
sendJSON('success', 'Data SPBU berhasil diambil', $data);
} else {
sendJSON('error', 'Gagal mengambil data: ' . $conn->error);
}
?>
+46
View File
@@ -0,0 +1,46 @@
<?php
// ========================================
// FILE: php/ambil_rumah_ibadah.php
// Mengambil data rumah ibadah
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Susun query dengan urutan benar: WHERE dulu, baru ORDER BY
$query = "SELECT * FROM rumah_ibadah";
$params = [];
$types = "";
if (!empty($_GET['jenis'])) {
$query .= " WHERE jenis = ?";
$params[] = $_GET['jenis'];
$types .= "s";
}
$query .= " ORDER BY created_at DESC";
$stmt = $conn->prepare($query);
if ($stmt === false) {
sendJSON('error', 'Query prepare gagal: ' . $conn->error);
}
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
if (!$stmt->execute()) {
sendJSON('error', 'Query gagal dieksekusi: ' . $stmt->error);
}
$result = $stmt->get_result();
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
sendJSON('success', 'Data berhasil diambil', $data);
}
?>
+43
View File
@@ -0,0 +1,43 @@
<?php
// ========================================
// FILE: php/hapus_data.php
// Menghapus data masyarakat miskin atau rumah ibadah
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$tipe = sanitize($_POST['tipe'] ?? '', $conn); // 'masyarakat' atau 'rumah_ibadah'
$id = intval($_POST['id'] ?? 0);
if ($id == 0 || empty($tipe)) {
sendJSON('error', 'Data tidak lengkap');
}
if ($tipe === 'masyarakat') {
$query = "DELETE FROM masyarakat_miskin WHERE id = $id";
} elseif ($tipe === 'ibadah' || $tipe === 'rumah_ibadah') { // To be safe
$query = "DELETE FROM rumah_ibadah WHERE id = $id";
} elseif ($tipe === 'jalan') {
$query = "DELETE FROM fasilitas_jalan WHERE id = $id";
} elseif ($tipe === 'polygon') {
$query = "DELETE FROM fasilitas_polygon WHERE id = $id";
} elseif ($tipe === 'spbu') {
$query = "DELETE FROM fasilitas_spbu WHERE id = $id";
} else {
sendJSON('error', 'Tipe tidak valid');
}
if ($conn->query($query) === TRUE) {
if ($conn->affected_rows > 0) {
sendJSON('success', 'Data berhasil dihapus');
} else {
sendJSON('error', 'Data tidak ditemukan');
}
} else {
sendJSON('error', 'Gagal menghapus: ' . $conn->error);
}
}
?>
+54
View File
@@ -0,0 +1,54 @@
<?php
// ========================================
// FILE: php/koneksi.php
// Konfigurasi Koneksi Database
// ========================================
// CORS HEADERS - Wajib ada agar request dari browser tidak diblok
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With');
header('Content-Type: application/json; charset=utf-8');
// Handle preflight request (OPTIONS)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$host = "localhost";
$username = "root";
$password = ""; // Jika XAMPP, kosongkan
$database = "pemetaan_kemiskinan";
// KONEKSI MYSQLI
$conn = new mysqli($host, $username, $password, $database);
// CEK KONEKSI
if ($conn->connect_error) {
sendJSON('error', 'Koneksi database gagal: ' . $conn->connect_error);
exit();
}
// SET CHARSET UTF-8
$conn->set_charset("utf8mb4");
// FUNCTION UNTUK ESCAPE INPUT
function sanitize($string, $conn) {
return $conn->real_escape_string(strip_tags(trim($string)));
}
// FUNCTION UNTUK RESPONSE JSON
function sendJSON($status, $message, $data = null) {
$response = [
'status' => $status,
'message' => $message
];
if ($data !== null) {
$response['data'] = $data;
}
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit();
}
?>
+49
View File
@@ -0,0 +1,49 @@
<?php
// ========================================
// FILE: php/setup_db.php
// Skrip Otomatis Impor Database
// ========================================
// Karena koneksi.php sudah memilih database, kita buat koneksi mentah dulu
// untuk memastikan database 'pemetaan_kemiskinan' dibuat jika belum ada.
$host = "localhost";
$user = "root";
$pass = "";
$conn_init = new mysqli($host, $user, $pass);
if ($conn_init->connect_error) {
die("Koneksi gagal: " . $conn_init->connect_error . " (Pastikan MySQL di XAMPP sudah menyala!)");
}
// Buat database jika belum ada
$conn_init->query("CREATE DATABASE IF NOT EXISTS `pemetaan_kemiskinan` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci");
$conn_init->select_db("pemetaan_kemiskinan");
// Baca isi file database.sql
$sql_file = __DIR__ . '/../database.sql';
if (!file_exists($sql_file)) {
die("File database.sql tidak ditemukan di $sql_file");
}
$sql_content = file_get_contents($sql_file);
// Eksekusi semua query sekaligus
if ($conn_init->multi_query($sql_content)) {
do {
// Buang hasil dari setiap statement yang dieksekusi agar bisa lanjut ke statement berikutnya
if ($res = $conn_init->store_result()) {
$res->free();
}
} while ($conn_init->more_results() && $conn_init->next_result());
echo "<div style='background:#dcfce3; color:#166534; padding:20px; border-radius:10px; font-family:sans-serif; text-align:center;'>";
echo "<h1>✅ Database Berhasil Diimpor Otomatis!</h1>";
echo "<p>Seluruh tabel dan titik contoh masyarakat miskin & rumah ibadah sudah dimasukkan ke MySQL.</p>";
echo "<a href='../index.html' style='display:inline-block; padding:10px 20px; background:#16a34a; color:white; text-decoration:none; border-radius:5px;'>Kembali ke Portal</a>";
echo "</div>";
} else {
echo "Gagal mengimpor database: " . $conn_init->error;
}
$conn_init->close();
?>
+43
View File
@@ -0,0 +1,43 @@
<?php
// ========================================
// FILE: php/simpan_bantuan.php
// Menyimpan riwayat bantuan
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id_masyarakat = intval($_POST['id_masyarakat'] ?? 0);
$jenis_bantuan = sanitize($_POST['jenis_bantuan'] ?? '', $conn);
$tanggal_bantuan = sanitize($_POST['tanggal_bantuan'] ?? '', $conn);
$nilai = intval($_POST['nilai'] ?? 0);
$satuan = sanitize($_POST['satuan'] ?? 'Rupiah', $conn);
$keterangan = sanitize($_POST['keterangan'] ?? '', $conn);
$nama_pemberi = sanitize($_POST['nama_pemberi'] ?? '', $conn);
// VALIDASI
if ($id_masyarakat == 0 || empty($jenis_bantuan) || empty($tanggal_bantuan)) {
sendJSON('error', 'Data wajib lengkap');
}
// CEK MASYARAKAT EXIST
$check = $conn->query("SELECT id FROM masyarakat_miskin WHERE id = $id_masyarakat");
if ($check->num_rows == 0) {
sendJSON('error', 'ID Masyarakat tidak ditemukan');
}
// QUERY INSERT
$query = "INSERT INTO bantuan_riwayat
(id_masyarakat, jenis_bantuan, tanggal_bantuan, nilai, satuan, keterangan, nama_pemberi)
VALUES
($id_masyarakat, '$jenis_bantuan', '$tanggal_bantuan', $nilai, '$satuan', '$keterangan', '$nama_pemberi')";
if ($conn->query($query) === TRUE) {
sendJSON('success', 'Riwayat bantuan berhasil disimpan', ['id' => $conn->insert_id]);
} else {
sendJSON('error', 'Gagal menyimpan: ' . $conn->error);
}
}
?>
+64
View File
@@ -0,0 +1,64 @@
<?php
// ========================================
// FILE: php/simpan_bpjs.php
// Menyimpan data BPJS
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id_masyarakat = intval($_POST['id_masyarakat'] ?? 0);
$no_peserta = sanitize($_POST['no_peserta'] ?? '', $conn);
$jenis_bpjs = sanitize($_POST['jenis_bpjs'] ?? 'Kesehatan', $conn);
$tingkat = sanitize($_POST['tingkat'] ?? 'Penerima Bantuan Iuran (PBI)', $conn);
$tanggal_daftar = sanitize($_POST['tanggal_daftar'] ?? '', $conn);
$status_aktif = sanitize($_POST['status_aktif'] ?? 'Aktif', $conn);
$faskes = sanitize($_POST['faskes_tingkat_1'] ?? '', $conn);
// VALIDASI
if ($id_masyarakat == 0 || empty($no_peserta) || empty($tanggal_daftar)) {
sendJSON('error', 'Data wajib lengkap: ID, No Peserta, Tanggal');
}
// CEK MASYARAKAT EXIST
$check = $conn->query("SELECT id FROM masyarakat_miskin WHERE id = $id_masyarakat");
if ($check->num_rows == 0) {
sendJSON('error', 'ID Masyarakat tidak ditemukan');
}
// CEK DUPLIKAT NO PESERTA
$cek_dup = $conn->query("SELECT id FROM bpjs_record WHERE no_peserta = '$no_peserta' AND id_masyarakat != $id_masyarakat");
if ($cek_dup->num_rows > 0) {
sendJSON('error', 'Nomor peserta sudah terdaftar untuk masyarakat lain');
}
// QUERY INSERT atau UPDATE
$existing = $conn->query("SELECT id FROM bpjs_record WHERE id_masyarakat = $id_masyarakat");
if ($existing->num_rows > 0) {
// UPDATE
$query = "UPDATE bpjs_record SET
no_peserta = '$no_peserta',
jenis_bpjs = '$jenis_bpjs',
tingkat = '$tingkat',
tanggal_daftar = '$tanggal_daftar',
status_aktif = '$status_aktif',
faskes_tingkat_1 = '$faskes'
WHERE id_masyarakat = $id_masyarakat";
} else {
// INSERT
$query = "INSERT INTO bpjs_record
(id_masyarakat, no_peserta, jenis_bpjs, tingkat, tanggal_daftar, status_aktif, faskes_tingkat_1)
VALUES
($id_masyarakat, '$no_peserta', '$jenis_bpjs', '$tingkat', '$tanggal_daftar', '$status_aktif', '$faskes')";
}
if ($conn->query($query) === TRUE) {
sendJSON('success', 'Data BPJS berhasil disimpan');
} else {
sendJSON('error', 'Gagal menyimpan: ' . $conn->error);
}
}
?>
+26
View File
@@ -0,0 +1,26 @@
<?php
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendJSON('error', 'Metode tidak diizinkan');
}
$nama = sanitize($_POST['nama'] ?? '', $conn);
$geojson = $_POST['geojson'] ?? ''; // Don't strip tags for JSON
$tipe = sanitize($_POST['tipe'] ?? '', $conn);
$keterangan = sanitize($_POST['keterangan'] ?? '', $conn);
if (empty($nama) || empty($geojson)) {
sendJSON('error', 'Nama dan Data GeoJSON wajib diisi');
}
$stmt = $conn->prepare("INSERT INTO fasilitas_jalan (nama, geojson, tipe, keterangan) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $nama, $geojson, $tipe, $keterangan);
if ($stmt->execute()) {
sendJSON('success', 'Data jalan berhasil disimpan');
} else {
sendJSON('error', 'Gagal menyimpan: ' . $stmt->error);
}
$stmt->close();
?>
+26
View File
@@ -0,0 +1,26 @@
<?php
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendJSON('error', 'Metode tidak diizinkan');
}
$nama = sanitize($_POST['nama'] ?? '', $conn);
$geojson = $_POST['geojson'] ?? ''; // Don't strip tags for JSON
$tipe = sanitize($_POST['tipe'] ?? '', $conn);
$keterangan = sanitize($_POST['keterangan'] ?? '', $conn);
if (empty($nama) || empty($geojson)) {
sendJSON('error', 'Nama dan Data GeoJSON wajib diisi');
}
$stmt = $conn->prepare("INSERT INTO fasilitas_polygon (nama, geojson, tipe, keterangan) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $nama, $geojson, $tipe, $keterangan);
if ($stmt->execute()) {
sendJSON('success', 'Data polygon berhasil disimpan');
} else {
sendJSON('error', 'Gagal menyimpan: ' . $stmt->error);
}
$stmt->close();
?>
+27
View File
@@ -0,0 +1,27 @@
<?php
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendJSON('error', 'Metode tidak diizinkan');
}
$nama = sanitize($_POST['nama'] ?? '', $conn);
$latitude = sanitize($_POST['latitude'] ?? '', $conn);
$longitude = sanitize($_POST['longitude'] ?? '', $conn);
$alamat = sanitize($_POST['alamat'] ?? '', $conn);
$is_24_jam = sanitize($_POST['is_24_jam'] ?? 'Tidak', $conn);
if (empty($nama) || empty($latitude) || empty($longitude)) {
sendJSON('error', 'Nama, Latitude, dan Longitude wajib diisi');
}
$stmt = $conn->prepare("INSERT INTO fasilitas_spbu (nama, latitude, longitude, alamat, is_24_jam) VALUES (?, ?, ?, ?, ?)");
$stmt->bind_param("sddss", $nama, $latitude, $longitude, $alamat, $is_24_jam);
if ($stmt->execute()) {
sendJSON('success', 'Data SPBU berhasil disimpan');
} else {
sendJSON('error', 'Gagal menyimpan: ' . $stmt->error);
}
$stmt->close();
?>
+75
View File
@@ -0,0 +1,75 @@
<?php
// ========================================
// FILE: php/simpan_masyarakat.php
// Menyimpan data masyarakat miskin
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nama = sanitize($_POST['nama'] ?? '', $conn);
$tanggal_lahir = sanitize($_POST['tanggal_lahir'] ?? '', $conn);
$jenis_kelamin = sanitize($_POST['jenis_kelamin'] ?? '', $conn);
$nik = sanitize($_POST['nik'] ?? '', $conn);
$alamat = sanitize($_POST['alamat'] ?? '', $conn);
$latitude = floatval($_POST['latitude'] ?? 0);
$longitude = floatval($_POST['longitude'] ?? 0);
$status_bpjs = sanitize($_POST['status_bpjs'] ?? 'Tidak', $conn);
$no_hp = sanitize($_POST['no_hp'] ?? '', $conn);
$pekerjaan = sanitize($_POST['pekerjaan'] ?? '', $conn);
$pendapatan = intval($_POST['pendapatan_bulanan'] ?? 0);
$tanggungan = intval($_POST['jumlah_tanggungan'] ?? 0);
$status_rumah = sanitize($_POST['status_rumah'] ?? 'Menumpang', $conn);
$kelurahan = sanitize($_POST['kelurahan'] ?? '', $conn);
$kecamatan = sanitize($_POST['kecamatan'] ?? '', $conn);
// VALIDASI INPUT
if (empty($nama)) {
sendJSON('error', 'Nama wajib diisi');
}
if (empty($alamat)) {
sendJSON('error', 'Alamat wajib diisi');
}
if ($latitude == 0 || $longitude == 0) {
sendJSON('error', 'Koordinat lokasi wajib diisi (klik peta untuk menentukan titik)');
}
// CEK DUPLIKAT NIK (jika NIK diisi)
if (!empty($nik)) {
$stmt_cek = $conn->prepare("SELECT id FROM masyarakat_miskin WHERE nik = ?");
$stmt_cek->bind_param("s", $nik);
$stmt_cek->execute();
$cek = $stmt_cek->get_result();
if ($cek && $cek->num_rows > 0) {
sendJSON('error', 'NIK sudah terdaftar di sistem');
}
$stmt_cek->close();
}
// QUERY INSERT
$query = "INSERT INTO masyarakat_miskin
(nama, tanggal_lahir, jenis_kelamin, nik, alamat, latitude, longitude,
status_bpjs, no_hp, pekerjaan, pendapatan_bulanan, jumlah_tanggungan,
status_rumah, kelurahan, kecamatan)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
if ($stmt === false) {
sendJSON('error', 'Gagal prepare statement: ' . $conn->error);
}
$stmt->bind_param("sssssddsssiisss", $nama, $tanggal_lahir, $jenis_kelamin, $nik, $alamat, $latitude, $longitude, $status_bpjs, $no_hp, $pekerjaan, $pendapatan, $tanggungan, $status_rumah, $kelurahan, $kecamatan);
if ($stmt->execute()) {
$id = $stmt->insert_id;
sendJSON('success', 'Data masyarakat berhasil disimpan', ['id' => $id]);
} else {
sendJSON('error', 'Gagal menyimpan data: ' . $stmt->error);
}
$stmt->close();
} else {
sendJSON('error', 'Method tidak diizinkan. Gunakan POST.');
}
?>
+69
View File
@@ -0,0 +1,69 @@
<?php
// ========================================
// FILE: php/simpan_rumah_ibadah.php
// Menyimpan data rumah ibadah
// ========================================
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nama = sanitize($_POST['nama'] ?? '', $conn);
$jenis = sanitize($_POST['jenis'] ?? '', $conn);
$alamat = sanitize($_POST['alamat'] ?? '', $conn);
$latitude = floatval($_POST['latitude'] ?? 0);
$longitude = floatval($_POST['longitude'] ?? 0);
$nama_pemimpin = sanitize($_POST['nama_pemimpin'] ?? '', $conn);
$no_hp_pemimpin = sanitize($_POST['no_hp_pemimpin'] ?? '', $conn);
$kapasitas = intval($_POST['kapasitas'] ?? 0);
$tahun = sanitize($_POST['tahun_didirikan'] ?? '', $conn);
$keterangan = sanitize($_POST['keterangan'] ?? '', $conn);
// VALIDASI
if (empty($nama)) {
sendJSON('error', 'Nama rumah ibadah wajib diisi');
}
if (empty($jenis)) {
sendJSON('error', 'Jenis rumah ibadah wajib diisi');
}
if (empty($alamat)) {
sendJSON('error', 'Alamat wajib diisi');
}
if ($latitude == 0 || $longitude == 0) {
sendJSON('error', 'Koordinat lokasi wajib diisi (klik peta untuk menentukan titik)');
}
// Validasi nilai ENUM jenis
$jenis_valid = ['Masjid', 'Gereja', 'Vihara', 'Kelenteng', 'Sinagog', 'Lain'];
if (!in_array($jenis, $jenis_valid)) {
sendJSON('error', 'Jenis rumah ibadah tidak valid');
}
// Isi tahun_didirikan dengan NULL jika kosong (karena tipe kolom YEAR)
$tahun_sql = empty($tahun) ? null : $tahun;
// QUERY INSERT
$query = "INSERT INTO rumah_ibadah
(nama, jenis, latitude, longitude, alamat,
nama_pemimpin, no_hp_pemimpin, kapasitas, tahun_didirikan, keterangan)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
if ($stmt === false) {
sendJSON('error', 'Gagal prepare statement: ' . $conn->error);
}
$stmt->bind_param("ssddsssiss", $nama, $jenis, $latitude, $longitude, $alamat, $nama_pemimpin, $no_hp_pemimpin, $kapasitas, $tahun_sql, $keterangan);
if ($stmt->execute()) {
$id = $stmt->insert_id;
sendJSON('success', 'Data rumah ibadah berhasil disimpan', ['id' => $id]);
} else {
sendJSON('error', 'Gagal menyimpan data: ' . $stmt->error);
}
$stmt->close();
} else {
sendJSON('error', 'Method tidak diizinkan. Gunakan POST.');
}
?>
+14
View File
@@ -0,0 +1,14 @@
<?php
include 'koneksi.php';
$tables = ['masyarakat_miskin', 'rumah_ibadah', 'fasilitas_jalan', 'fasilitas_polygon', 'fasilitas_spbu', 'bantuan_riwayat'];
foreach ($tables as $table) {
$res = $conn->query("SELECT COUNT(*) FROM $table");
if ($res) {
echo "$table count: " . $res->fetch_row()[0] . "\n";
} else {
echo "$table error: " . $conn->error . "\n";
}
}
?>
+25
View File
@@ -0,0 +1,25 @@
<?php
include 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendJSON('error', 'Metode tidak diizinkan');
}
$id = intval($_POST['id'] ?? 0);
$latitude = sanitize($_POST['latitude'] ?? '', $conn);
$longitude = sanitize($_POST['longitude'] ?? '', $conn);
if ($id === 0 || empty($latitude) || empty($longitude)) {
sendJSON('error', 'ID, Latitude, dan Longitude wajib diisi');
}
$stmt = $conn->prepare("UPDATE fasilitas_spbu SET latitude = ?, longitude = ? WHERE id = ?");
$stmt->bind_param("ddi", $latitude, $longitude, $id);
if ($stmt->execute()) {
sendJSON('success', 'Lokasi SPBU berhasil diperbarui');
} else {
sendJSON('error', 'Gagal memperbarui lokasi: ' . $stmt->error);
}
$stmt->close();
?>