Files
UasSIGWebgis/infra/script.js
T
2026-06-13 01:07:22 +07:00

775 lines
37 KiB
JavaScript

var map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// Layer Groups yang Spesifik
var layerMasjid = L.layerGroup().addTo(map);
var layerMasjidRadius = L.layerGroup().addTo(map);
var layerPelaporan = L.layerGroup().addTo(map);
var layerWarga = L.layerGroup().addTo(map);
var layerSpbu24 = L.layerGroup().addTo(map);
var layerSpbuNon24 = L.layerGroup().addTo(map);
var layerLines = L.layerGroup().addTo(map);
var layerPolygons = L.layerGroup().addTo(map);
// Layers Control
var overlays = {};
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
overlays["🕌 Tempat Ibadah"] = layerMasjid;
overlays["📡 Radius Ibadah/Bantuan"] = layerMasjidRadius;
overlays["🚨 Pelaporan Darurat"] = layerPelaporan;
overlays["📍 Warga Rentan"] = layerWarga;
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
overlays["⛽ SPBU 24 Jam (Hijau)"] = layerSpbu24;
overlays["⛽ SPBU Terbatas (Merah)"] = layerSpbuNon24;
overlays["🛣️ Jalan (Polyline)"] = layerLines;
overlays["🟩 Kavling (Polygon)"] = layerPolygons;
}
L.control.layers(null, overlays, { collapsed: false, position: 'topright' }).addTo(map);
var drawnItems = new L.FeatureGroup().addTo(map);
var currentRadius = 500;
var cachedData = null;
var drawControl = new L.Control.Draw({
draw: {
marker: false, circle: false, circlemarker: false,
polyline: { shapeOptions: { color: 'blue', weight: 4 } },
polygon: { shapeOptions: { color: 'red' } },
rectangle: false
},
edit: { featureGroup: drawnItems }
});
map.addControl(drawControl);
// Locate Control
var locateBtn = L.control({ position: 'topleft' });
locateBtn.onAdd = function (map) {
var div = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
div.style.backgroundColor = 'white';
div.style.width = '34px';
div.style.height = '34px';
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.alignItems = 'center';
div.style.justifyContent = 'center';
div.innerHTML = '<i class="fas fa-crosshairs" style="font-size:1.2rem; color:#2563eb;"></i>';
div.title = "Lokasi Anda Saat Ini";
div.onclick = function () { map.locate({ setView: true, maxZoom: 16 }); }
return div;
};
locateBtn.addTo(map);
map.on('locationfound', function (e) {
L.circleMarker(e.latlng, { radius: 8, fillColor: '#3b82f6', color: '#fff', weight: 3, fillOpacity: 1 }).addTo(map).bindPopup("Lokasi Anda").openPopup();
});
// Event Draw
map.on(L.Draw.Event.CREATED, function (e) {
var layer = e.layer;
var type = e.layerType;
drawnItems.addLayer(layer);
var panel = document.getElementById('panelInput');
if (panel) panel.classList.remove('collapsed');
if (type === 'polyline') {
document.getElementById('jenis').value = 'jalan';
toggleForm();
var coords = layer.getLatLngs();
document.getElementById('jalan_coords').value = JSON.stringify(coords);
// Calculate Length automatically
var length = 0;
for (var i = 0; i < coords.length - 1; i++) {
length += coords[i].distanceTo(coords[i + 1]);
}
document.getElementById('panjang_jalan').value = length.toFixed(2);
} else if (type === 'polygon') {
document.getElementById('jenis').value = 'parsil';
toggleForm();
var coords = layer.getLatLngs()[0];
document.getElementById('parsil_coords').value = JSON.stringify(coords);
}
});
map.on(L.Draw.Event.EDITED, function (e) {
var layers = e.layers;
layers.eachLayer(function (layer) {
if (layer.db_id && layer.db_tipe) {
var coords = (layer.db_tipe === 'parsil') ? layer.getLatLngs()[0] : layer.getLatLngs();
var fd = new FormData();
fd.append('action', 'update_koordinat');
fd.append('id', layer.db_id);
fd.append('tipe', layer.db_tipe);
fd.append('koordinat', JSON.stringify(coords));
if (layer.db_tipe === 'jalan') {
var length = 0;
for (var i = 0; i < coords.length - 1; i++) {
length += coords[i].distanceTo(coords[i + 1]);
}
fd.append('panjang_m', length.toFixed(2));
}
fetch('../api/api.php', { method: 'POST', body: fd });
}
});
Swal.fire({ icon: 'success', title: 'Tersimpan', text: 'Perubahan bentuk berhasil disimpan!', timer: 1500, showConfirmButton: false });
});
map.on(L.Draw.Event.DELETED, function (e) {
var layers = e.layers;
layers.eachLayer(function (layer) {
if (layer.db_id && layer.db_tipe) {
var fd = new FormData();
fd.append('action', 'hapus_data');
fd.append('id', layer.db_id);
fd.append('tipe', layer.db_tipe);
fetch('../api/api.php', { method: 'POST', body: fd });
}
});
Swal.fire({ icon: 'success', title: 'Terhapus', text: 'Data berhasil dihapus dari peta!', timer: 1500, showConfirmButton: false });
});
function toggleSidebar() { document.getElementById('sidebar').classList.toggle('active'); }
function toggleRightPanel() {
var panel = document.getElementById('panelInput');
if (panel) panel.classList.toggle('collapsed');
}
function filterData() {
var input = document.getElementById('searchInput').value.toLowerCase();
var cards = document.getElementsByClassName('item-data');
if (input.length > 0) document.getElementById('sidebar').classList.add('active');
for (var i = 0; i < cards.length; i++) {
cards[i].style.display = cards[i].getAttribute('data-nama').includes(input) ? "" : "none";
}
}
function changeRadius(v) {
currentRadius = parseInt(v);
document.getElementById('radiusVal').innerText = v;
if (cachedData) renderMap(cachedData);
}
var prevReportCount = -1;
function loadData(isPolling = false) {
fetch('../api/api.php?data=all')
.then(r => r.json())
.then(data => {
cachedData = data;
// Logika Notifikasi Bubble
if (prevReportCount !== -1 && data.pelaporan.length > prevReportCount) {
var newCount = data.pelaporan.length - prevReportCount;
Swal.fire({
title: 'Laporan Baru!',
text: `Ada ${newCount} laporan warga baru yang masuk.`,
icon: 'info',
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 5000
});
}
prevReportCount = data.pelaporan.length;
var badge = document.getElementById('notifBadge');
if (badge) {
if (data.pelaporan.length > 0) {
badge.style.display = 'block';
badge.innerText = data.pelaporan.length;
} else {
badge.style.display = 'none';
}
}
if (!isPolling) {
renderSidebar(data);
renderMap(data);
} else {
// If polling, maybe just update sidebar if it's open, or wait for manual refresh
// But for real-time map, we can re-render map as well (might cause flicker if doing heavy edits)
// We'll skip re-rendering map to avoid interrupting user edits, just update the bubble
renderSidebar(data);
renderMap(data);
}
});
}
// Auto-poll every 10 seconds
setInterval(() => {
// Only poll if not currently editing (edit_id is 0)
if (document.getElementById('edit_id') && document.getElementById('edit_id').value === "0") {
loadData(true);
}
}, 10000);
function renderSidebar(data) {
var html = `<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:20px">`;
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
html += `
<div style="background:#fee2e2; padding:15px 10px; border-radius:10px; text-align:center; box-shadow:0 4px 6px rgba(0,0,0,0.05)">
<b style="color:#ef4444; font-size:1.5rem">${data.pelaporan.length}</b><br><small style="color:#991b1b; font-weight:600">Laporan</small>
</div>
<div style="background:#fef3c7; padding:15px 10px; border-radius:10px; text-align:center; box-shadow:0 4px 6px rgba(0,0,0,0.05)">
<b style="color:#d97706; font-size:1.5rem">${data.warga.length}</b><br><small style="color:#92400e; font-weight:600">Warga Rentan</small>
</div>`;
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
html += `
<div style="background:#dcfce7; padding:15px 10px; border-radius:10px; text-align:center; box-shadow:0 4px 6px rgba(0,0,0,0.05)">
<b style="color:#16a34a; font-size:1.5rem">${data.spbu.length}</b><br><small style="color:#166534; font-weight:600">SPBU Terdata</small>
</div>
<div style="background:#e0e7ff; padding:15px 10px; border-radius:10px; text-align:center; box-shadow:0 4px 6px rgba(0,0,0,0.05)">
<b style="color:#4f46e5; font-size:1.5rem">${data.jalan.length}</b><br><small style="color:#3730a3; font-weight:600">Jalur Jalan</small>
</div>`;
}
html += `</div>`;
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
// Pelaporan
html += `<div class="section-title" style="color:#ef4444">🚨 Laporan Darurat</div>`;
if (data.pelaporan.length === 0) html += `<small style="color:#666">Belum ada laporan</small>`;
data.pelaporan.forEach(p => {
html += `<div class="card item-data" data-nama="${p.nama_pelapor.toLowerCase()} ${p.isi_laporan.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span onclick="fokus(${p.latitude}, ${p.longitude})" style="cursor:pointer">
<b>${p.nama_pelapor}</b><small><i class="far fa-clock"></i> ${p.waktu}</small><br><span>${p.isi_laporan.substring(0, 40)}...</span>
</span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('pelaporan', ${p.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${p.id}, 'pelaporan')">Hapus</button>
</div>
</div>
</div>`;
});
// Warga
html += `<div class="section-title">📍 Penduduk Miskin / Rentan</div>`;
data.warga.forEach(w => {
html += `<div class="card item-data" data-nama="${w.nama.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span onclick="fokus(${w.latitude}, ${w.longitude})" style="cursor:pointer">
<b>${w.nama}</b>
${w.riwayat_penyakit ? '<span class="badge" style="background:#fef3c7; color:#d97706;"><i class="fas fa-heartbeat"></i> Medis</span>' : ''}
</span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('warga', ${w.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${w.id}, 'warga')">Hapus</button>
</div>
</div>
</div>`;
});
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
// SPBU
html += `<div class="section-title">⛽ Stasiun Pengisian (SPBU)</div>`;
data.spbu.forEach(s => {
var is24 = s.kategori === '24jam';
html += `<div class="card item-data" data-nama="${s.nama.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span onclick="fokus(${s.latitude}, ${s.longitude})" style="cursor:pointer">
<b>${s.nama}</b>
<small><i class="fas fa-phone"></i> ${s.no_hp}</small><br>
${is24 ? '<span class="badge" style="background:#dcfce7; color:#16a34a;">Buka 24 Jam</span>' : '<span class="badge" style="background:#fee2e2; color:#ef4444;">Tidak 24 Jam</span>'}
</span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('spbu', ${s.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${s.id}, 'spbu')">Hapus</button>
</div>
</div>
</div>`;
});
}
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
// Tempat Ibadah
html += `<div class="section-title">🕌 Tempat Ibadah & Bantuan</div>`;
data.masjid.forEach(m => {
html += `<div class="card item-data" data-nama="${m.nama.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span onclick="fokus(${m.latitude}, ${m.longitude})" style="cursor:pointer"><b>${m.nama}</b></span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('masjid', ${m.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${m.id}, 'masjid')">Hapus</button>
</div>
</div>
</div>`;
});
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
// Jalan
html += `<div class="section-title">🛣️ Manajemen Jalan</div>`;
data.jalan.forEach(j => {
var color = '#ef4444'; // default red
if (j.status_jalan === 'Nasional') color = '#ef4444';
else if (j.status_jalan === 'Provinsi') color = '#eab308';
else if (j.status_jalan === 'Kabupaten') color = '#22c55e';
html += `<div class="card item-data" data-nama="${j.nama_jalan.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span>
<b>${j.nama_jalan}</b>
<small style="color:${color}; font-weight:bold;">${j.status_jalan}</small><br>
<small>${j.panjang_m} meter</small>
</span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('jalan', ${j.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${j.id}, 'jalan')">Hapus</button>
</div>
</div>
</div>`;
});
// Parsil
html += `<div class="section-title">🟩 Manajemen Kavling / Parsil</div>`;
data.parsil.forEach(p => {
html += `<div class="card item-data" data-nama="${p.nama_pemilik.toLowerCase()}">
<div style="display:flex; justify-content:space-between">
<span>
<b>${p.nama_pemilik}</b><br>
<small>Status: <b>${p.status_hak}</b></small>
</span>
<div>
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-bottom:5px;" onclick="editData('parsil', ${p.id})">Edit</button><br>
<button class="btn-del" onclick="hapusData(${p.id}, 'parsil')">Hapus</button>
</div>
</div>
</div>`;
});
}
document.getElementById('sidebarContent').innerHTML = html;
}
function renderMap(data) {
layerMasjid.clearLayers();
layerMasjidRadius.clearLayers();
layerPelaporan.clearLayers();
layerWarga.clearLayers();
layerSpbu24.clearLayers();
layerSpbuNon24.clearLayers();
layerLines.clearLayers();
layerPolygons.clearLayers();
var lokMasjid = data.masjid.map(m => L.latLng(m.latitude, m.longitude));
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
// Render Masjid / Tempat Ibadah
data.masjid.forEach(m => {
var popup = `<b>${m.jenis || 'Tempat Ibadah'}:</b> ${m.nama}<br>
<div style="margin-top:10px; text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('masjid', ${m.id})">Edit</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${m.id}, 'masjid')">Hapus</button>
</div>`;
var faClass = 'fa-mosque';
if (m.jenis === 'Gereja') faClass = 'fa-church';
else if (m.jenis === 'Vihara') faClass = 'fa-vihara';
else if (m.jenis === 'Pura') faClass = 'fa-torii-gate';
// Buat custom marker bentuk map pin dengan icon sesuai jenis
var masjidIcon = L.divIcon({
html: `
<div style="
width: 34px;
height: 34px;
background-color: #2563eb;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
border: 2px solid white;
box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
">
<i class="fas ${faClass}" style="
transform: rotate(45deg);
color: white;
font-size: 14px;
margin-top: 2px;
margin-left: -2px;
"></i>
</div>
`,
className: '', // Hilangkan background default Leaflet
iconSize: [34, 34],
iconAnchor: [17, 40], // Anchor ujung lancip di bawah
popupAnchor: [0, -40]
});
L.marker([m.latitude, m.longitude], { icon: masjidIcon, draggable: true }).addTo(layerMasjid).bindPopup(popup)
.on('dragend', function (e) { updateMarkerCoord(m.id, 'masjid', e.target.getLatLng()); });
L.circle([m.latitude, m.longitude], {
radius: currentRadius, color: '#2563eb', fillOpacity: 0.1, weight: 1
}).addTo(layerMasjidRadius);
});
// Render Pelaporan
data.pelaporan.forEach(p => {
var popup = `<b>🚨 Laporan:</b> ${p.isi_laporan}<br>
<div style="margin-top:10px; text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('pelaporan', ${p.id})">Edit</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${p.id}, 'pelaporan')">Hapus</button>
</div>`;
var pelaporanIcon = L.divIcon({
html: `
<div style="
width: 32px;
height: 32px;
background-color: #f97316; /* Orange color */
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
border: 2px solid white;
box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
">
<i class="fas fa-exclamation" style="
transform: rotate(45deg);
color: white;
font-size: 16px;
font-weight: bold;
margin-top: 2px;
margin-left: -2px;
"></i>
</div>
`,
className: '',
iconSize: [32, 32],
iconAnchor: [16, 38],
popupAnchor: [0, -38]
});
L.marker([p.latitude, p.longitude], {
icon: pelaporanIcon, draggable: true
}).addTo(layerPelaporan).bindPopup(popup)
.on('dragend', function (e) { updateMarkerCoord(p.id, 'pelaporan', e.target.getLatLng()); });
});
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
// Render SPBU
data.spbu.forEach(s => {
var is24 = s.kategori === '24jam';
var iconUrl = is24 ? 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png' : 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png';
var targetLayer = is24 ? layerSpbu24 : layerSpbuNon24;
var popup = `<b>⛽ ${s.nama}</b><br>Kategori: ${s.kategori === '24jam' ? 'Buka 24 Jam' : 'Tidak 24 Jam'}<br>
<div style="margin-top:10px; text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('spbu', ${s.id})">Edit</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${s.id}, 'spbu')">Hapus</button>
</div>`;
L.marker([s.latitude, s.longitude], {
icon: L.icon({ iconUrl: iconUrl, iconSize: [25, 41], iconAnchor: [12, 41] }), draggable: true
}).addTo(targetLayer).bindPopup(popup)
.on('dragend', function (e) { updateMarkerCoord(s.id, 'spbu', e.target.getLatLng()); });
});
}
if (!window.dashboardType || window.dashboardType === 'sosial' || window.dashboardType === 'admin') {
// Render Warga
data.warga.forEach(w => {
var pos = L.latLng(w.latitude, w.longitude);
var isCovered = lokMasjid.some(m => pos.distanceTo(m) <= currentRadius);
// Buat elemen point berbentuk map pin (teardrop) dengan ikon rumah di dalamnya
var color = isCovered ? '#16a34a' : '#ef4444'; // Hijau aman, merah di luar
var houseIcon = L.divIcon({
html: `
<div style="
width: 30px;
height: 30px;
background-color: ${color};
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
border: 2px solid white;
box-shadow: 2px 2px 5px rgba(0,0,0,0.4);
display: flex;
align-items: center;
justify-content: center;
">
<div style="
width: 18px;
height: 18px;
background-color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
">
<i class="fas fa-home" style="
transform: rotate(45deg);
color: ${color};
font-size: 10px;
"></i>
</div>
</div>
`,
className: '', // Kosongkan agar background default leaflet tidak muncul
iconSize: [30, 30],
iconAnchor: [15, 36], // Titik jangkar di ujung bawah pin
popupAnchor: [0, -36]
});
var isPenerima = (w.histori_bantuan && w.histori_bantuan.trim().length > 0) ? 'Ya' : 'Belum';
var txtPendidikan = w.pendidikan ? w.pendidikan : 'Tidak diketahui';
var txtPenyakit = (w.riwayat_penyakit && w.riwayat_penyakit.trim().length > 0) ? w.riwayat_penyakit : 'Tidak ada';
var popup = `
<div style="min-width: 200px;">
<b style="font-size: 1.1em; color: #1e293b;"><i class="fas fa-user-circle"></i> Warga: ${w.nama}</b>
<hr style="margin: 5px 0; border: 0; border-top: 1px solid #cbd5e1;">
<table style="width: 100%; font-size: 0.85em; margin-bottom: 8px;">
<tr><td style="color: #64748b; padding-right: 10px;">Jangkauan</td><td>: <b>${isCovered ? '<span style="color:#16a34a">Aman (Dekat Fasilitas)</span>' : '<span style="color:#ef4444">Di Luar</span>'}</b></td></tr>
<tr><td style="color: #64748b;">Tanggungan</td><td>: <b>${w.jumlah_kk} Jiwa</b></td></tr>
<tr><td style="color: #64748b;">Pendidikan</td><td>: ${txtPendidikan}</td></tr>
<tr><td style="color: #64748b;">Riwayat Sakit</td><td>: <span style="color: ${txtPenyakit !== 'Tidak ada' ? '#ef4444' : '#64748b'}">${txtPenyakit}</span></td></tr>
<tr><td style="color: #64748b;">Terima Bantuan</td><td>: <b>${isPenerima === 'Ya' ? '<span style="color:#16a34a">Sudah</span>' : '<span style="color:#ef4444">Belum</span>'}</b></td></tr>
</table>
<div style="font-size: 0.8em; color: #64748b; margin-bottom: 10px; background: #f8fafc; padding: 5px; border-radius: 4px; border: 1px solid #e2e8f0;">
<b>Histori:</b> ${w.histori_bantuan || '<i>Tidak ada riwayat bantuan</i>'}
</div>
<div style="text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('warga', ${w.id})">Edit Data</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${w.id}, 'warga')">Hapus</button>
</div>
</div>`;
L.marker(pos, { icon: houseIcon, draggable: true })
.addTo(layerWarga).bindPopup(popup)
.on('dragend', function (e) { updateMarkerCoord(w.id, 'warga', e.target.getLatLng()); });
});
}
if (!window.dashboardType || window.dashboardType === 'infra' || window.dashboardType === 'admin') {
// Render Jalan (Polyline)
data.jalan.forEach(j => {
var coords = JSON.parse(j.koordinat);
var color = '#ef4444'; // default red
if (j.status_jalan === 'Nasional') color = '#ef4444'; // Merah
else if (j.status_jalan === 'Provinsi') color = '#eab308'; // Kuning
else if (j.status_jalan === 'Kabupaten') color = '#22c55e'; // Hijau
var popup = `<b>🛣️ ${j.nama_jalan}</b><br>Status: ${j.status_jalan}<br>Panjang: ${j.panjang_m} meter<br>
<div style="margin-top:10px; text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('jalan', ${j.id})">Edit</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${j.id}, 'jalan')">Hapus</button>
</div>`;
var polyline = L.polyline(coords, { color: color, weight: 6 }).addTo(layerLines).bindPopup(popup);
polyline.db_id = j.id;
polyline.db_tipe = 'jalan';
drawnItems.addLayer(polyline);
});
// Render Parsil (Polygon)
data.parsil.forEach(p => {
var coords = JSON.parse(p.koordinat);
var popup = `<b>🟩 Kavling</b><br>Pemilik: ${p.nama_pemilik}<br>Status Hak: ${p.status_hak}<br>
<div style="margin-top:10px; text-align:center;">
<button class="btn-save" style="padding:4px 8px; font-size:0.75rem; width:auto; margin-right:5px;" onclick="editData('parsil', ${p.id})">Edit</button>
<button class="btn-del" style="padding:4px 8px; font-size:0.75rem; width:auto;" onclick="hapusData(${p.id}, 'parsil')">Hapus</button>
</div>`;
var polygon = L.polygon(coords, { color: '#2563eb', fillOpacity: 0.4 }).addTo(layerPolygons).bindPopup(popup);
polygon.db_id = p.id;
polygon.db_tipe = 'parsil';
drawnItems.addLayer(polygon);
});
}
}
function toggleForm() {
var j = document.getElementById('jenis').value;
['pelaporan', 'warga', 'masjid', 'spbu', 'jalan', 'parsil'].forEach(f => {
var el = document.getElementById('form_' + f);
if (el) {
el.classList.toggle('hidden', j !== f);
}
});
}
map.on('click', e => {
document.getElementById('lat').value = e.latlng.lat;
document.getElementById('lng').value = e.latlng.lng;
L.popup().setLatLng(e.latlng).setContent("Dipilih: " + e.latlng.lat.toFixed(4) + ", " + e.latlng.lng.toFixed(4)).openOn(map);
});
function simpanData() {
var j = document.getElementById('jenis').value;
var fd = new FormData();
fd.append('action', 'simpan_' + j);
fd.append('edit_id', document.getElementById('edit_id').value);
var lat = document.getElementById('lat').value;
var lng = document.getElementById('lng').value;
if (j === 'jalan') {
fd.append('nama', document.getElementById('nama_jalan').value);
fd.append('status_jalan', document.getElementById('status_jalan').value);
fd.append('panjang', document.getElementById('panjang_jalan').value);
fd.append('koordinat', document.getElementById('jalan_coords').value);
} else if (j === 'parsil') {
fd.append('nama', document.getElementById('nama_pemilik').value);
fd.append('status_hak', document.getElementById('status_hak').value);
fd.append('koordinat', document.getElementById('parsil_coords').value);
} else {
if (j !== 'geometri' && (!lat || !lng) && document.getElementById('edit_id').value === "0") {
Swal.fire({ icon: 'warning', title: 'Oops...', text: 'Silakan klik titik koordinat di peta terlebih dahulu!' });
return;
}
fd.append('lat', lat); fd.append('lng', lng);
if (j === 'pelaporan') {
fd.append('nama', document.getElementById('nama_pelapor').value);
fd.append('laporan', document.getElementById('isi_laporan').value);
} else if (j === 'warga') {
fd.append('nama', document.getElementById('nama_kk').value);
fd.append('jumlah_kk', document.getElementById('jumlah_kk').value);
fd.append('tanggal_lahir', document.getElementById('tanggal_lahir').value);
fd.append('pendidikan', document.getElementById('pendidikan').value);
fd.append('riwayat_penyakit', document.getElementById('riwayat_penyakit').value);
fd.append('histori_bantuan', document.getElementById('histori_bantuan').value);
fd.append('alamat', document.getElementById('alamat_warga').value);
} else if (j === 'masjid') {
fd.append('nama', document.getElementById('nama_masjid').value);
fd.append('jenis', document.getElementById('jenis_masjid').value);
fd.append('pic', document.getElementById('pic_masjid').value);
fd.append('alamat', document.getElementById('alamat_masjid').value);
} else if (j === 'spbu') {
fd.append('nama', document.getElementById('nama_spbu').value);
fd.append('no_hp', document.getElementById('no_hp_spbu').value);
fd.append('kategori', document.getElementById('kategori_spbu').value);
}
}
Swal.fire({ title: 'Menyimpan...', allowOutsideClick: false, didOpen: () => { Swal.showLoading() } });
fetch('../api/api.php', { method: 'POST', body: fd })
.then(res => res.json())
.then((res) => {
if (res.status === 'error') {
Swal.fire('Gagal!', res.message, 'error');
} else {
Swal.fire({ icon: 'success', title: 'Berhasil', text: 'Data berhasil disimpan!', timer: 1500, showConfirmButton: false })
.then(() => location.reload());
}
});
}
function hapusData(id, tipe) {
Swal.fire({
title: 'Hapus Data?', text: "Data yang dihapus tidak dapat dikembalikan!", icon: 'warning',
showCancelButton: true, confirmButtonColor: '#ef4444', cancelButtonColor: '#94a3b8', confirmButtonText: 'Ya, Hapus!'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({ title: 'Menghapus...', allowOutsideClick: false, didOpen: () => { Swal.showLoading() } });
var fd = new FormData();
fd.append('action', 'hapus_data');
fd.append('id', id);
fd.append('tipe', tipe);
fetch('../api/api.php', { method: 'POST', body: fd })
.then(res => res.json())
.then((res) => {
if (res.status === 'error') {
Swal.fire('Gagal!', res.message, 'error');
} else {
location.reload();
}
});
}
});
}
function fokus(lat, lng) { map.flyTo([lat, lng], 18); }
function batalEdit() {
document.getElementById('edit_id').value = "0";
document.getElementById('btnSimpanUtama').innerText = "Simpan Data";
document.getElementById('btnBatalEdit').style.display = "none";
document.getElementById('lat').value = "";
document.getElementById('lng').value = "";
}
function editData(tipe, id) {
var item = cachedData[tipe].find(x => x.id == id);
if (!item) return;
document.getElementById('edit_id').value = id;
document.getElementById('jenis').value = tipe;
toggleForm();
var panel = document.getElementById('panelInput');
if (panel) panel.classList.remove('collapsed');
if (tipe == 'warga') {
document.getElementById('nama_kk').value = item.nama;
document.getElementById('jumlah_kk').value = item.jumlah_kk;
document.getElementById('tanggal_lahir').value = item.tanggal_lahir || '';
document.getElementById('pendidikan').value = item.pendidikan || '';
document.getElementById('riwayat_penyakit').value = item.riwayat_penyakit || '';
document.getElementById('histori_bantuan').value = item.histori_bantuan || '';
document.getElementById('alamat_warga').value = item.alamat || '';
} else if (tipe == 'pelaporan') {
document.getElementById('nama_pelapor').value = item.nama_pelapor;
document.getElementById('isi_laporan').value = item.isi_laporan;
} else if (tipe == 'spbu') {
document.getElementById('nama_spbu').value = item.nama;
document.getElementById('no_hp_spbu').value = item.no_hp;
document.getElementById('kategori_spbu').value = item.kategori;
} else if (tipe == 'masjid') {
document.getElementById('nama_masjid').value = item.nama;
document.getElementById('pic_masjid').value = item.pic;
document.getElementById('alamat_masjid').value = item.alamat;
} else if (tipe == 'jalan') {
document.getElementById('nama_jalan').value = item.nama_jalan;
document.getElementById('status_jalan').value = item.status_jalan;
document.getElementById('panjang_jalan').value = item.panjang_m;
document.getElementById('jalan_coords').value = item.koordinat;
} else if (tipe == 'parsil') {
document.getElementById('nama_pemilik').value = item.nama_pemilik;
document.getElementById('status_hak').value = item.status_hak;
document.getElementById('parsil_coords').value = item.koordinat;
}
if (item.latitude && item.longitude) {
document.getElementById('lat').value = item.latitude;
document.getElementById('lng').value = item.longitude;
fokus(item.latitude, item.longitude);
}
document.getElementById('btnSimpanUtama').innerText = "Update Data";
document.getElementById('btnBatalEdit').style.display = "inline-block";
}
// INIT
loadData();
function updateMarkerCoord(id, tipe, latlng) {
var fd = new FormData();
fd.append('action', 'update_koordinat');
fd.append('id', id);
fd.append('tipe', tipe);
fd.append('lat', latlng.lat);
fd.append('lng', latlng.lng);
fetch('../api/api.php', { method: 'POST', body: fd })
.then(() => {
Swal.fire({
icon: 'success',
title: 'Tersimpan',
text: 'Posisi titik berhasil digeser!',
timer: 1500,
showConfirmButton: false,
toast: true,
position: 'top-end'
});
});
}