Files
2026-06-10 19:48:00 +07:00

350 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<title>Peta SPBU</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family: 'Segoe UI', sans-serif; }
#map { height: 100vh; width: 100%; }
.modal-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 9999;
align-items: center;
justify-content: center;
}
.modal-overlay.show { display: flex; }
.modal-box {
background: #fff;
border-radius: 12px;
padding: 24px;
width: 300px;
box-shadow: 0 8px 32px rgba(0,0,0,0.25);
}
.modal-box h3 {
font-size: 15px;
margin-bottom: 14px;
color: #222;
border-bottom: 2px solid #f0f0f0;
padding-bottom: 10px;
}
.modal-box label {
display: block;
font-size: 12px;
font-weight: 600;
color: #555;
margin: 10px 0 3px;
}
.modal-box input, .modal-box select {
width: 100%;
padding: 8px 10px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 13px;
outline: none;
}
.modal-box input:focus, .modal-box select:focus { border-color: #3498db; }
.modal-actions { display:flex; gap:8px; margin-top:16px; }
.btn-save {
flex:1; padding:9px;
background:#3498db; color:#fff;
border:none; border-radius:6px;
font-size:13px; font-weight:600; cursor:pointer;
}
.btn-save:hover { background:#2980b9; }
.btn-cancel {
flex:1; padding:9px;
background:#f0f0f0; color:#555;
border:none; border-radius:6px;
font-size:13px; font-weight:600; cursor:pointer;
}
.btn-cancel:hover { background:#e0e0e0; }
#info-bar {
position: fixed;
bottom: 16px; left: 50%;
transform: translateX(-50%);
background: rgba(20,20,20,0.8);
color: #fff;
padding: 8px 20px;
border-radius: 20px;
font-size: 12px;
z-index: 1000;
pointer-events: none;
white-space: nowrap;
}
#toast {
position: fixed;
bottom: 56px; left: 50%;
transform: translateX(-50%) translateY(10px);
background: #2ecc71; color: #fff;
padding: 8px 22px;
border-radius: 20px;
font-size: 13px; font-weight:600;
z-index: 9998;
opacity: 0;
transition: all .3s;
pointer-events: none;
}
#toast.show { opacity:1; transform: translateX(-50%) translateY(0); }
#toast.error { background: #e74c3c; }
</style>
</head>
<body>
<div id="map"></div>
<div id="info-bar">🖱️ Klik peta untuk tambah SPBU &nbsp;·&nbsp; Klik marker untuk Edit/Hapus &nbsp;·&nbsp; Seret marker untuk pindah lokasi</div>
<div id="toast"></div>
<!-- ===== MODAL TAMBAH ===== -->
<div class="modal-overlay" id="modalTambah">
<div class="modal-box">
<h3> Tambah SPBU Baru</h3>
<input type="hidden" id="tLat">
<input type="hidden" id="tLng">
<label>Nama SPBU</label>
<input type="text" id="tNama" placeholder="Contoh: SPBU 64.751.01">
<label>No. WhatsApp</label>
<input type="text" id="tWa" placeholder="08xxxxxxxxxx">
<label>Status</label>
<select id="tStatus">
<option value="yes">🟢 Buka 24 Jam</option>
<option value="no">🔴 Tidak 24 Jam</option>
</select>
<div class="modal-actions">
<button class="btn-cancel" id="btnBatalTambah">Batal</button>
<button class="btn-save" id="btnSimpanTambah">💾 Simpan</button>
</div>
</div>
</div>
<!-- ===== MODAL EDIT ===== -->
<div class="modal-overlay" id="modalEdit">
<div class="modal-box">
<h3>✏️ Edit Data SPBU</h3>
<input type="hidden" id="eId">
<input type="hidden" id="eLat">
<input type="hidden" id="eLng">
<label>Nama SPBU</label>
<input type="text" id="eNama" placeholder="Nama SPBU...">
<label>No. WhatsApp</label>
<input type="text" id="eWa" placeholder="08xxxxxxxxxx">
<label>Status</label>
<select id="eStatus">
<option value="yes">🟢 Buka 24 Jam</option>
<option value="no">🔴 Tidak 24 Jam</option>
</select>
<div class="modal-actions">
<button class="btn-cancel" id="btnBatalEdit">Batal</button>
<button class="btn-save" id="btnSimpanEdit">💾 Simpan</button>
</div>
</div>
</div>
<script>
// ── PETA ──────────────────────────────────────────────────
const map = L.map('map').setView([-0.02, 109.34], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// ── ICON ──────────────────────────────────────────────────
function buatIcon(warna) {
return L.icon({
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${warna}.png`,
shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png',
iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34]
});
}
const ikonHijau = buatIcon('green');
const ikonMerah = buatIcon('red');
// ── TOAST ─────────────────────────────────────────────────
function toast(msg, tipe) {
const el = document.getElementById('toast');
el.textContent = msg;
el.className = 'show' + (tipe === 'err' ? ' error' : '');
setTimeout(() => el.className = '', 2800);
}
// ── LOAD DATA ─────────────────────────────────────────────
function loadData() {
fetch('ambil_data.php')
.then(r => r.json())
.then(data => {
map.eachLayer(l => { if (l instanceof L.Marker) map.removeLayer(l); });
data.forEach(d => {
const jam24 = d.status === 'yes';
const mk = L.marker([parseFloat(d.latitude), parseFloat(d.longitude)], {
icon: jam24 ? ikonHijau : ikonMerah,
draggable: true
}).addTo(map);
mk.bindPopup(popupHTML(d, jam24));
// drag → pindah lokasi
mk.on('dragend', function(e) {
const p = e.target.getLatLng();
kirim('update.php', {
id: d.id, nama: d.nama_spbu, wa: d.no_wa,
status: d.status, lat: p.lat, lng: p.lng
}).then(r => {
if (r === 'success') { toast('📍 Lokasi dipindahkan'); loadData(); }
else toast('Gagal pindah: ' + r, 'err');
});
});
});
})
.catch(err => toast('Gagal memuat data: ' + err, 'err'));
}
// ── POPUP HTML ────────────────────────────────────────────
function popupHTML(d, jam24) {
const bgBadge = jam24 ? '#d4edda' : '#f8d7da';
const clBadge = jam24 ? '#155724' : '#721c24';
const label = jam24 ? 'Buka 24 Jam' : 'Tidak 24 Jam';
return `
<div style="min-width:180px;font-family:'Segoe UI',sans-serif">
<b style="font-size:14px">${d.nama_spbu}</b><br>
<span style="color:#666;font-size:12px">📞 ${d.no_wa}</span><br>
<span style="display:inline-block;margin-top:5px;padding:2px 10px;
border-radius:12px;font-size:11px;font-weight:600;
background:${bgBadge};color:${clBadge}">⏰ ${label}</span>
<hr style="margin:8px 0;border:none;border-top:1px solid #eee">
<div style="display:flex;gap:6px">
<button onclick="bukaEdit(${d.id},'${esc(d.nama_spbu)}','${esc(d.no_wa)}','${d.status}',${d.latitude},${d.longitude})"
style="flex:1;padding:6px 0;background:#3498db;color:#fff;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600">
✏️ Edit
</button>
<button onclick="hapus(${d.id})"
style="flex:1;padding:6px 0;background:#e74c3c;color:#fff;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600">
🗑️ Hapus
</button>
</div>
</div>`;
}
function esc(s) { return String(s).replace(/\\/g,'\\\\').replace(/'/g,"\\'"); }
// ── FETCH HELPER ──────────────────────────────────────────
function kirim(url, data) {
return fetch(url, { method:'POST', body: new URLSearchParams(data) })
.then(r => r.text());
}
// ── KLIK PETA → TAMBAH ────────────────────────────────────
map.on('click', function(e) {
document.getElementById('tLat').value = e.latlng.lat;
document.getElementById('tLng').value = e.latlng.lng;
document.getElementById('tNama').value = '';
document.getElementById('tWa').value = '';
document.getElementById('tStatus').value = 'yes';
document.getElementById('modalTambah').classList.add('show');
});
document.getElementById('btnBatalTambah').onclick = function() {
document.getElementById('modalTambah').classList.remove('show');
};
document.getElementById('modalTambah').onclick = function(e) {
if (e.target === this) this.classList.remove('show');
};
document.getElementById('btnSimpanTambah').onclick = function() {
const nama = document.getElementById('tNama').value.trim();
const wa = document.getElementById('tWa').value.trim();
const status = document.getElementById('tStatus').value;
const lat = document.getElementById('tLat').value;
const lng = document.getElementById('tLng').value;
if (!nama || !wa) { toast('Nama & No. WA wajib diisi', 'err'); return; }
kirim('simpan.php', { nama, wa, status, lat, lng }).then(r => {
if (r === 'success') {
document.getElementById('modalTambah').classList.remove('show');
toast('✅ SPBU berhasil ditambahkan');
loadData();
} else {
toast('Gagal simpan: ' + r, 'err');
}
});
};
// ── EDIT ──────────────────────────────────────────────────
window.bukaEdit = function(id, nama, wa, status, lat, lng) {
document.getElementById('eId').value = id;
document.getElementById('eNama').value = nama;
document.getElementById('eWa').value = wa;
document.getElementById('eStatus').value = status;
document.getElementById('eLat').value = lat;
document.getElementById('eLng').value = lng;
document.getElementById('modalEdit').classList.add('show');
map.closePopup();
};
document.getElementById('btnBatalEdit').onclick = function() {
document.getElementById('modalEdit').classList.remove('show');
};
document.getElementById('modalEdit').onclick = function(e) {
if (e.target === this) this.classList.remove('show');
};
document.getElementById('btnSimpanEdit').onclick = function() {
const id = document.getElementById('eId').value;
const nama = document.getElementById('eNama').value.trim();
const wa = document.getElementById('eWa').value.trim();
const status = document.getElementById('eStatus').value;
const lat = document.getElementById('eLat').value;
const lng = document.getElementById('eLng').value;
if (!nama || !wa) { toast('Nama & No. WA wajib diisi', 'err'); return; }
kirim('update.php', { id, nama, wa, status, lat, lng }).then(r => {
if (r === 'success') {
document.getElementById('modalEdit').classList.remove('show');
toast('✅ Data berhasil diperbarui');
loadData();
} else {
toast('Gagal update: ' + r, 'err');
}
});
};
// ── HAPUS ─────────────────────────────────────────────────
window.hapus = function(id) {
if (!confirm('Yakin hapus SPBU ini?')) return;
kirim('hapus.php', { id }).then(r => {
if (r === 'success') {
map.closePopup();
toast('🗑️ Data dihapus');
loadData();
} else {
toast('Gagal hapus: ' + r, 'err');
}
});
};
// ── MULAI ─────────────────────────────────────────────────
loadData();
</script>
</body>
</html>