update fitur peta pada tambah spbu
This commit is contained in:
+244
-16
@@ -473,11 +473,85 @@
|
|||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Map SPBU (pin klik) ── */
|
||||||
|
.spbu-map-wrap {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
.spbu-map-box {
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 300px;
|
||||||
|
position: relative;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.spbu-map-box.has-pin {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
.spbu-coords-hint {
|
||||||
|
padding: 9px 12px;
|
||||||
|
background: #eff6ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: #1e40af;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.spbu-coords-hint strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
.coords-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 9px 12px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: monospace;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.coords-pill.filled {
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: #eff6ff;
|
||||||
|
}
|
||||||
|
.coords-pill span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.coords-clear {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #e2e8f0;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.coords-pill.filled .coords-clear {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Responsive ── */
|
/* ── Responsive ── */
|
||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
.map-input-wrap {
|
.map-input-wrap {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
.spbu-map-wrap {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.form-grid {
|
.form-grid {
|
||||||
@@ -554,14 +628,75 @@
|
|||||||
placeholder="Jl. ..., Kelurahan, Kecamatan"
|
placeholder="Jl. ..., Kelurahan, Kecamatan"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="fLat">Latitude *</label>
|
<!-- Peta pin SPBU -->
|
||||||
<input id="fLat" type="text" placeholder="Contoh: -0.0350" />
|
<div class="spbu-map-wrap">
|
||||||
</div>
|
<div>
|
||||||
<div class="form-group">
|
<div class="spbu-coords-hint">
|
||||||
<label for="fLng">Longitude *</label>
|
<strong>📍 Tentukan lokasi SPBU di peta</strong>
|
||||||
<input id="fLng" type="text" placeholder="Contoh: 109.3400" />
|
Klik langsung di peta untuk menempatkan pin lokasi SPBU.
|
||||||
|
Klik di tempat lain untuk memindahkan pin.
|
||||||
|
</div>
|
||||||
|
<div id="mapSpbu" class="spbu-map-box"></div>
|
||||||
|
<div class="coords-pill" id="coordsPillSpbu">
|
||||||
|
<span id="coordsTextSpbu"
|
||||||
|
>Belum ada pin. Klik di peta untuk menentukan
|
||||||
|
lokasi.</span
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="coords-clear"
|
||||||
|
id="clearPinSpbu"
|
||||||
|
title="Hapus pin"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Input lat/lng tetap ada tapi read-only dan tersembunyi untuk keperluan submit -->
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 10px">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Latitude (otomatis dari peta)</label>
|
||||||
|
<input
|
||||||
|
id="fLat"
|
||||||
|
type="text"
|
||||||
|
placeholder="Klik peta untuk mengisi"
|
||||||
|
readonly
|
||||||
|
style="
|
||||||
|
background: #f1f5f9;
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: var(--muted);
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Longitude (otomatis dari peta)</label>
|
||||||
|
<input
|
||||||
|
id="fLng"
|
||||||
|
type="text"
|
||||||
|
placeholder="Klik peta untuk mengisi"
|
||||||
|
readonly
|
||||||
|
style="
|
||||||
|
background: #f1f5f9;
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: var(--muted);
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.5;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Koordinat diisi otomatis saat kamu klik peta. Tidak perlu
|
||||||
|
mengetik secara manual.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="fJamBuka">Jam Buka *</label>
|
<label for="fJamBuka">Jam Buka *</label>
|
||||||
<input id="fJamBuka" type="time" value="06:00" />
|
<input id="fJamBuka" type="time" value="06:00" />
|
||||||
@@ -939,7 +1074,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ─── Tab switching ────────────────────────────────────────────────────────────
|
// ─── Tab switching ────────────────────────────────────────────────────────────
|
||||||
let mapsInitialized = { jalan: false, bangunan: false };
|
let mapsInitialized = { spbu: false, jalan: false, bangunan: false };
|
||||||
|
|
||||||
document.querySelectorAll(".tab-btn").forEach((btn) => {
|
document.querySelectorAll(".tab-btn").forEach((btn) => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
@@ -954,6 +1089,12 @@
|
|||||||
document.getElementById(tabId).classList.add("active");
|
document.getElementById(tabId).classList.add("active");
|
||||||
|
|
||||||
// Init peta saat tab dibuka pertama kali (Leaflet butuh container visible)
|
// Init peta saat tab dibuka pertama kali (Leaflet butuh container visible)
|
||||||
|
if (tabId === "tab-spbu" && !mapsInitialized.spbu) {
|
||||||
|
setTimeout(() => {
|
||||||
|
initSpbuMap();
|
||||||
|
mapsInitialized.spbu = true;
|
||||||
|
}, 80);
|
||||||
|
}
|
||||||
if (tabId === "tab-jalan" && !mapsInitialized.jalan) {
|
if (tabId === "tab-jalan" && !mapsInitialized.jalan) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initJalanMap();
|
initJalanMap();
|
||||||
@@ -969,6 +1110,79 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
// ─── PETA SPBU (pin klik) ────────────────────────────────────────────────────
|
||||||
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
|
let spbuMap, spbuPinMarker;
|
||||||
|
|
||||||
|
function initSpbuMap() {
|
||||||
|
spbuMap = L.map("mapSpbu").setView(PONTIANAK_CENTER, 13);
|
||||||
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution:
|
||||||
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||||
|
}).addTo(spbuMap);
|
||||||
|
|
||||||
|
spbuMap.on("click", (e) => {
|
||||||
|
const { lat, lng } = e.latlng;
|
||||||
|
setSpbuPin(lat, lng);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpbuPin(lat, lng) {
|
||||||
|
const latR = parseFloat(lat.toFixed(6));
|
||||||
|
const lngR = parseFloat(lng.toFixed(6));
|
||||||
|
|
||||||
|
// Hapus marker lama, pasang yang baru
|
||||||
|
if (spbuPinMarker) spbuMap.removeLayer(spbuPinMarker);
|
||||||
|
spbuPinMarker = L.marker([latR, lngR], {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: "",
|
||||||
|
html: `<div style="
|
||||||
|
width:36px;height:36px;display:grid;place-items:center;
|
||||||
|
background:#dc2626;border:3px solid white;border-radius:50% 50% 50% 10%;
|
||||||
|
transform:rotate(-45deg);box-shadow:0 6px 18px rgba(0,0,0,.3);">
|
||||||
|
<span style="transform:rotate(45deg);font-size:18px">⛽</span>
|
||||||
|
</div>`,
|
||||||
|
iconSize: [36, 36],
|
||||||
|
iconAnchor: [18, 34],
|
||||||
|
}),
|
||||||
|
draggable: true,
|
||||||
|
}).addTo(spbuMap);
|
||||||
|
|
||||||
|
// Drag marker juga update koordinat
|
||||||
|
spbuPinMarker.on("dragend", (ev) => {
|
||||||
|
const pos = ev.target.getLatLng();
|
||||||
|
setSpbuPin(pos.lat, pos.lng);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update input fields & pill
|
||||||
|
spbuEl.fLat.value = latR;
|
||||||
|
spbuEl.fLng.value = lngR;
|
||||||
|
document.getElementById("coordsTextSpbu").textContent =
|
||||||
|
`Lat: ${latR} | Lng: ${lngR}`;
|
||||||
|
const pill = document.getElementById("coordsPillSpbu");
|
||||||
|
pill.classList.add("filled");
|
||||||
|
document.getElementById("mapSpbu").classList.add("has-pin");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSpbuPin() {
|
||||||
|
if (spbuPinMarker) {
|
||||||
|
spbuMap.removeLayer(spbuPinMarker);
|
||||||
|
spbuPinMarker = null;
|
||||||
|
}
|
||||||
|
spbuEl.fLat.value = "";
|
||||||
|
spbuEl.fLng.value = "";
|
||||||
|
document.getElementById("coordsTextSpbu").textContent =
|
||||||
|
"Belum ada pin. Klik di peta untuk menentukan lokasi.";
|
||||||
|
document.getElementById("coordsPillSpbu").classList.remove("filled");
|
||||||
|
document.getElementById("mapSpbu").classList.remove("has-pin");
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById("clearPinSpbu")
|
||||||
|
.addEventListener("click", clearSpbuPin);
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
// ─── TAB SPBU ────────────────────────────────────────────────────────────────
|
// ─── TAB SPBU ────────────────────────────────────────────────────────────────
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -1080,16 +1294,12 @@
|
|||||||
}
|
}
|
||||||
function resetSpbuForm() {
|
function resetSpbuForm() {
|
||||||
spbuEl.editId.value = "";
|
spbuEl.editId.value = "";
|
||||||
spbuEl.fKode.value =
|
spbuEl.fKode.value = spbuEl.fNama.value = spbuEl.fAlamat.value = "";
|
||||||
spbuEl.fNama.value =
|
|
||||||
spbuEl.fAlamat.value =
|
|
||||||
spbuEl.fLat.value =
|
|
||||||
spbuEl.fLng.value =
|
|
||||||
"";
|
|
||||||
spbuEl.fJamBuka.value = "06:00";
|
spbuEl.fJamBuka.value = "06:00";
|
||||||
spbuEl.fJamTutup.value = "22:00";
|
spbuEl.fJamTutup.value = "22:00";
|
||||||
spbuEl.fOverride.value = "auto";
|
spbuEl.fOverride.value = "auto";
|
||||||
renderFuelChecks([]);
|
renderFuelChecks([]);
|
||||||
|
clearSpbuPin();
|
||||||
spbuEl.formTitle.textContent = "➕ Tambah SPBU Baru";
|
spbuEl.formTitle.textContent = "➕ Tambah SPBU Baru";
|
||||||
spbuEl.submitBtn.textContent = "💾 Simpan SPBU";
|
spbuEl.submitBtn.textContent = "💾 Simpan SPBU";
|
||||||
spbuEl.resetBtn.style.display = "none";
|
spbuEl.resetBtn.style.display = "none";
|
||||||
@@ -1101,8 +1311,6 @@
|
|||||||
spbuEl.fKode.value = spbu.kode;
|
spbuEl.fKode.value = spbu.kode;
|
||||||
spbuEl.fNama.value = spbu.nama;
|
spbuEl.fNama.value = spbu.nama;
|
||||||
spbuEl.fAlamat.value = spbu.alamat;
|
spbuEl.fAlamat.value = spbu.alamat;
|
||||||
spbuEl.fLat.value = spbu.lat;
|
|
||||||
spbuEl.fLng.value = spbu.lng;
|
|
||||||
spbuEl.fJamBuka.value = spbu.jam_buka;
|
spbuEl.fJamBuka.value = spbu.jam_buka;
|
||||||
spbuEl.fJamTutup.value = spbu.jam_tutup;
|
spbuEl.fJamTutup.value = spbu.jam_tutup;
|
||||||
spbuEl.fOverride.value = spbu.manual_override || "auto";
|
spbuEl.fOverride.value = spbu.manual_override || "auto";
|
||||||
@@ -1111,6 +1319,21 @@
|
|||||||
spbuEl.submitBtn.textContent = "💾 Update SPBU";
|
spbuEl.submitBtn.textContent = "💾 Update SPBU";
|
||||||
spbuEl.resetBtn.style.display = "";
|
spbuEl.resetBtn.style.display = "";
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
|
||||||
|
// Set pin di peta — inisialisasi peta dulu kalau belum
|
||||||
|
const doPin = () => {
|
||||||
|
setSpbuPin(spbu.lat, spbu.lng);
|
||||||
|
spbuMap.setView([spbu.lat, spbu.lng], 15);
|
||||||
|
};
|
||||||
|
if (!mapsInitialized.spbu) {
|
||||||
|
setTimeout(() => {
|
||||||
|
initSpbuMap();
|
||||||
|
mapsInitialized.spbu = true;
|
||||||
|
setTimeout(doPin, 200);
|
||||||
|
}, 80);
|
||||||
|
} else {
|
||||||
|
doPin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function submitSpbu() {
|
async function submitSpbu() {
|
||||||
const id = spbuEl.editId.value;
|
const id = spbuEl.editId.value;
|
||||||
@@ -1171,6 +1394,11 @@
|
|||||||
spbuEl.resetBtn.addEventListener("click", resetSpbuForm);
|
spbuEl.resetBtn.addEventListener("click", resetSpbuForm);
|
||||||
renderFuelChecks([]);
|
renderFuelChecks([]);
|
||||||
loadSpbu();
|
loadSpbu();
|
||||||
|
// Tab SPBU aktif saat load — init petanya langsung
|
||||||
|
setTimeout(() => {
|
||||||
|
initSpbuMap();
|
||||||
|
mapsInitialized.spbu = true;
|
||||||
|
}, 120);
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════════
|
||||||
// ─── TAB JALAN ───────────────────────────────────────────────────────────────
|
// ─── TAB JALAN ───────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
<div class="brand">
|
<div class="brand">
|
||||||
<small>Gereja Peduli</small>
|
<small>Gereja Peduli</small>
|
||||||
<h1>WebGIS Poverty Mapping</h1>
|
<h1>WebGIS Poverty Mapping</h1>
|
||||||
<p>Peta prioritas keluarga penerima bantuan berbasis SAW, terpusat, dan mendukung mode privasi.</p>
|
<p>Peta prioritas keluarga penerima bantuan berbasis SAW, terpusat, dan mudah dipantau.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
@@ -209,12 +209,6 @@
|
|||||||
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2>Filter Peta</h2>
|
<h2>Filter Peta</h2>
|
||||||
<label class="field">Role Akses
|
|
||||||
<select id="roleSelect">
|
|
||||||
<option value="admin">Admin - semua data</option>
|
|
||||||
<option value="pengurus">Pengurus - data sensitif disembunyikan</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="field">Kategori
|
<label class="field">Kategori
|
||||||
<select id="categoryFilter">
|
<select id="categoryFilter">
|
||||||
<option value="">Semua Kategori</option>
|
<option value="">Semua Kategori</option>
|
||||||
@@ -227,7 +221,6 @@
|
|||||||
<label class="field">Pencarian Keluarga
|
<label class="field">Pencarian Keluarga
|
||||||
<input id="searchInput" placeholder="Nama, alamat, atau wilayah" />
|
<input id="searchInput" placeholder="Nama, alamat, atau wilayah" />
|
||||||
</label>
|
</label>
|
||||||
<label class="switch"><span>Mode Privasi</span><input id="privacyToggle" type="checkbox" /></label>
|
|
||||||
<label class="switch"><span>Heatmap persebaran</span><input id="heatToggle" type="checkbox" checked /></label>
|
<label class="switch"><span>Heatmap persebaran</span><input id="heatToggle" type="checkbox" checked /></label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -296,11 +289,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function params() {
|
function params() {
|
||||||
const role = el("roleSelect").value;
|
|
||||||
const privacy = el("privacyToggle").checked ? "1" : "0";
|
|
||||||
const kategori = encodeURIComponent(el("categoryFilter").value);
|
const kategori = encodeURIComponent(el("categoryFilter").value);
|
||||||
const search = encodeURIComponent(el("searchInput").value.trim());
|
const search = encodeURIComponent(el("searchInput").value.trim());
|
||||||
return `role=${role}&privacy=${privacy}&kategori=${kategori}&search=${search}`;
|
return `kategori=${kategori}&search=${search}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
@@ -416,7 +407,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let debounce;
|
let debounce;
|
||||||
["roleSelect", "categoryFilter", "privacyToggle", "heatToggle"].forEach((id) => el(id).addEventListener("change", () => loadData().catch((error) => toast(error.message))));
|
["categoryFilter", "heatToggle"].forEach((id) => el(id).addEventListener("change", () => loadData().catch((error) => toast(error.message))));
|
||||||
el("searchInput").addEventListener("input", () => {
|
el("searchInput").addEventListener("input", () => {
|
||||||
clearTimeout(debounce);
|
clearTimeout(debounce);
|
||||||
debounce = setTimeout(() => loadData().catch((error) => toast(error.message)), 260);
|
debounce = setTimeout(() => loadData().catch((error) => toast(error.message)), 260);
|
||||||
|
|||||||
Reference in New Issue
Block a user