Files
D1041231091-WebGIS-All/01/index.php
T

423 lines
17 KiB
PHP

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGIS SPBU</title>
<!-- Leaflet CSS - tanpa integrity -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
/* Reset total */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { height: 100%; }
body {
height: 100%;
font-family: sans-serif;
display: flex;
flex-direction: column;
}
/* Header */
#header {
background: #1a56db;
color: white;
padding: 0 20px;
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
z-index: 1000;
}
#header h1 { font-size: 1rem; font-weight: 700; }
#header span { font-size: 0.75rem; opacity: 0.85; }
/* PETA - ini kuncinya */
#map {
width: 100%;
height: calc(100vh - 50px);
flex: 1;
}
/* Toast */
#toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%) translateY(60px);
background: #1f2937;
color: white;
padding: 8px 20px;
border-radius: 20px;
font-size: 0.85rem;
z-index: 9999;
opacity: 0;
transition: all 0.3s;
pointer-events: none;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast.ok { background: #057a55; }
#toast.err { background: #e02424; }
/* Loading */
#loading {
display: none;
position: fixed;
inset: 0;
background: rgba(255,255,255,0.5);
z-index: 8000;
align-items: center;
justify-content: center;
}
#loading.show { display: flex; }
.spin {
width: 36px; height: 36px;
border: 4px solid #ddd;
border-top-color: #1a56db;
border-radius: 50%;
animation: sp 0.7s linear infinite;
}
@keyframes sp { to { transform: rotate(360deg); } }
/* Popup Leaflet */
.leaflet-popup-content-wrapper {
border-radius: 10px !important;
padding: 0 !important;
overflow: hidden;
min-width: 270px;
}
.leaflet-popup-content { margin: 0 !important; }
.ph {
background: #1a56db;
color: white;
padding: 10px 14px;
font-weight: 700;
font-size: 0.9rem;
}
.pb { padding: 12px 14px; background: white; }
table.dt { width: 100%; font-size: 0.82rem; border-collapse: collapse; margin-bottom: 10px; }
table.dt td { padding: 4px 3px; vertical-align: top; }
table.dt td:first-child { color: #6b7280; width: 105px; }
table.dt td:last-child { font-weight: 600; }
.fg { margin-bottom: 9px; }
.fg label { display: block; font-size: 0.75rem; font-weight: 700; color: #6b7280;
text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px; }
.fg input, .fg select {
width: 100%; padding: 6px 9px;
border: 1.5px solid #d1d9e0; border-radius: 6px;
font-size: 0.84rem; outline: none;
}
.fg input:focus, .fg select:focus { border-color: #1a56db; }
.br { display: flex; gap: 7px; margin-top: 10px; }
.btn {
flex: 1; padding: 7px 10px; border: none; border-radius: 6px;
font-size: 0.82rem; font-weight: 600; cursor: pointer;
}
.bp { background: #1a56db; color: white; }
.bd { background: #e02424; color: white; }
.bs { background: #f1f5f9; color: #333; border: 1px solid #ddd; }
.bg { background: #057a55; color: white; }
.coord-box {
background: #f0f9ff; border: 1px solid #bae6fd;
border-radius: 6px; padding: 6px 10px;
font-size: 0.78rem; color: #0369a1;
margin-bottom: 9px;
}
/* Legend */
.legend {
background: white; border-radius: 8px;
padding: 10px 12px; font-size: 0.77rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
}
.legend b { display: block; margin-bottom: 6px; font-size: 0.8rem; }
.li { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; }
.dot {
width: 13px; height: 13px; border-radius: 50%;
border: 2px solid rgba(0,0,0,0.2); flex-shrink: 0;
}
</style>
</head>
<body>
<div id="header">
<h1> WebGIS SPBU Manajemen Stasiun Bahan Bakar</h1>
<span>💡 Klik lokasi pada peta untuk menambahkan SPBU</span>
</div>
<div id="map"></div>
<div id="toast"></div>
<div id="loading"><div class="spin"></div></div>
<!-- Leaflet JS - tanpa integrity -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// ── Cek Leaflet termuat ──────────────────────────────────
if (typeof L === 'undefined') {
document.getElementById('map').innerHTML =
'<p style="padding:20px;color:red;">❌ Leaflet gagal dimuat. Periksa koneksi internet.</p>';
throw new Error('Leaflet not loaded');
}
// ── Variabel global ──────────────────────────────────────
const markersMap = {};
let tempPopup = null;
// ── Inisialisasi peta ────────────────────────────────────
const map = L.map('map').setView([-0.05566422376778593, 109.34854622732368], 17);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
// ── Legend ───────────────────────────────────────────────
const leg = L.control({ position: 'bottomright' });
leg.onAdd = () => {
const d = L.DomUtil.create('div', 'legend');
d.innerHTML = `<b>🗺️ Keterangan</b>
<div class="li"><div class="dot" style="background:#f97316"></div>Ramai · 24 Jam · Buka</div>
<div class="li"><div class="dot" style="background:#22c55e"></div>Tdk Ramai · 24 Jam · Buka</div>
<div class="li"><div class="dot" style="background:#ef4444"></div>Ramai · Tdk 24 Jam · Buka</div>
<div class="li"><div class="dot" style="background:#3b82f6"></div>Tdk Ramai · Tdk 24 Jam · Buka</div>
<div class="li"><div class="dot" style="background:#1f2937"></div>Tutup</div>`;
return d;
};
leg.addTo(map);
// ── Utilitas ─────────────────────────────────────────────
function toast(msg, type='ok') {
const t = document.getElementById('toast');
t.textContent = msg;
t.className = 'show ' + type;
setTimeout(() => t.className = '', 3000);
}
function showLoad() { document.getElementById('loading').classList.add('show'); }
function hideLoad() { document.getElementById('loading').classList.remove('show'); }
function getColor(s) {
if (s.status_operasional === 'tutup') return '#1f2937';
if (s.status_ramai === 'ramai' && s.buka_24_jam === 'ya') return '#f97316';
if (s.status_ramai === 'tidak_ramai' && s.buka_24_jam === 'ya') return '#22c55e';
if (s.status_ramai === 'ramai' && s.buka_24_jam === 'tidak') return '#ef4444';
if (s.status_ramai === 'tidak_ramai' && s.buka_24_jam === 'tidak') return '#3b82f6';
return '#9ca3af';
}
function makeIcon(spbu) {
const c = getColor(spbu);
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 36" width="26" height="38">
<path d="M12 0C5.4 0 0 5.4 0 12c0 9 12 24 12 24S24 21 24 12C24 5.4 18.6 0 12 0z"
fill="${c}" stroke="#fff" stroke-width="1.5"/>
<circle cx="12" cy="12" r="5" fill="#fff" opacity="0.9"/>
</svg>`;
return L.divIcon({ html: svg, className:'', iconSize:[26,38], iconAnchor:[13,38], popupAnchor:[0,-38] });
}
function badgeR(v) { return v==='ramai'?'<span style="background:#fef3c7;color:#92400e;padding:1px 7px;border-radius:10px;font-size:.75rem">Ramai</span>':'<span style="background:#ecfdf5;color:#065f46;padding:1px 7px;border-radius:10px;font-size:.75rem">Tidak Ramai</span>'; }
function badge24(v){ return v==='ya'?'<span style="background:#eff6ff;color:#1e40af;padding:1px 7px;border-radius:10px;font-size:.75rem">Ya</span>':'<span style="background:#f9fafb;color:#374151;padding:1px 7px;border-radius:10px;font-size:.75rem">Tidak</span>'; }
function badgeO(v) { return v==='buka'?'<span style="background:#d1fae5;color:#065f46;padding:1px 7px;border-radius:10px;font-size:.75rem">Buka</span>':'<span style="background:#fee2e2;color:#991b1b;padding:1px 7px;border-radius:10px;font-size:.75rem">Tutup</span>'; }
function popupDetail(s) {
return `<div class="ph">⛽ ${s.nama_spbu}</div>
<div class="pb">
<table class="dt">
<tr><td>Status Ramai</td><td>${badgeR(s.status_ramai)}</td></tr>
<tr><td>Buka 24 Jam</td><td>${badge24(s.buka_24_jam)}</td></tr>
<tr><td>Status</td><td>${badgeO(s.status_operasional)}</td></tr>
<tr><td>Nomor HP</td><td>📞 ${s.nomor_hp}</td></tr>
<tr><td>Latitude</td><td>${parseFloat(s.latitude).toFixed(6)}</td></tr>
<tr><td>Longitude</td><td>${parseFloat(s.longitude).toFixed(6)}</td></tr>
</table>
<div class="br">
<button class="btn bp" onclick="bukaEdit(${s.id})">✏️ Edit</button>
<button class="btn bd" onclick="hapus(${s.id},'${s.nama_spbu.replace(/'/g,"\\'")}')">🗑️ Hapus</button>
</div>
</div>`;
}
// ── Muat semua marker dari DB ────────────────────────────
async function muatMarker() {
try {
const r = await fetch('ambil_data.php');
const j = await r.json();
if (j.status !== 'success') return;
Object.values(markersMap).forEach(m => map.removeLayer(m));
for (const k in markersMap) delete markersMap[k];
j.data.forEach(addMarker);
} catch(e) { console.error(e); }
}
function addMarker(s) {
const m = L.marker([s.latitude, s.longitude], { icon: makeIcon(s) })
.addTo(map)
.bindPopup(popupDetail(s), { maxWidth: 310 });
markersMap[s.id] = m;
}
// ── Klik peta → form tambah ──────────────────────────────
map.on('click', function(e) {
const lat = e.latlng.lat.toFixed(7);
const lng = e.latlng.lng.toFixed(7);
if (tempPopup) { map.closePopup(tempPopup); tempPopup = null; }
const html = `<div class="ph">📍 Tambah SPBU Baru</div>
<div class="pb">
<div class="coord-box">📌 Lat: <b>${lat}</b> &nbsp; Lng: <b>${lng}</b></div>
<div class="fg"><label>Nama SPBU</label><input id="f-nm" placeholder="Contoh: SPBU 44.501.12" maxlength="100"></div>
<div class="fg"><label>Status Keramaian</label>
<select id="f-rm"><option value="">-- Pilih --</option>
<option value="ramai">Ramai</option><option value="tidak_ramai">Tidak Ramai</option></select></div>
<div class="fg"><label>Buka 24 Jam?</label>
<select id="f-24"><option value="">-- Pilih --</option>
<option value="ya">Ya</option><option value="tidak">Tidak</option></select></div>
<div class="fg"><label>Status Operasional</label>
<select id="f-op"><option value="">-- Pilih --</option>
<option value="buka">Buka</option><option value="tutup">Tutup</option></select></div>
<div class="fg"><label>Nomor HP</label><input id="f-hp" placeholder="0812-3456-7890" maxlength="20"></div>
<div class="br">
<button class="btn bp" onclick="simpan(${lat},${lng})">💾 Simpan</button>
<button class="btn bs" onclick="map.closePopup()">✕ Batal</button>
</div>
</div>`;
tempPopup = L.popup({ maxWidth: 320 }).setLatLng(e.latlng).setContent(html).openOn(map);
});
// ── Simpan data baru ─────────────────────────────────────
async function simpan(lat, lng) {
const nm = document.getElementById('f-nm')?.value.trim();
const rm = document.getElementById('f-rm')?.value;
const j24= document.getElementById('f-24')?.value;
const op = document.getElementById('f-op')?.value;
const hp = document.getElementById('f-hp')?.value.trim();
if (!nm||!rm||!j24||!op||!hp) { toast('⚠️ Semua field wajib diisi!','err'); return; }
const fd = new FormData();
fd.append('nama_spbu',nm); fd.append('status_ramai',rm);
fd.append('buka_24_jam',j24); fd.append('status_operasional',op);
fd.append('nomor_hp',hp); fd.append('latitude',lat); fd.append('longitude',lng);
showLoad();
try {
const r = await fetch('tambah.php',{method:'POST',body:fd});
const j = await r.json();
if (j.status==='success') {
map.closePopup(); tempPopup=null;
addMarker(j.data);
map.panTo([j.data.latitude,j.data.longitude]);
markersMap[j.data.id].openPopup();
toast('✅ Data berhasil ditambahkan!','ok');
} else { toast('❌ '+j.message,'err'); }
} catch(e) { toast('❌ Gagal ke server','err'); }
finally { hideLoad(); }
}
// ── Buka form edit ───────────────────────────────────────
async function bukaEdit(id) {
showLoad();
try {
const r = await fetch('ambil_data.php');
const j = await r.json();
hideLoad();
const s = j.data.find(x => x.id == id);
if (!s) { toast('❌ Data tidak ditemukan','err'); return; }
const html = `<div class="ph">✏️ Edit Data SPBU</div>
<div class="pb">
<div class="fg"><label>Nama SPBU</label><input id="e-nm" value="${s.nama_spbu.replace(/"/g,'&quot;')}" maxlength="100"></div>
<div class="fg"><label>Status Keramaian</label>
<select id="e-rm">
<option value="ramai" ${s.status_ramai==='ramai'?'selected':''}>Ramai</option>
<option value="tidak_ramai" ${s.status_ramai==='tidak_ramai'?'selected':''}>Tidak Ramai</option></select></div>
<div class="fg"><label>Buka 24 Jam?</label>
<select id="e-24">
<option value="ya" ${s.buka_24_jam==='ya'?'selected':''}>Ya</option>
<option value="tidak" ${s.buka_24_jam==='tidak'?'selected':''}>Tidak</option></select></div>
<div class="fg"><label>Status Operasional</label>
<select id="e-op">
<option value="buka" ${s.status_operasional==='buka'?'selected':''}>Buka</option>
<option value="tutup" ${s.status_operasional==='tutup'?'selected':''}>Tutup</option></select></div>
<div class="fg"><label>Nomor HP</label><input id="e-hp" value="${s.nomor_hp}" maxlength="20"></div>
<div class="br">
<button class="btn bg" onclick="simpanEdit(${id})">💾 Simpan</button>
<button class="btn bs" onclick="kembaliDetail(${id})">✕ Batal</button>
</div>
</div>`;
markersMap[id].setPopupContent(html).openPopup();
} catch(e) { hideLoad(); toast('❌ Gagal ke server','err'); }
}
// ── Simpan edit ──────────────────────────────────────────
async function simpanEdit(id) {
const nm = document.getElementById('e-nm')?.value.trim();
const rm = document.getElementById('e-rm')?.value;
const j24= document.getElementById('e-24')?.value;
const op = document.getElementById('e-op')?.value;
const hp = document.getElementById('e-hp')?.value.trim();
if (!nm||!rm||!j24||!op||!hp) { toast('⚠️ Semua field wajib diisi!','err'); return; }
const fd = new FormData();
fd.append('id',id); fd.append('nama_spbu',nm);
fd.append('status_ramai',rm); fd.append('buka_24_jam',j24);
fd.append('status_operasional',op); fd.append('nomor_hp',hp);
showLoad();
try {
const r = await fetch('edit.php',{method:'POST',body:fd});
const j = await r.json();
if (j.status==='success') {
markersMap[id].setIcon(makeIcon(j.data));
markersMap[id].setPopupContent(popupDetail(j.data)).openPopup();
toast('✅ Data berhasil diperbarui!','ok');
} else { toast('❌ '+j.message,'err'); }
} catch(e) { toast('❌ Gagal ke server','err'); }
finally { hideLoad(); }
}
// ── Hapus data ───────────────────────────────────────────
async function hapus(id, nama) {
if (!confirm(`Hapus SPBU "${nama}"?`)) return;
const fd = new FormData(); fd.append('id',id);
showLoad();
try {
const r = await fetch('hapus.php',{method:'POST',body:fd});
const j = await r.json();
if (j.status==='success') {
map.removeLayer(markersMap[id]); delete markersMap[id];
toast('🗑️ Data berhasil dihapus','ok');
} else { toast('❌ '+j.message,'err'); }
} catch(e) { toast('❌ Gagal ke server','err'); }
finally { hideLoad(); }
}
// ── Kembali ke detail dari form edit ────────────────────
async function kembaliDetail(id) {
try {
const r = await fetch('ambil_data.php');
const j = await r.json();
const s = j.data.find(x => x.id == id);
if (s && markersMap[id]) markersMap[id].setPopupContent(popupDetail(s)).openPopup();
} catch(e) {}
}
// ── Muat marker saat halaman dibuka ─────────────────────
muatMarker();
</script>
</body>
</html>