Files
2026-06-05 03:08:55 +00:00

611 lines
43 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>