775 lines
37 KiB
JavaScript
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'
|
|
});
|
|
});
|
|
}
|