684 lines
33 KiB
HTML
684 lines
33 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="id" data-theme="light">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Input Data SPBU - WebGIS</title>
|
|
|
|
<!-- Leaflet -->
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
|
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
|
<!-- DaisyUI + Tailwind -->
|
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css" rel="stylesheet">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<!-- Font Awesome -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
|
<!-- Plus Jakarta Sans Font -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
|
|
|
<style>
|
|
* {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
}
|
|
body {
|
|
font-weight: 500;
|
|
}
|
|
h1, h2, h3, h4, h5, h6, .btn, .card-title, .stat-value {
|
|
font-weight: 700 !important;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
html, body { height: 100%; }
|
|
body { display: flex; flex-direction: column; overflow: hidden; }
|
|
.map-wrapper { flex: 1; position: relative; min-height: 0; }
|
|
#map { width: 100%; height: 100%; }
|
|
/* Drag cursor for Leaflet marker */
|
|
.leaflet-marker-draggable { cursor: grab !important; }
|
|
.leaflet-dragging .leaflet-marker-draggable { cursor: grabbing !important; }
|
|
/* Spinner inside leaflet popup */
|
|
@keyframes _spin { to { transform: rotate(360deg); } }
|
|
._spin { animation: _spin .7s linear infinite; }
|
|
|
|
/* Custom Leaflet Control Layers Styling */
|
|
.leaflet-control-layers {
|
|
border-radius: 12px !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15) !important;
|
|
backdrop-filter: blur(8px);
|
|
-webkit-backdrop-filter: blur(8px);
|
|
background: rgba(255, 255, 255, 0.85) !important;
|
|
padding: 8px 12px !important;
|
|
font-family: inherit !important;
|
|
font-weight: 700 !important;
|
|
font-size: 0.85rem !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
.leaflet-control-layers:hover {
|
|
background: rgba(255, 255, 255, 0.95) !important;
|
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.25) !important;
|
|
}
|
|
.leaflet-control-layers-list label {
|
|
margin-bottom: 4px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 4px 6px;
|
|
border-radius: 6px;
|
|
font-weight: 600 !important;
|
|
}
|
|
.leaflet-control-layers-list label:hover {
|
|
background: rgba(0, 0, 0, 0.05);
|
|
}
|
|
.leaflet-control-layers-separator {
|
|
margin: 8px 0 !important;
|
|
border-top: 1px solid rgba(0, 0, 0, 0.1) !important;
|
|
}
|
|
.leaflet-control-layers-selector {
|
|
accent-color: #2563eb;
|
|
width: 16px;
|
|
height: 16px;
|
|
margin-top: 0 !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<!-- ═══ NAVBAR ═══════════════════════════════════════════════════════════ -->
|
|
<div class="navbar bg-base-100 border-b border-base-200 shadow-sm flex-shrink-0 z-[1000]">
|
|
<div class="flex-1">
|
|
<a href="../index.html" class="btn btn-ghost text-lg font-bold gap-2 text-primary">
|
|
<i class="fa-solid fa-gas-pump"></i>
|
|
WebGIS SPBU
|
|
</a>
|
|
</div>
|
|
<div class="flex-none">
|
|
<ul class="menu menu-horizontal px-1 gap-1">
|
|
<li>
|
|
<a href="input.html"
|
|
class="font-semibold bg-primary/10 text-primary rounded-lg">
|
|
<i class="fa-solid fa-map-location-dot"></i> Input Data
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="showdata.html" class="font-medium">
|
|
<i class="fa-solid fa-table-list"></i> Lihat Data
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ MAP ══════════════════════════════════════════════════════════════ -->
|
|
<div class="map-wrapper">
|
|
<div id="map"></div>
|
|
|
|
<!-- Normal hint -->
|
|
<div id="mapHint"
|
|
class="absolute bottom-8 left-1/2 -translate-x-1/2 z-[999] pointer-events-none
|
|
badge badge-primary badge-lg py-3 px-4 gap-2 shadow-md">
|
|
<i class="fa-solid fa-hand-pointer"></i>
|
|
Klik peta untuk tambah SPBU • Drag marker untuk pindah lokasi
|
|
</div>
|
|
|
|
<!-- Drag hint -->
|
|
<div id="dragHint"
|
|
class="absolute bottom-8 left-1/2 -translate-x-1/2 z-[999] pointer-events-none hidden
|
|
badge badge-warning badge-lg py-3 px-4 gap-2 shadow-md">
|
|
<i class="fa-solid fa-arrows-up-down-left-right"></i>
|
|
Lepaskan untuk menyimpan lokasi baru
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ MODAL: INPUT (dialog DaisyUI) ════════════════════════════════════ -->
|
|
<dialog id="inputModal" class="modal">
|
|
<div class="modal-box">
|
|
<!-- Header -->
|
|
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
|
id="btnCloseModal">✕</button>
|
|
<h3 class="font-bold text-lg flex items-center gap-2 mb-1">
|
|
<i class="fa-solid fa-gas-pump text-primary"></i>
|
|
Tambah Data SPBU
|
|
</h3>
|
|
<p class="text-sm text-base-content/50 mb-4">Isi atribut titik lokasi baru</p>
|
|
|
|
<!-- Koordinat -->
|
|
<div class="alert alert-info py-2 mb-4 text-sm">
|
|
<i class="fa-solid fa-location-crosshairs"></i>
|
|
<span id="coordText">Memuat koordinat...</span>
|
|
</div>
|
|
|
|
<!-- Form -->
|
|
<label class="form-control mb-3">
|
|
<div class="label"><span class="label-text font-semibold">Nama SPBU</span></div>
|
|
<input type="text" id="namaSPBU" class="input input-bordered w-full"
|
|
placeholder="Contoh: SPBU Jl. Ahmad Yani" autocomplete="off">
|
|
</label>
|
|
|
|
<label class="form-control mb-3">
|
|
<div class="label"><span class="label-text font-semibold">Nomor SPBU</span></div>
|
|
<input type="text" id="nomorSPBU" class="input input-bordered w-full"
|
|
placeholder="Contoh: 64.751.01" autocomplete="off">
|
|
</label>
|
|
|
|
<label class="form-control mb-4">
|
|
<div class="label"><span class="label-text font-semibold">Status Operasional</span></div>
|
|
<select id="statusSPBU" class="select select-bordered w-full">
|
|
<option value="">-- Pilih Status --</option>
|
|
<option value="Buka 24 Jam">☀️🌙 Buka 24 Jam</option>
|
|
<option value="Tidak Buka 24 Jam">⏰ Tidak Buka 24 Jam</option>
|
|
</select>
|
|
</label>
|
|
|
|
<div class="modal-action">
|
|
<button class="btn" id="btnCancel">
|
|
<i class="fa-solid fa-xmark"></i> Batal
|
|
</button>
|
|
<button class="btn btn-primary" id="btnSimpan">
|
|
<i class="fa-solid fa-floppy-disk"></i> Simpan
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Backdrop click = close -->
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button id="btnBackdropInput">close</button>
|
|
</form>
|
|
</dialog>
|
|
|
|
<!-- ═══ MODAL: EDIT ═══════════════════════════════════════════════════════ -->
|
|
<dialog id="editModal" class="modal">
|
|
<div class="modal-box">
|
|
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
|
id="btnCloseEditModal">✕</button>
|
|
<h3 class="font-bold text-lg flex items-center gap-2 mb-1 text-warning">
|
|
<i class="fa-solid fa-pen-to-square"></i>
|
|
Edit Data SPBU
|
|
</h3>
|
|
<p class="text-sm text-base-content/50 mb-4">Perbarui atribut titik lokasi</p>
|
|
|
|
<div class="alert alert-info py-2 mb-4 text-sm">
|
|
<i class="fa-solid fa-location-crosshairs"></i>
|
|
<span id="editCoordText">Memuat koordinat...</span>
|
|
</div>
|
|
|
|
<label class="form-control mb-3">
|
|
<div class="label"><span class="label-text font-semibold">Nama SPBU</span></div>
|
|
<input type="text" id="editNamaSPBU" class="input input-bordered w-full"
|
|
placeholder="Contoh: SPBU Jl. Ahmad Yani" autocomplete="off">
|
|
</label>
|
|
|
|
<label class="form-control mb-3">
|
|
<div class="label"><span class="label-text font-semibold">Nomor SPBU</span></div>
|
|
<input type="text" id="editNomorSPBU" class="input input-bordered w-full"
|
|
placeholder="Contoh: 64.751.01" autocomplete="off">
|
|
</label>
|
|
|
|
<label class="form-control mb-4">
|
|
<div class="label"><span class="label-text font-semibold">Status Operasional</span></div>
|
|
<select id="editStatusSPBU" class="select select-bordered w-full">
|
|
<option value="">-- Pilih Status --</option>
|
|
<option value="Buka 24 Jam">☀️🌙 Buka 24 Jam</option>
|
|
<option value="Tidak Buka 24 Jam">⏰ Tidak Buka 24 Jam</option>
|
|
</select>
|
|
</label>
|
|
|
|
<div class="modal-action">
|
|
<button class="btn" id="btnCancelEdit">
|
|
<i class="fa-solid fa-xmark"></i> Batal
|
|
</button>
|
|
<button class="btn btn-warning text-white" id="btnUpdate">
|
|
<i class="fa-solid fa-floppy-disk"></i> Perbarui
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button id="btnBackdropEdit">close</button>
|
|
</form>
|
|
</dialog>
|
|
|
|
<!-- ═══ MODAL: KONFIRMASI HAPUS ══════════════════════════════════════════ -->
|
|
<dialog id="deleteModal" class="modal">
|
|
<div class="modal-box text-center max-w-sm">
|
|
<div class="text-5xl mb-3">🗑️</div>
|
|
<h3 class="font-bold text-lg mb-1">Hapus Data SPBU?</h3>
|
|
<p class="text-sm text-base-content/60 mb-1">
|
|
Data <strong id="deleteSpbuName"></strong> akan dihapus secara permanen.
|
|
</p>
|
|
<p class="text-sm text-error mb-5">⚠️ Tindakan ini tidak dapat dibatalkan.</p>
|
|
|
|
<div class="modal-action justify-center">
|
|
<button class="btn" id="btnCancelDelete">
|
|
<i class="fa-solid fa-xmark"></i> Batal
|
|
</button>
|
|
<button class="btn btn-error" id="btnConfirmDelete">
|
|
<i class="fa-solid fa-trash"></i> Ya, Hapus
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button id="btnBackdropDelete">close</button>
|
|
</form>
|
|
</dialog>
|
|
|
|
<!-- ═══ TOAST CONTAINER ════════════════════════════════════════════════════ -->
|
|
<div class="toast toast-end toast-bottom z-[99999]" id="toastContainer"></div>
|
|
|
|
|
|
<!-- ═══ JAVASCRIPT ════════════════════════════════════════════════════════ -->
|
|
<script>
|
|
// ── CONFIG ──────────────────────────────────────────────────────────────
|
|
const API = '../api';
|
|
|
|
// ── MODAL REFS ─────────────────────────────────────────────────────────
|
|
const inputModal = document.getElementById('inputModal');
|
|
const editModal = document.getElementById('editModal');
|
|
const deleteModal = document.getElementById('deleteModal');
|
|
|
|
// ── MAP INIT ────────────────────────────────────────────────────────────
|
|
const map = L.map('map', {
|
|
center: [-0.06058602521208944, 109.34498961534207],
|
|
zoom: 16
|
|
});
|
|
|
|
// 1. Base Maps
|
|
const osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
});
|
|
|
|
const esriSatellite = L.layerGroup([
|
|
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
|
maxZoom: 19,
|
|
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
|
|
}),
|
|
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}', {
|
|
maxZoom: 19
|
|
}),
|
|
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}', {
|
|
maxZoom: 19
|
|
})
|
|
]).addTo(map);
|
|
|
|
// 2. Layer Groups (Overlays)
|
|
const layer24h = L.layerGroup().addTo(map);
|
|
const layerRegular = L.layerGroup().addTo(map);
|
|
|
|
const baseMaps = {
|
|
"Satelit (Hybrid)": esriSatellite,
|
|
"Standard Map": osm
|
|
};
|
|
|
|
const overlays = {
|
|
"SPBU Buka 24 Jam": layer24h,
|
|
"SPBU Tidak Buka 24 Jam": layerRegular
|
|
};
|
|
|
|
L.control.layers(baseMaps, overlays, { collapsed: false }).addTo(map);
|
|
|
|
function createSpbuIcon(color = '#2563eb') {
|
|
const svg = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 42" width="32" height="42">
|
|
<path d="M16 0C7.163 0 0 7.163 0 16c0 12 16 26 16 26S32 28 32 16C32 7.163 24.837 0 16 0z"
|
|
fill="${color}" stroke="white" stroke-width="2"/>
|
|
<text x="16" y="21" text-anchor="middle" fill="white"
|
|
font-size="13" font-family="Arial" font-weight="bold">⛽</text>
|
|
</svg>`;
|
|
return L.divIcon({ html: svg, className: '', iconSize:[32,42], iconAnchor:[16,42], popupAnchor:[0,-44] });
|
|
}
|
|
|
|
// ── STATE ───────────────────────────────────────────────────────────────
|
|
let pendingLatLng = null;
|
|
let tempMarker = null;
|
|
const spbuMap = new Map(); // id => { marker, data }
|
|
|
|
// ── POPUP CONTENT ───────────────────────────────────────────────────────
|
|
function buildPopupContent(spbu) {
|
|
const isOpen = spbu.status === 'Buka 24 Jam';
|
|
const badgeCls = isOpen ? 'badge-success' : 'badge-error';
|
|
const color = isOpen ? '#16a34a' : '#dc2626';
|
|
const lat = parseFloat(spbu.latitude).toFixed(6);
|
|
const lng = parseFloat(spbu.longitude).toFixed(6);
|
|
return `
|
|
<div style="font-family:ui-sans-serif,system-ui,sans-serif;min-width:210px">
|
|
<div style="font-weight:700;font-size:0.95rem;margin-bottom:5px">⛽ ${spbu.nama_spbu}</div>
|
|
<div style="font-size:0.8rem;color:#6b7280;margin-bottom:6px">
|
|
<b>No:</b> ${spbu.nomor_spbu}
|
|
</div>
|
|
<span class="badge ${badgeCls}" style="font-size:0.72rem">
|
|
${isOpen ? '🟢' : '🔴'} ${spbu.status}
|
|
</span>
|
|
<div style="font-size:0.7rem;color:#9ca3af;margin-top:5px">${lat}, ${lng}</div>
|
|
<div style="display:flex;gap:6px;margin-top:10px;padding-top:8px;border-top:1px solid #f3f4f6">
|
|
<button class="btn btn-xs btn-outline btn-primary"
|
|
onclick="openEditModal(${spbu.id})">
|
|
<i class="fa-solid fa-pen-to-square"></i> Edit
|
|
</button>
|
|
<button class="btn btn-xs btn-outline btn-error"
|
|
onclick="openDeleteConfirm(${spbu.id})">
|
|
<i class="fa-solid fa-trash"></i> Hapus
|
|
</button>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
// ── LOAD DATA ───────────────────────────────────────────────────────────
|
|
function loadExistingData() {
|
|
fetch(`${API}/get_spbu.php`)
|
|
.then(r => r.json())
|
|
.then(data => data.forEach(addSpbuMarker))
|
|
.catch(() => console.log('Tidak dapat memuat data dari server.'));
|
|
}
|
|
|
|
function addSpbuMarker(spbu) {
|
|
const isOpen = spbu.status === 'Buka 24 Jam';
|
|
const color = isOpen ? '#16a34a' : '#dc2626';
|
|
const group = isOpen ? layer24h : layerRegular;
|
|
|
|
const m = L.marker([parseFloat(spbu.latitude), parseFloat(spbu.longitude)], {
|
|
icon: createSpbuIcon(color),
|
|
draggable: true
|
|
})
|
|
.addTo(group)
|
|
.bindPopup(buildPopupContent(spbu), { maxWidth: 270 });
|
|
|
|
const id = parseInt(spbu.id);
|
|
spbuMap.set(id, { marker: m, data: { ...spbu } });
|
|
|
|
// ── DRAG START
|
|
m.on('dragstart', function () {
|
|
this.setOpacity(0.55);
|
|
map.closePopup();
|
|
document.getElementById('dragHint').classList.remove('hidden');
|
|
document.getElementById('mapHint').classList.add('hidden');
|
|
});
|
|
|
|
// ── DRAG END → update DB
|
|
m.on('dragend', function (e) {
|
|
this.setOpacity(1);
|
|
document.getElementById('dragHint').classList.add('hidden');
|
|
document.getElementById('mapHint').classList.remove('hidden');
|
|
|
|
const newLatLng = e.target.getLatLng();
|
|
const entry = spbuMap.get(id);
|
|
if (!entry) return;
|
|
|
|
// Loading popup
|
|
this.bindPopup(`
|
|
<div style="font-family:ui-sans-serif,system-ui,sans-serif;
|
|
padding:12px;text-align:center;min-width:160px">
|
|
<span class="loading loading-spinner loading-sm text-warning"></span>
|
|
<span style="font-size:0.84rem;margin-left:6px;vertical-align:middle">
|
|
Menyimpan lokasi baru...
|
|
</span>
|
|
</div>`, { maxWidth: 270 }).openPopup();
|
|
|
|
fetch(`${API}/update_spbu.php`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
id,
|
|
nama_spbu : entry.data.nama_spbu,
|
|
nomor_spbu : entry.data.nomor_spbu,
|
|
status : entry.data.status,
|
|
latitude : newLatLng.lat,
|
|
longitude : newLatLng.lng
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
if (res.success) {
|
|
entry.data.latitude = newLatLng.lat;
|
|
entry.data.longitude = newLatLng.lng;
|
|
this.setPopupContent(buildPopupContent(entry.data)).openPopup();
|
|
showToast(`📍 Lokasi ${entry.data.nama_spbu} berhasil dipindahkan!`, 'success');
|
|
} else {
|
|
this.setLatLng([parseFloat(entry.data.latitude), parseFloat(entry.data.longitude)]);
|
|
this.setPopupContent(buildPopupContent(entry.data)).openPopup();
|
|
showToast('Gagal memperbarui lokasi: ' + (res.message || 'Error'), 'error');
|
|
}
|
|
})
|
|
.catch(() => {
|
|
this.setLatLng([parseFloat(entry.data.latitude), parseFloat(entry.data.longitude)]);
|
|
this.setPopupContent(buildPopupContent(entry.data)).openPopup();
|
|
showToast('Tidak dapat terhubung ke server!', 'error');
|
|
});
|
|
});
|
|
}
|
|
|
|
loadExistingData();
|
|
|
|
// ── MAP CLICK → buka modal tambah ──────────────────────────────────────
|
|
map.on('click', function (e) {
|
|
pendingLatLng = e.latlng;
|
|
if (tempMarker) map.removeLayer(tempMarker);
|
|
tempMarker = L.marker(e.latlng, {
|
|
icon: createSpbuIcon('#d97706'), opacity: 0.85
|
|
}).addTo(map);
|
|
|
|
document.getElementById('coordText').textContent =
|
|
`Lat: ${e.latlng.lat.toFixed(6)}, Lng: ${e.latlng.lng.toFixed(6)}`;
|
|
document.getElementById('namaSPBU').value = '';
|
|
document.getElementById('nomorSPBU').value = '';
|
|
document.getElementById('statusSPBU').value = '';
|
|
|
|
inputModal.showModal();
|
|
setTimeout(() => document.getElementById('namaSPBU').focus(), 300);
|
|
});
|
|
|
|
// ── CLOSE INPUT MODAL ──────────────────────────────────────────────────
|
|
function closeModal(removeTemp = true) {
|
|
inputModal.close();
|
|
if (removeTemp && tempMarker) { map.removeLayer(tempMarker); tempMarker = null; }
|
|
pendingLatLng = null;
|
|
}
|
|
|
|
document.getElementById('btnCloseModal').addEventListener('click', () => closeModal());
|
|
document.getElementById('btnCancel').addEventListener('click', () => closeModal());
|
|
// Backdrop closes via method=dialog; cleanup state via 'close' event
|
|
inputModal.addEventListener('close', () => {
|
|
if (tempMarker) { map.removeLayer(tempMarker); tempMarker = null; }
|
|
pendingLatLng = null;
|
|
});
|
|
|
|
// ── SIMPAN DATA ────────────────────────────────────────────────────────
|
|
document.getElementById('btnSimpan').addEventListener('click', function () {
|
|
const nama = document.getElementById('namaSPBU').value.trim();
|
|
const nomor = document.getElementById('nomorSPBU').value.trim();
|
|
const status = document.getElementById('statusSPBU').value;
|
|
|
|
if (!nama) { document.getElementById('namaSPBU').focus(); showToast('Nama SPBU tidak boleh kosong!', 'error'); return; }
|
|
if (!nomor) { document.getElementById('nomorSPBU').focus(); showToast('Nomor SPBU tidak boleh kosong!', 'error'); return; }
|
|
if (!status) { document.getElementById('statusSPBU').focus(); showToast('Pilih status operasional!', 'error'); return; }
|
|
|
|
const btn = document.getElementById('btnSimpan');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="loading loading-spinner loading-sm"></span> Menyimpan...';
|
|
|
|
fetch(`${API}/save_spbu.php`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ nama_spbu: nama, nomor_spbu: nomor, status,
|
|
latitude: pendingLatLng.lat, longitude: pendingLatLng.lng })
|
|
})
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-floppy-disk"></i> Simpan';
|
|
|
|
if (res.success) {
|
|
if (tempMarker) map.removeLayer(tempMarker);
|
|
tempMarker = null;
|
|
addSpbuMarker({ id: res.id, nama_spbu: nama, nomor_spbu: nomor, status,
|
|
latitude: pendingLatLng.lat, longitude: pendingLatLng.lng });
|
|
const entry = spbuMap.get(parseInt(res.id));
|
|
if (entry) entry.marker.openPopup();
|
|
inputModal.close();
|
|
pendingLatLng = null;
|
|
showToast('Data SPBU berhasil disimpan! ✅', 'success');
|
|
} else {
|
|
showToast('Gagal menyimpan: ' + (res.message || 'Error server'), 'error');
|
|
}
|
|
})
|
|
.catch(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-floppy-disk"></i> Simpan';
|
|
showToast('Tidak dapat terhubung ke server PHP!', 'error');
|
|
});
|
|
});
|
|
|
|
// ── TOAST ──────────────────────────────────────────────────────────────
|
|
function showToast(msg, type = 'info') {
|
|
const t = document.createElement('div');
|
|
const cls = type === 'success' ? 'alert-success' : type === 'error' ? 'alert-error' : 'alert-info';
|
|
t.className = `alert ${cls} shadow-md`;
|
|
t.innerHTML = `<span>${msg}</span>`;
|
|
document.getElementById('toastContainer').appendChild(t);
|
|
setTimeout(() => t.remove(), 3500);
|
|
}
|
|
|
|
// ── EDIT MODAL ─────────────────────────────────────────────────────────
|
|
let editingId = null;
|
|
|
|
function openEditModal(id) {
|
|
const entry = spbuMap.get(id);
|
|
if (!entry) return;
|
|
editingId = id;
|
|
const { data } = entry;
|
|
|
|
document.getElementById('editNamaSPBU').value = data.nama_spbu;
|
|
document.getElementById('editNomorSPBU').value = data.nomor_spbu;
|
|
document.getElementById('editStatusSPBU').value = data.status;
|
|
document.getElementById('editCoordText').textContent =
|
|
`Lat: ${parseFloat(data.latitude).toFixed(6)}, Lng: ${parseFloat(data.longitude).toFixed(6)}`;
|
|
|
|
map.closePopup();
|
|
editModal.showModal();
|
|
setTimeout(() => document.getElementById('editNamaSPBU').focus(), 300);
|
|
}
|
|
|
|
function closeEditModal() { editModal.close(); editingId = null; }
|
|
|
|
document.getElementById('btnCloseEditModal').addEventListener('click', closeEditModal);
|
|
document.getElementById('btnCancelEdit').addEventListener('click', closeEditModal);
|
|
editModal.addEventListener('close', () => { editingId = null; });
|
|
|
|
document.getElementById('btnUpdate').addEventListener('click', function () {
|
|
const nama = document.getElementById('editNamaSPBU').value.trim();
|
|
const nomor = document.getElementById('editNomorSPBU').value.trim();
|
|
const status = document.getElementById('editStatusSPBU').value;
|
|
|
|
if (!nama) { showToast('Nama SPBU tidak boleh kosong!', 'error'); document.getElementById('editNamaSPBU').focus(); return; }
|
|
if (!nomor) { showToast('Nomor SPBU tidak boleh kosong!', 'error'); document.getElementById('editNomorSPBU').focus(); return; }
|
|
if (!status) { showToast('Pilih status operasional!', 'error'); document.getElementById('editStatusSPBU').focus(); return; }
|
|
|
|
const entry = spbuMap.get(editingId);
|
|
if (!entry) return;
|
|
|
|
const btn = document.getElementById('btnUpdate');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="loading loading-spinner loading-sm"></span> Menyimpan...';
|
|
|
|
fetch(`${API}/update_spbu.php`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
id: editingId, nama_spbu: nama, nomor_spbu: nomor, status,
|
|
latitude: parseFloat(entry.data.latitude),
|
|
longitude: parseFloat(entry.data.longitude)
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-floppy-disk"></i> Perbarui';
|
|
|
|
if (res.success) {
|
|
const oldStatus = entry.data.status;
|
|
entry.data.nama_spbu = nama;
|
|
entry.data.nomor_spbu = nomor;
|
|
entry.data.status = status;
|
|
|
|
const color = status === 'Buka 24 Jam' ? '#16a34a' : '#dc2626';
|
|
entry.marker.setIcon(createSpbuIcon(color));
|
|
entry.marker.setPopupContent(buildPopupContent(entry.data));
|
|
|
|
// Move to correct layer group if status changed
|
|
if (oldStatus !== status) {
|
|
const oldGroup = oldStatus === 'Buka 24 Jam' ? layer24h : layerRegular;
|
|
const newGroup = status === 'Buka 24 Jam' ? layer24h : layerRegular;
|
|
oldGroup.removeLayer(entry.marker);
|
|
newGroup.addLayer(entry.marker);
|
|
}
|
|
|
|
closeEditModal();
|
|
showToast('Data SPBU berhasil diperbarui! ✏️', 'success');
|
|
} else {
|
|
showToast('Gagal memperbarui: ' + (res.message || 'Error server'), 'error');
|
|
}
|
|
})
|
|
.catch(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-floppy-disk"></i> Perbarui';
|
|
showToast('Tidak dapat terhubung ke server PHP!', 'error');
|
|
});
|
|
});
|
|
|
|
// ── DELETE CONFIRM ─────────────────────────────────────────────────────
|
|
let deletingId = null;
|
|
|
|
function openDeleteConfirm(id) {
|
|
const entry = spbuMap.get(id);
|
|
if (!entry) return;
|
|
deletingId = id;
|
|
document.getElementById('deleteSpbuName').textContent = entry.data.nama_spbu;
|
|
map.closePopup();
|
|
deleteModal.showModal();
|
|
}
|
|
|
|
function closeDeleteModal() { deleteModal.close(); deletingId = null; }
|
|
|
|
document.getElementById('btnCancelDelete').addEventListener('click', closeDeleteModal);
|
|
deleteModal.addEventListener('close', () => { deletingId = null; });
|
|
|
|
document.getElementById('btnConfirmDelete').addEventListener('click', function () {
|
|
if (!deletingId) return;
|
|
const entry = spbuMap.get(deletingId);
|
|
if (!entry) return;
|
|
|
|
const btn = document.getElementById('btnConfirmDelete');
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="loading loading-spinner loading-sm"></span> Menghapus...';
|
|
|
|
fetch(`${API}/delete_spbu.php`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id: deletingId, _method: 'DELETE' })
|
|
})
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-trash"></i> Ya, Hapus';
|
|
|
|
if (res.success) {
|
|
map.removeLayer(entry.marker);
|
|
spbuMap.delete(deletingId);
|
|
closeDeleteModal();
|
|
showToast('Data SPBU berhasil dihapus! 🗑️', 'success');
|
|
} else {
|
|
showToast('Gagal menghapus: ' + (res.message || 'Error server'), 'error');
|
|
}
|
|
})
|
|
.catch(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fa-solid fa-trash"></i> Ya, Hapus';
|
|
showToast('Tidak dapat terhubung ke server!', 'error');
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|