feat: implement responsive UI layouts and collapsible control panels across application pages
This commit is contained in:
+120
-6
@@ -1099,6 +1099,67 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Floating Panels Toggle ---- */
|
||||
.map-floating-panel {
|
||||
transition: width 0.3s ease, height 0.3s ease, background 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.map-floating-panel .panel-icon,
|
||||
.map-floating-panel .panel-close-btn {
|
||||
display: none;
|
||||
}
|
||||
.map-floating-panel .panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.map-floating-panel.collapsed {
|
||||
width: 44px !important;
|
||||
height: 44px !important;
|
||||
min-height: 44px !important;
|
||||
padding: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.map-floating-panel.collapsed:hover {
|
||||
background: rgba(59, 130, 246, 0.2) !important;
|
||||
border-color: rgba(59, 130, 246, 0.5) !important;
|
||||
}
|
||||
.map-floating-panel.collapsed > *:not(.panel-icon) {
|
||||
display: none !important;
|
||||
}
|
||||
.map-floating-panel .panel-icon {
|
||||
display: none;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
.map-floating-panel.collapsed .panel-icon {
|
||||
display: block;
|
||||
}
|
||||
.map-floating-panel .panel-close-btn {
|
||||
display: flex;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--clr-muted);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.map-floating-panel .panel-close-btn:hover {
|
||||
color: var(--clr-text);
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Custom Marker ---- */
|
||||
.custom-marker {
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
@@ -1163,6 +1224,46 @@
|
||||
}
|
||||
.routing-panel h3 { font-size: 12px; font-weight: 700; color: #f1f5f9; margin-bottom: 12px; border-bottom: 1px solid #334155; padding-bottom: 8px; text-transform: uppercase; margin-top:0; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.app-wrapper { height: 100dvh; }
|
||||
header { height: auto; min-height: 64px; padding: 10px 14px; gap: 10px; flex-wrap: wrap; }
|
||||
.header-title h1 { font-size: 16px; }
|
||||
.header-actions { width: 100%; justify-content: flex-start; flex-wrap: wrap; }
|
||||
.content-panel-header { align-items: flex-start; flex-direction: column; gap: 10px; }
|
||||
.content-panel-header .btn-group { width: 100%; flex-wrap: wrap; }
|
||||
.content-panel-header .btn-group .btn { flex: 1; min-width: 130px; }
|
||||
.table { min-width: 680px; }
|
||||
.modal { width: calc(100vw - 24px); max-height: calc(100dvh - 24px); }
|
||||
.modal-footer { flex-wrap: wrap; }
|
||||
.modal-footer .btn { flex: 1; min-width: 120px; }
|
||||
.search-panel { left: 82px; right: 12px; top: 12px; width: auto; margin-left: 0; transform: none; }
|
||||
.routing-panel { top: auto; right: 12px; left: 82px; bottom: 12px; width: auto; }
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.sidebar { width: 56px; }
|
||||
.sidebar-header { padding: 12px 8px; justify-content: center; }
|
||||
.sidebar-logo-icon { width: 34px; height: 34px; font-size: 14px; }
|
||||
.sidebar-toggle,
|
||||
.sidebar-logo-text,
|
||||
.sidebar-item-text,
|
||||
.sidebar-item-badge,
|
||||
.sidebar-section-title { display: none !important; }
|
||||
.sidebar-nav { padding: 10px 0; }
|
||||
.sidebar-section { padding: 8px 0; }
|
||||
.sidebar-item { padding: 12px 14px; justify-content: center; }
|
||||
.content-panel-body { padding: 12px; }
|
||||
.search-box input,
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea { font-size: 16px; }
|
||||
.search-panel { left: 68px; right: 8px; top: 8px; padding: 10px; }
|
||||
.search-results { max-height: 135px; }
|
||||
.routing-panel { left: 68px; right: 8px; bottom: 8px; padding: 10px; }
|
||||
.routing-panel h3,
|
||||
.search-panel h3 { font-size: 11px; margin-bottom: 8px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1272,8 +1373,12 @@
|
||||
<div class="panel-map" id="map" role="main" aria-label="Peta persebaran WebGIS"></div>
|
||||
|
||||
<!-- Search Panel (Top Center) -->
|
||||
<div class="search-panel" id="admin-search-panel">
|
||||
<h3>🔍 Pencarian Global</h3>
|
||||
<div class="search-panel map-floating-panel collapsed" id="admin-search-panel" onclick="if(this.classList.contains('collapsed')) this.classList.remove('collapsed');">
|
||||
<div class="panel-icon">🔍</div>
|
||||
<div class="panel-header">
|
||||
<h3 style="margin-bottom: 0;">🔍 Pencarian Global</h3>
|
||||
<button class="panel-close-btn" onclick="event.stopPropagation(); document.getElementById('admin-search-panel').classList.add('collapsed');" title="Tutup">×</button>
|
||||
</div>
|
||||
<div class="search-input-wrap">
|
||||
<input type="text" id="global-search" class="ctrl-input" placeholder="Cari SPBU, Jalan, Ibadah..." autocomplete="off">
|
||||
</div>
|
||||
@@ -1295,14 +1400,18 @@
|
||||
</div> -->
|
||||
|
||||
<!-- Floating "Tambah Data" Toolbar -->
|
||||
<div id="map-add-toolbar" style="
|
||||
<div id="map-add-toolbar" class="map-floating-panel collapsed" onclick="if(this.classList.contains('collapsed')) this.classList.remove('collapsed');" style="
|
||||
position:absolute; top:80px; left:16px; z-index:1000;
|
||||
background:rgba(15,23,42,0.96); backdrop-filter:blur(10px);
|
||||
border:1px solid #334155; border-radius:12px;
|
||||
padding:14px 12px; box-shadow:0 4px 24px rgba(0,0,0,0.6);
|
||||
width:158px; font-family:'Inter',sans-serif;
|
||||
">
|
||||
<div style="font-size:10px;font-weight:700;color:#60a5fa;text-transform:uppercase;letter-spacing:0.6px;margin-bottom:10px;">⊕ Tambah Data</div>
|
||||
<div class="panel-icon" style="color:#60a5fa;">⊕</div>
|
||||
<div class="panel-header" style="margin-bottom:10px;">
|
||||
<div style="font-size:10px;font-weight:700;color:#60a5fa;text-transform:uppercase;letter-spacing:0.6px;margin-bottom:0;">⊕ Tambah Data</div>
|
||||
<button class="panel-close-btn" onclick="event.stopPropagation(); document.getElementById('map-add-toolbar').classList.add('collapsed');" title="Tutup">×</button>
|
||||
</div>
|
||||
|
||||
<div style="font-size:10px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.4px;margin-bottom:5px;">📍 Titik</div>
|
||||
<button class="map-tool-btn" id="map-add-spbu"
|
||||
@@ -1330,8 +1439,13 @@
|
||||
" onclick="window.mapToolCancel()">✕ Batal</button>
|
||||
</div>
|
||||
|
||||
<div class="legend" id="legend-peta">
|
||||
<div class="legend-title">SPBU</div>
|
||||
<div class="legend map-floating-panel collapsed" id="legend-peta" onclick="if(this.classList.contains('collapsed')) this.classList.remove('collapsed');">
|
||||
<div class="panel-icon">📋</div>
|
||||
<div class="panel-header" style="margin-bottom:8px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px;">
|
||||
<div style="font-size:11px;font-weight:700;color:var(--clr-text);text-transform:uppercase;margin:0;">Legenda</div>
|
||||
<button class="panel-close-btn" onclick="event.stopPropagation(); document.getElementById('legend-peta').classList.add('collapsed');" title="Tutup">×</button>
|
||||
</div>
|
||||
<div class="legend-title" style="margin-top:0;">SPBU</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot" style="background:#22c55e;"></div>
|
||||
<span>Beroperasi 24 Jam</span>
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
.bg-pattern { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: radial-gradient(#334155 1px, transparent 1px); background-size: 40px 40px; opacity: 0.3; z-index: -1; }
|
||||
.glow { position: absolute; width: 600px; height: 600px; background: radial-gradient(circle, rgba(59,130,246,0.15) 0%, rgba(15,23,42,0) 70%); top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: -1; pointer-events: none; }
|
||||
|
||||
header { text-align: center; margin-bottom: 60px; z-index: 10; }
|
||||
header h1 { font-size: 42px; font-weight: 800; background: linear-gradient(135deg, #60a5fa, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 16px; }
|
||||
header p { font-size: 18px; color: #cbd5e1; max-width: 600px; margin: 0 auto; line-height: 1.6; }
|
||||
header { text-align: center; margin-bottom: 48px; z-index: 10; padding: 0 20px; }
|
||||
header h1 { font-size: clamp(32px, 5vw, 42px); font-weight: 800; background: linear-gradient(135deg, #60a5fa, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 16px; }
|
||||
header p { font-size: clamp(15px, 2.4vw, 18px); color: #cbd5e1; max-width: 640px; margin: 0 auto; line-height: 1.6; }
|
||||
|
||||
.cards-container { display: flex; gap: 28px; max-width: 1160px; width: 100%; padding: 0 20px; z-index: 10; flex-wrap: wrap; justify-content: center; }
|
||||
|
||||
@@ -48,9 +48,17 @@
|
||||
.admin-link:hover { color: #f8fafc; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body { justify-content: flex-start; padding-top: 32px; }
|
||||
.choice-card { width: 100%; }
|
||||
header h1 { font-size: 32px; }
|
||||
body { justify-content: flex-start; padding: 32px 0 86px; }
|
||||
header { margin-bottom: 28px; }
|
||||
.cards-container { gap: 18px; padding: 0 14px; }
|
||||
.choice-card { width: 100%; min-width: 0; max-width: 430px; padding: 28px 22px; }
|
||||
.choice-card .icon-box { width: 68px; height: 68px; font-size: 30px; margin-bottom: 18px; }
|
||||
.choice-card h2 { font-size: 21px; }
|
||||
.admin-link { bottom: 18px; }
|
||||
}
|
||||
|
||||
@media (min-width: 769px) and (max-width: 1100px) {
|
||||
.choice-card { width: calc(50% - 18px); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
+112
-43
@@ -112,6 +112,14 @@
|
||||
.legend-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; color: var(--clr-text); font-size: 12px; }
|
||||
.legend-line { width: 24px; height: 0; border-top: 4px solid; border-radius: 999px; flex-shrink: 0; }
|
||||
.legend-swatch { width: 14px; height: 14px; border-radius: 4px; flex-shrink: 0; border: 1px solid rgba(255,255,255,.3); }
|
||||
.panel-toggle {
|
||||
width: 100%; display: none; align-items: center; justify-content: space-between; gap: 10px;
|
||||
background: transparent; border: none; color: var(--clr-text); cursor: pointer;
|
||||
font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .4px; padding: 0;
|
||||
}
|
||||
.panel-toggle .toggle-icon { color: var(--clr-muted); transition: transform var(--transition); }
|
||||
.panel-collapsed .panel-body { display: none; }
|
||||
.panel-collapsed .toggle-icon { transform: rotate(-90deg); }
|
||||
|
||||
.popup-title { font-weight: 700; color: #0f172a; margin-bottom: 4px; }
|
||||
.popup-row { font-size: 12px; line-height: 1.5; }
|
||||
@@ -119,15 +127,51 @@
|
||||
@media (max-width: 900px) {
|
||||
.sidebar { width: var(--sidebar-collapsed); }
|
||||
.sidebar-logo-text, .sidebar-item-text, .sidebar-section-title { display: none; }
|
||||
.sidebar-header { padding: 16px 14px; }
|
||||
header { align-items: flex-start; flex-direction: column; padding: 10px 14px; }
|
||||
.header-title h1 { font-size: 16px; }
|
||||
.header-breadcrumb { font-size: 12px; }
|
||||
.search-panel, .filter-panel, .stats-panel, .legend-panel {
|
||||
left: 12px; right: 12px; width: auto;
|
||||
}
|
||||
.search-panel { top: 12px; }
|
||||
.filter-panel { top: 190px; }
|
||||
.stats-panel { display: none; }
|
||||
.stats-panel { bottom: 58px; }
|
||||
.legend-panel { bottom: 12px; }
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.app-wrapper { height: 100dvh; }
|
||||
.sidebar { width: 56px; }
|
||||
.sidebar-header { padding: 12px 8px; justify-content: center; }
|
||||
.sidebar-logo-icon { width: 34px; height: 34px; font-size: 12px; }
|
||||
.sidebar-toggle { display: none; }
|
||||
.sidebar-nav { padding: 10px 0; }
|
||||
.sidebar-item { padding: 12px 14px; justify-content: center; }
|
||||
.header-actions { width: 100%; justify-content: space-between; }
|
||||
.search-panel { top: 10px; padding: 10px; }
|
||||
.search-results { max-height: 112px; }
|
||||
.filter-panel { top: 58px; padding: 10px; }
|
||||
.panel-toggle { display: flex; }
|
||||
.search-panel h3,
|
||||
.filter-panel h3,
|
||||
.stats-panel h3,
|
||||
.legend-panel h3 { display: none; }
|
||||
.panel-body { margin-top: 10px; }
|
||||
.mode-grid { gap: 6px; margin-bottom: 8px; }
|
||||
.mode-btn { padding: 7px; font-size: 11px; }
|
||||
.field-label { margin: 7px 0 4px; }
|
||||
.ctrl-input, .ctrl-select { padding: 8px 10px; font-size: 12px; }
|
||||
.analysis-box { margin-top: 8px; padding-top: 8px; max-height: 74px; overflow-y: auto; }
|
||||
.legend-panel { bottom: 10px; padding: 10px; }
|
||||
.legend-row { font-size: 11px; margin-bottom: 5px; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.header-breadcrumb { display: none; }
|
||||
.count-pill, .refresh-btn { font-size: 12px; padding: 5px 9px; }
|
||||
.filter-panel { top: 56px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -176,67 +220,79 @@
|
||||
<div id="map"></div>
|
||||
|
||||
<div class="panel search-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="search-panel"><span>Pencarian</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Pencarian</h3>
|
||||
<input id="search-input" class="ctrl-input" type="text" placeholder="Cari nama jalan atau pemilik parsil..." autocomplete="off">
|
||||
<div id="search-results" class="search-results"></div>
|
||||
<div class="panel-body">
|
||||
<input id="search-input" class="ctrl-input" type="text" placeholder="Cari nama jalan atau pemilik parsil..." autocomplete="off">
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel filter-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="filter-panel"><span>Filter & Analisis</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Filter & Analisis</h3>
|
||||
<div class="mode-grid">
|
||||
<button class="mode-btn active" data-mode="all">Semua</button>
|
||||
<button class="mode-btn" data-mode="jalan">Jalan</button>
|
||||
<button class="mode-btn" data-mode="parsil">Parsil</button>
|
||||
<button class="mode-btn" data-mode="analysis">Akses</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="mode-grid">
|
||||
<button class="mode-btn active" data-mode="all">Semua</button>
|
||||
<button class="mode-btn" data-mode="jalan">Jalan</button>
|
||||
<button class="mode-btn" data-mode="parsil">Parsil</button>
|
||||
<button class="mode-btn" data-mode="analysis">Akses</button>
|
||||
</div>
|
||||
|
||||
<label class="field-label" for="filter-jalan">Status Jalan</label>
|
||||
<select id="filter-jalan" class="ctrl-select">
|
||||
<option value="all">Semua status</option>
|
||||
<option value="Nasional">Nasional</option>
|
||||
<option value="Provinsi">Provinsi</option>
|
||||
<option value="Kabupaten">Kabupaten</option>
|
||||
</select>
|
||||
<label class="field-label" for="filter-jalan">Status Jalan</label>
|
||||
<select id="filter-jalan" class="ctrl-select">
|
||||
<option value="all">Semua status</option>
|
||||
<option value="Nasional">Nasional</option>
|
||||
<option value="Provinsi">Provinsi</option>
|
||||
<option value="Kabupaten">Kabupaten</option>
|
||||
</select>
|
||||
|
||||
<label class="field-label" for="filter-parsil">Status Parsil</label>
|
||||
<select id="filter-parsil" class="ctrl-select">
|
||||
<option value="all">Semua status</option>
|
||||
<option value="SHM">SHM</option>
|
||||
<option value="HGB">HGB</option>
|
||||
<option value="HGU">HGU</option>
|
||||
<option value="HP">HP</option>
|
||||
</select>
|
||||
<label class="field-label" for="filter-parsil">Status Parsil</label>
|
||||
<select id="filter-parsil" class="ctrl-select">
|
||||
<option value="all">Semua status</option>
|
||||
<option value="SHM">SHM</option>
|
||||
<option value="HGB">HGB</option>
|
||||
<option value="HGU">HGU</option>
|
||||
<option value="HP">HP</option>
|
||||
</select>
|
||||
|
||||
<label class="field-label" for="filter-access">Akses Parsil ke Jalan</label>
|
||||
<select id="filter-access" class="ctrl-select">
|
||||
<option value="all">Semua jarak</option>
|
||||
<option value="near">Dekat (< 50 m)</option>
|
||||
<option value="medium">Sedang (50-200 m)</option>
|
||||
<option value="far">Jauh (> 200 m)</option>
|
||||
</select>
|
||||
<label class="field-label" for="filter-access">Akses Parsil ke Jalan</label>
|
||||
<select id="filter-access" class="ctrl-select">
|
||||
<option value="all">Semua jarak</option>
|
||||
<option value="near">Dekat (< 50 m)</option>
|
||||
<option value="medium">Sedang (50-200 m)</option>
|
||||
<option value="far">Jauh (> 200 m)</option>
|
||||
</select>
|
||||
|
||||
<div id="analysis-box" class="analysis-box">
|
||||
<span class="analysis-empty">Klik satu parsil untuk melihat jalan terdekat dan estimasi jaraknya.</span>
|
||||
<div id="analysis-box" class="analysis-box">
|
||||
<span class="analysis-empty">Klik satu parsil untuk melihat jalan terdekat dan estimasi jaraknya.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel stats-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="stats-panel"><span>Statistik</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Statistik Data</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card"><div id="stat-jalan" class="stat-value">0</div><div class="stat-label">Total jalan tampil</div></div>
|
||||
<div class="stat-card"><div id="stat-parsil" class="stat-value">0</div><div class="stat-label">Total parsil tampil</div></div>
|
||||
<div class="stat-card"><div id="stat-panjang" class="stat-value">0 km</div><div class="stat-label">Akumulasi panjang jalan</div></div>
|
||||
<div class="stat-card"><div id="stat-luas" class="stat-value">0 m2</div><div class="stat-label">Akumulasi luas parsil</div></div>
|
||||
<div class="panel-body">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card"><div id="stat-jalan" class="stat-value">0</div><div class="stat-label">Total jalan tampil</div></div>
|
||||
<div class="stat-card"><div id="stat-parsil" class="stat-value">0</div><div class="stat-label">Total parsil tampil</div></div>
|
||||
<div class="stat-card"><div id="stat-panjang" class="stat-value">0 km</div><div class="stat-label">Akumulasi panjang jalan</div></div>
|
||||
<div class="stat-card"><div id="stat-luas" class="stat-value">0 m2</div><div class="stat-label">Akumulasi luas parsil</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel legend-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="legend-panel"><span>Legenda</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Legenda</h3>
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#ef4444;"></div> Jalan Nasional</div>
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#3b82f6;"></div> Jalan Provinsi</div>
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#22c55e;"></div> Jalan Kabupaten</div>
|
||||
<div class="legend-row"><div class="legend-swatch" style="background:#14b8a6;"></div> Parsil SHM</div>
|
||||
<div class="legend-row"><div class="legend-swatch" style="background:#f59e0b;"></div> Parsil HGB/HGU/HP</div>
|
||||
<div class="panel-body">
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#ef4444;"></div> Jalan Nasional</div>
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#3b82f6;"></div> Jalan Provinsi</div>
|
||||
<div class="legend-row"><div class="legend-line" style="border-color:#22c55e;"></div> Jalan Kabupaten</div>
|
||||
<div class="legend-row"><div class="legend-swatch" style="background:#14b8a6;"></div> Parsil SHM</div>
|
||||
<div class="legend-row"><div class="legend-swatch" style="background:#f59e0b;"></div> Parsil HGB/HGU/HP</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -249,6 +305,19 @@
|
||||
document.getElementById('sidebar').classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
function setupCollapsiblePanels() {
|
||||
document.querySelectorAll('[data-panel-toggle]').forEach(btn => {
|
||||
const panel = btn.closest('.panel');
|
||||
btn.addEventListener('click', () => panel.classList.toggle('panel-collapsed'));
|
||||
});
|
||||
if (window.matchMedia('(max-width: 760px)').matches) {
|
||||
document.querySelectorAll('.search-panel, .filter-panel, .stats-panel, .legend-panel').forEach(panel => {
|
||||
panel.classList.add('panel-collapsed');
|
||||
});
|
||||
}
|
||||
}
|
||||
setupCollapsiblePanels();
|
||||
|
||||
const map = L.map('map').setView([-0.026, 109.342], 13);
|
||||
const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19 });
|
||||
const dark = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { maxZoom: 19 });
|
||||
|
||||
@@ -894,6 +894,7 @@ function initMap() {
|
||||
L.control.layers(baseMaps).addTo(map);
|
||||
|
||||
// Initialize Geoman Controls (only if Geoman library is loaded)
|
||||
/*
|
||||
if (map.pm && typeof map.pm.addControls === 'function') {
|
||||
map.pm.addControls({
|
||||
position: 'topleft',
|
||||
@@ -906,6 +907,7 @@ function initMap() {
|
||||
removalMode: false
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
// ALWAYS set mapInstance — regardless of whether Geoman loaded
|
||||
window.appState.mapInstance = map;
|
||||
|
||||
+9
-1
@@ -7,7 +7,7 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: 'Inter', sans-serif; background: #0f172a; color: #f1f5f9; display: flex; align-items: center; justify-content: center; height: 100vh; }
|
||||
body { font-family: 'Inter', sans-serif; background: #0f172a; color: #f1f5f9; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 24px; }
|
||||
.login-container { background: #1e293b; padding: 40px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); width: 100%; max-width: 400px; position: relative; }
|
||||
.login-container h2 { margin-bottom: 24px; text-align: center; color: #3b82f6; }
|
||||
.form-group { margin-bottom: 20px; }
|
||||
@@ -26,6 +26,14 @@
|
||||
|
||||
.form-section { display: none; }
|
||||
.form-section.active { display: block; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body { align-items: flex-start; padding: 18px 14px; }
|
||||
.login-container { padding: 28px 20px; border-radius: 10px; }
|
||||
.login-container h2 { font-size: 22px; margin-bottom: 20px; }
|
||||
.form-group { margin-bottom: 16px; }
|
||||
.form-group input, .btn { min-height: 44px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+83
-15
@@ -135,6 +135,14 @@
|
||||
.legend-panel h3 { font-size: 11px; font-weight: 700; color: var(--clr-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; }
|
||||
.legend-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; color: var(--clr-text); font-size: 12px; }
|
||||
.legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
|
||||
.panel-toggle {
|
||||
width: 100%; display: none; align-items: center; justify-content: space-between; gap: 10px;
|
||||
background: transparent; border: none; color: var(--clr-text); cursor: pointer;
|
||||
font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .4px; padding: 0;
|
||||
}
|
||||
.panel-toggle .toggle-icon { color: var(--clr-muted); transition: transform var(--transition); }
|
||||
.panel-collapsed .panel-body { display: none; }
|
||||
.panel-collapsed .toggle-icon { transform: rotate(-90deg); }
|
||||
|
||||
/* ---- Popup Slider ---- */
|
||||
.popup-slider-wrap { margin-top: 10px; }
|
||||
@@ -172,6 +180,48 @@
|
||||
0% { transform: scale(0.5); opacity: 1; }
|
||||
100% { transform: scale(1.5); opacity: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.sidebar { width: var(--sidebar-collapsed); }
|
||||
.sidebar-logo-text,
|
||||
.sidebar-item-text,
|
||||
.sidebar-section-title,
|
||||
.sidebar-item-user { display: none; }
|
||||
.sidebar-header { padding: 16px 14px; }
|
||||
header { height: auto; min-height: 64px; padding: 10px 14px; }
|
||||
.header-title h1 { font-size: 16px; }
|
||||
.header-breadcrumb { font-size: 12px; }
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.app-wrapper { height: 100dvh; }
|
||||
.sidebar { width: 56px; }
|
||||
.sidebar-header { padding: 12px 8px; justify-content: center; }
|
||||
.sidebar-logo-icon { width: 34px; height: 34px; font-size: 12px; }
|
||||
.sidebar-toggle { display: none; }
|
||||
.sidebar-nav { padding: 10px 0; }
|
||||
.sidebar-item { padding: 12px 14px; justify-content: center; }
|
||||
.search-panel,
|
||||
.legend-panel {
|
||||
left: 10px; right: 10px; width: auto;
|
||||
}
|
||||
.search-panel { top: 10px; padding: 10px; }
|
||||
.search-results { max-height: 105px; }
|
||||
.search-panel h3 { font-size: 11px; margin-bottom: 8px; }
|
||||
.panel-toggle { display: flex; }
|
||||
.search-panel h3,
|
||||
.legend-panel h3 { display: none; }
|
||||
.panel-body { margin-top: 10px; }
|
||||
.legend-panel { bottom: 10px; padding: 10px; }
|
||||
.legend-panel h3 { margin-bottom: 7px; }
|
||||
.legend-row { font-size: 11px; margin-bottom: 5px; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.header-breadcrumb { display: none; }
|
||||
.search-panel { top: 8px; }
|
||||
.ctrl-input { padding: 8px 10px; font-size: 12px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -222,28 +272,33 @@
|
||||
|
||||
<!-- Search Panel (Left) -->
|
||||
<div class="search-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="search-panel"><span>Pencarian</span><span class="toggle-icon">▾</span></button>
|
||||
<h3><i class="fa-solid fa-magnifying-glass" style="color:var(--clr-primary)"></i> Pencarian</h3>
|
||||
|
||||
<div style="font-size:11px; font-weight:600; color:#94a3b8; margin-bottom:4px;">RUMAH IBADAH</div>
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-ibadah" class="ctrl-input" type="text" placeholder="Cari Rumah Ibadah..." autocomplete="off">
|
||||
</div>
|
||||
<div id="results-ibadah" class="search-results"></div>
|
||||
<div class="panel-body">
|
||||
<div style="font-size:11px; font-weight:600; color:#94a3b8; margin-bottom:4px;">RUMAH IBADAH</div>
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-ibadah" class="ctrl-input" type="text" placeholder="Cari Rumah Ibadah..." autocomplete="off">
|
||||
</div>
|
||||
<div id="results-ibadah" class="search-results"></div>
|
||||
|
||||
<div style="font-size:11px; font-weight:600; color:#94a3b8; margin-top:8px; margin-bottom:4px;">PENDUDUK MISKIN</div>
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-miskin" class="ctrl-input" type="text" placeholder="Cari Nama KK Penduduk Miskin..." autocomplete="off">
|
||||
<div style="font-size:11px; font-weight:600; color:#94a3b8; margin-top:8px; margin-bottom:4px;">PENDUDUK MISKIN</div>
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-miskin" class="ctrl-input" type="text" placeholder="Cari Nama KK Penduduk Miskin..." autocomplete="off">
|
||||
</div>
|
||||
<div id="results-miskin" class="search-results"></div>
|
||||
</div>
|
||||
<div id="results-miskin" class="search-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="legend-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="legend-panel"><span>Legenda</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Legenda</h3>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#22c55e;"></div> Rumah Ibadah</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#ef4444;"></div> Penduduk Miskin (Dalam Radius)</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#1e293b; border: 1px solid #64748b;"></div> Penduduk Miskin (Luar Radius)</div>
|
||||
<hr style="border-color:#334155; margin: 8px 0;">
|
||||
<div style="font-size:11px; color: var(--clr-muted);">💡 Klik Rumah Ibadah untuk atur radius jangkauan</div>
|
||||
<div class="panel-body">
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#22c55e;"></div> Rumah Ibadah</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#ef4444;"></div> Penduduk Miskin (Dalam Radius)</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#1e293b; border: 1px solid #64748b;"></div> Penduduk Miskin (Luar Radius)</div>
|
||||
<hr style="border-color:#334155; margin: 8px 0;">
|
||||
<div style="font-size:11px; color: var(--clr-muted);">💡 Klik Rumah Ibadah untuk atur radius jangkauan</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -257,6 +312,19 @@
|
||||
document.getElementById('sidebar').classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
function setupCollapsiblePanels() {
|
||||
document.querySelectorAll('[data-panel-toggle]').forEach(btn => {
|
||||
const panel = btn.closest('.search-panel, .legend-panel');
|
||||
btn.addEventListener('click', () => panel.classList.toggle('panel-collapsed'));
|
||||
});
|
||||
if (window.matchMedia('(max-width: 760px)').matches) {
|
||||
document.querySelectorAll('.search-panel, .legend-panel').forEach(panel => {
|
||||
panel.classList.add('panel-collapsed');
|
||||
});
|
||||
}
|
||||
}
|
||||
setupCollapsiblePanels();
|
||||
|
||||
// Global state
|
||||
let ibadahData = [], miskinData = [];
|
||||
let ibadahLayer, miskinLayer, map;
|
||||
|
||||
@@ -147,6 +147,14 @@
|
||||
.legend-panel h3 { font-size: 11px; font-weight: 700; color: var(--clr-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; }
|
||||
.legend-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; color: var(--clr-text); font-size: 12px; }
|
||||
.legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
|
||||
.panel-toggle {
|
||||
width: 100%; display: none; align-items: center; justify-content: space-between; gap: 10px;
|
||||
background: transparent; border: none; color: var(--clr-text); cursor: pointer;
|
||||
font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .4px; padding: 0;
|
||||
}
|
||||
.panel-toggle .toggle-icon { color: var(--clr-muted); transition: transform var(--transition); }
|
||||
.panel-collapsed .panel-body { display: none; }
|
||||
.panel-collapsed .toggle-icon { transform: rotate(-90deg); }
|
||||
|
||||
/* ---- Custom Marker ---- */
|
||||
.custom-marker {
|
||||
@@ -175,6 +183,59 @@
|
||||
0% { transform: scale(0.5); opacity: 1; }
|
||||
100% { transform: scale(1.5); opacity: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.sidebar { width: var(--sidebar-collapsed); }
|
||||
.sidebar-logo-text,
|
||||
.sidebar-item-text,
|
||||
.sidebar-section-title { display: none; }
|
||||
.sidebar-header { padding: 16px 14px; }
|
||||
header { height: auto; min-height: 64px; padding: 10px 14px; gap: 10px; flex-wrap: wrap; }
|
||||
.header-title h1 { font-size: 16px; }
|
||||
.header-breadcrumb { font-size: 12px; }
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.app-wrapper { height: 100dvh; }
|
||||
.sidebar { width: 56px; }
|
||||
.sidebar-header { padding: 12px 8px; justify-content: center; }
|
||||
.sidebar-logo-icon { width: 34px; height: 34px; font-size: 12px; }
|
||||
.sidebar-toggle { display: none; }
|
||||
.sidebar-nav { padding: 10px 0; }
|
||||
.sidebar-item { padding: 12px 14px; justify-content: center; }
|
||||
header { align-items: flex-start; }
|
||||
header > div:last-child { width: 100%; justify-content: space-between; }
|
||||
.content-area { min-height: 0; }
|
||||
.search-panel,
|
||||
.controls-panel,
|
||||
.legend-panel {
|
||||
left: 10px; right: 10px; width: auto;
|
||||
}
|
||||
.search-panel { top: 10px; padding: 10px; }
|
||||
.search-results { max-height: 130px; }
|
||||
.controls-panel { top: 142px; padding: 10px; }
|
||||
.controls-panel h3,
|
||||
.search-panel h3 { font-size: 11px; margin-bottom: 8px; }
|
||||
.panel-toggle { display: flex; }
|
||||
.search-panel h3,
|
||||
.controls-panel h3,
|
||||
.legend-panel h3 { display: none; }
|
||||
.search-panel,
|
||||
.controls-panel,
|
||||
.legend-panel { padding: 10px; }
|
||||
.panel-body { margin-top: 10px; }
|
||||
.ctrl-btn { padding: 9px; font-size: 12px; }
|
||||
.legend-panel { bottom: 10px; padding: 10px; }
|
||||
.legend-panel h3 { margin-bottom: 7px; }
|
||||
.legend-row { font-size: 11px; margin-bottom: 5px; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.search-panel { top: 8px; }
|
||||
.controls-panel { top: 136px; }
|
||||
.header-breadcrumb { display: none; }
|
||||
#data-count, #btn-refresh { font-size: 12px !important; padding: 5px 9px !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -228,29 +289,38 @@
|
||||
|
||||
<!-- Search Panel (Left) -->
|
||||
<div class="search-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="search-panel"><span>Cari SPBU</span><span class="toggle-icon">▾</span></button>
|
||||
<h3><i class="fa-solid fa-magnifying-glass" style="color:var(--clr-primary)"></i> Cari SPBU</h3>
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-spbu" class="ctrl-input" type="text" placeholder="Ketik nama atau no SPBU..." autocomplete="off">
|
||||
<div class="panel-body">
|
||||
<div class="search-input-wrap">
|
||||
<input id="search-spbu" class="ctrl-input" type="text" placeholder="Ketik nama atau no SPBU..." autocomplete="off">
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="controls-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="controls-panel"><span>Lokasi & Rute</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>📍 Lokasi & Rute</h3>
|
||||
<button id="btn-location" class="ctrl-btn">📍 Dapatkan Lokasi Saya</button>
|
||||
<button id="btn-nearest" class="ctrl-btn" disabled>🎯 Cari SPBU Terdekat</button>
|
||||
<div id="info-box" class="info-box">
|
||||
<strong>SPBU Terdekat:</strong><br>
|
||||
<span id="nearest-name">-</span><br>
|
||||
<strong>Jarak:</strong> <span id="nearest-distance">-</span> km
|
||||
<div class="panel-body">
|
||||
<button id="btn-location" class="ctrl-btn">📍 Dapatkan Lokasi Saya</button>
|
||||
<button id="btn-nearest" class="ctrl-btn" disabled>🎯 Cari SPBU Terdekat</button>
|
||||
<div id="info-box" class="info-box">
|
||||
<strong>SPBU Terdekat:</strong><br>
|
||||
<span id="nearest-name">-</span><br>
|
||||
<strong>Jarak:</strong> <span id="nearest-distance">-</span> km
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend-panel">
|
||||
<button class="panel-toggle" type="button" data-panel-toggle="legend-panel"><span>Legenda</span><span class="toggle-icon">▾</span></button>
|
||||
<h3>Legenda</h3>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#22c55e;"></div> SPBU 24 Jam</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#f59e0b;"></div> SPBU Tidak 24 Jam</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#ef4444;"></div> Lokasi Anda</div>
|
||||
<div class="panel-body">
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#22c55e;"></div> SPBU 24 Jam</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#f59e0b;"></div> SPBU Tidak 24 Jam</div>
|
||||
<div class="legend-row"><div class="legend-dot" style="background:#ef4444;"></div> Lokasi Anda</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -264,6 +334,19 @@
|
||||
document.getElementById('sidebar').classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
function setupCollapsiblePanels() {
|
||||
document.querySelectorAll('[data-panel-toggle]').forEach(btn => {
|
||||
const panel = btn.closest('.search-panel, .controls-panel, .legend-panel');
|
||||
btn.addEventListener('click', () => panel.classList.toggle('panel-collapsed'));
|
||||
});
|
||||
if (window.matchMedia('(max-width: 760px)').matches) {
|
||||
document.querySelectorAll('.search-panel, .controls-panel, .legend-panel').forEach(panel => {
|
||||
panel.classList.add('panel-collapsed');
|
||||
});
|
||||
}
|
||||
}
|
||||
setupCollapsiblePanels();
|
||||
|
||||
// Init Map
|
||||
const map = L.map('map').setView([-0.026, 109.342], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
|
||||
Reference in New Issue
Block a user