fix: update final code deployment

This commit is contained in:
muthianura
2026-06-15 21:53:17 +07:00
parent 62a5a26fe0
commit 9cc6fcf52d
42 changed files with 177 additions and 23 deletions
+12
View File
@@ -0,0 +1,12 @@
FROM php:8.2-apache
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
RUN a2enmod rewrite
COPY . /var/www/html/
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html
EXPOSE 80
+47
View File
@@ -0,0 +1,47 @@
<?php
include 'db.php';
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
$id = (int) $_GET['id'];
$conn->query("DELETE FROM tempat WHERE id = $id");
header('Location: index.php');
exit;
}
if (isset($_GET['action']) && $_GET['action'] === 'edit' &&
isset($_GET['id'], $_GET['nama'], $_GET['nomor'], $_GET['status'])) {
$id = (int) $_GET['id'];
$nama = $conn->real_escape_string($_GET['nama']);
$nomor = $conn->real_escape_string($_GET['nomor']);
$status = $conn->real_escape_string($_GET['status']);
$conn->query("UPDATE tempat SET nama_tempat='$nama', nomor_tempat='$nomor', status='$status' WHERE id=$id");
header('Location: index.php');
exit;
}
if (isset($_GET['action']) && $_GET['action'] === 'update_pos' &&
isset($_GET['id'], $_GET['lat'], $_GET['lng'])) {
$id = (int) $_GET['id'];
$lat = (float) $_GET['lat'];
$lng = (float) $_GET['lng'];
$result = $conn->query("UPDATE tempat SET latitude='$lat', longitude='$lng' WHERE id=$id");
header('Content-Type: text/plain');
echo ($result) ? 'ok' : 'error: ' . $conn->error;
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save'])) {
$nama = $conn->real_escape_string($_POST['nama']);
$nomor = $conn->real_escape_string($_POST['nomor']);
$status = $conn->real_escape_string($_POST['status']);
$lat = (float) $_POST['lat'];
$lng = (float) $_POST['lng'];
$sql = "INSERT INTO tempat (nama_tempat, nomor_tempat, status, latitude, longitude)
VALUES ('$nama','$nomor','$status','$lat','$lng')";
if ($conn->query($sql)) {
header('Location: index.php?saved=1');
exit;
}
header('Location: tambah.php?err=' . urlencode($conn->error));
exit;
}
+40
View File
@@ -0,0 +1,40 @@
function initBaseLayers(map) {
var baseMap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap', maxZoom: 19
});
var baseSat = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: '&copy; Esri', maxZoom: 19
});
baseMap.addTo(map);
document.getElementById('base-map').addEventListener('click', function() {
if (map.hasLayer(baseSat)) map.removeLayer(baseSat);
if (!map.hasLayer(baseMap)) baseMap.addTo(map);
this.classList.add('active');
document.getElementById('base-sat').classList.remove('active');
});
document.getElementById('base-sat').addEventListener('click', function() {
if (map.hasLayer(baseMap)) map.removeLayer(baseMap);
if (!map.hasLayer(baseSat)) baseSat.addTo(map);
this.classList.add('active');
document.getElementById('base-map').classList.remove('active');
});
}
function makeSpbuIcon(color, size) {
size = size || 18;
var h = size / 2;
return L.divIcon({
className: '',
html: '<div style="width:'+size+'px;height:'+size+'px;border-radius:50%;background:'+color+';border:2px solid #fff;box-shadow:0 1px 4px rgba(0,0,0,.35);"></div>',
iconSize: [size, size], iconAnchor: [h, h], popupAnchor: [0, -12]
});
}
function showToast(msg) {
var el = document.getElementById('toast');
if (!el) return;
el.textContent = msg;
el.classList.add('show');
setTimeout(function() { el.classList.remove('show'); }, 3000);
}
+234
View File
@@ -0,0 +1,234 @@
:root {
--bg: #f1f5f9; --white: #fff; --border: #e2e8f0;
--text: #0f172a; --text2: #475569; --text3: #94a3b8;
--blue: #1d4ed8; --blue-lt: #eff6ff;
--green: #15803d; --green-lt: #f0fdf4;
--red: #b91c1c; --red-lt: #fef2f2;
--sidebar-w: 320px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; font-family: 'DM Sans', sans-serif; color: var(--text); background: var(--bg); overflow: hidden; }
body { display: flex; }
#sidebar {
width: var(--sidebar-w); min-width: var(--sidebar-w);
background: var(--white); border-right: 1px solid var(--border);
display: flex; flex-direction: column; z-index: 200;
}
.sb-head {
padding: 18px 16px 14px; border-bottom: 1px solid var(--border);
background: var(--white); color: var(--text);
}
.sb-head-top { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.sb-logo {
width: 38px; height: 38px; border-radius: 10px;
background: linear-gradient(135deg, var(--blue), #60a5fa);
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
box-shadow: 0 2px 8px rgba(29,78,216,.25);
}
.sb-logo svg { width: 18px; height: 18px; fill: #fff; }
.sb-title h1 { font-size: 14px; font-weight: 700; color: var(--text); }
.sb-title p { font-size: 10.5px; color: var(--text3); margin-top: 2px; }
.sb-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.sb-stat {
background: var(--bg); border: 1px solid var(--border);
border-radius: 8px; padding: 8px 6px; text-align: center;
}
.sb-stat b { display: block; font-size: 16px; font-weight: 800; font-family: 'DM Mono', monospace; line-height: 1; }
.sb-stat span { font-size: 8.5px; color: var(--text3); margin-top: 4px; display: block; text-transform: uppercase; letter-spacing: .3px; font-weight: 600; }
.sb-stat.total b { color: var(--blue); }
.sb-stat.open b { color: var(--green); }
.sb-stat.closed b{ color: var(--red); }
.sb-section-lbl {
font-size: 9.5px; font-weight: 700; color: var(--text3);
text-transform: uppercase; letter-spacing: .8px; padding: 10px 8px 4px;
}
.sb-item {
display: flex; align-items: center; gap: 9px; padding: 8px 10px;
border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 500;
color: var(--text2); transition: all .15s; border: 1px solid transparent; text-decoration: none;
}
.sb-item:hover { background: var(--bg); color: var(--text); }
.sb-item.active { background: var(--blue-lt); color: var(--blue); border-color: #bfdbfe; font-weight: 600; }
.sb-item .ico { font-size: 16px; width: 20px; text-align: center; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.sb-body { flex: 1; overflow: hidden; display: flex; flex-direction: column; }
.sb-search { padding: 12px; border-bottom: 1px solid var(--border); }
.sb-search input {
width: 100%; padding: 9px 12px 9px 34px;
border: 1px solid var(--border); border-radius: 8px; font-size: 13px;
outline: none; font-family: inherit; background: var(--bg);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='%2394a3b8' viewBox='0 0 16 16'%3E%3Cpath d='M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.099zM6.5 12a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11z'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-position: 11px center;
}
.sb-search input:focus { border-color: var(--blue); background-color: #fff; }
.sb-filter { display: flex; padding: 0 12px 10px; gap: 5px; }
.sb-filter button {
flex: 1; padding: 6px 4px; font-size: 11px; font-weight: 600;
border: 1px solid var(--border); border-radius: 6px; background: var(--white);
color: var(--text2); cursor: pointer; font-family: inherit; transition: all .15s;
}
.sb-filter button.active { background: var(--blue); color: #fff; border-color: var(--blue); }
.sb-list-head {
padding: 0 14px 8px; display: flex; justify-content: space-between; align-items: center;
font-size: 11px; color: var(--text3); font-weight: 600; text-transform: uppercase; letter-spacing: .4px;
}
.sb-list { flex: 1; overflow-y: auto; }
.sb-list::-webkit-scrollbar { width: 4px; }
.sb-list::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.spbu-item {
padding: 11px 14px; border-bottom: 1px solid #f1f5f9; cursor: pointer;
display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start;
transition: background .12s;
}
.spbu-item:hover { background: #f8fafc; }
.spbu-item.active { background: var(--blue-lt); border-left: 3px solid var(--blue); padding-left: 11px; }
.spbu-dot {
width: 10px; height: 10px; border-radius: 50%; margin-top: 4px;
border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.1);
}
.spbu-nama { font-size: 13px; font-weight: 600; line-height: 1.3; }
.spbu-meta { font-size: 11px; color: var(--text3); margin-top: 3px; font-family: 'DM Mono', monospace; }
.spbu-badge { font-size: 10px; font-weight: 700; padding: 3px 7px; border-radius: 5px; white-space: nowrap; }
.badge-green { background: var(--green-lt); color: var(--green); }
.badge-red { background: var(--red-lt); color: var(--red); }
.no-result { padding: 32px 16px; text-align: center; color: var(--text3); font-size: 13px; }
.sb-foot {
padding: 10px 14px; border-top: 1px solid var(--border);
font-size: 11px; color: var(--text3); display: flex; justify-content: space-between;
}
.sb-foot b { color: var(--text2); }
/* Add form in sidebar */
.form-panel { padding: 16px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 0; }
.form-panel h3 { font-size: 14px; font-weight: 700; margin-bottom: 4px; }
.form-panel .form-desc { font-size: 12px; color: var(--text2); line-height: 1.5; margin-bottom: 16px; }
.form-group { margin-bottom: 14px; }
.form-group label {
display: block; font-size: 11px; font-weight: 700; color: var(--text3);
text-transform: uppercase; letter-spacing: .4px; margin-bottom: 5px;
}
.form-group input, .form-group select {
width: 100%; padding: 10px 12px; border: 1px solid var(--border);
border-radius: 8px; font-size: 14px; font-family: inherit; outline: none; background: #fff;
transition: border-color .15s, box-shadow .15s;
}
.form-group input:focus, .form-group select:focus {
border-color: var(--blue); box-shadow: 0 0 0 3px rgba(29,78,216,.1);
}
.form-group input[readonly] { background: var(--bg); color: var(--text2); font-family: 'DM Mono', monospace; font-size: 12px; }
.coord-box {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.coord-box input { text-align: center; }
.form-alert {
padding: 10px 12px; border-radius: 8px; font-size: 12px; margin-bottom: 14px;
background: var(--red-lt); color: var(--red); border: 1px solid #fecaca;
}
.form-hint {
font-size: 11px; color: var(--text3); padding: 10px 12px;
background: var(--bg); border-radius: 8px; border: 1px solid var(--border);
line-height: 1.5; margin-top: auto;
}
.form-hint.active { border-color: #93c5fd; background: var(--blue-lt); color: var(--blue); }
.form-actions { display: flex; gap: 8px; margin-top: 16px; }
.btn-primary {
flex: 1; padding: 11px; background: var(--blue); color: #fff; border: none;
border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.btn-primary:hover { background: #1e40af; }
.btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.btn-secondary {
padding: 11px 14px; background: #f1f5f9; color: var(--text2); border: 1px solid var(--border);
border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit;
}
#map-area { flex: 1; position: relative; min-width: 0; display: flex; flex-direction: column; height: 100%; }
#map { flex: 1; min-height: 0; width: 100%; position: relative; }
#map-controls {
position: absolute; top: 14px; right: 14px; z-index: 800;
background: var(--white); border: 1px solid var(--border);
border-radius: 10px; box-shadow: 0 2px 12px rgba(0,0,0,.1);
width: 190px; overflow: hidden; font-size: 12px;
}
.mc-section { padding: 10px 12px; border-bottom: 1px solid var(--border); }
.mc-section:last-child { border-bottom: none; }
.mc-label {
font-size: 10px; font-weight: 700; color: var(--text3);
text-transform: uppercase; letter-spacing: .5px; margin-bottom: 8px;
}
.mc-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; cursor: pointer; color: var(--text2); }
.mc-row:last-child { margin-bottom: 0; }
.mc-row input { accent-color: var(--blue); }
.mc-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.mc-base { display: flex; gap: 5px; }
.mc-base-btn {
flex: 1; padding: 7px 4px; border: 1px solid var(--border); border-radius: 6px;
background: var(--white); font-size: 11px; font-weight: 600; color: var(--text2);
cursor: pointer; font-family: inherit; transition: all .15s;
}
.mc-base-btn.active { background: var(--blue); color: #fff; border-color: var(--blue); }
#map-hint {
position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%);
z-index: 800; background: rgba(15,23,42,.88); color: #fff;
padding: 8px 16px; border-radius: 8px; font-size: 12px; pointer-events: none;
}
#toast {
position: fixed; bottom: 20px; right: 20px; z-index: 9000;
background: #0f172a; color: #fff; padding: 11px 16px; border-radius: 8px;
font-size: 13px; display: none; box-shadow: 0 4px 16px rgba(0,0,0,.2);
}
#toast.show { display: block; }
#edit-modal {
display: none; position: fixed; inset: 0; z-index: 9999;
background: rgba(15,23,42,.4); align-items: center; justify-content: center; padding: 20px;
}
#edit-modal.active { display: flex; }
#edit-box {
background: #fff; border-radius: 12px; padding: 24px;
width: 100%; max-width: 380px; box-shadow: 0 16px 48px rgba(0,0,0,.18);
}
#edit-box h3 { font-size: 16px; font-weight: 700; margin-bottom: 16px; }
#edit-box label {
display: block; font-size: 11px; font-weight: 700; color: var(--text3);
text-transform: uppercase; letter-spacing: .4px; margin: 12px 0 4px;
}
#edit-box input, #edit-box select {
width: 100%; padding: 9px 12px; border: 1px solid var(--border);
border-radius: 8px; font-size: 14px; font-family: inherit; outline: none;
}
#edit-box input:focus, #edit-box select:focus { border-color: var(--blue); }
.edit-btns { display: flex; gap: 8px; margin-top: 20px; }
.edit-btns button {
flex: 1; padding: 10px; border-radius: 8px; font-size: 13px;
font-weight: 600; cursor: pointer; font-family: inherit; border: none;
}
.btn-save { background: var(--blue); color: #fff; }
.btn-cancel { background: #f1f5f9; color: var(--text2); }
.popup-wrap { font-family: 'DM Sans', sans-serif; min-width: 190px; }
.popup-title { font-size: 14px; font-weight: 700; margin-bottom: 6px; }
.popup-row { font-size: 12px; color: var(--text2); margin-bottom: 3px; }
.popup-coord { font-size: 11px; color: var(--text3); font-family: 'DM Mono', monospace; margin: 4px 0 8px; }
.popup-badge { display: inline-block; padding: 2px 8px; border-radius: 5px; font-size: 10px; font-weight: 700; }
.popup-actions { display: flex; gap: 6px; margin-top: 10px; }
.btn-edit, .btn-delete {
flex: 1; border: none; padding: 7px; border-radius: 6px;
font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.btn-edit { background: var(--blue); color: #fff; }
.btn-delete { background: var(--red); color: #fff; }
.btn-yes, .btn-no {
border: none; padding: 7px 16px; border-radius: 6px;
font-size: 12px; font-weight: 600; cursor: pointer; margin: 3px; font-family: inherit;
}
.btn-yes { background: var(--green); color: #fff; }
.btn-no { background: #e2e8f0; color: var(--text2); }
+11
View File
@@ -0,0 +1,11 @@
<?php
define('DB_HOST', getenv('DB_HOST'));
define('DB_USER', getenv('DB_USER'));
define('DB_PASS', getenv('DB_PASS'));
define('DB_NAME', getenv('DB_NAME'));
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
?>
+3
View File
@@ -0,0 +1,3 @@
<?php
require_once 'config.php';
?>
+115
View File
@@ -0,0 +1,115 @@
<style>
#confirm-overlay {
display: none; position: fixed; inset: 0; z-index: 99999;
background: rgba(15,23,42,.4); backdrop-filter: blur(2px);
align-items: center; justify-content: center; padding: 20px;
}
#confirm-overlay.show { display: flex; }
#confirm-overlay.confirm-top {
align-items: flex-start; justify-content: center;
padding-top: 72px; background: transparent; backdrop-filter: none;
pointer-events: none;
}
#confirm-box {
background: #fff; border: 1px solid #e2e8f0; border-radius: 12px;
width: 100%; max-width: 400px;
box-shadow: 0 16px 48px rgba(15,23,42,.14);
animation: confirmIn .18s ease; overflow: hidden;
}
#confirm-overlay.confirm-top #confirm-box {
pointer-events: all; max-width: 460px;
box-shadow: 0 8px 28px rgba(15,23,42,.12);
}
@keyframes confirmIn { from { opacity:0; transform: translateY(-6px); } to { opacity:1; transform: none; } }
#confirm-box .cf-head {
display: flex; gap: 14px; padding: 20px 22px 0; align-items: flex-start;
}
#confirm-box .cf-icon {
width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
}
#confirm-box .cf-icon.warn { background: #fffbeb; border: 1px solid #fde68a; }
#confirm-box .cf-icon.danger { background: #fef2f2; border: 1px solid #fecaca; }
#confirm-box .cf-icon.info { background: #eff6ff; border: 1px solid #bfdbfe; }
#confirm-box .cf-icon.move { background: #eff6ff; border: 1px solid #bfdbfe; }
#confirm-box .cf-text { flex: 1; min-width: 0; }
#confirm-box .cf-title { font-size: 15px; font-weight: 700; color: #0f172a; margin-bottom: 4px; }
#confirm-box .cf-msg { font-size: 13px; color: #64748b; line-height: 1.55; }
#confirm-box .cf-actions {
display: flex; gap: 8px; padding: 18px 22px 20px; justify-content: flex-end;
}
#confirm-box .cf-btn {
padding: 9px 16px; border-radius: 8px; font-size: 13px;
font-weight: 600; cursor: pointer; border: 1px solid transparent;
font-family: inherit; transition: background .15s;
}
#confirm-box .cf-cancel { background: #fff; color: #475569; border-color: #e2e8f0; }
#confirm-box .cf-cancel:hover { background: #f8fafc; }
#confirm-box .cf-ok { background: #1d4ed8; color: #fff; }
#confirm-box .cf-ok:hover { background: #1e40af; }
#confirm-box .cf-ok.danger { background: #dc2626; }
#confirm-box .cf-ok.danger:hover { background: #b91c1c; }
</style>
<div id="confirm-overlay" role="dialog" aria-modal="true">
<div id="confirm-box">
<div class="cf-head">
<div class="cf-icon warn" id="cf-icon"></div>
<div class="cf-text">
<div class="cf-title" id="cf-title">Konfirmasi</div>
<div class="cf-msg" id="cf-msg"></div>
</div>
</div>
<div class="cf-actions">
<button type="button" class="cf-btn cf-cancel" id="cf-cancel">Batal</button>
<button type="button" class="cf-btn cf-ok" id="cf-ok">Lanjutkan</button>
</div>
</div>
</div>
<script>
(function(){
var overlay = document.getElementById('confirm-overlay');
var iconEl = document.getElementById('cf-icon');
var titleEl = document.getElementById('cf-title');
var msgEl = document.getElementById('cf-msg');
var btnOk = document.getElementById('cf-ok');
var btnCancel = document.getElementById('cf-cancel');
var resolver = null;
var icons = {
warn: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" stroke-linecap="round"><path d="M12 9v4m0 4h.01"/><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>',
danger: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" stroke-linecap="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/></svg>',
move: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round"><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="3"/></svg>'
};
function closeConfirm(result) {
overlay.classList.remove('show', 'confirm-top');
document.body.style.overflow = '';
if (resolver) { var r = resolver; resolver = null; r(result); }
}
btnCancel.addEventListener('click', function(){ closeConfirm(false); });
btnOk.addEventListener('click', function(){ closeConfirm(true); });
overlay.addEventListener('click', function(e){ if (e.target === overlay && !overlay.classList.contains('confirm-top')) closeConfirm(false); });
document.addEventListener('keydown', function(e){
if (!overlay.classList.contains('show')) return;
if (e.key === 'Escape') closeConfirm(false);
});
window.showConfirm = function(opts) {
opts = opts || {};
var danger = !!opts.danger;
var kind = danger ? 'danger' : (opts.icon || 'warn');
iconEl.className = 'cf-icon ' + kind;
iconEl.innerHTML = icons[kind] || icons.warn;
titleEl.textContent = opts.title || 'Konfirmasi';
msgEl.innerHTML = opts.message || 'Apakah Anda yakin?';
btnOk.textContent = opts.confirmText || (danger ? 'Ya, Hapus' : 'Lanjutkan');
btnCancel.textContent = opts.cancelText || 'Batal';
btnOk.classList.toggle('danger', danger);
overlay.classList.toggle('confirm-top', opts.position === 'top');
if (opts.position !== 'top') document.body.style.overflow = 'hidden';
overlay.classList.add('show');
return new Promise(function(resolve){ resolver = resolve; });
};
})();
</script>
+14
View File
@@ -0,0 +1,14 @@
<div id="map-controls" style="top: 70px;">
<div class="mc-section">
<div class="mc-label">Layer SPBU</div>
<label class="mc-row"><input type="checkbox" id="ly-24" checked><span class="mc-dot" style="background:#22c55e"></span> Buka 24 Jam</label>
<label class="mc-row"><input type="checkbox" id="ly-tdk" checked><span class="mc-dot" style="background:#ef4444"></span> Tidak 24 Jam</label>
</div>
<div class="mc-section">
<div class="mc-label">Tampilan Peta</div>
<div class="mc-base">
<button class="mc-base-btn active" id="base-map" type="button">Peta</button>
<button class="mc-base-btn" id="base-sat" type="button">Satelit</button>
</div>
</div>
</div>
+44
View File
@@ -0,0 +1,44 @@
<?php
/** @var array $stats @var string $activePage 'index'|'tambah' */
$activePage = $activePage ?? 'index';
?>
<aside id="sidebar">
<div class="sb-head">
<a href="index.php" style="display:flex; align-items:center; gap:10px; text-decoration:none; color:inherit">
<div class="sb-logo" style="width:38px; height:38px; border-radius:10px; background:linear-gradient(135deg, #1d4ed8, #60a5fa); display:flex; align-items:center; justify-content:center; flex-shrink:0; box-shadow:0 2px 8px rgba(29,78,216,.25)">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#fff" stroke-width="2" style="flex-shrink:0"><path d="M6 3h12l-1 7H7L6 3zm0 9h12v9H6v-9zm3 2v5h6v-5H9z" fill="currentColor"/></svg>
</div>
<div class="sb-title"><h1>Geografis SPBU</h1><p>Sistem Informasi Geografis</p></div>
</a>
<!-- 2x2 Stats Grid -->
<div class="sb-stats" style="display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:12px">
<div class="sb-stat" style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:9px 10px; text-align:center">
<div class="sb-stat-n" style="font-size:20px; font-weight:800; font-family:'DM Mono',monospace; line-height:1; color:#1d4ed8" id="s-total"><?= $stats['total'] ?></div>
<div class="sb-stat-l" style="font-size:9.5px; color:#94a3b8; margin-top:3px; font-weight:600; text-transform:uppercase; letter-spacing:.4px">Total SPBU</div>
</div>
<div class="sb-stat" style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:9px 10px; text-align:center">
<div class="sb-stat-n" style="font-size:20px; font-weight:800; font-family:'DM Mono',monospace; line-height:1; color:#16a34a" id="s-jam24"><?= $stats['jam24'] ?></div>
<div class="sb-stat-l" style="font-size:9.5px; color:#94a3b8; margin-top:3px; font-weight:600; text-transform:uppercase; letter-spacing:.4px">24 Jam</div>
</div>
<div class="sb-stat" style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:9px 10px; text-align:center">
<div class="sb-stat-n" style="font-size:20px; font-weight:800; font-family:'DM Mono',monospace; line-height:1; color:#dc2626" id="s-tidak24"><?= $stats['tidak24'] ?></div>
<div class="sb-stat-l" style="font-size:9.5px; color:#94a3b8; margin-top:3px; font-weight:600; text-transform:uppercase; letter-spacing:.4px">Tdk 24 Jam</div>
</div>
<div class="sb-stat" style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:9px 10px; text-align:center">
<div class="sb-stat-n" style="font-size:20px; font-weight:800; font-family:'DM Mono',monospace; line-height:1; color:#ca8a04" id="s-pct"><?= $stats['pct24'] ?>%</div>
<div class="sb-stat-l" style="font-size:9.5px; color:#94a3b8; margin-top:3px; font-weight:600; text-transform:uppercase; letter-spacing:.4px">Ratio 24h</div>
</div>
</div>
</div>
<!-- Navigation Sections -->
<div class="sb-nav-container" style="padding:8px; display:flex; flex-direction:column; gap:2px; border-bottom:1px solid #e2e8f0">
<div class="sb-section-lbl" style="font-size:9.5px; font-weight:700; color:#94a3b8; text-transform:uppercase; letter-spacing:.8px; padding:10px 8px 4px;">Aksi Admin</div>
<a href="tambah.php" class="sb-item <?= $activePage==='tambah'?'active':'' ?>" style="display:flex; align-items:center; gap:9px; padding:8px 10px; border-radius:8px; cursor:pointer; font-size:13px; font-weight:500; color:#475569; text-decoration:none; transition:all .15s; border:1px solid transparent;">
<span class="ico" style="font-size:16px; width:20px; text-align:center; flex-shrink:0; display:flex; align-items:center; justify-content:center">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</span>
<span>Tambah SPBU Baru</span>
</a>
</div>
+8
View File
@@ -0,0 +1,8 @@
<?php
function spbu_stats($conn) {
$total = (int) $conn->query("SELECT COUNT(*) c FROM tempat")->fetch_assoc()['c'];
$jam24 = (int) $conn->query("SELECT COUNT(*) c FROM tempat WHERE status='24 Jam'")->fetch_assoc()['c'];
$tidak24 = $total - $jam24;
$pct24 = $total > 0 ? round($jam24 / $total * 100) : 0;
return compact('total', 'jam24', 'tidak24', 'pct24');
}
+283
View File
@@ -0,0 +1,283 @@
<?php
if (isset($_GET['action'])) { include 'api.php'; exit; }
include 'db.php';
require_once 'includes/stats.php';
$stats = spbu_stats($conn);
$activePage = 'index';
$saved = isset($_GET['saved']);
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Sistem Informasi Geografis SPBU — Pontianak</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
<link rel="stylesheet" href="assets/spbu.css?v=1.2"/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
</head>
<body>
<?php include 'includes/sidebar.php'; ?>
<div class="sb-body">
<div class="sb-search"><input type="text" id="search-input" placeholder="Cari nama atau nomor SPBU..."></div>
<div class="sb-filter">
<button class="active" data-filter="semua">Semua (<?= $stats['total'] ?>)</button>
<button data-filter="24 Jam">24 Jam (<?= $stats['jam24'] ?>)</button>
<button data-filter="Tidak 24 Jam">Lainnya (<?= $stats['tidak24'] ?>)</button>
</div>
<div class="sb-list-head">
<span>Daftar SPBU</span>
<span id="list-count"><?= $stats['total'] ?> lokasi</span>
</div>
<div class="sb-list" id="spbu-list"></div>
</div>
<div class="sb-foot" style="flex-direction:column; gap:10px">
<div style="display:flex; justify-content:space-between; width:100%">
<span>Operasional 24 jam: <b><?= $stats['pct24'] ?>%</b></span>
<span><?= date('d M Y') ?></span>
</div>
<a href="../index.php" class="btn-portal" style="display:flex; align-items:center; justify-content:center; gap:6px; padding:10px; background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; border-radius:8px; font-weight:600; text-decoration:none; font-size:12px; transition:all 0.15s; width:100%">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Kembali ke Portal
</a>
</div>
</aside>
<div id="map-area">
<!-- Topbar -->
<div id="topbar" style="position:relative; z-index:1000; height:56px; background:#fff; border-bottom:1px solid #e2e8f0; display:flex; align-items:center; justify-content:space-between; padding:0 16px; flex-shrink:0">
<div style="display:flex; align-items:center; gap:8px">
<button type="button" onclick="document.getElementById('sidebar').style.display=document.getElementById('sidebar').style.display==='none'?'flex':'none';setTimeout(function(){map.invalidateSize()},300)" style="background:none;border:none;cursor:pointer;color:#64748b;padding:4px;display:flex;align-items:center"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button>
<div style="font-size:15px; font-weight:700; color:#1e293b">Peta GIS <span style="font-size:13px; font-weight:500; color:#64748b; margin-left:4px">Kanalisasi SPBU</span></div>
</div>
<div style="display:flex; gap:8px">
<a href="tambah.php" style="display:flex; align-items:center; gap:6px; padding:8px 14px; background:#1d4ed8; color:#fff; border-radius:8px; font-size:12px; font-weight:600; text-decoration:none; box-shadow:0 2px 4px rgba(29,78,216,0.15)">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Tambah Baru
</a>
</div>
</div>
<div id="map"></div>
<?php include 'includes/map-controls.php'; ?>
</div>
<div id="edit-modal">
<div id="edit-box">
<h3>Edit Data SPBU</h3>
<input type="hidden" id="edit-id">
<label>Nama SPBU</label>
<input type="text" id="edit-nama">
<label>Nomor Depot</label>
<input type="text" id="edit-nomor">
<label>Jam Operasional</label>
<select id="edit-status">
<option value="24 Jam">24 Jam</option>
<option value="Tidak 24 Jam">Tidak 24 Jam</option>
</select>
<div class="edit-btns">
<button class="btn-cancel" type="button" onclick="closeModal()">Batal</button>
<button class="btn-save" type="button" onclick="submitEdit()">Simpan</button>
</div>
</div>
</div>
<div id="toast"></div>
<?php include 'includes/confirm-modal.php'; ?>
<script src="assets/map-base.js"></script>
<script>
var map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
L.control.zoom({ position: 'bottomright' }).addTo(map);
initBaseLayers(map);
<?php if ($saved): ?>showToast('Data SPBU berhasil disimpan.');<?php endif ?>
var spbuData = [];
var layer24Jam = L.layerGroup().addTo(map);
var layerTidak24 = L.layerGroup().addTo(map);
document.getElementById('ly-24').addEventListener('change', function() {
this.checked ? map.addLayer(layer24Jam) : map.removeLayer(layer24Jam);
});
document.getElementById('ly-tdk').addEventListener('change', function() {
this.checked ? map.addLayer(layerTidak24) : map.removeLayer(layerTidak24);
});
async function deleteData(id) {
var ok = await showConfirm({
title: 'Hapus Data SPBU',
message: 'Data ini akan dihapus permanen dari database.',
confirmText: 'Ya, Hapus', danger: true
});
if (ok) window.location.href = 'index.php?action=delete&id=' + id;
}
function openEdit(id, nama, nomor, status) {
document.getElementById('edit-id').value = id;
document.getElementById('edit-nama').value = nama;
document.getElementById('edit-nomor').value = nomor;
document.getElementById('edit-status').value = status;
document.getElementById('edit-modal').classList.add('active');
}
function closeModal() { document.getElementById('edit-modal').classList.remove('active'); }
function submitEdit() {
var id = document.getElementById('edit-id').value;
var nama = document.getElementById('edit-nama').value.trim();
var nomor = document.getElementById('edit-nomor').value.trim();
var status = document.getElementById('edit-status').value;
if (!nama || !nomor) { showToast('Nama dan nomor wajib diisi.'); return; }
window.location.href = 'index.php?action=edit&id=' + id
+ '&nama=' + encodeURIComponent(nama)
+ '&nomor=' + encodeURIComponent(nomor)
+ '&status=' + encodeURIComponent(status);
}
document.getElementById('edit-modal').addEventListener('click', function(e) {
if (e.target === this) closeModal();
});
function flyToSpbu(id) {
var s = spbuData.find(function(x) { return x.id === id; });
if (!s) return;
document.querySelectorAll('.spbu-item').forEach(function(el) { el.classList.remove('active'); });
var el = document.querySelector('.spbu-item[data-id="' + id + '"]');
if (el) el.classList.add('active');
map.flyTo([s.lat, s.lng], 17, { duration: 1 });
setTimeout(function() { s.marker.openPopup(); }, 1100);
}
<?php
$data = $conn->query("SELECT * FROM tempat ORDER BY nama_tempat ASC");
while ($row = $data->fetch_assoc()):
$id = $row['id']; $lat = $row['latitude']; $lng = $row['longitude'];
$nama = addslashes($row['nama_tempat']); $nomor = addslashes($row['nomor_tempat']);
$status = addslashes($row['status']);
$color = ($row['status'] === '24 Jam') ? '#22c55e' : '#ef4444';
$badge = ($row['status'] === '24 Jam') ? 'badge-green' : 'badge-red';
$layerVar = ($row['status'] === '24 Jam') ? 'layer24Jam' : 'layerTidak24';
$latFmt = number_format($lat, 5); $lngFmt = number_format($lng, 5);
?>
(function() {
var savedLat = <?= $lat ?>, savedLng = <?= $lng ?>, markerId = <?= $id ?>, color = '<?= $color ?>';
var marker = L.marker([savedLat, savedLng], { icon: makeSpbuIcon(color), draggable: true }).addTo(<?= $layerVar ?>);
spbuData.push({ id: markerId, lat: savedLat, lng: savedLng, nama: '<?= $nama ?>', nomor: '<?= $nomor ?>', status: '<?= $status ?>', color: color, badge: '<?= $badge ?>', marker: marker });
function showInfoPopup() {
marker.unbindPopup();
marker.bindPopup(
'<div class="popup-wrap"><div class="popup-title"><?= $nama ?></div>' +
'<div class="popup-row">Depot No. <?= $nomor ?></div>' +
'<div class="popup-coord"><?= $latFmt ?>, <?= $lngFmt ?></div>' +
'<span class="popup-badge <?= $badge ?>"><?= $status ?></span>' +
'<div class="popup-actions">' +
'<button class="btn-edit" onclick="openEdit(<?= $id ?>,\'<?= $nama ?>\',\'<?= $nomor ?>\',\'<?= $status ?>\')">Edit</button>' +
'<button class="btn-delete" onclick="deleteData(<?= $id ?>)">Hapus</button></div></div>'
);
}
showInfoPopup();
var dragStartLat, dragStartLng;
marker.on('dragstart', function() {
dragStartLat = marker.getLatLng().lat; dragStartLng = marker.getLatLng().lng;
marker.closePopup(); marker.unbindPopup();
});
marker.on('dragend', function() {
var np = marker.getLatLng();
var msgBase = '<div style="text-align:left;line-height:1.6">' +
'<b>' + '<?= $nama ?>' + '</b><br>' +
'<span style="color:var(--text3);font-size:12px">Koordinat: ' + np.lat.toFixed(6) + ', ' + np.lng.toFixed(6) + '</span><br>';
fetch('https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat='+np.lat+'&lon='+np.lng+'&accept-language=id&zoom=18')
.then(r => r.json())
.then(d => {
var alamat = d.display_name || 'Alamat tidak ditemukan';
showConfirm({
title: 'Simpan posisi baru?',
message: msgBase + '<span style="font-size:13px;color:var(--text2)"><b>Alamat Baru:</b> ' + alamat + '</span></div>',
confirmText: 'Ya, Simpan',
icon: 'move',
position: 'top'
}).then(function(ok) {
if (ok) {
fetch('index.php?action=update_pos&id=' + markerId + '&lat=' + np.lat + '&lng=' + np.lng)
.then(function(r) { return r.text(); }).then(function(t) {
if (t.trim() === 'ok') {
savedLat = np.lat; savedLng = np.lng;
var s = spbuData.find(function(x) { return x.id === markerId; });
if (s) { s.lat = savedLat; s.lng = savedLng; }
showToast('Posisi diperbarui.');
} else {
showToast('Gagal menyimpan.');
marker.setLatLng([dragStartLat, dragStartLng]);
}
showInfoPopup();
});
} else {
marker.setLatLng([dragStartLat, dragStartLng]);
showInfoPopup();
}
});
})
.catch(() => {
showConfirm({
title: 'Simpan posisi baru?',
message: msgBase + '<span style="font-size:13px;color:var(--text2)"><b>Alamat Baru:</b> Gagal memuat alamat</span></div>',
confirmText: 'Ya, Simpan',
icon: 'move',
position: 'top'
}).then(function(ok) {
if (ok) {
fetch('index.php?action=update_pos&id=' + markerId + '&lat=' + np.lat + '&lng=' + np.lng)
.then(function(r) { return r.text(); }).then(function(t) {
if (t.trim() === 'ok') {
savedLat = np.lat; savedLng = np.lng;
var s = spbuData.find(function(x) { return x.id === markerId; });
if (s) { s.lat = savedLat; s.lng = savedLng; }
showToast('Posisi diperbarui.');
} else {
showToast('Gagal menyimpan.');
marker.setLatLng([dragStartLat, dragStartLng]);
}
showInfoPopup();
});
} else {
marker.setLatLng([dragStartLat, dragStartLng]);
showInfoPopup();
}
});
});
});
marker.on('click', function() {
document.querySelectorAll('.spbu-item').forEach(function(el) { el.classList.remove('active'); });
var el = document.querySelector('.spbu-item[data-id="' + markerId + '"]');
if (el) { el.classList.add('active'); el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
});
})();
<?php endwhile; ?>
var currentFilter = 'semua', currentSearch = '';
function renderList() {
var list = document.getElementById('spbu-list');
var kw = currentSearch.toLowerCase();
var filtered = spbuData.filter(function(s) {
var f = (currentFilter === 'semua') || (s.status === currentFilter);
var q = s.nama.toLowerCase().indexOf(kw) !== -1 || s.nomor.toLowerCase().indexOf(kw) !== -1;
return f && q;
});
document.getElementById('list-count').textContent = filtered.length + ' lokasi';
if (!filtered.length) { list.innerHTML = '<div class="no-result">Tidak ada SPBU ditemukan</div>'; return; }
list.innerHTML = filtered.map(function(s) {
return '<div class="spbu-item" data-id="' + s.id + '" onclick="flyToSpbu(' + s.id + ')">' +
'<div class="spbu-dot" style="background:' + s.color + '"></div>' +
'<div><div class="spbu-nama">' + s.nama + '</div>' +
'<div class="spbu-meta">No. ' + s.nomor + ' · ' + s.lat.toFixed(5) + ', ' + s.lng.toFixed(5) + '</div></div>' +
'<span class="spbu-badge ' + s.badge + '">' + (s.status === '24 Jam' ? '24 Jam' : 'Tdk 24 Jam') + '</span></div>';
}).join('');
}
document.getElementById('search-input').addEventListener('input', function() { currentSearch = this.value; renderList(); });
document.querySelectorAll('.sb-filter button').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('.sb-filter button').forEach(function(b) { b.classList.remove('active'); });
this.classList.add('active'); currentFilter = this.dataset.filter; renderList();
});
});
renderList();
</script>
</body>
</html>
+149
View File
@@ -0,0 +1,149 @@
<?php
include 'db.php';
require_once 'includes/stats.php';
$stats = spbu_stats($conn);
$activePage = 'tambah';
$err = isset($_GET['err']) ? $_GET['err'] : '';
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Tambah SPBU — Sistem Informasi Geografis SPBU</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
<link rel="stylesheet" href="assets/spbu.css?v=1.2"/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
</head>
<body>
<?php include 'includes/sidebar.php'; ?>
<div class="sb-body">
<form class="form-panel" id="form-spbu" method="POST" action="api.php">
<h3>Input Data SPBU</h3>
<p class="form-desc">Klik titik pada peta untuk menentukan koordinat, lalu lengkapi formulir di bawah.</p>
<?php if ($err): ?><div class="form-alert"><?= htmlspecialchars($err) ?></div><?php endif ?>
<div class="form-group">
<label for="nama">Nama SPBU</label>
<input type="text" id="nama" name="nama" placeholder="Contoh: SPBU Kota Baru" required>
</div>
<div class="form-group">
<label for="nomor">Nomor Depot</label>
<input type="text" id="nomor" name="nomor" placeholder="Contoh: 34.123.45" required>
</div>
<div class="form-group">
<label for="status">Jam Operasional</label>
<select id="status" name="status">
<option value="24 Jam">Buka 24 Jam</option>
<option value="Tidak 24 Jam">Tidak 24 Jam</option>
</select>
</div>
<div class="form-group">
<label>Koordinat Lokasi</label>
<div class="coord-box">
<input type="text" id="lat-display" placeholder="Latitude" readonly>
<input type="text" id="lng-display" placeholder="Longitude" readonly>
</div>
<input type="hidden" name="lat" id="lat" required>
<input type="hidden" name="lng" id="lng" required>
</div>
<div class="form-actions">
<button type="button" class="btn-secondary" onclick="resetForm()">Reset</button>
<button type="submit" name="save" class="btn-primary" id="btn-save" disabled>Simpan Data</button>
</div>
<div class="form-hint" id="form-hint">Belum ada lokasi dipilih — klik peta di sebelah kanan</div>
</form>
</div>
<div class="sb-foot" style="flex-direction:column; gap:10px">
<div style="display:flex; justify-content:space-between; width:100%">
<span>Operasional 24 jam: <b><?= $stats['pct24'] ?>%</b></span>
<span><?= date('d M Y') ?></span>
</div>
<a href="../index.php" class="btn-portal" style="display:flex; align-items:center; justify-content:center; gap:6px; padding:10px; background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; border-radius:8px; font-weight:600; text-decoration:none; font-size:12px; transition:all 0.15s; width:100%">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Kembali ke Portal
</a>
</div>
</aside>
<div id="map-area">
<!-- Topbar -->
<div id="topbar" style="position:relative; z-index:1000; height:56px; background:#fff; border-bottom:1px solid #e2e8f0; display:flex; align-items:center; justify-content:space-between; padding:0 16px; flex-shrink:0">
<div style="display:flex; align-items:center; gap:8px">
<div style="font-size:15px; font-weight:700; color:#1e293b">Tambah Baru <span style="font-size:13px; font-weight:500; color:#64748b; margin-left:4px">Lokasi Depot SPBU</span></div>
</div>
<div style="display:flex; gap:8px">
<a href="index.php" style="display:flex; align-items:center; gap:6px; padding:8px 14px; border:1px solid #cbd5e1; color:#475569; border-radius:8px; font-size:12px; font-weight:600; text-decoration:none; background:#fff">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg>
Lihat Peta
</a>
</div>
</div>
<div id="map"></div>
<?php include 'includes/map-controls.php'; ?>
<div id="map-hint" style="display:block">Klik peta untuk menandai lokasi SPBU</div>
</div>
<div id="toast"></div>
<script src="assets/map-base.js"></script>
<script>
var map = L.map('map', { zoomControl: false }).setView([-0.0263, 109.3425], 13);
L.control.zoom({ position: 'bottomright' }).addTo(map);
initBaseLayers(map);
var newMarker = null;
var newIcon = makeSpbuIcon('#3b82f6', 22);
var hintEl = document.getElementById('form-hint');
var btnSave = document.getElementById('btn-save');
<?php
$existing = $conn->query("SELECT * FROM tempat");
while ($row = $existing->fetch_assoc()):
$nama = addslashes($row['nama_tempat']); $nomor = addslashes($row['nomor_tempat']);
$status = addslashes($row['status']);
$color = ($row['status'] === '24 Jam') ? '#22c55e' : '#ef4444';
$badge = ($row['status'] === '24 Jam') ? 'badge-green' : 'badge-red';
?>
L.marker([<?= $row['latitude'] ?>, <?= $row['longitude'] ?>], { icon: makeSpbuIcon('<?= $color ?>') })
.addTo(map).bindPopup(
'<div class="popup-wrap"><div class="popup-title"><?= $nama ?></div>' +
'<div class="popup-row">Depot No. <?= $nomor ?></div>' +
'<span class="popup-badge <?= $badge ?>"><?= $status ?></span></div>'
);
<?php endwhile; ?>
function setCoords(lat, lng) {
document.getElementById('lat').value = lat.toFixed(6);
document.getElementById('lng').value = lng.toFixed(6);
document.getElementById('lat-display').value = lat.toFixed(6);
document.getElementById('lng-display').value = lng.toFixed(6);
btnSave.disabled = false;
hintEl.textContent = 'Lokasi terpilih — lengkapi data lalu simpan';
hintEl.classList.add('active');
}
function resetForm() {
document.getElementById('form-spbu').reset();
document.getElementById('lat').value = '';
document.getElementById('lng').value = '';
document.getElementById('lat-display').value = '';
document.getElementById('lng-display').value = '';
btnSave.disabled = true;
hintEl.textContent = 'Belum ada lokasi dipilih — klik peta di sebelah kanan';
hintEl.classList.remove('active');
if (newMarker) { map.removeLayer(newMarker); newMarker = null; }
}
map.on('click', function(e) {
if (newMarker) map.removeLayer(newMarker);
newMarker = L.marker(e.latlng, { icon: newIcon }).addTo(map);
setCoords(e.latlng.lat, e.latlng.lng);
});
</script>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- Host: 127.0.0.1
-- Waktu pembuatan: 13 Jun 2026 pada 06.34
-- Versi server: 10.4.32-MariaDB
-- Versi PHP: 8.2.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `webgis`
--
-- --------------------------------------------------------
--
-- Struktur dari tabel `tempat`
--
CREATE TABLE `tempat` (
`id` int(11) NOT NULL,
`nama_tempat` varchar(100) DEFAULT NULL,
`nomor_tempat` varchar(50) DEFAULT NULL,
`status` varchar(20) DEFAULT NULL,
`latitude` double(10,8) DEFAULT NULL,
`longitude` double(11,8) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Dumping data untuk tabel `tempat`
--
INSERT INTO `tempat` (`id`, `nama_tempat`, `nomor_tempat`, `status`, `latitude`, `longitude`) VALUES
(1, 'SPBU Kota Baru', '1', '24 Jam', -0.04783974, 109.31868017),
(6, 'SPBU OSO MT. Haryono', '2', 'Tidak 24 Jam', -0.04492163, 109.33674753),
(9, 'SPBU A. Dahlan', '3', 'Tidak 24 Jam', -0.03438503, 109.33189809),
(10, 'SPBU Paris 2', '4', 'Tidak 24 Jam', -0.06431425, 109.35622036);
--
-- Indexes for dumped tables
--
--
-- Indeks untuk tabel `tempat`
--
ALTER TABLE `tempat`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT untuk tabel yang dibuang
--
--
-- AUTO_INCREMENT untuk tabel `tempat`
--
ALTER TABLE `tempat`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;