Upload files to "/"

This commit is contained in:
2026-06-05 03:08:55 +00:00
commit afcd403620
5 changed files with 1961 additions and 0 deletions
File diff suppressed because one or more lines are too long
+611
View File
@@ -0,0 +1,611 @@
<!DOCTYPE html>
<html lang="id">
<head>
<?php
session_start();
if (!isset($_SESSION['login']) || $_SESSION['login'] !== true) {
header("Location: login.php");
exit;
}
?>
<meta charset="UTF-8">
<title>WebGIS Kemiskinan - Dashboard Admin</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
* { box-sizing: border-box; }
body { margin:0; font-family:Arial,sans-serif; display:flex; background:#f0f2f5; }
.sidebar {
width: 360px;
min-width: 360px;
background: #2c3e50;
color: white;
padding: 15px;
overflow-y: auto;
height: 100vh;
}
.sidebar h2 {
text-align: center;
margin: 0 0 5px 0;
font-size: 16px;
color: #ecf0f1;
}
.sidebar .user-info {
text-align: center;
font-size: 12px;
color: #bdc3c7;
margin-bottom: 15px;
}
.card {
background: white;
color: #2c3e50;
padding: 12px;
border-radius: 8px;
margin-bottom: 12px;
}
.card h3 {
margin: 0 0 10px 0;
font-size: 14px;
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 5px;
}
.card input,
.card select,
.card textarea {
width: 100%;
padding: 7px 9px;
margin-bottom: 8px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 13px;
outline: none;
}
.card input:focus,
.card select:focus,
.card textarea:focus { border-color: #3498db; }
.card button {
width: 100%;
padding: 9px;
background: #27ae60;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
transition: background 0.2s;
}
.card button:hover { background: #219a52; }
.card button.btn-logout {
background: #e74c3c;
}
.card button.btn-logout:hover { background: #c0392b; }
.stat-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.stat-item {
background: #f8f9fa;
border-radius: 6px;
padding: 8px;
text-align: center;
font-size: 12px;
}
.stat-item .num {
font-size: 22px;
font-weight: bold;
display: block;
}
.stat-item.green .num { color: #27ae60; }
.stat-item.red .num { color: #e74c3c; }
.stat-item.blue .num { color: #3498db; }
.stat-item.orange .num { color: #e67e22; }
.table-box { max-height: 180px; overflow-y: auto; }
table { width: 100%; border-collapse: collapse; font-size: 11px; }
table th { background: #3498db; color: white; padding: 5px 6px; text-align: left; }
table td { padding: 5px 6px; border-bottom: 1px solid #eee; }
table tr:hover td { background: #f5f5f5; }
.radius-val { font-size: 20px; font-weight: bold; color: #e67e22; text-align: center; }
#map { flex: 1; height: 100vh; }
.btn-edit {
background: #f39c12 !important;
color: white !important;
width: 48% !important;
display: inline-block !important;
margin-top: 5px !important;
padding: 5px !important;
font-size: 10px !important;
border: none !important;
border-radius: 4px !important;
cursor: pointer !important;
}
.btn-hapus {
background: #e74c3c !important;
color: white !important;
width: 48% !important;
display: inline-block !important;
margin-top: 5px !important;
padding: 5px !important;
font-size: 10px !important;
border: none !important;
border-radius: 4px !important;
cursor: pointer !important;
float: right !important;
}
.btn-edit:hover { background: #e67e22 !important; }
.btn-hapus:hover { background: #c0392b !important; }
.clearfix::after { content: ""; clear: both; display: table; }
.popup-form input, .popup-form textarea, .popup-form select {
width: 100%;
padding: 6px;
margin-bottom: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 12px;
box-sizing: border-box;
}
.foto-preview {
margin: 10px 0;
text-align: center;
}
.foto-preview img {
max-width: 100%;
max-height: 120px;
border-radius: 8px;
border: 1px solid #ddd;
padding: 3px;
}
</style>
</head>
<body>
<div class="sidebar">
<h2>🗺️ Dashboard Admin</h2>
<div class="user-info">Login sebagai: <strong><?= htmlspecialchars($_SESSION['username']) ?></strong></div>
<div class="card">
<h3>📊 Statistik</h3>
<div class="stat-grid">
<div class="stat-item blue"><span class="num" id="ibadah">0</span> Rumah Ibadah</div>
<div class="stat-item red"><span class="num" id="miskin">0</span> Penduduk Miskin</div>
<div class="stat-item green"><span class="num" id="dalam">0</span> ✅ Dalam Radius</div>
<div class="stat-item orange"><span class="num" id="luar">0</span> ❌ Luar Radius</div>
</div>
</div>
<div class="card">
<h3>🎯 Radius Bantuan</h3>
<input type="range" min="100" max="500" step="50" value="300" id="radiusSlider" style="padding:0; border:none; background:transparent;">
<div class="radius-val"><span id="radiusValue">300</span> Meter</div>
<button onclick="terapkanRadius()" style="margin-top:8px;">🔄 Terapkan Radius</button>
</div>
<div class="card">
<h3> Tambah Bantuan</h3>
<select id="bJenis">
<option value="Beras">🌾 Beras</option>
<option value="Sembako">🛒 Sembako</option>
<option value="Uang Tunai">💰 Uang Tunai</option>
<option value="Pendidikan">📚 Pendidikan</option>
<option value="Kesehatan">🏥 Kesehatan</option>
</select>
<input type="text" id="bJumlah" placeholder="Jumlah (misal: 10 kg / Rp 500.000)">
<input type="date" id="bTanggal">
<textarea id="bKeterangan" placeholder="Keterangan tambahan" rows="2"></textarea>
<button onclick="simpanBantuan()">💾 Simpan Bantuan</button>
</div>
<div class="card">
<h3>📋 Histori Bantuan</h3>
<div class="table-box"><table id="tabelBantuan"><tr><th>Tanggal</th><th>Nama</th><th>Bantuan</th></tr></table></div>
</div>
<div class="card">
<h3>📈 Grafik Bantuan (Bulanan)</h3>
<canvas id="grafik" height="150"></canvas>
</div>
<div class="card">
<button onclick="window.location='laporan.php'" style="background:#8e44ad;margin-bottom:8px;">📋 Download Laporan</button>
<button class="btn-logout" onclick="window.location='logout.php'">🚪 Logout</button>
</div>
</div>
<!-- MODAL TAMBAH PENDUDUK MISKIN -->
<div id="modalPoverty" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:9999; align-items:center; justify-content:center;">
<div style="background:white; width:400px; max-width:95vw; border-radius:12px; padding:25px; max-height:90vh; overflow-y:auto; box-shadow: 0 20px 60px rgba(0,0,0,0.4);">
<h3 style="margin:0 0 15px 0; color:#e74c3c; font-size:16px;">⚠️ Tambah Penduduk Miskin</h3>
<input type="text" id="fNamaPoverty" placeholder="Nama Kepala Keluarga *" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="fNikPoverty" placeholder="NIK" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="fAlamatPoverty" placeholder="Alamat" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="fPekerjaanPoverty" placeholder="Pekerjaan" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="fPenghasilanPoverty" placeholder="Penghasilan (Rp)" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="fAnggotaPoverty" placeholder="Jumlah Anggota Keluarga" value="1" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="fTanggunganPoverty" placeholder="Jumlah Tanggungan" value="0" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<select id="fKategoriPoverty" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<option value="Miskin">Miskin</option><option value="Miskin Ekstrem">Miskin Ekstrem</option><option value="Hampir Miskin">Hampir Miskin</option>
</select>
<select id="fStatusBantuanPoverty" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<option value="Belum">Belum Menerima</option><option value="Pernah">Pernah Menerima</option><option value="Sedang">Sedang Menerima</option>
</select>
<textarea id="fKetPoverty" placeholder="Keterangan" rows="2" style="width:100%;padding:9px;margin-bottom:12px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;"></textarea>
<input type="file" id="fFotoPoverty" accept="image/*" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;" onchange="previewFoto(this)">
<div id="previewFotoPoverty" class="foto-preview" style="display:none;"><img id="previewImgPoverty" src=""></div>
<p id="modalKoordinat" style="font-size:12px;color:#888;margin:0 0 12px 0;"></p>
<div style="display:flex;gap:8px;">
<button onclick="simpanPoverty()" style="flex:1;padding:10px;background:#e74c3c;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;font-weight:bold;">💾 Simpan</button>
<button onclick="tutupModalPoverty()" style="flex:1;padding:10px;background:#95a5a6;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">✖ Batal</button>
</div>
</div>
</div>
<!-- MODAL EDIT PENDUDUK MISKIN -->
<div id="modalEditPoverty" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:9999; align-items:center; justify-content:center;">
<div style="background:white; width:400px; max-width:95vw; border-radius:12px; padding:25px; max-height:90vh; overflow-y:auto; box-shadow: 0 20px 60px rgba(0,0,0,0.4);">
<h3 style="margin:0 0 15px 0; color:#f39c12; font-size:16px;">✏️ Edit Penduduk Miskin</h3>
<input type="hidden" id="eIdPoverty">
<input type="text" id="eNamaPoverty" placeholder="Nama Kepala Keluarga *" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="eNikPoverty" placeholder="NIK" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="eAlamatPoverty" placeholder="Alamat" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="text" id="ePekerjaanPoverty" placeholder="Pekerjaan" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="ePenghasilanPoverty" placeholder="Penghasilan (Rp)" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="eAnggotaPoverty" placeholder="Jumlah Anggota Keluarga" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<input type="number" id="eTanggunganPoverty" placeholder="Jumlah Tanggungan" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<select id="eKategoriPoverty" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<option value="Miskin">Miskin</option><option value="Miskin Ekstrem">Miskin Ekstrem</option><option value="Hampir Miskin">Hampir Miskin</option>
</select>
<select id="eStatusBantuanPoverty" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;">
<option value="Belum">Belum Menerima</option><option value="Pernah">Pernah Menerima</option><option value="Sedang">Sedang Menerima</option>
</select>
<textarea id="eKetPoverty" placeholder="Keterangan" rows="2" style="width:100%;padding:9px;margin-bottom:12px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;"></textarea>
<input type="file" id="eFotoPoverty" accept="image/*" style="width:100%;padding:9px;margin-bottom:8px;border:1px solid #ddd;border-radius:5px;font-size:13px;box-sizing:border-box;" onchange="previewEditFoto(this)">
<input type="hidden" id="eFotoLama" value="">
<div id="editPreviewFoto" class="foto-preview" style="display:none;"><img id="editPreviewImg" src=""></div>
<div style="display:flex;gap:8px;">
<button onclick="simpanEditPoverty()" style="flex:1;padding:10px;background:#f39c12;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;font-weight:bold;">💾 Simpan</button>
<button onclick="tutupModalEditPoverty()" style="flex:1;padding:10px;background:#95a5a6;color:white;border:none;border-radius:5px;cursor:pointer;font-size:14px;">✖ Batal</button>
</div>
</div>
</div>
<!-- MODAL HAPUS DAN BERI BANTUAN (SEDERHANA) -->
<div id="modalHapusPoverty" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:9999; align-items:center; justify-content:center;">
<div style="background:white; width:340px; border-radius:12px; padding:25px; text-align:center;">
<h3 style="color:#e74c3c;">Hapus Data?</h3>
<p id="modalHapusNama" style="margin:15px 0;"></p>
<button onclick="konfirmasiHapusPoverty()" style="background:#e74c3c;color:white;padding:10px 20px;border:none;border-radius:5px;cursor:pointer;">Ya, Hapus</button>
<button onclick="document.getElementById('modalHapusPoverty').style.display='none'" style="background:#95a5a6;color:white;padding:10px 20px;border:none;border-radius:5px;cursor:pointer;">Batal</button>
</div>
</div>
<div id="modalBeriBantuan" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); z-index:9999; align-items:center; justify-content:center;">
<div style="background:white; width:380px; border-radius:12px; padding:25px;">
<h3 style="color:#27ae60;">🎁 Beri Bantuan</h3>
<p id="beriNamaPenduduk"></p>
<input type="hidden" id="beriPendudukId">
<select id="beriJenis" style="width:100%;padding:8px;margin:10px 0;"><option value="Beras">🌾 Beras</option><option value="Sembako">🛒 Sembako</option><option value="Uang Tunai">💰 Uang Tunai</option></select>
<input type="text" id="beriJumlah" placeholder="Jumlah" style="width:100%;padding:8px;margin:10px 0;">
<input type="date" id="beriTanggal" style="width:100%;padding:8px;margin:10px 0;">
<textarea id="beriKeterangan" placeholder="Keterangan" rows="2" style="width:100%;padding:8px;margin:10px 0;"></textarea>
<button onclick="simpanBeriBantuan()" style="background:#27ae60;color:white;padding:10px;width:100%;border:none;border-radius:5px;cursor:pointer;">💾 Simpan</button>
<button onclick="document.getElementById('modalBeriBantuan').style.display='none'" style="background:#95a5a6;color:white;padding:10px;width:100%;border:none;border-radius:5px;cursor:pointer;margin-top:5px;">Batal</button>
</div>
</div>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// ==================== MAP INIT ====================
var map = L.map('map').setView([-0.0263, 109.3425], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(map);
var masjidLayer = L.layerGroup().addTo(map);
var gerejaLayer = L.layerGroup().addTo(map);
var povertyLayer = L.layerGroup().addTo(map);
var radiusLayer = L.layerGroup().addTo(map);
var masjidPoints = [], gerejaPoints = [], povertyPoints = [];
var currentRadius = 300;
// ==================== IKON ====================
function createMasjidIcon() {
return L.divIcon({ className: 'masjid-icon', html: '<div style="background:#27ae60; width:28px; height:28px; border-radius:50%; display:flex; align-items:center; justify-content:center; border:2px solid white; box-shadow:0 2px 5px rgba(0,0,0,0.3);"><span style="font-size:14px;">🕌</span></div>', iconSize: [28,28], iconAnchor: [14,14], popupAnchor: [0,-14] });
}
function createGerejaIcon() {
return L.divIcon({ className: 'gereja-icon', html: '<div style="background:#9b59b6; width:28px; height:28px; border-radius:50%; display:flex; align-items:center; justify-content:center; border:2px solid white; box-shadow:0 2px 5px rgba(0,0,0,0.3);"><span style="font-size:14px;">⛪</span></div>', iconSize: [28,28], iconAnchor: [14,14], popupAnchor: [0,-14] });
}
function createPovertyIcon(isInside) {
var color = isInside ? '#27ae60' : '#e74c3c';
var iconChar = isInside ? '✓' : '⚠️';
return L.divIcon({ className: 'poverty-icon', html: '<div style="background:'+color+'; width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; border:2px solid white; box-shadow:0 2px 5px rgba(0,0,0,0.3);"><span style="font-size:11px; color:white; font-weight:bold;">'+iconChar+'</span></div>', iconSize: [22,22], iconAnchor: [11,11], popupAnchor: [0,-11] });
}
function escapeHtml(str) { if (!str) return ''; return str.replace(/['"\\]/g, '\\$&').replace(/\n/g, '\\n'); }
// ==================== PREVIEW FOTO ====================
function previewFoto(input) {
var previewDiv = document.getElementById('previewFotoPoverty');
var previewImg = document.getElementById('previewImgPoverty');
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) { previewImg.src = e.target.result; previewDiv.style.display = 'block'; }
reader.readAsDataURL(input.files[0]);
} else { previewDiv.style.display = 'none'; previewImg.src = ''; }
}
function previewEditFoto(input) {
var previewDiv = document.getElementById('editPreviewFoto');
var previewImg = document.getElementById('editPreviewImg');
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) { previewImg.src = e.target.result; previewDiv.style.display = 'block'; }
reader.readAsDataURL(input.files[0]);
} else if (document.getElementById('eFotoLama').value) { previewImg.src = document.getElementById('eFotoLama').value; previewDiv.style.display = 'block'; }
else { previewDiv.style.display = 'none'; previewImg.src = ''; }
}
// ==================== LOAD DATA ====================
function loadSemua() { loadMasjid(); loadGereja(); loadPoverty(); loadBantuan(); loadGrafik(); }
function loadMasjid() {
fetch('api.php?action=get_masjid').then(r=>r.json()).then(data=>{
masjidLayer.clearLayers(); masjidPoints = Array.isArray(data)?data:[];
masjidPoints.forEach(item=>{
var marker = L.marker([parseFloat(item.lat), parseFloat(item.lng)], {icon: createMasjidIcon()});
marker.data = item; marker.on('click', e=>showMasjidPopup(e.target.data));
marker.addTo(masjidLayer);
});
updateStats();
}).catch(()=>console.error('Gagal load masjid'));
}
function loadGereja() {
fetch('api.php?action=get_gereja').then(r=>r.json()).then(data=>{
gerejaLayer.clearLayers(); gerejaPoints = Array.isArray(data)?data:[];
gerejaPoints.forEach(item=>{
var marker = L.marker([parseFloat(item.lat), parseFloat(item.lng)], {icon: createGerejaIcon()});
marker.data = item; marker.on('click', e=>showGerejaPopup(e.target.data));
marker.addTo(gerejaLayer);
});
updateStats();
}).catch(()=>console.error('Gagal load gereja'));
}
function loadPoverty() {
fetch('api.php?action=get_poverty').then(r=>r.json()).then(data=>{
povertyPoints = Array.isArray(data)?data:[];
document.getElementById('miskin').innerHTML = povertyPoints.length;
updateStats();
}).catch(()=>console.error('Gagal load poverty'));
}
function loadBantuan() {
fetch('api.php?action=get_bantuan').then(r=>r.json()).then(data=>{
var tabel = document.getElementById('tabelBantuan');
while(tabel.rows.length>1) tabel.deleteRow(1);
if(Array.isArray(data)) data.forEach(b=>{ var row=tabel.insertRow(); row.insertCell(0).innerHTML=b.tanggal_bantuan||'-'; row.insertCell(1).innerHTML=b.nama_penduduk||'-'; row.insertCell(2).innerHTML=b.jenis_bantuan||'-'; });
}).catch(()=>{});
}
var grafikChart=null;
function loadGrafik() {
fetch('api.php?action=get_stats_bantuan').then(r=>r.json()).then(data=>{
var labels=['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agt','Sep','Okt','Nov','Des'], values=new Array(12).fill(0);
if(Array.isArray(data)) data.forEach(d=>{ var idx=labels.indexOf(d.bulan); if(idx>=0) values[idx]=parseInt(d.jumlah); });
var ctx=document.getElementById('grafik'); if(grafikChart) grafikChart.destroy();
grafikChart=new Chart(ctx,{type:'bar',data:{labels:labels,datasets:[{label:'Jumlah Bantuan',data:values,backgroundColor:'#3498db'}]},options:{responsive:true,plugins:{legend:{display:false}},scales:{y:{beginAtZero:true,ticks:{stepSize:1}}}}});
}).catch(()=>{ var ctx=document.getElementById('grafik'); if(grafikChart) grafikChart.destroy(); grafikChart=new Chart(ctx,{type:'bar',data:{labels:['Jan','Feb','Mar','Apr','Mei'],datasets:[{label:'Jumlah Bantuan',data:[0,0,0,0,0],backgroundColor:'#3498db'}]},options:{responsive:true,plugins:{legend:{display:false}}}}); });
}
// ==================== POPUP ====================
function showMasjidPopup(data){
var content='<div style="min-width:250px;"><h4 style="margin:0 0 8px 0;color:#2c3e50;">🕌 MASJID</h4><div><strong>Nama:</strong> '+(data.nama||'-')+'</div><div><strong>Alamat:</strong> '+(data.alamat||'-')+'</div><div><strong>Koordinat:</strong> '+data.lat+', '+data.lng+'</div><div class="clearfix"><button class="btn-edit" onclick="editMasjid('+data.id+',\''+escapeHtml(data.nama)+'\',\''+escapeHtml(data.alamat)+'\','+data.lat+','+data.lng+')">✏️ Edit</button><button class="btn-hapus" onclick="hapusMasjid('+data.id+')">🗑️ Hapus</button></div></div>';
L.popup().setLatLng([data.lat,data.lng]).setContent(content).openOn(map);
}
function showGerejaPopup(data){
var content='<div style="min-width:250px;"><h4 style="margin:0 0 8px 0;color:#2c3e50;">⛪ GEREJA</h4><div><strong>Nama:</strong> '+(data.nama||'-')+'</div><div><strong>Alamat:</strong> '+(data.alamat||'-')+'</div><div><strong>Koordinat:</strong> '+data.lat+', '+data.lng+'</div><div class="clearfix"><button class="btn-edit" onclick="editGereja('+data.id+',\''+escapeHtml(data.nama)+'\',\''+escapeHtml(data.alamat)+'\','+data.lat+','+data.lng+')">✏️ Edit</button><button class="btn-hapus" onclick="hapusGereja('+data.id+')">🗑️ Hapus</button></div></div>';
L.popup().setLatLng([data.lat,data.lng]).setContent(content).openOn(map);
}
function editMasjid(id,namaLama,alamatLama,lat,lng){
map.closePopup();
var form='<div class="popup-form"><h4>✏️ Edit Masjid</h4><input type="text" id="editNama" value="'+escapeHtml(namaLama)+'" placeholder="Nama Masjid"><input type="text" id="editAlamat" value="'+escapeHtml(alamatLama)+'" placeholder="Alamat"><button onclick="simpanEditMasjid('+id+','+lat+','+lng+')" style="width:100%;padding:8px;background:#27ae60;color:white;border:none;border-radius:4px;cursor:pointer;">💾 Simpan Perubahan</button></div>';
L.popup().setLatLng([lat,lng]).setContent(form).openOn(map);
}
function simpanEditMasjid(id,lat,lng){
var namaBaru=document.getElementById('editNama').value.trim(), alamatBaru=document.getElementById('editAlamat').value.trim();
if(!namaBaru){ alert('Nama masjid wajib diisi!'); return; }
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'update_masjid',id:id,nama:namaBaru,alamat:alamatBaru,lat:lat,lng:lng})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Masjid berhasil diupdate!'); map.closePopup(); loadMasjid(); loadGereja(); } else { alert('❌ Gagal: '+(res.msg||'')); } });
}
function editGereja(id,namaLama,alamatLama,lat,lng){
map.closePopup();
var form='<div class="popup-form"><h4>✏️ Edit Gereja</h4><input type="text" id="editNama" value="'+escapeHtml(namaLama)+'" placeholder="Nama Gereja"><input type="text" id="editAlamat" value="'+escapeHtml(alamatLama)+'" placeholder="Alamat"><button onclick="simpanEditGereja('+id+','+lat+','+lng+')" style="width:100%;padding:8px;background:#9b59b6;color:white;border:none;border-radius:4px;cursor:pointer;">💾 Simpan Perubahan</button></div>';
L.popup().setLatLng([lat,lng]).setContent(form).openOn(map);
}
function simpanEditGereja(id,lat,lng){
var namaBaru=document.getElementById('editNama').value.trim(), alamatBaru=document.getElementById('editAlamat').value.trim();
if(!namaBaru){ alert('Nama gereja wajib diisi!'); return; }
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'update_gereja',id:id,nama:namaBaru,alamat:alamatBaru,lat:lat,lng:lng})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Gereja berhasil diupdate!'); map.closePopup(); loadMasjid(); loadGereja(); } else { alert('❌ Gagal: '+(res.msg||'')); } });
}
function hapusMasjid(id){ if(confirm('Yakin ingin menghapus masjid ini?')){ fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'delete_masjid',id:id})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Masjid dihapus!'); map.closePopup(); loadMasjid(); loadGereja(); } }); } }
function hapusGereja(id){ if(confirm('Yakin ingin menghapus gereja ini?')){ fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'delete_gereja',id:id})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Gereja dihapus!'); map.closePopup(); loadMasjid(); loadGereja(); } }); } }
var _hapusPovertyId=0;
function hapusPoverty(id){ _hapusPovertyId=id; var p=povertyPoints.find(x=>x.id==id); var nama=p?p.nama:'data ini'; document.getElementById('modalHapusNama').innerHTML='Data "'+nama+'" akan dihapus permanen.'; map.closePopup(); document.getElementById('modalHapusPoverty').style.display='flex'; }
function konfirmasiHapusPoverty(){ document.getElementById('modalHapusPoverty').style.display='none'; fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'delete_poverty',id:_hapusPovertyId})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Data berhasil dihapus!'); loadPoverty(); } else { alert('❌ Gagal: '+(res.msg||'')); } }).catch(err=>{ alert('❌ Error: '+err.message); }); }
// ==================== STATISTIK & RADIUS ====================
function updateStats(){
var allIbadah=masjidPoints.length+gerejaPoints.length;
document.getElementById('ibadah').innerHTML=allIbadah;
document.getElementById('miskin').innerHTML=povertyPoints.length;
radiusLayer.clearLayers();
var allIbadahPoints=masjidPoints.concat(gerejaPoints);
allIbadahPoints.forEach(item=>{ L.circle([parseFloat(item.lat),parseFloat(item.lng)],{radius:currentRadius,color:'#3498db',fillColor:'#3498db',fillOpacity:0.1,weight:1.5}).addTo(radiusLayer); });
povertyLayer.clearLayers();
var dalam=0,luar=0;
povertyPoints.forEach(p=>{
var isInside=false, pLat=parseFloat(p.lat), pLng=parseFloat(p.lng);
allIbadahPoints.forEach(item=>{ if(map.distance([parseFloat(item.lat),parseFloat(item.lng)],[pLat,pLng])<=currentRadius) isInside=true; });
if(isInside) dalam++; else luar++;
var fotoHtml=(p.foto && p.foto!='')?'<img src="'+p.foto+'" style="max-width:100%; max-height:100px; border-radius:8px; margin:5px 0;"><br>':'';
L.marker([pLat,pLng],{icon:createPovertyIcon(isInside)}).bindPopup('<b>👤 '+(p.nama||'-')+'</b><br>'+fotoHtml+'NIK: '+(p.nik||'-')+'<br>Pekerjaan: '+(p.pekerjaan||'-')+'<br>Penghasilan: Rp '+(parseInt(p.penghasilan||0)).toLocaleString('id-ID')+'<br>Anggota: '+(p.jumlah_anggota_keluarga||'1')+'<br>Kategori: '+(p.kategori_kemiskinan||'-')+'<br>Status Bantuan: '+(p.status_bantuan||'-')+'<br>Status: '+(isInside?'✅ Dalam Radius':'❌ Luar Radius')+'<br><div style="margin-top:8px;"><button onclick="bukaBeriBantuan('+p.id+',\''+(p.nama||'')+'\')" style="width:100%;padding:6px;background:#27ae60;color:white;border:none;border-radius:4px;cursor:pointer;margin-bottom:4px;font-size:11px;">🎁 Beri Bantuan</button></div><div class="clearfix"><button class="btn-edit" onclick="bukaEditPoverty('+p.id+')">✏️ Edit</button><button class="btn-hapus" onclick="hapusPoverty('+p.id+')">🗑️ Hapus</button></div>').addTo(povertyLayer);
});
document.getElementById('dalam').innerHTML=dalam;
document.getElementById('luar').innerHTML=luar;
}
document.getElementById('radiusSlider').addEventListener('input',function(){ document.getElementById('radiusValue').innerHTML=this.value; });
function terapkanRadius(){ currentRadius=parseInt(document.getElementById('radiusSlider').value); updateStats(); }
function simpanBantuan(){
var jenis=document.getElementById('bJenis').value, jumlah=document.getElementById('bJumlah').value.trim(), tanggal=document.getElementById('bTanggal').value, ket=document.getElementById('bKeterangan').value.trim();
if(!jumlah||!tanggal){ alert('Jumlah dan tanggal wajib diisi!'); return; }
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'add_bantuan',jenis_bantuan:jenis,jumlah:jumlah,tanggal:tanggal,keterangan:ket})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Bantuan berhasil disimpan!'); document.getElementById('bJumlah').value=''; document.getElementById('bTanggal').value=''; document.getElementById('bKeterangan').value=''; loadBantuan(); loadGrafik(); } else { alert('❌ Gagal: '+(res.msg||'')); } });
}
// ==================== KLIK PETA ====================
map.on('click',function(e){ var lat=e.latlng.lat, lng=e.latlng.lng; var form='<div class="popup-form"><b>📍 Tambah Data</b><br><br><button onclick="bukaFormMasjid('+lat+','+lng+')" style="width:100%;padding:7px;margin-bottom:5px;background:#27ae60;color:white;border:none;border-radius:4px;cursor:pointer;">🕌 Tambah Masjid</button><button onclick="bukaFormGereja('+lat+','+lng+')" style="width:100%;padding:7px;margin-bottom:5px;background:#9b59b6;color:white;border:none;border-radius:4px;cursor:pointer;">⛪ Tambah Gereja</button><button onclick="bukaFormPoverty('+lat+','+lng+')" style="width:100%;padding:7px;background:#e74c3c;color:white;border:none;border-radius:4px;cursor:pointer;">⚠️ Tambah Penduduk Miskin</button></div>'; L.popup().setLatLng([lat,lng]).setContent(form).openOn(map); });
function bukaFormMasjid(lat,lng){ map.closePopup(); var form='<div class="popup-form"><b>🕌 Input Masjid</b><br><br><input type="text" id="fNamaMasjid" placeholder="Nama Masjid *"><input type="text" id="fAlamatMasjid" placeholder="Alamat"><button onclick="simpanMasjid('+lat+','+lng+')" style="width:100%;padding:7px;background:#27ae60;color:white;border:none;border-radius:4px;cursor:pointer;">💾 Simpan</button></div>'; L.popup().setLatLng([lat,lng]).setContent(form).openOn(map); }
function bukaFormGereja(lat,lng){ map.closePopup(); var form='<div class="popup-form"><b>⛪ Input Gereja</b><br><br><input type="text" id="fNamaGereja" placeholder="Nama Gereja *"><input type="text" id="fAlamatGereja" placeholder="Alamat"><button onclick="simpanGereja('+lat+','+lng+')" style="width:100%;padding:7px;background:#9b59b6;color:white;border:none;border-radius:4px;cursor:pointer;">💾 Simpan</button></div>'; L.popup().setLatLng([lat,lng]).setContent(form).openOn(map); }
var _povertyLat=0,_povertyLng=0;
function bukaFormPoverty(lat,lng){ map.closePopup(); _povertyLat=lat; _povertyLng=lng; document.getElementById('fNamaPoverty').value=''; document.getElementById('fNikPoverty').value=''; document.getElementById('fAlamatPoverty').value=''; document.getElementById('fPekerjaanPoverty').value=''; document.getElementById('fPenghasilanPoverty').value=''; document.getElementById('fAnggotaPoverty').value='1'; document.getElementById('fTanggunganPoverty').value='0'; document.getElementById('fKategoriPoverty').value='Miskin'; document.getElementById('fStatusBantuanPoverty').value='Belum'; document.getElementById('fKetPoverty').value=''; document.getElementById('fFotoPoverty').value=''; document.getElementById('previewFotoPoverty').style.display='none'; document.getElementById('modalKoordinat').innerHTML='📍 Koordinat: '+lat.toFixed(6)+', '+lng.toFixed(6); document.getElementById('modalPoverty').style.display='flex'; }
function tutupModalPoverty(){ document.getElementById('modalPoverty').style.display='none'; }
function bukaEditPoverty(id){
map.closePopup();
var p=povertyPoints.find(x=>x.id==id);
if(!p){ alert('Data tidak ditemukan!'); return; }
document.getElementById('eIdPoverty').value=p.id;
document.getElementById('eNamaPoverty').value=p.nama||'';
document.getElementById('eNikPoverty').value=p.nik||'';
document.getElementById('eAlamatPoverty').value=p.alamat||'';
document.getElementById('ePekerjaanPoverty').value=p.pekerjaan||'';
document.getElementById('ePenghasilanPoverty').value=p.penghasilan||0;
document.getElementById('eAnggotaPoverty').value=p.jumlah_anggota_keluarga||1;
document.getElementById('eTanggunganPoverty').value=p.jumlah_tanggungan||0;
document.getElementById('eKategoriPoverty').value=p.kategori_kemiskinan||'Miskin';
document.getElementById('eStatusBantuanPoverty').value=p.status_bantuan||'Belum';
document.getElementById('eKetPoverty').value=p.keterangan||'';
var previewDiv=document.getElementById('editPreviewFoto'), previewImg=document.getElementById('editPreviewImg');
if(p.foto && p.foto!=''){ previewImg.src=p.foto; previewDiv.style.display='block'; document.getElementById('eFotoLama').value=p.foto; }
else{ previewDiv.style.display='none'; document.getElementById('eFotoLama').value=''; }
document.getElementById('eFotoPoverty').value='';
document.getElementById('modalEditPoverty').style.display='flex';
}
function tutupModalEditPoverty(){ document.getElementById('modalEditPoverty').style.display='none'; }
function bukaBeriBantuan(id,nama){ map.closePopup(); document.getElementById('beriPendudukId').value=id; document.getElementById('beriNamaPenduduk').innerHTML='Penerima: '+nama; document.getElementById('beriJenis').value='Sembako'; document.getElementById('beriJumlah').value=''; document.getElementById('beriTanggal').value=new Date().toISOString().split('T')[0]; document.getElementById('beriKeterangan').value=''; document.getElementById('modalBeriBantuan').style.display='flex'; }
function simpanBeriBantuan(){
var penduduk_id=parseInt(document.getElementById('beriPendudukId').value), jenis=document.getElementById('beriJenis').value, jumlah=document.getElementById('beriJumlah').value.trim(), tanggal=document.getElementById('beriTanggal').value, ket=document.getElementById('beriKeterangan').value.trim();
if(!jumlah||!tanggal){ alert('Jumlah dan tanggal wajib diisi!'); return; }
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'add_bantuan',id_penduduk:penduduk_id,jenis_bantuan:jenis,jumlah:jumlah,tanggal:tanggal,keterangan:ket})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ return fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'update_poverty',id:penduduk_id,status_bantuan:'Pernah'})}).then(r=>r.json()); } else { throw new Error(res.msg||'Gagal simpan bantuan'); } }).then(res2=>{ document.getElementById('modalBeriBantuan').style.display='none'; alert('✅ Bantuan berhasil dicatat!'); loadPoverty(); loadBantuan(); loadGrafik(); }).catch(err=>{ alert('❌ Error: '+err.message); });
}
// ==================== SIMPAN POVERTY DENGAN FOTO (YANG DIPERBAIKI) ====================
function simpanPoverty(){
var nama=document.getElementById('fNamaPoverty').value.trim();
var nik=document.getElementById('fNikPoverty').value.trim();
var alamat=document.getElementById('fAlamatPoverty').value.trim();
var pekerjaan=document.getElementById('fPekerjaanPoverty').value.trim();
var penghasilan=parseInt(document.getElementById('fPenghasilanPoverty').value)||0;
var anggota=parseInt(document.getElementById('fAnggotaPoverty').value)||1;
var tanggungan=parseInt(document.getElementById('fTanggunganPoverty').value)||0;
var kategori=document.getElementById('fKategoriPoverty').value;
var status_bantuan=document.getElementById('fStatusBantuanPoverty').value;
var ket=document.getElementById('fKetPoverty').value.trim();
var lat=_povertyLat;
var lng=_povertyLng;
var fotoFile=document.getElementById('fFotoPoverty').files[0];
if(!nama){ alert('Nama wajib diisi!'); return; }
if(fotoFile){
var formData=new FormData();
formData.append('action','upload_foto');
formData.append('foto',fotoFile);
fetch('api.php',{method:'POST',body:formData})
.then(r=>r.json())
.then(uploadRes=>{
if(uploadRes.status==='ok'){
simpanPovertyData(uploadRes.foto_url,nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket,lat,lng);
} else { alert('Gagal upload foto: '+uploadRes.msg); }
}).catch(err=>{ alert('Error upload: '+err.message); });
} else {
simpanPovertyData('',nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket,lat,lng);
}
}
function simpanPovertyData(fotoUrl,nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket,lat,lng){
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'add_poverty',nama:nama,nik:nik,alamat:alamat,pekerjaan:pekerjaan,penghasilan:penghasilan,anggota:anggota,tanggungan:tanggungan,kategori:kategori,status_bantuan:status_bantuan,keterangan:ket,foto:fotoUrl,lat:lat,lng:lng})})
.then(r=>r.json())
.then(res=>{
if(res.status==='ok'){
alert('✅ Data penduduk miskin berhasil ditambahkan!');
tutupModalPoverty();
document.getElementById('fFotoPoverty').value='';
document.getElementById('previewFotoPoverty').style.display='none';
loadPoverty();
} else { alert('❌ Gagal: '+(res.msg||'')); }
}).catch(err=>{ alert('❌ Error: '+err.message); });
}
function simpanEditPoverty(){
var id=parseInt(document.getElementById('eIdPoverty').value);
var nama=document.getElementById('eNamaPoverty').value.trim();
var nik=document.getElementById('eNikPoverty').value.trim();
var alamat=document.getElementById('eAlamatPoverty').value.trim();
var pekerjaan=document.getElementById('ePekerjaanPoverty').value.trim();
var penghasilan=parseInt(document.getElementById('ePenghasilanPoverty').value)||0;
var anggota=parseInt(document.getElementById('eAnggotaPoverty').value)||1;
var tanggungan=parseInt(document.getElementById('eTanggunganPoverty').value)||0;
var kategori=document.getElementById('eKategoriPoverty').value;
var status_bantuan=document.getElementById('eStatusBantuanPoverty').value;
var ket=document.getElementById('eKetPoverty').value.trim();
var fotoLama=document.getElementById('eFotoLama').value;
var fotoFile=document.getElementById('eFotoPoverty').files[0];
if(!nama){ alert('Nama wajib diisi!'); return; }
if(fotoFile){
var formData=new FormData();
formData.append('action','upload_foto');
formData.append('foto',fotoFile);
fetch('api.php',{method:'POST',body:formData})
.then(r=>r.json())
.then(uploadRes=>{
if(uploadRes.status==='ok'){ updatePovertyData(id,uploadRes.foto_url,nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket); }
else{ alert('Gagal upload foto: '+uploadRes.msg); }
}).catch(err=>{ alert('Error upload: '+err.message); });
} else { updatePovertyData(id,fotoLama,nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket); }
}
function updatePovertyData(id,fotoUrl,nama,nik,alamat,pekerjaan,penghasilan,anggota,tanggungan,kategori,status_bantuan,ket){
fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'update_poverty',id:id,nama:nama,nik:nik,alamat:alamat,pekerjaan:pekerjaan,penghasilan:penghasilan,anggota:anggota,tanggungan:tanggungan,kategori:kategori,status_bantuan:status_bantuan,keterangan:ket,foto:fotoUrl})})
.then(r=>r.json())
.then(res=>{
if(res.status==='ok'){ alert('✅ Data berhasil diupdate!'); tutupModalEditPoverty(); loadPoverty(); }
else{ alert('❌ Gagal: '+(res.msg||'')); }
}).catch(err=>{ alert('❌ Error: '+err.message); });
}
function simpanMasjid(lat,lng){ var nama=document.getElementById('fNamaMasjid').value.trim(), alamat=document.getElementById('fAlamatMasjid').value.trim(); if(!nama){ alert('Nama masjid wajib diisi!'); return; } fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'add_masjid',nama:nama,alamat:alamat,lat:lat,lng:lng})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Masjid berhasil ditambahkan!'); map.closePopup(); loadMasjid(); } else { alert('❌ Gagal: '+(res.msg||'')); } }); }
function simpanGereja(lat,lng){ var nama=document.getElementById('fNamaGereja').value.trim(), alamat=document.getElementById('fAlamatGereja').value.trim(); if(!nama){ alert('Nama gereja wajib diisi!'); return; } fetch('api.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'add_gereja',nama:nama,alamat:alamat,lat:lat,lng:lng})}).then(r=>r.json()).then(res=>{ if(res.status==='ok'){ alert('✅ Gereja berhasil ditambahkan!'); map.closePopup(); loadGereja(); } else { alert('❌ Gagal: '+(res.msg||'')); } }); }
// ==================== LOAD KECAMATAN & START ====================
fetch('Admin_Kecamatan.json').then(r=>r.json()).then(data=>{ L.geoJSON(data,{style:{color:'#2c3e50',weight:2,fillOpacity:0.04,dashArray:'5,5'}}).addTo(map); }).catch(err=>console.warn('GeoJSON tidak ditemukan:',err));
loadSemua();
</script>
</body>
</html>
+465
View File
@@ -0,0 +1,465 @@
<?php
ob_start();
error_reporting(0);
ini_set('display_errors', 0);
ini_set('log_errors', 0);
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
include 'koneksi.php';
ob_clean();
function sendJson($data) {
ob_end_clean();
echo json_encode($data);
exit;
}
if ($conn->connect_error) {
sendJson(['status' => 'error', 'msg' => 'Database connection failed: ' . $conn->connect_error]);
}
$action = $_GET['action'] ?? $_POST['action'] ?? '';
$input = [];
$rawBody = file_get_contents('php://input');
if (!empty($rawBody)) {
$decoded = json_decode($rawBody, true);
if ($decoded) {
$input = $decoded;
if (isset($input['action'])) {
$action = $input['action'];
}
}
}
// ==================== UPLOAD FOTO ====================
if ($action == 'upload_foto') {
// Pakai path absolut supaya tidak bergantung letak file yang memanggil
$base_dir = dirname(__FILE__);
$target_dir = $base_dir . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
$web_path = 'uploads/'; // path relatif untuk disimpan ke DB & ditampilkan di browser
if (!file_exists($target_dir)) {
if (!mkdir($target_dir, 0777, true)) {
sendJson(['status' => 'error', 'msg' => 'Gagal membuat folder uploads. Cek permission direktori parent.']);
}
}
if (!is_writable($target_dir)) {
sendJson(['status' => 'error', 'msg' => 'Folder uploads tidak bisa ditulis. Jalankan di server: chmod 777 uploads/']);
}
if (!isset($_FILES['foto']) || $_FILES['foto']['error'] === UPLOAD_ERR_NO_FILE) {
sendJson(['status' => 'error', 'msg' => 'Tidak ada file yang diupload']);
exit;
}
if ($_FILES['foto']['error'] !== UPLOAD_ERR_OK) {
$err_map = [
UPLOAD_ERR_INI_SIZE => 'Ukuran file melebihi batas server (upload_max_filesize)',
UPLOAD_ERR_FORM_SIZE => 'Ukuran file melebihi MAX_FILE_SIZE',
UPLOAD_ERR_PARTIAL => 'File hanya terupload sebagian, coba lagi',
UPLOAD_ERR_NO_TMP_DIR=> 'Folder temporary tidak ditemukan',
UPLOAD_ERR_CANT_WRITE=> 'Gagal menulis file ke disk, cek permission',
UPLOAD_ERR_EXTENSION => 'Upload dihentikan oleh ekstensi PHP',
];
$msg = $err_map[$_FILES['foto']['error']] ?? 'Error upload: kode ' . $_FILES['foto']['error'];
sendJson(['status' => 'error', 'msg' => $msg]);
exit;
}
if ($_FILES['foto']['size'] <= 0) {
sendJson(['status' => 'error', 'msg' => 'File kosong (ukuran 0 byte)']);
exit;
}
// Maksimal 5MB
if ($_FILES['foto']['size'] > 5000000) {
sendJson(['status' => 'error', 'msg' => 'Ukuran file maksimal 5MB']);
exit;
}
// Validasi benar-benar gambar
$check = @getimagesize($_FILES['foto']['tmp_name']);
if ($check === false) {
sendJson(['status' => 'error', 'msg' => 'File bukan gambar yang valid']);
exit;
}
// Ekstensi yang diizinkan
$ext = strtolower(pathinfo($_FILES['foto']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
sendJson(['status' => 'error', 'msg' => 'Format tidak didukung. Gunakan JPG, PNG, GIF, atau WEBP']);
exit;
}
$clean_name = preg_replace('/[^a-zA-Z0-9._-]/', '_', basename($_FILES['foto']['name']));
$file_name = time() . '_' . $clean_name;
$target_file = $target_dir . $file_name;
$foto_url = $web_path . $file_name; // simpan path relatif ke DB
if (move_uploaded_file($_FILES['foto']['tmp_name'], $target_file)) {
sendJson(['status' => 'ok', 'foto_url' => $foto_url]);
} else {
// Cek permission folder untuk bantu debug
$perm = substr(sprintf('%o', fileperms($target_dir)), -4);
sendJson([
'status' => 'error',
'msg' => 'Gagal menyimpan file. Permission folder uploads: ' . $perm . '. Pastikan folder bisa ditulis (chmod 777).',
]);
}
exit;
}
// ==================== GET DATA ====================
if ($action == 'get_masjid') {
$data = [];
$query = mysqli_query($conn, "SELECT id, nama_rumah_ibadah as nama, alamat, lat, lng FROM rumah_ibadah WHERE jenis_rumah_ibadah = 'masjid' OR jenis_rumah_ibadah IS NULL OR jenis_rumah_ibadah = ''");
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
sendJson($data);
exit;
}
if ($action == 'get_gereja') {
$data = [];
$query = mysqli_query($conn, "SELECT id, nama_rumah_ibadah as nama, alamat, lat, lng FROM rumah_ibadah WHERE jenis_rumah_ibadah = 'gereja'");
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
sendJson($data);
exit;
}
if ($action == 'get_poverty') {
$data = [];
$query = mysqli_query($conn, "SELECT * FROM penduduk_miskin ORDER BY id DESC");
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
sendJson($data);
exit;
}
if ($action == 'get_bantuan') {
$data = [];
$query = mysqli_query($conn, "SELECT b.*, p.nama as nama_penduduk FROM bantuan b LEFT JOIN penduduk_miskin p ON b.penduduk_id = p.id ORDER BY b.tanggal_bantuan DESC LIMIT 50");
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
sendJson($data);
exit;
}
if ($action == 'get_stats_bantuan') {
$data = [];
$query = mysqli_query($conn, "SELECT DATE_FORMAT(tanggal_bantuan, '%b') as bulan, COUNT(*) as jumlah FROM bantuan GROUP BY MONTH(tanggal_bantuan) ORDER BY MONTH(tanggal_bantuan)");
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
sendJson($data);
exit;
}
// ==================== CREATE (TAMBAH DATA) ====================
if ($action == 'add_masjid') {
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$lat = isset($input['lat']) ? floatval($input['lat']) : 0;
$lng = isset($input['lng']) ? floatval($input['lng']) : 0;
if (empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Nama wajib diisi']);
exit;
}
$nama = mysqli_real_escape_string($conn, $nama);
$alamat = mysqli_real_escape_string($conn, $alamat);
$sql = "INSERT INTO rumah_ibadah (nama_rumah_ibadah, alamat, lat, lng, jenis_rumah_ibadah) VALUES ('$nama', '$alamat', $lat, $lng, 'masjid')";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok', 'id' => mysqli_insert_id($conn)]);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'add_gereja') {
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$lat = isset($input['lat']) ? floatval($input['lat']) : 0;
$lng = isset($input['lng']) ? floatval($input['lng']) : 0;
if (empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Nama wajib diisi']);
exit;
}
$nama = mysqli_real_escape_string($conn, $nama);
$alamat = mysqli_real_escape_string($conn, $alamat);
$sql = "INSERT INTO rumah_ibadah (nama_rumah_ibadah, alamat, lat, lng, jenis_rumah_ibadah) VALUES ('$nama', '$alamat', $lat, $lng, 'gereja')";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok', 'id' => mysqli_insert_id($conn)]);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'add_poverty') {
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$nik = isset($input['nik']) ? trim($input['nik']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$pekerjaan = isset($input['pekerjaan']) ? trim($input['pekerjaan']) : '';
$penghasilan = isset($input['penghasilan']) ? intval($input['penghasilan']) : 0;
$anggota = isset($input['anggota']) ? intval($input['anggota']) : 1;
$tanggungan = isset($input['tanggungan']) ? intval($input['tanggungan']) : 0;
$kategori = isset($input['kategori']) ? trim($input['kategori']) : 'Miskin';
$status_bantuan = isset($input['status_bantuan']) ? trim($input['status_bantuan']) : 'Belum';
$keterangan = isset($input['keterangan']) ? trim($input['keterangan']) : '';
$lat = isset($input['lat']) ? floatval($input['lat']) : 0;
$lng = isset($input['lng']) ? floatval($input['lng']) : 0;
$foto = isset($input['foto']) ? trim($input['foto']) : '';
if (empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Nama wajib diisi']);
exit;
}
$nama = mysqli_real_escape_string($conn, $nama);
$nik = mysqli_real_escape_string($conn, $nik);
$alamat = mysqli_real_escape_string($conn, $alamat);
$pekerjaan = mysqli_real_escape_string($conn, $pekerjaan);
$kategori = mysqli_real_escape_string($conn, $kategori);
$status_bantuan = mysqli_real_escape_string($conn, $status_bantuan);
$keterangan = mysqli_real_escape_string($conn, $keterangan);
$foto = mysqli_real_escape_string($conn, $foto);
$sql = "INSERT INTO penduduk_miskin (nama, nik, alamat, pekerjaan, penghasilan, jumlah_anggota_keluarga, jumlah_tanggungan, kategori_kemiskinan, status_bantuan, keterangan, foto, lat, lng)
VALUES ('$nama', '$nik', '$alamat', '$pekerjaan', $penghasilan, $anggota, $tanggungan, '$kategori', '$status_bantuan', '$keterangan', '$foto', $lat, $lng)";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok', 'id' => mysqli_insert_id($conn)]);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'add_bantuan') {
$jenis = isset($input['jenis_bantuan']) ? trim($input['jenis_bantuan']) : '';
$jumlah = isset($input['jumlah']) ? trim($input['jumlah']) : '';
$tanggal = isset($input['tanggal']) ? trim($input['tanggal']) : date('Y-m-d');
$keterangan = isset($input['keterangan']) ? trim($input['keterangan']) : '';
$penduduk_id = isset($input['id_penduduk']) ? intval($input['id_penduduk']) : 0;
$jenis = mysqli_real_escape_string($conn, $jenis);
$jumlah = mysqli_real_escape_string($conn, $jumlah);
$tanggal = mysqli_real_escape_string($conn, $tanggal);
$keterangan = mysqli_real_escape_string($conn, $keterangan);
$sql = "INSERT INTO bantuan (penduduk_id, jenis_bantuan, jumlah_bantuan, tanggal_bantuan, keterangan)
VALUES ($penduduk_id, '$jenis', '$jumlah', '$tanggal', '$keterangan')";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
// ==================== UPDATE (EDIT DATA) ====================
if ($action == 'update_masjid') {
$id = isset($input['id']) ? intval($input['id']) : 0;
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$lat = isset($input['lat']) ? floatval($input['lat']) : 0;
$lng = isset($input['lng']) ? floatval($input['lng']) : 0;
if ($id == 0 || empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Data tidak lengkap']);
exit;
}
$nama = mysqli_real_escape_string($conn, $nama);
$alamat = mysqli_real_escape_string($conn, $alamat);
$sql = "UPDATE rumah_ibadah SET nama_rumah_ibadah = '$nama', alamat = '$alamat', lat = $lat, lng = $lng WHERE id = $id AND (jenis_rumah_ibadah = 'masjid' OR jenis_rumah_ibadah IS NULL)";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'update_gereja') {
$id = isset($input['id']) ? intval($input['id']) : 0;
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$lat = isset($input['lat']) ? floatval($input['lat']) : 0;
$lng = isset($input['lng']) ? floatval($input['lng']) : 0;
if ($id == 0 || empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Data tidak lengkap']);
exit;
}
$nama = mysqli_real_escape_string($conn, $nama);
$alamat = mysqli_real_escape_string($conn, $alamat);
$sql = "UPDATE rumah_ibadah SET nama_rumah_ibadah = '$nama', alamat = '$alamat', lat = $lat, lng = $lng WHERE id = $id AND jenis_rumah_ibadah = 'gereja'";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'update_poverty') {
$id = isset($input['id']) ? intval($input['id']) : 0;
if ($id == 0) {
sendJson(['status' => 'error', 'msg' => 'ID tidak valid']);
}
if (isset($input['status_bantuan']) && !isset($input['nama'])) {
$status_bantuan = mysqli_real_escape_string($conn, trim($input['status_bantuan']));
$sql = "UPDATE penduduk_miskin SET status_bantuan = '$status_bantuan' WHERE id = $id";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
$nama = isset($input['nama']) ? trim($input['nama']) : '';
$nik = isset($input['nik']) ? trim($input['nik']) : '';
$alamat = isset($input['alamat']) ? trim($input['alamat']) : '';
$pekerjaan = isset($input['pekerjaan']) ? trim($input['pekerjaan']) : '';
$penghasilan = isset($input['penghasilan']) ? intval($input['penghasilan']) : 0;
$anggota = isset($input['anggota']) ? intval($input['anggota']) : 1;
$tanggungan = isset($input['tanggungan']) ? intval($input['tanggungan']) : 0;
$kategori = isset($input['kategori']) ? trim($input['kategori']) : 'Miskin';
$status_bantuan = isset($input['status_bantuan']) ? trim($input['status_bantuan']) : 'Belum';
$keterangan = isset($input['keterangan']) ? trim($input['keterangan']) : '';
$foto = isset($input['foto']) ? trim($input['foto']) : '';
if (empty($nama)) {
sendJson(['status' => 'error', 'msg' => 'Nama wajib diisi']);
}
$nama = mysqli_real_escape_string($conn, $nama);
$nik = mysqli_real_escape_string($conn, $nik);
$alamat = mysqli_real_escape_string($conn, $alamat);
$pekerjaan = mysqli_real_escape_string($conn, $pekerjaan);
$kategori = mysqli_real_escape_string($conn, $kategori);
$status_bantuan = mysqli_real_escape_string($conn, $status_bantuan);
$keterangan = mysqli_real_escape_string($conn, $keterangan);
$foto = mysqli_real_escape_string($conn, $foto);
$sql = "UPDATE penduduk_miskin SET
nama = '$nama',
nik = '$nik',
alamat = '$alamat',
pekerjaan = '$pekerjaan',
penghasilan = $penghasilan,
jumlah_anggota_keluarga = $anggota,
jumlah_tanggungan = $tanggungan,
kategori_kemiskinan = '$kategori',
status_bantuan = '$status_bantuan',
keterangan = '$keterangan',
foto = '$foto'
WHERE id = $id";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
// ==================== DELETE (HAPUS DATA) ====================
if ($action == 'delete_masjid') {
$id = isset($input['id']) ? intval($input['id']) : 0;
if ($id == 0) {
sendJson(['status' => 'error', 'msg' => 'ID tidak valid']);
exit;
}
$sql = "DELETE FROM rumah_ibadah WHERE id = $id AND (jenis_rumah_ibadah = 'masjid' OR jenis_rumah_ibadah IS NULL)";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'delete_gereja') {
$id = isset($input['id']) ? intval($input['id']) : 0;
if ($id == 0) {
sendJson(['status' => 'error', 'msg' => 'ID tidak valid']);
exit;
}
$sql = "DELETE FROM rumah_ibadah WHERE id = $id AND jenis_rumah_ibadah = 'gereja'";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
if ($action == 'delete_poverty') {
$id = isset($input['id']) ? intval($input['id']) : 0;
if ($id == 0) {
sendJson(['status' => 'error', 'msg' => 'ID tidak valid']);
exit;
}
$query = mysqli_query($conn, "SELECT foto FROM penduduk_miskin WHERE id = $id");
if ($row = mysqli_fetch_assoc($query)) {
if (!empty($row['foto']) && file_exists($row['foto'])) {
unlink($row['foto']);
}
}
mysqli_query($conn, "UPDATE bantuan SET penduduk_id = NULL WHERE penduduk_id = $id");
$sql = "DELETE FROM penduduk_miskin WHERE id = $id";
if (mysqli_query($conn, $sql)) {
sendJson(['status' => 'ok']);
} else {
sendJson(['status' => 'error', 'msg' => mysqli_error($conn)]);
}
exit;
}
sendJson(['status' => 'error', 'msg' => 'Aksi tidak dikenal: ' . $action]);
+861
View File
@@ -0,0 +1,861 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>WebGIS Pontianak - Pemetaan Rumah Ibadah & Penduduk Miskin</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
#map { height: 100vh; width: 100%; }
/* LAYER CONTROL - POSISI OTOMATIS DI KANAN ATAS */
/* STATISTIK - DI BAWAH LAYER CONTROL */
.stats-panel {
position: absolute;
top: 110px;
right: 10px;
background: white;
padding: 8px 10px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
width: 190px;
font-size: 11px;
}
.stats-panel h4 {
margin: 0 0 5px 0;
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 3px;
font-size: 12px;
}
.stats-panel .count { font-size: 15px; font-weight: bold; }
.stats-panel .green { color: #27ae60; }
.stats-panel .purple { color: #9b59b6; }
.stats-panel .red { color: #e74c3c; }
.stats-panel hr { margin: 4px 0; }
/* RADIUS - PALING BAWAH KANAN */
.radius-control {
position: absolute;
bottom: 20px;
right: 20px;
background: white;
padding: 8px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 900;
width: 200px;
font-size: 11px;
}
.radius-control h4 {
margin: 0 0 6px 0;
color: #2c3e50;
font-size: 12px;
}
.radius-control input { width: 100%; padding: 4px; margin: 4px 0; }
.radius-control button {
width: 100%;
padding: 5px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 4px;
font-size: 11px;
}
.radius-value {
font-weight: bold;
color: #e67e22;
text-align: center;
margin-top: 4px;
font-size: 11px;
}
/* MAP SELECTOR - KIRI ATAS */
.map-selector {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 6px 10px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
font-size: 11px;
}
/* FILTER BUTTONS - KIRI BAWAH MAP SELECTOR */
.filter-buttons {
position: absolute;
top: 70px;
left: 20px;
background: white;
padding: 6px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.filter-buttons button {
padding: 4px 8px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 10px;
font-weight: bold;
}
.filter-btn-all { background: #3498db; color: white; }
.filter-btn-masjid { background: #27ae60; color: white; }
.filter-btn-gereja { background: #9b59b6; color: white; }
.filter-btn-poverty { background: #e74c3c; color: white; }
/* LEGEND - KIRI BAWAH */
.legend {
position: absolute;
bottom: 20px;
left: 20px;
background: white;
padding: 6px 8px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
font-size: 10px;
pointer-events: none;
}
.legend span {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 4px;
vertical-align: middle;
}
.legend .masjid-icon { background: #27ae60; border-radius: 50%; width: 8px; height: 8px; }
.legend .gereja-icon { background: #9b59b6; border-radius: 50%; width: 8px; height: 8px; }
.legend .poverty-icon { background: #e74c3c; border-radius: 50%; width: 8px; height: 8px; display: inline-block; }
.legend .radius-icon { background: #3498db; border-radius: 50%; width: 8px; height: 8px; }
/* INSTRUCTION - TENGAH BAWAH */
.instruction-box {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.7);
color: white;
padding: 4px 10px;
border-radius: 20px;
font-size: 10px;
z-index: 1000;
pointer-events: none;
white-space: nowrap;
}
/* TOAST */
.toast-notification {
position: fixed;
bottom: 30px;
right: 30px;
background: #2c3e50;
color: white;
padding: 6px 12px;
border-radius: 8px;
z-index: 2000;
animation: fadeOut 3s ease forwards;
font-size: 11px;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; visibility: hidden; }
}
/* POPUP */
.btn-hapus { background: #e74c3c !important; width: 100% !important; margin-top: 5px; padding: 4px !important; font-size: 10px !important; }
.btn-hapus:hover { background: #c0392b !important; }
.leaflet-popup-content { min-width: 240px; max-width: 260px; }
.popup-form { min-width: 230px; padding: 5px; }
.popup-form h4 { margin: 0 0 6px 0; color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 3px; font-size: 12px; }
.popup-form input, .popup-form textarea {
width: 100%;
margin-bottom: 6px;
padding: 4px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 11px;
}
.popup-form button {
width: 100%;
background: #27ae60;
color: white;
border: none;
padding: 5px;
cursor: pointer;
border-radius: 4px;
font-weight: bold;
font-size: 11px;
}
.info-card {
background: #f8f9fa;
border-radius: 5px;
padding: 5px;
margin-top: 4px;
border-left: 3px solid #27ae60;
font-size: 10px;
}
.info-card.masjid { border-left-color: #27ae60; }
.info-card.gereja { border-left-color: #9b59b6; }
.info-card.poverty { border-left-color: #e74c3c; }
.info-card .address { font-size: 9px; color: #666; margin-top: 3px; word-break: break-all; }
.masjid-icon-leaflet, .gereja-icon-leaflet, .poverty-icon-leaflet {
background: none;
border: none;
}
.leaflet-control-layers {
margin-top: 10px !important;
margin-right: 10px !important;
}
</style>
</head>
<body>
<div id="map"></div>
<!-- TOMBOL PETUNJUK -->
<div id="btnPetunjuk" onclick="document.getElementById('modalPetunjuk').style.display='flex'" style="position:fixed;bottom:80px;left:20px;z-index:9999;background:#3498db;color:white;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:bold;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.3);transition:transform 0.2s;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'">?</div>
<!-- MODAL PETUNJUK PENGGUNAAN -->
<div id="modalPetunjuk" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:99999;align-items:center;justify-content:center;">
<div style="background:white;width:520px;max-width:95vw;max-height:88vh;border-radius:14px;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,0.4);">
<div style="background:#2c3e50;color:white;padding:18px 22px;border-radius:14px 14px 0 0;display:flex;justify-content:space-between;align-items:center;">
<h2 style="margin:0;font-size:17px;">📖 Petunjuk Penggunaan WebGIS</h2>
<span onclick="document.getElementById('modalPetunjuk').style.display='none'" style="cursor:pointer;font-size:22px;line-height:1;opacity:0.7;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.7"></span>
</div>
<div style="padding:20px 22px;">
<div style="background:#eaf4fd;border-left:4px solid #3498db;border-radius:0 8px 8px 0;padding:12px 14px;margin-bottom:18px;font-size:13px;color:#2c3e50;">
Selamat datang di <strong>WebGIS Pontianak</strong> — sistem pemetaan rumah ibadah &amp; penduduk miskin berbasis peta interaktif.
</div>
<!-- Bagian 1 -->
<div style="margin-bottom:16px;">
<h4 style="margin:0 0 8px 0;color:#2c3e50;font-size:14px;border-bottom:2px solid #3498db;padding-bottom:5px;">🗺️ Navigasi Peta</h4>
<ul style="margin:0;padding-left:18px;font-size:13px;color:#444;line-height:1.9;">
<li><strong>Scroll mouse</strong> → Zoom in / zoom out</li>
<li><strong>Klik &amp; drag</strong> → Geser tampilan peta</li>
<li><strong>Pilih Peta</strong> (pojok kiri atas) → Ganti tampilan Street/Satellite/OSM</li>
<li><strong>Layer Control</strong> (pojok kanan atas) → Tampilkan/sembunyikan layer data</li>
</ul>
</div>
<!-- Bagian 2 -->
<div style="margin-bottom:16px;">
<h4 style="margin:0 0 8px 0;color:#2c3e50;font-size:14px;border-bottom:2px solid #27ae60;padding-bottom:5px;">📌 Melihat Data</h4>
<ul style="margin:0;padding-left:18px;font-size:13px;color:#444;line-height:1.9;">
<li><strong>Klik marker 🕌 / ⛪</strong> → Lihat detail rumah ibadah</li>
<li><strong>Klik marker ⚠️</strong> → Lihat detail penduduk miskin</li>
<li>Marker <span style="color:#27ae60;">✓ hijau</span> = dalam radius bantuan, <span style="color:#e74c3c;">⚠️ merah</span> = di luar radius</li>
<li>Tombol filter (Semua / Masjid / Gereja / Miskin) → Filter tampilan peta</li>
</ul>
</div>
<!-- Bagian 3 -->
<div style="margin-bottom:16px;">
<h4 style="margin:0 0 8px 0;color:#2c3e50;font-size:14px;border-bottom:2px solid #e67e22;padding-bottom:5px;">⚙️ Radius Bantuan</h4>
<ul style="margin:0;padding-left:18px;font-size:13px;color:#444;line-height:1.9;">
<li>Geser <strong>slider Radius</strong> (pojok kanan bawah) untuk mengubah jangkauan</li>
<li>Klik <strong>"Terapkan Radius"</strong> agar perubahan berlaku</li>
<li>Lingkaran biru di sekitar rumah ibadah = area radius aktif</li>
<li>Statistik dalam/luar radius otomatis diperbarui</li>
</ul>
</div>
<!-- Bagian 4 -->
<div style="margin-bottom:16px;">
<h4 style="margin:0 0 8px 0;color:#2c3e50;font-size:14px;border-bottom:2px solid #9b59b6;padding-bottom:5px;">🔐 Login Admin</h4>
<ul style="margin:0;padding-left:18px;font-size:13px;color:#444;line-height:1.9;">
<li>Halaman ini bersifat <strong>publik</strong> (hanya melihat)</li>
<li>Untuk tambah/edit/hapus data, masuk via <a href="login.php" style="color:#3498db;">Login Admin</a></li>
<li>Di dashboard admin, klik peta untuk menambah data baru</li>
<li>Tersedia fitur <strong>Download Laporan</strong> per bulan di dashboard admin</li>
</ul>
</div>
<!-- Bagian 5 -->
<div style="margin-bottom:16px;">
<h4 style="margin:0 0 8px 0;color:#2c3e50;font-size:14px;border-bottom:2px solid #e74c3c;padding-bottom:5px;">🎨 Keterangan Ikon</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:12px;">
<div style="background:#f8f9fa;border-radius:6px;padding:8px;">🕌 <strong>Masjid</strong> — marker hijau</div>
<div style="background:#f8f9fa;border-radius:6px;padding:8px;"><strong>Gereja</strong> — marker ungu</div>
<div style="background:#f8f9fa;border-radius:6px;padding:8px;"><strong>Dalam radius</strong> — titik hijau</div>
<div style="background:#f8f9fa;border-radius:6px;padding:8px;">⚠️ <strong>Luar radius</strong> — titik merah</div>
</div>
</div>
<button onclick="document.getElementById('modalPetunjuk').style.display='none'" style="width:100%;padding:11px;background:#3498db;color:white;border:none;border-radius:8px;cursor:pointer;font-size:14px;font-weight:bold;margin-top:4px;">✅ Mengerti, Tutup Petunjuk</button>
</div>
</div>
</div>
<!-- KIRI ATAS -->
<div class="map-selector">
<label>🗺️ Pilih Peta:</label>
<select id="mapSelector">
<option value="street">🌍 Street Map</option>
<option value="satellite">🛰️ Satellite</option>
<option value="osm">🗺️ OpenStreetMap</option>
</select>
</div>
<div class="filter-buttons">
<button class="filter-btn-all" onclick="filterAll()">📌 Semua</button>
<button class="filter-btn-masjid" onclick="filterMasjid()">🕌 Masjid</button>
<button class="filter-btn-gereja" onclick="filterGereja()">⛪ Gereja</button>
<button class="filter-btn-poverty" onclick="filterPoverty()">⚠️ Miskin</button>
</div>
<!-- KANAN - STATISTIK (di bawah layer control) -->
<div class="stats-panel" id="statsPanel">
<h4>📊 Statistik</h4>
<div>🕌 Masjid: <span class="count green" id="masjidCount">0</span></div>
<div>⛪ Gereja: <span class="count purple" id="gerejaCount">0</span></div>
<div>⚠️ Miskin: <span class="count red" id="povertyCount">0</span></div>
<hr>
<div>✅ Dalam Radius: <span class="count green" id="insideRadiusCount">0</span></div>
<div>❌ Luar Radius: <span class="count red" id="outsideRadiusCount">0</span></div>
</div>
<!-- KANAN BAWAH - RADIUS -->
<div class="radius-control" id="radiusControl">
<h4>🎯 Radius Deteksi</h4>
<input type="range" id="radiusSlider" min="100" max="500" step="50" value="300">
<div class="radius-value">Radius: <span id="radiusValue">300</span> meter</div>
<button id="applyRadiusBtn">🔄 Terapkan Radius</button>
</div>
<!-- KIRI BAWAH - LEGEND -->
<div class="legend">
<strong>Legenda</strong><br>
<span class="masjid-icon"></span> Masjid<br>
<span class="gereja-icon"></span> Gereja<br>
<span class="poverty-icon"></span> Penduduk Miskin<br>
<span class="radius-icon"></span> Radius
</div>
<div class="instruction-box">
💡 Klik di peta untuk tambah data | Klik marker untuk detail
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script>
<script src="https://unpkg.com/leaflet-geometryutil@0.3.0/src/leaflet.geometryutil.js"></script>
<script>
// ==================== INISIALISASI ====================
var map = L.map('map').setView([-0.0263, 109.3425], 12);
var baseMaps = {
street: L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '&copy; OSM & CartoDB', subdomains: 'abcd', maxZoom: 19
}),
satellite: L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', {
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'], attribution: '&copy; Google Maps', maxZoom: 20
}),
osm: L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap', maxZoom: 19
})
};
baseMaps.street.addTo(map);
document.getElementById('mapSelector').addEventListener('change', function(e) {
for (var key in baseMaps) map.removeLayer(baseMaps[key]);
baseMaps[e.target.value].addTo(map);
});
// Layer Groups
var masjidLayer = L.layerGroup();
var gerejaLayer = L.layerGroup();
var povertyLayer = L.layerGroup();
var radiusLayer = L.layerGroup();
masjidLayer.addTo(map);
gerejaLayer.addTo(map);
povertyLayer.addTo(map);
radiusLayer.addTo(map);
// Layer Control - muncul di kanan atas
var overlayMaps = {
"🕌 Masjid": masjidLayer,
"⛪ Gereja": gerejaLayer,
"⚠️ Penduduk Miskin": povertyLayer,
"🔵 Radius": radiusLayer
};
L.control.layers(null, overlayMaps, { collapsed: false, position: 'topright' }).addTo(map);
// Data
var masjidPoints = [];
var gerejaPoints = [];
var povertyPoints = [];
var allCircles = [];
var currentRadius = 300;
var currentFilter = 'all';
function showToast(message) {
var existingToasts = document.querySelectorAll('.toast-notification');
existingToasts.forEach(function(toast) { toast.remove(); });
var toast = document.createElement('div');
toast.className = 'toast-notification';
toast.innerHTML = message;
document.body.appendChild(toast);
setTimeout(function() { if (toast.parentNode) toast.remove(); }, 3000);
}
async function reverseGeocode(lat, lng) {
try {
var url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`;
var response = await fetch(url, { headers: { 'User-Agent': 'WebGIS-Pontianak/1.0' } });
var data = await response.json();
if (data && data.display_name) return data.display_name;
return lat.toFixed(6) + ", " + lng.toFixed(6);
} catch (error) {
return lat.toFixed(6) + ", " + lng.toFixed(6);
}
}
function createMasjidIcon() {
return L.divIcon({
className: 'masjid-icon-leaflet',
html: '<div style="background: #27ae60; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 5px rgba(0,0,0,0.3);"><span style="font-size: 13px;">🕌</span></div>',
iconSize: [24, 24], iconAnchor: [12, 12], popupAnchor: [0, -12]
});
}
function createGerejaIcon() {
return L.divIcon({
className: 'gereja-icon-leaflet',
html: '<div style="background: #9b59b6; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 5px rgba(0,0,0,0.3);"><span style="font-size: 13px;">⛪</span></div>',
iconSize: [24, 24], iconAnchor: [12, 12], popupAnchor: [0, -12]
});
}
function createPovertyIcon(isInside) {
var color = isInside ? '#27ae60' : '#e74c3c';
var iconChar = isInside ? '✓' : '⚠️';
return L.divIcon({
className: 'poverty-icon-leaflet',
html: '<div style="background: ' + color + '; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 5px rgba(0,0,0,0.3);"><span style="font-size: 10px; color: white; font-weight: bold;">' + iconChar + '</span></div>',
iconSize: [20, 20], iconAnchor: [10, 10], popupAnchor: [0, -10]
});
}
function updateAllRadii() {
radiusLayer.clearLayers();
allCircles = [];
for (var i = 0; i < masjidPoints.length; i++) {
var m = masjidPoints[i];
var circle = L.circle([m.lat, m.lng], {
radius: currentRadius, color: '#27ae60', fillColor: '#27ae60', fillOpacity: 0.1, weight: 1.5
});
circle.addTo(radiusLayer);
allCircles.push(circle);
}
for (var j = 0; j < gerejaPoints.length; j++) {
var g = gerejaPoints[j];
var circle2 = L.circle([g.lat, g.lng], {
radius: currentRadius, color: '#9b59b6', fillColor: '#9b59b6', fillOpacity: 0.1, weight: 1.5
});
circle2.addTo(radiusLayer);
allCircles.push(circle2);
}
updatePovertyStatus();
}
function updatePovertyStatus() {
povertyLayer.clearLayers();
var insideCount = 0, outsideCount = 0;
for (var k = 0; k < povertyPoints.length; k++) {
var poor = povertyPoints[k];
var isInside = false;
for (var c = 0; c < allCircles.length; c++) {
var center = allCircles[c].getLatLng();
var distance = map.distance(center, L.latLng(poor.lat, poor.lng));
if (distance <= currentRadius) {
isInside = true;
break;
}
}
var marker = L.marker([poor.lat, poor.lng], { icon: createPovertyIcon(isInside) });
marker.povertyData = poor;
marker.isInside = isInside;
marker.on('click', function(e) {
showPovertyPopup(e.target.povertyData, e.target.isInside);
});
marker.addTo(povertyLayer);
if (isInside) insideCount++; else outsideCount++;
}
document.getElementById('insideRadiusCount').innerHTML = insideCount;
document.getElementById('outsideRadiusCount').innerHTML = outsideCount;
}
async function showPovertyPopup(data, isInside) {
var address = await reverseGeocode(data.lat, data.lng);
var statusText = isInside ? '✅ Dalam Radius' : '❌ Luar Radius';
var statusColor = isInside ? '#27ae60' : '#e74c3c';
var content = '<div style="min-width:230px;">' +
'<h4 style="margin:0 0 5px 0;color:#2c3e50;">⚠️ PENDUDUK MISKIN</h4>' +
'<div class="info-card poverty">' +
'<strong>👤 Nama:</strong> ' + data.nama + '<br>' +
'<strong>📝 Keterangan:</strong> ' + data.keterangan + '<br>' +
'<strong>📍 Status:</strong> <span style="color:' + statusColor + '">' + statusText + '</span><br>' +
'<div class="address"><strong>🏠 Alamat:</strong><br>' + address + '</div>' +
'<button class="btn-hapus" onclick="hapusPoverty(' + data.id + ')">🗑️ Hapus Data</button>' +
'</div></div>';
L.popup().setLatLng([data.lat, data.lng]).setContent(content).openOn(map);
}
async function showMasjidPopup(data) {
var address = await reverseGeocode(data.lat, data.lng);
var content = '<div style="min-width:230px;">' +
'<h4 style="margin:0 0 5px 0;color:#2c3e50;">🕌 MASJID</h4>' +
'<div class="info-card masjid">' +
'<strong>🏛️ Nama:</strong> ' + data.nama + '<br>' +
'<strong>📝 Alamat:</strong> ' + data.alamat + '<br>' +
'<div class="address"><strong>📍 Lokasi:</strong><br>' + address + '</div>' +
'<button class="btn-hapus" onclick="hapusMasjid(' + data.id + ')">🗑️ Hapus Masjid</button>' +
'</div></div>';
L.popup().setLatLng([data.lat, data.lng]).setContent(content).openOn(map);
}
async function showGerejaPopup(data) {
var address = await reverseGeocode(data.lat, data.lng);
var content = '<div style="min-width:230px;">' +
'<h4 style="margin:0 0 5px 0;color:#2c3e50;">⛪ GEREJA</h4>' +
'<div class="info-card gereja">' +
'<strong>🏛️ Nama:</strong> ' + data.nama + '<br>' +
'<strong>📝 Alamat:</strong> ' + data.alamat + '<br>' +
'<div class="address"><strong>📍 Lokasi:</strong><br>' + address + '</div>' +
'<button class="btn-hapus" onclick="hapusGereja(' + data.id + ')">🗑️ Hapus Gereja</button>' +
'</div></div>';
L.popup().setLatLng([data.lat, data.lng]).setContent(content).openOn(map);
}
function loadMasjidData() {
fetch('api.php?action=get_masjid')
.then(function(r) { return r.json(); })
.then(function(data) {
masjidLayer.clearLayers();
masjidPoints = [];
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
var item = data[i];
var lat = parseFloat(item.lat);
var lng = parseFloat(item.lng);
var marker = L.marker([lat, lng], { icon: createMasjidIcon() });
marker.data = { id: item.id, nama: item.nama, alamat: item.alamat || '-', lat: lat, lng: lng };
marker.on('click', function(e) { showMasjidPopup(e.target.data); });
if (currentFilter === 'all' || currentFilter === 'masjid') {
marker.addTo(masjidLayer);
}
masjidPoints.push(marker.data);
}
}
document.getElementById('masjidCount').innerHTML = masjidPoints.length;
updateAllRadii();
}).catch(function(err) { showToast('Gagal load Masjid'); });
}
function loadGerejaData() {
fetch('api.php?action=get_gereja')
.then(function(r) { return r.json(); })
.then(function(data) {
gerejaLayer.clearLayers();
gerejaPoints = [];
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
var item = data[i];
var lat = parseFloat(item.lat);
var lng = parseFloat(item.lng);
var marker = L.marker([lat, lng], { icon: createGerejaIcon() });
marker.data = { id: item.id, nama: item.nama, alamat: item.alamat || '-', lat: lat, lng: lng };
marker.on('click', function(e) { showGerejaPopup(e.target.data); });
if (currentFilter === 'all' || currentFilter === 'gereja') {
marker.addTo(gerejaLayer);
}
gerejaPoints.push(marker.data);
}
}
document.getElementById('gerejaCount').innerHTML = gerejaPoints.length;
updateAllRadii();
}).catch(function(err) { showToast('Gagal load Gereja'); });
}
function loadPovertyData() {
fetch('api.php?action=get_poverty')
.then(function(r) { return r.json(); })
.then(function(data) {
povertyPoints = [];
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
var item = data[i];
povertyPoints.push({
id: item.id,
nama: item.nama || '-',
alamat: item.alamat || '-',
keterangan: item.keterangan || '-',
lat: parseFloat(item.lat),
lng: parseFloat(item.lng)
});
}
}
document.getElementById('povertyCount').innerHTML = povertyPoints.length;
updatePovertyStatus();
}).catch(function(err) { showToast('Gagal load Kemiskinan'); });
}
function hapusMasjid(id) {
if (confirm('Hapus masjid ini?')) {
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete_masjid', id: id })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Masjid dihapus');
loadMasjidData();
map.closePopup();
}
});
}
}
function hapusGereja(id) {
if (confirm('Hapus gereja ini?')) {
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete_gereja', id: id })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Gereja dihapus');
loadGerejaData();
map.closePopup();
}
});
}
}
function hapusPoverty(id) {
if (confirm('Hapus data penduduk miskin ini?')) {
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete_poverty', id: id })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Data dihapus');
loadPovertyData();
map.closePopup();
}
});
}
}
function simpanMasjid(lat, lng) {
var nama = document.getElementById('nama_masjid').value;
var alamat = document.getElementById('alamat_masjid').value;
if (!nama) { alert('Nama harus diisi!'); return; }
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'add_masjid', nama: nama, alamat: alamat, lat: lat, lng: lng })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Masjid tersimpan');
map.closePopup();
loadMasjidData();
}
});
}
function simpanGereja(lat, lng) {
var nama = document.getElementById('nama_gereja').value;
var alamat = document.getElementById('alamat_gereja').value;
if (!nama) { alert('Nama harus diisi!'); return; }
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'add_gereja', nama: nama, alamat: alamat, lat: lat, lng: lng })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Gereja tersimpan');
map.closePopup();
loadGerejaData();
}
});
}
function simpanPoverty(lat, lng) {
var nama = document.getElementById('nama_poverty').value;
var alamat = document.getElementById('alamat_poverty').value;
var keterangan = document.getElementById('keterangan_poverty').value;
if (!nama) { alert('Nama harus diisi!'); return; }
fetch('api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'add_poverty', nama: nama, alamat: alamat, keterangan: keterangan, lat: lat, lng: lng })
}).then(function(r) { return r.json(); })
.then(function(res) {
if (res.status === 'ok') {
showToast('✅ Data tersimpan');
map.closePopup();
loadPovertyData();
}
});
}
map.on('click', function(e) {
var lat = e.latlng.lat;
var lng = e.latlng.lng;
var form = '<div class="popup-form">' +
'<h4> Pilih Jenis Data</h4>' +
'<button onclick="showMasjidForm(' + lat + ', ' + lng + ')" style="background:#27ae60; margin-bottom:6px;">🕌 Tambah Masjid</button>' +
'<button onclick="showGerejaForm(' + lat + ', ' + lng + ')" style="background:#9b59b6; margin-bottom:6px;">⛪ Tambah Gereja</button>' +
'<button onclick="showPovertyForm(' + lat + ', ' + lng + ')" style="background:#e74c3c;">⚠️ Tambah Penduduk Miskin</button>' +
'</div>';
L.popup().setLatLng([lat, lng]).setContent(form).openOn(map);
});
function showMasjidForm(lat, lng) {
var form = '<div class="popup-form">' +
'<h4>🕌 Input Masjid</h4>' +
'<input type="text" id="nama_masjid" placeholder="Nama Masjid">' +
'<input type="text" id="alamat_masjid" placeholder="Alamat">' +
'<button onclick="simpanMasjid(' + lat + ', ' + lng + ')">💾 Simpan</button>' +
'</div>';
map.closePopup();
L.popup().setLatLng([lat, lng]).setContent(form).openOn(map);
}
function showGerejaForm(lat, lng) {
var form = '<div class="popup-form">' +
'<h4>⛪ Input Gereja</h4>' +
'<input type="text" id="nama_gereja" placeholder="Nama Gereja">' +
'<input type="text" id="alamat_gereja" placeholder="Alamat">' +
'<button onclick="simpanGereja(' + lat + ', ' + lng + ')">💾 Simpan</button>' +
'</div>';
map.closePopup();
L.popup().setLatLng([lat, lng]).setContent(form).openOn(map);
}
function showPovertyForm(lat, lng) {
var form = '<div class="popup-form">' +
'<h4>⚠️ Input Penduduk Miskin</h4>' +
'<input type="text" id="nama_poverty" placeholder="Nama Kepala Keluarga">' +
'<input type="text" id="alamat_poverty" placeholder="Alamat">' +
'<textarea id="keterangan_poverty" placeholder="Keterangan" rows="2"></textarea>' +
'<button onclick="simpanPoverty(' + lat + ', ' + lng + ')">💾 Simpan</button>' +
'</div>';
map.closePopup();
L.popup().setLatLng([lat, lng]).setContent(form).openOn(map);
}
function filterAll() {
currentFilter = 'all';
masjidLayer.clearLayers();
gerejaLayer.clearLayers();
for (var i = 0; i < masjidPoints.length; i++) {
var item = masjidPoints[i];
var marker = L.marker([item.lat, item.lng], { icon: createMasjidIcon() });
marker.data = item;
marker.on('click', function(e) { showMasjidPopup(e.target.data); });
marker.addTo(masjidLayer);
}
for (var j = 0; j < gerejaPoints.length; j++) {
var item2 = gerejaPoints[j];
var marker2 = L.marker([item2.lat, item2.lng], { icon: createGerejaIcon() });
marker2.data = item2;
marker2.on('click', function(e) { showGerejaPopup(e.target.data); });
marker2.addTo(gerejaLayer);
}
showToast('📌 Menampilkan semua data');
}
function filterMasjid() {
currentFilter = 'masjid';
masjidLayer.clearLayers();
gerejaLayer.clearLayers();
for (var i = 0; i < masjidPoints.length; i++) {
var item = masjidPoints[i];
var marker = L.marker([item.lat, item.lng], { icon: createMasjidIcon() });
marker.data = item;
marker.on('click', function(e) { showMasjidPopup(e.target.data); });
marker.addTo(masjidLayer);
}
showToast('🕌 Hanya menampilkan Masjid');
}
function filterGereja() {
currentFilter = 'gereja';
masjidLayer.clearLayers();
gerejaLayer.clearLayers();
for (var i = 0; i < gerejaPoints.length; i++) {
var item = gerejaPoints[i];
var marker = L.marker([item.lat, item.lng], { icon: createGerejaIcon() });
marker.data = item;
marker.on('click', function(e) { showGerejaPopup(e.target.data); });
marker.addTo(gerejaLayer);
}
showToast('⛪ Hanya menampilkan Gereja');
}
function filterPoverty() {
showToast('⚠️ Data kemiskinan tetap tampil');
}
var radiusSlider = document.getElementById('radiusSlider');
var radiusValue = document.getElementById('radiusValue');
radiusSlider.addEventListener('input', function(e) {
currentRadius = parseInt(e.target.value);
radiusValue.innerText = currentRadius;
});
document.getElementById('applyRadiusBtn').addEventListener('click', function() {
updateAllRadii();
showToast('Radius: ' + currentRadius + ' meter');
});
// Load kecamatan
fetch('Admin_Kecamatan.json')
.then(function(r) { return r.json(); })
.then(function(data) {
L.geoJSON(data, { style: { color: "#2c3e50", weight: 2, fillOpacity: 0.05, dashArray: '5,5' } }).addTo(map);
}).catch(function(err) { console.error("Gagal load GeoJSON:", err); });
// Start
loadMasjidData();
loadGerejaData();
loadPovertyData();
</script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
<?php
$host = 'localhost';
$user = 'root';
$password = '';
$database = 'webgis_kemiskinan';
$conn = new mysqli($host, $user, $password, $database);
if ($conn->connect_error) {
die(json_encode([
'status' => 'error',
'msg' => 'Koneksi database gagal: ' . $conn->connect_error
]));
}
$conn->set_charset("utf8");