Files
SIG_Tugas_SPBU_Marker/index.html
T

285 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Sistem GIS SPBU - Mandiri</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Mengatur tinggi peta agar serasi dengan sidebar */
#map { height: 650px; width: 100%; border-radius: 12px; border: 2px solid #dee2e6; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
/* Sidebar diperpanjang ke bawah */
.sidebar-panel {
background: white; padding: 20px; border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
height: 730px; /* Tinggi total sidebar ditambah */
display: flex; flex-direction: column;
}
/* Kolom Daftar SPBU diperpanjang maksimal */
.data-list {
flex-grow: 1;
overflow-y: auto;
margin-top: 10px;
padding-right: 8px;
border-top: 1px solid #eee;
padding-top: 10px;
}
/* Desain item daftar lebih elegan */
.data-item {
background: #ffffff; padding: 15px; margin-bottom: 12px; border-radius: 10px;
cursor: pointer; border: 1px solid #e9ecef; border-left: 6px solid #0d6efd;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.data-item:hover { background: #f1f4f9; transform: translateX(5px); border-color: #0d6efd; }
.data-item strong { color: #333; display: block; margin-bottom: 3px; }
.data-item small { color: #6c757d; font-size: 0.85rem; display: block; line-height: 1.3; }
.is-editing { border-left-color: #ffc107 !important; background: #fffdf5 !important; border-color: #ffc107; }
.custom-pin { filter: drop-shadow(0 3px 3px rgba(0,0,0,0.4)); }
/* Scrollbar kustom agar terlihat modern */
.data-list::-webkit-scrollbar { width: 6px; }
.data-list::-webkit-scrollbar-track { background: #f1f1f1; }
.data-list::-webkit-scrollbar-thumb { background: #ccc; border-radius: 10px; }
</style>
</head>
<body class="bg-light">
<div class="container-fluid py-4">
<div class="row g-4">
<div class="col-md-8">
<div id="map" class="mb-3"></div>
<div class="bg-white p-3 rounded shadow-sm d-flex justify-content-between align-items-center">
<button class="btn btn-success px-4 py-2 fw-bold shadow-sm" onclick="startAdding()">
<i class="fas fa-plus-circle me-2"></i> TAMBAH SPBU BARU
</button>
<div class="text-muted small">
<i class="fas fa-info-circle text-primary me-1"></i>
<b>Navigasi:</b> Klik item di daftar untuk Edit. Geser pin di peta untuk pindah lokasi.
</div>
</div>
</div>
<div class="col-md-4">
<div class="sidebar-panel">
<div id="formSection">
<h5 id="formTitle" class="fw-bold mb-3 text-primary d-flex justify-content-between">
<span><i class="fas fa-edit me-2"></i>Form SPBU</span>
<button class="btn btn-sm btn-link p-0 text-decoration-none" onclick="resetForm()">Reset</button>
</h5>
<form id="spbuForm">
<input type="hidden" id="spbuId">
<div class="mb-2">
<label class="small fw-bold text-secondary">Nama SPBU</label>
<input type="text" id="namaSpbu" class="form-control form-control-sm border-2" placeholder="Contoh: SPBU 64.001" required>
</div>
<div class="row mb-2 g-2">
<div class="col-7">
<label class="small fw-bold text-secondary">No. Register</label>
<input type="text" id="noSpbu" class="form-control form-control-sm" placeholder="ID SPBU" required>
</div>
<div class="col-5">
<label class="small fw-bold text-secondary">Tipe</label>
<select id="tipeSpbu" class="form-select form-select-sm">
<option value="24jam">24 Jam</option>
<option value="tidak">Biasa</option>
</select>
</div>
</div>
<div class="mb-2">
<label class="small fw-bold text-secondary">Alamat (Geocode Otomatis)</label>
<textarea id="alamatSpbu" class="form-control form-control-sm" rows="2" placeholder="Klik lokasi di peta..."></textarea>
</div>
<div class="mb-3">
<label class="small fw-bold text-secondary">Koordinat (Lat, Lng)</label>
<input type="text" id="koordinatSpbu" class="form-control form-control-sm bg-light text-muted" readonly>
</div>
<button type="submit" id="btnSubmit" class="btn btn-primary w-100 fw-bold shadow-sm py-2">SIMPAN DATA</button>
</form>
</div>
<div class="mt-4 d-flex justify-content-between align-items-center">
<h6 class="fw-bold mb-0"><i class="fas fa-list-ul me-2"></i>Daftar SPBU Terdaftar</h6>
<span id="countBadges" class="badge bg-secondary rounded-pill">0</span>
</div>
<div id="spbuList" class="data-list">
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var map = L.map('map').setView([-0.026330, 109.342519], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var spbuGroup = L.layerGroup().addTo(map);
var tempMarker = null;
var isAddingMode = false;
function getIcon(tipe) {
const color = tipe === '24jam' ? '#198754' : '#dc3545';
return L.divIcon({
html: `<i class="fas fa-map-marker-alt fa-2x custom-pin" style="color: ${color};"></i>`,
className: 'custom-div-icon',
iconSize: [30, 30],
iconAnchor: [15, 30],
popupAnchor: [0, -30]
});
}
async function reverseGeocode(lat, lng) {
const target = document.getElementById('alamatSpbu');
target.value = "Sedang melacak alamat...";
try {
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`);
const data = await response.json();
target.value = data.display_name || "Alamat tidak ditemukan di sistem";
} catch (e) {
target.value = "Koneksi ke layanan alamat terputus";
}
}
async function loadData() {
const res = await fetch('api_spbu.php?action=getAll');
const json = await res.json();
spbuGroup.clearLayers();
const listContainer = document.getElementById('spbuList');
listContainer.innerHTML = '';
document.getElementById('countBadges').innerText = json.data.length;
json.data.forEach(spbu => {
const marker = L.marker([spbu.latitude, spbu.longitude], {
icon: getIcon(spbu.tipe),
draggable: true
}).addTo(spbuGroup);
marker.on('dragend', async function(e) {
const pos = e.target.getLatLng();
if(document.getElementById('spbuId').value == spbu.id) {
document.getElementById('koordinatSpbu').value = `${pos.lat.toFixed(6)}, ${pos.lng.toFixed(6)}`;
reverseGeocode(pos.lat, pos.lng);
}
await saveUpdate(spbu.id, spbu.nama, spbu.no_spbu, spbu.alamat, spbu.tipe, pos.lat, pos.lng);
});
marker.bindPopup(`<b>${spbu.nama}</b><br><button class="btn btn-danger btn-sm mt-2 w-100" onclick="deleteData(${spbu.id})">Hapus</button>`);
const item = document.createElement('div');
item.className = 'data-item';
item.id = `item-${spbu.id}`;
item.innerHTML = `
<strong>${spbu.nama}</strong>
<small><i class="fas fa-tag me-1"></i> No: ${spbu.no_spbu}</small>
<small><i class="fas fa-map-marked-alt me-1"></i> ${spbu.alamat ? spbu.alamat.substring(0, 60) + '...' : 'Alamat belum diatur'}</small>
`;
item.onclick = () => prepareEdit(spbu, marker);
listContainer.appendChild(item);
});
}
function startAdding() {
resetForm();
isAddingMode = true;
document.getElementById('map').style.cursor = 'crosshair';
}
map.on('click', function(e) {
if (!isAddingMode) return;
const {lat, lng} = e.latlng;
document.getElementById('koordinatSpbu').value = `${lat.toFixed(6)}, ${lng.toFixed(6)}`;
reverseGeocode(lat, lng);
if (tempMarker) map.removeLayer(tempMarker);
tempMarker = L.marker([lat, lng], { draggable: true }).addTo(map);
tempMarker.on('dragend', function(ev) {
const p = ev.target.getLatLng();
document.getElementById('koordinatSpbu').value = `${p.lat.toFixed(6)}, ${p.lng.toFixed(6)}`;
reverseGeocode(p.lat, p.lng);
});
isAddingMode = false;
document.getElementById('map').style.cursor = '';
});
function prepareEdit(spbu, marker) {
document.querySelectorAll('.data-item').forEach(i => i.classList.remove('is-editing'));
document.getElementById(`item-${spbu.id}`).classList.add('is-editing');
document.getElementById('formTitle').innerHTML = '<span><i class="fas fa-edit me-2"></i>Edit SPBU</span><button class="btn btn-sm btn-link p-0 text-decoration-none" onclick="resetForm()">Batal</button>';
document.getElementById('spbuId').value = spbu.id;
document.getElementById('namaSpbu').value = spbu.nama;
document.getElementById('noSpbu').value = spbu.no_spbu;
document.getElementById('alamatSpbu').value = spbu.alamat || '';
document.getElementById('tipeSpbu').value = spbu.tipe;
document.getElementById('koordinatSpbu').value = `${spbu.latitude}, ${spbu.longitude}`;
document.getElementById('btnSubmit').innerText = "UPDATE DATA SPBU";
document.getElementById('btnSubmit').className = "btn btn-warning w-100 fw-bold py-2";
map.flyTo([spbu.latitude, spbu.longitude], 17);
marker.openPopup();
}
document.getElementById('spbuForm').onsubmit = async (e) => {
e.preventDefault();
const id = document.getElementById('spbuId').value;
const coords = document.getElementById('koordinatSpbu').value.split(', ');
if(!coords[0]) return alert("Tentukan lokasi di peta terlebih dahulu!");
const data = {
nama: document.getElementById('namaSpbu').value,
no_spbu: document.getElementById('noSpbu').value,
alamat: document.getElementById('alamatSpbu').value,
tipe: document.getElementById('tipeSpbu').value,
latitude: coords[0],
longitude: coords[1]
};
const url = id ? `api_spbu.php?action=update&id=${id}` : `api_spbu.php?action=create`;
await fetch(url, { method: id ? 'PUT' : 'POST', body: JSON.stringify(data) });
resetForm();
loadData();
};
async function saveUpdate(id, nama, no, alamat, tipe, lat, lng) {
await fetch(`api_spbu.php?action=update&id=${id}`, {
method: 'PUT',
body: JSON.stringify({ nama, no_spbu: no, alamat, tipe, latitude: lat, longitude: lng })
});
}
async function deleteData(id) {
if (confirm('Apakah Anda yakin ingin menghapus data SPBU ini dari sistem?')) {
await fetch(`api_spbu.php?action=delete&id=${id}`, { method: 'DELETE' });
loadData();
resetForm();
}
}
function resetForm() {
document.getElementById('spbuForm').reset();
document.getElementById('spbuId').value = '';
document.getElementById('formTitle').innerHTML = '<span><i class="fas fa-edit me-2"></i>Form SPBU</span>';
document.getElementById('btnSubmit').innerText = "SIMPAN DATA";
document.getElementById('btnSubmit').className = "btn btn-primary w-100 fw-bold py-2";
document.querySelectorAll('.data-item').forEach(i => i.classList.remove('is-editing'));
if (tempMarker) map.removeLayer(tempMarker);
isAddingMode = false;
document.getElementById('map').style.cursor = '';
}
loadData();
</script>
</body>
</html>