161 lines
9.1 KiB
PHP
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'; ?>
|