Files
2026-06-13 11:24:58 +07:00

161 lines
9.1 KiB
PHP

<?php
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../config/auth_check.php';
requireAnyRole(['operator']);
$pageTitle = 'Kelola Wilayah';
$activeNav = 'wilayah';
$extraHead = '
<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">
';
require_once __DIR__ . '/partials/header.php';
?>
<div class="page-header" style="margin-bottom:12px;">
<div class="breadcrumb"><span>Operator</span><span class="sep">/</span><span>Kelola Wilayah</span></div>
<h1>🗺️ Kelola Batas Wilayah (Kelurahan)</h1>
<p>Gambar polygon batas kelurahan di peta, ubah bentuk/ukuran (geser titik), atur jumlah penduduk, atau hapus.</p>
</div>
<div style="display:grid;grid-template-columns:1.7fr 1fr;gap:16px;">
<div style="position:relative;">
<div id="wil-map" style="height:72vh;border-radius:12px;border:1px solid #E5E7EB;z-index:1;"></div>
<div style="position:absolute;bottom:12px;left:12px;z-index:500;background:#fff;padding:8px 12px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.15);font-size:0.78rem;max-width:260px;">
<b>Cara pakai:</b> klik ikon ⬠ (kiri atas peta) untuk gambar polygon baru. Ikon ✏️ untuk geser titik/ubah ukuran, 🗑️ untuk hapus. Klik polygon untuk edit atribut.
</div>
</div>
<div class="card">
<h3 id="formTitle" style="font-size:0.95rem;margin-bottom:10px;">Atribut Wilayah</h3>
<input type="hidden" id="wId">
<div class="form-group"><label class="form-label">Nama Kelurahan *</label><input id="wNama" class="form-control" placeholder="mis. Kel. Sungai Jawi"></div>
<div class="form-group"><label class="form-label">Kode Wilayah (BPS)</label><input id="wKode" class="form-control" placeholder="mis. 6171010005"></div>
<div class="form-group"><label class="form-label">Jenis</label>
<select id="wJenis" class="form-control"><option value="kelurahan">Kelurahan</option><option value="kecamatan">Kecamatan</option></select></div>
<div class="form-group"><label class="form-label">Jumlah Penduduk (KK)</label><input id="wPenduduk" type="number" min="0" class="form-control" placeholder="0"></div>
<div id="geomNote" style="font-size:0.78rem;color:#9CA3AF;margin-bottom:10px;">Belum ada polygon. Gambar di peta dulu untuk wilayah baru.</div>
<button class="btn btn-primary" style="width:100%;" onclick="simpanWilayah()"><i class="fas fa-save"></i> Simpan</button>
<button class="btn btn-ghost" style="width:100%;margin-top:6px;" onclick="resetForm()">Bersihkan Form</button>
<h3 style="font-size:0.95rem;margin:16px 0 8px;">Daftar Wilayah</h3>
<div id="wList" style="font-size:0.85rem;"></div>
</div>
</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>
const WAPI = APP_BASE + '/api/wilayah.php';
let map, drawn, pendingLayer = null;
const layerById = {};
function colorMiskin(p){ return p>=10?'#EF4444':p>=5?'#FB923C':p>=2?'#FBBF24':p>0?'#A3E635':'#2ECC71'; }
function initMap(){
map = L.map('wil-map').setView([-0.0500,109.3450],13);
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{subdomains:'abcd',maxZoom:19}).addTo(map);
drawn = new L.FeatureGroup().addTo(map);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawn, remove: true },
draw: { polygon:{allowIntersection:false,showArea:true}, marker:false, polyline:false, circle:false, circlemarker:false, rectangle:false }
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, e => {
pendingLayer = e.layer;
drawn.addLayer(pendingLayer);
resetForm(); // mode wilayah baru
document.getElementById('formTitle').textContent = 'Wilayah Baru (polygon tergambar)';
document.getElementById('geomNote').innerHTML = '<span style="color:#059669">✔ Polygon siap. Isi atribut lalu Simpan.</span>';
});
map.on(L.Draw.Event.EDITED, async e => {
for (const layer of Object.values(e.layers._layers)) {
const id = layer.wilayahId; if(!id) continue;
const p = layer.props || {};
await fetch(`${WAPI}?id=${id}`, {method:'PUT',headers:{'Content-Type':'application/json'},
body:JSON.stringify({nama:p.nama,kode_wilayah:p.kode_wilayah,jenis:p.jenis,jumlah_penduduk:p.jumlah_penduduk,geometry:layer.toGeoJSON().geometry})});
}
showToast('Bentuk wilayah diperbarui'); loadWilayah();
});
map.on(L.Draw.Event.DELETED, async e => {
for (const layer of Object.values(e.layers._layers)) {
if (layer.wilayahId) await fetch(`${WAPI}?id=${layer.wilayahId}`, {method:'DELETE'});
}
showToast('Wilayah dihapus'); loadWilayah();
});
}
async function loadWilayah(){
Object.keys(layerById).forEach(k=>delete layerById[k]);
drawn.clearLayers(); pendingLayer=null;
const d = (await (await fetch(WAPI)).json()).data;
(d.features||[]).forEach(f=>{
const layer = L.geoJSON(f,{style:{color:'#374151',weight:1.5,fillColor:colorMiskin(f.properties.persentase_miskin),fillOpacity:.45}});
layer.eachLayer(l=>{
l.wilayahId = f.properties.id; l.props = f.properties;
l.bindTooltip(f.properties.nama,{sticky:true});
l.on('click',()=>editAttr(f.properties));
drawn.addLayer(l); layerById[f.properties.id]=l;
});
});
// daftar
document.getElementById('wList').innerHTML = (d.features||[]).map(f=>{const p=f.properties;
return `<div style="display:flex;justify-content:space-between;align-items:center;padding:7px 0;border-bottom:1px solid #F3F4F6;">
<div><b>${p.nama}</b><div style="font-size:0.72rem;color:#9CA3AF;">${p.jumlah_penduduk} KK · ${p.jumlah_miskin} miskin · ${p.persentase_miskin}%</div></div>
<div style="display:flex;gap:4px;">
<button class="btn btn-ghost btn-sm btn-icon" title="Edit atribut" onclick='editAttr(${JSON.stringify(p)})'><i class="fas fa-pen"></i></button>
<button class="btn btn-ghost btn-sm btn-icon" title="Zoom" onclick="zoomTo(${p.id})"><i class="fas fa-magnifying-glass"></i></button>
<button class="btn btn-danger btn-sm btn-icon" title="Hapus" onclick="hapusWilayah(${p.id},'${p.nama.replace(/'/g,'')}')"><i class="fas fa-trash"></i></button>
</div></div>`;}).join('') || '<p style="color:#9CA3AF">Belum ada wilayah.</p>';
}
function zoomTo(id){ const l=layerById[id]; if(l) map.fitBounds(l.getBounds(),{padding:[40,40]}); }
function editAttr(p){
document.getElementById('formTitle').textContent = 'Edit Atribut: '+p.nama;
document.getElementById('wId').value = p.id;
document.getElementById('wNama').value = p.nama||'';
document.getElementById('wKode').value = p.kode_wilayah||'';
document.getElementById('wJenis').value = p.jenis||'kelurahan';
document.getElementById('wPenduduk').value = p.jumlah_penduduk||0;
document.getElementById('geomNote').innerHTML = 'Mengubah atribut wilayah ini (bentuk diubah lewat tool ✏️ di peta).';
pendingLayer = null;
zoomTo(p.id);
}
function resetForm(){
document.getElementById('formTitle').textContent='Atribut Wilayah';
['wId','wNama','wKode','wPenduduk'].forEach(i=>document.getElementById(i).value='');
document.getElementById('wJenis').value='kelurahan';
document.getElementById('geomNote').innerHTML='Belum ada polygon. Gambar di peta dulu untuk wilayah baru.';
}
async function simpanWilayah(){
const nama=document.getElementById('wNama').value.trim();
if(!nama) return showToast('Nama kelurahan wajib','error');
const body={ nama, kode_wilayah:document.getElementById('wKode').value.trim(), jenis:document.getElementById('wJenis').value, jumlah_penduduk:parseInt(document.getElementById('wPenduduk').value)||0 };
const id=document.getElementById('wId').value;
if(pendingLayer && !id){
body.geometry = pendingLayer.toGeoJSON().geometry;
const r=await (await fetch(WAPI,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})).json();
if(r.status==='success'){ showToast('Wilayah baru disimpan'); loadWilayah(); resetForm(); } else showToast(r.message,'error');
} else if(id){
const r=await (await fetch(`${WAPI}?id=${id}`,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})).json();
if(r.status==='success'){ showToast('Atribut diperbarui'); loadWilayah(); } else showToast(r.message,'error');
} else {
showToast('Gambar polygon dulu di peta untuk wilayah baru','error');
}
}
async function hapusWilayah(id,nama){
if(!confirm(`Hapus wilayah "${nama}"?`))return;
const r=await (await fetch(`${WAPI}?id=${id}`,{method:'DELETE'})).json();
if(r.status==='success'){ showToast('Dihapus'); loadWilayah(); resetForm(); } else showToast(r.message,'error');
}
document.addEventListener('DOMContentLoaded',()=>{ initMap(); loadWilayah(); });
</script>
<?php $extraScript=''; ?>
<?php require_once __DIR__ . '/partials/footer.php'; ?>