From 977dec4e36cac8aa80a9682bfdd5f75fd583bb1a Mon Sep 17 00:00:00 2001 From: Syariffullah Date: Sat, 6 Jun 2026 07:35:03 +0700 Subject: [PATCH] feat: implement web GIS layout with sidebar, custom UI controls, and modular map feature integration --- api/penduduk_miskin/read.php | 1 + api/rumah_ibadah/read.php | 1 + assets/css/style.css | 33 ++-- assets/js/features/draw_control.js | 19 +-- assets/js/features/jalan.js | 1 + assets/js/features/kemiskinan.js | 28 +++- assets/js/features/parsil.js | 1 + assets/js/features/spbu.js | 1 + assets/js/map.js | 5 + assets/js/panel.js | 4 + index.php | 252 +++++++++++++++-------------- 11 files changed, 193 insertions(+), 153 deletions(-) diff --git a/api/penduduk_miskin/read.php b/api/penduduk_miskin/read.php index c07f393..3ddb630 100644 --- a/api/penduduk_miskin/read.php +++ b/api/penduduk_miskin/read.php @@ -13,6 +13,7 @@ if($result->num_rows > 0) { "id" => $row['id'], "nama" => $row['nama'], "kategori_bantuan" => $row['kategori_bantuan'], + "jumlah_jiwa" => isset($row['jumlah_jiwa']) ? (int)$row['jumlah_jiwa'] : 1, "lat" => (float)$row['lat'], "lng" => (float)$row['lng'] ); diff --git a/api/rumah_ibadah/read.php b/api/rumah_ibadah/read.php index aa5181c..92b5a3c 100644 --- a/api/rumah_ibadah/read.php +++ b/api/rumah_ibadah/read.php @@ -12,6 +12,7 @@ if($result->num_rows > 0) { $item = array( "id" => $row['id'], "nama" => $row['nama'], + "jenis" => $row['jenis'], "alamat" => $row['alamat'], "radius" => (float)$row['radius'], "lat" => (float)$row['lat'], diff --git a/assets/css/style.css b/assets/css/style.css index d7d40e7..f80837f 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -84,10 +84,6 @@ body, html { /* Custom Layer Control Button */ .custom-layer-btn { - position: absolute; - top: 20px; - right: 20px; - z-index: 1000; background-color: white; border-radius: 8px; width: 48px; @@ -105,6 +101,18 @@ body, html { background-color: #f8f9fa; } +#layerBtn { + position: absolute; + top: 20px; + right: 20px; + z-index: 1000; + transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +body.right-panel-open #layerBtn { + right: 340px; +} + .custom-layer-panel { position: absolute; top: 80px; @@ -116,6 +124,11 @@ body, html { padding: 15px; width: 250px; display: none; + transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +body.right-panel-open .custom-layer-panel { + right: 340px; } @@ -138,13 +151,13 @@ body, html { .sidebar-toggle-btn { position: absolute; left: 20px; - top: 90px; + top: 190px; z-index: 1001; - width: 36px; - height: 36px; + border-radius: 8px; + width: 48px; + height: 48px; background: white; border: none; - border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.15); cursor: pointer; display: flex; @@ -170,7 +183,7 @@ body, html { .left-sidebar { position: absolute; left: 20px; - top: 136px; /* di bawah toggle button */ + top: 250px; /* di bawah toggle button */ z-index: 1000; display: flex; flex-direction: column; @@ -795,7 +808,7 @@ body, html { display: flex; flex-direction: column; align-items: center; - pointer-events: none; + pointer-events: auto; /* changed from none to allow click/drag */ } .emoji-marker .bubble { diff --git a/assets/js/features/draw_control.js b/assets/js/features/draw_control.js index 3fedfd5..1ef9797 100644 --- a/assets/js/features/draw_control.js +++ b/assets/js/features/draw_control.js @@ -37,36 +37,33 @@ map.on(L.Draw.Event.DRAWSTOP, function (e) { isDrawingMode = false; window.currentDrawMode = null; window.activeDrawHandler = null; - deactivateAddMode(); + window.deactivateAddMode(); }, 200); }); window.activateDraw = function(type) { - // Toggle: cek dari class button, lebih reliable daripada state variable - const btnJalan = document.getElementById('btnMenuJalan'); - const btnParsil = document.getElementById('btnMenuParsil'); - const isJalanActive = btnJalan.classList.contains('active'); - const isParsilActive = btnParsil.classList.contains('active'); - - if ((type === 'polyline' && isJalanActive) || (type === 'polygon' && isParsilActive)) { + console.log("-> activateDraw dipanggil untuk tipe:", type); + + // Toggle: jika mode yang sama ditekan lagi, batalkan + if (window.currentDrawMode === type) { + console.log("Mode", type, "sudah aktif, membatalkan..."); window.deactivateAddMode(); return; } - deactivateAddMode(); + window.deactivateAddMode(); window.currentDrawMode = type; + console.log("Mengaktifkan mode menggambar:", type); if (type === 'polyline') { const handler = new L.Draw.Polyline(map, drawControl.options.draw.polyline); handler.enable(); window.activeDrawHandler = handler; - btnJalan.classList.add('active'); if (window.cursorTooltip) window.cursorTooltip.textContent = 'Klik untuk menggambar Jalan'; } else if (type === 'polygon') { const handler = new L.Draw.Polygon(map, drawControl.options.draw.polygon); handler.enable(); window.activeDrawHandler = handler; - btnParsil.classList.add('active'); if (window.cursorTooltip) window.cursorTooltip.textContent = 'Klik untuk menggambar Parsil'; } }; diff --git a/assets/js/features/jalan.js b/assets/js/features/jalan.js index 733b72e..1baf41e 100644 --- a/assets/js/features/jalan.js +++ b/assets/js/features/jalan.js @@ -105,6 +105,7 @@ window.saveEditJalan = function(id, namaId, statusId) { .then(data => { if(data.status === 'success') { map.closePopup(); + closeModal(); loadJalan(); } else { alert(data.message); diff --git a/assets/js/features/kemiskinan.js b/assets/js/features/kemiskinan.js index 1f9fa55..f606edf 100644 --- a/assets/js/features/kemiskinan.js +++ b/assets/js/features/kemiskinan.js @@ -63,7 +63,7 @@ map.on('mouseup', function(e) { isResizing = false; map.dragging.enable(); if (resizingIbadah) { - updateIbadah(resizingIbadah.id, resizingIbadah.nama, resizingIbadah.alamat, resizingIbadah.radius, resizingIbadah.lat, resizingIbadah.lng); + updateIbadah(resizingIbadah.id, resizingIbadah.nama, resizingIbadah.jenis, resizingIbadah.alamat, resizingIbadah.radius, resizingIbadah.lat, resizingIbadah.lng); } resizingCircle = null; resizingIbadah = null; @@ -89,11 +89,17 @@ function loadRumahIbadah() { } function addIbadahMarker(item) { - const marker = L.marker([item.lat, item.lng], { icon: makeIbadahIcon(item.jenis), draggable: true }); + const lat = parseFloat(item.lat); + const lng = parseFloat(item.lng); + const radius = parseFloat(item.radius) || 100; // fallback if invalid + + if (isNaN(lat) || isNaN(lng)) return; + + const marker = L.marker([lat, lng], { icon: makeIbadahIcon(item.jenis), draggable: true }); // Lingkaran radius - const circle = L.circle([item.lat, item.lng], { - radius: item.radius, + const circle = L.circle([lat, lng], { + radius: radius, color: '#ff7800', weight: 2, fillColor: '#ff7800', @@ -153,7 +159,7 @@ function addIbadahMarker(item) { item.lat = newPos.lat; item.lng = newPos.lng; // Update ke DB - updateIbadah(item.id, item.nama, item.alamat, item.radius, newPos.lat, newPos.lng); + updateIbadah(item.id, item.nama, item.jenis, item.alamat, item.radius, newPos.lat, newPos.lng); }); rumahIbadahLayer.addLayer(circle); @@ -340,7 +346,11 @@ function updateIbadah(id, nama, jenis, alamat, radius, lat, lng) { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, nama, jenis, alamat, radius, lat, lng }) }).then(res => res.json()).then(data => { - if(data.status === 'success') { map.closePopup(); loadRumahIbadah(); } + if(data.status === 'success') { + if (typeof closeModal === 'function') closeModal(); + map.closePopup(); + loadRumahIbadah(); + } }); } @@ -375,7 +385,11 @@ function loadPendudukMiskin() { } function addMiskinMarker(item) { - const marker = L.marker([item.lat, item.lng], { icon: makeMiskinIcon(false), draggable: true }); + const lat = parseFloat(item.lat); + const lng = parseFloat(item.lng); + if (isNaN(lat) || isNaN(lng)) return; + + const marker = L.marker([lat, lng], { icon: makeMiskinIcon(false), draggable: true }); marker.miskinData = item; const d = item; diff --git a/assets/js/features/parsil.js b/assets/js/features/parsil.js index 7895ad2..a430b24 100644 --- a/assets/js/features/parsil.js +++ b/assets/js/features/parsil.js @@ -97,6 +97,7 @@ window.saveEditParsil = function(id, namaId, statusId) { .then(data => { if(data.status === 'success') { map.closePopup(); + closeModal(); loadParsil(); } else { alert(data.message); diff --git a/assets/js/features/spbu.js b/assets/js/features/spbu.js index 499209a..73778b9 100644 --- a/assets/js/features/spbu.js +++ b/assets/js/features/spbu.js @@ -175,6 +175,7 @@ function updateSpbu(id, nama, no_wa, is_24_jam, lat, lng) { .then(data => { if(data.status === 'success') { map.closePopup(); + closeModal(); loadSpbu(); } else { alert(data.message); diff --git a/assets/js/map.js b/assets/js/map.js index bb3352f..714f5f1 100644 --- a/assets/js/map.js +++ b/assets/js/map.js @@ -22,6 +22,10 @@ const geoJsonLayer = L.featureGroup().addTo(map); const searchInput = document.getElementById('searchInput'); const searchClear = document.getElementById('searchClear'); +searchInput.addEventListener('focus', function() { + if (typeof window.deactivateAddMode === 'function') window.deactivateAddMode(); +}); + searchInput.addEventListener('input', function() { const val = this.value.toLowerCase(); if (val.length > 0) { @@ -111,6 +115,7 @@ const layerBtn = document.getElementById('layerBtn'); const layerPanel = document.getElementById('layerPanel'); layerBtn.addEventListener('click', function() { + if (typeof window.deactivateAddMode === 'function') window.deactivateAddMode(); layerPanel.style.display = layerPanel.style.display === 'block' ? 'none' : 'block'; }); diff --git a/assets/js/panel.js b/assets/js/panel.js index 8fca1a2..67939c9 100644 --- a/assets/js/panel.js +++ b/assets/js/panel.js @@ -55,8 +55,10 @@ // ---- Open / Close Panel ---- function openPanel(panel) { + if (typeof window.deactivateAddMode === 'function') window.deactivateAddMode(); activePanel = panel; rightPanel.classList.add('open'); + document.body.classList.add('right-panel-open'); document.querySelectorAll('.sidebar-btn').forEach(b => b.classList.remove('active')); const activeBtn = document.querySelector(`.sidebar-btn[data-panel="${panel}"]`); if (activeBtn) activeBtn.classList.add('active'); @@ -64,8 +66,10 @@ } function closePanel() { + if (typeof window.deactivateAddMode === 'function') window.deactivateAddMode(); activePanel = null; rightPanel.classList.remove('open'); + document.body.classList.remove('right-panel-open'); document.querySelectorAll('.sidebar-btn').forEach(b => b.classList.remove('active')); } diff --git a/index.php b/index.php index a3ff930..345e427 100644 --- a/index.php +++ b/index.php @@ -76,130 +76,132 @@ - -
-
-

Data

- -
-
-
-
Pilih menu di kiri untuk menampilkan data
-
-
- - - -
-

Daftar Layer

- - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - - - - -
-
- - -
- -
- - -
-
- GeoJSON Eksternal - -
- + @@ -298,16 +300,16 @@ - + - - - - - - - - + + + + + + + +