Initial commit - Fresh Clean Code
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
https://sigwebsite.free.je/SPBU_Marker/
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
// api_spbu.php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
$action = isset($_GET['action']) ? $_GET['action'] : '';
|
||||||
|
|
||||||
|
if ($method === 'GET' && $action === 'getAll') {
|
||||||
|
$result = $conn->query("SELECT * FROM spbu ORDER BY created_at DESC");
|
||||||
|
$data = [];
|
||||||
|
while ($row = $result->fetch_assoc()) { $data[] = $row; }
|
||||||
|
sendResponse(true, 'Data dimuat', $data);
|
||||||
|
}
|
||||||
|
elseif ($method === 'POST' && $action === 'create') {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$stmt = $conn->prepare("INSERT INTO spbu (nama, no_spbu, alamat, tipe, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->bind_param("ssssdd", $input['nama'], $input['no_spbu'], $input['alamat'], $input['tipe'], $input['latitude'], $input['longitude']);
|
||||||
|
if ($stmt->execute()) sendResponse(true, 'Berhasil simpan', ['id' => $conn->insert_id]);
|
||||||
|
else sendResponse(false, 'Gagal simpan: ' . $stmt->error);
|
||||||
|
}
|
||||||
|
elseif ($method === 'PUT' && $action === 'update') {
|
||||||
|
$id = intval($_GET['id']);
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$stmt = $conn->prepare("UPDATE spbu SET nama=?, no_spbu=?, alamat=?, tipe=?, latitude=?, longitude=? WHERE id=?");
|
||||||
|
$stmt->bind_param("ssssddi", $input['nama'], $input['no_spbu'], $input['alamat'], $input['tipe'], $input['latitude'], $input['longitude'], $id);
|
||||||
|
if ($stmt->execute()) sendResponse(true, 'Berhasil update');
|
||||||
|
else sendResponse(false, 'Gagal update');
|
||||||
|
}
|
||||||
|
elseif ($method === 'DELETE' && $action === 'delete') {
|
||||||
|
$id = intval($_GET['id']);
|
||||||
|
$stmt = $conn->prepare("DELETE FROM spbu WHERE id=?");
|
||||||
|
$stmt->bind_param("i", $id);
|
||||||
|
if ($stmt->execute()) sendResponse(true, 'Berhasil hapus');
|
||||||
|
else sendResponse(false, 'Gagal hapus');
|
||||||
|
}
|
||||||
|
?>
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
// config.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
$host = 'localhost';
|
||||||
|
$username = 'root';
|
||||||
|
$password = '';
|
||||||
|
$database = 'gis_management';
|
||||||
|
$port = 3307; // Sesuaikan port XAMPP Anda
|
||||||
|
|
||||||
|
$conn = new mysqli($host, $username, $password, $database, $port);
|
||||||
|
|
||||||
|
if ($conn->connect_error) {
|
||||||
|
die(json_encode(['success' => false, 'message' => 'Koneksi gagal: ' . $conn->connect_error]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->set_charset("utf8");
|
||||||
|
|
||||||
|
function sendResponse($success, $message, $data = null) {
|
||||||
|
echo json_encode(['success' => $success, 'message' => $message, 'data' => $data]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
+285
@@ -0,0 +1,285 @@
|
|||||||
|
<!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>
|
||||||
Reference in New Issue
Block a user