refactor(ui): two-tier bottom dock (collapsible filters over trip cards), full-width map
Moved filters out of the right dock into a bottom dock with two tiers: filter tier (top) + trip-card tier (beneath) — a selection→results hierarchy. Map is now full-width. Live mode: filter form expanded, no cards. Trips mode: filters collapse to a one-line summary (with Edit to re-expand) and cards show beneath, keeping the map tall. Plate combobox dropdown opens upward. map.resize() on every tier change. Verified: full-width map in all states, live=form, trips=summary+ cards (150), Edit re-expands with cards still shown, back=form, no overflow, no errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
169fc21f36
commit
70928c0b2d
2 changed files with 126 additions and 108 deletions
11
README.md
11
README.md
|
|
@ -41,13 +41,16 @@ trips** into one view for the Fireside Communications / Tracksolid fleet.
|
||||||
(vehicle / cost centre / city) plus the **first trip** and **last trip** —
|
(vehicle / cost centre / city) plus the **first trip** and **last trip** —
|
||||||
each with its **reverse-geocoded location and timestamp** — alongside the KPI
|
each with its **reverse-geocoded location and timestamp** — alongside the KPI
|
||||||
totals (trips, km, driving/idle hours, vehicles, drivers, date range).
|
totals (trips, km, driving/idle hours, vehicles, drivers, date range).
|
||||||
- **Fixed chrome (no floating panels).** Filters are a **slim, content-height dock
|
- **Full-width map + two-tier bottom dock (no floating/side panels).** All controls
|
||||||
on the right**; in trips view the trips are **cards in a fixed bottom bar**
|
live in a bottom dock with two tiers: a **filter tier** on top and a **trip-card
|
||||||
(horizontally scrollable) that mirrors the top bar. Click a card to fit +
|
tier** beneath (selection → results hierarchy). In **live** mode the filter row
|
||||||
animate that route.
|
is expanded and there are no cards; in **trips** mode the filters **collapse to a
|
||||||
|
one-line summary** (`Filters: KCA 542Q · roll out · nairobi · Last 1 month`, with
|
||||||
|
**Edit** to expand) and the trip cards show beneath — keeping the map tall.
|
||||||
- **Plate picker is a searchable combobox** — type to filter, click to add a
|
- **Plate picker is a searchable combobox** — type to filter, click to add a
|
||||||
removable chip (multi-select), instead of a tall scrolling list. Cost centre /
|
removable chip (multi-select), instead of a tall scrolling list. Cost centre /
|
||||||
city / time stay single-line; date pickers appear only for a custom range.
|
city / time stay single-line; date pickers appear only for a custom range.
|
||||||
|
Trip cards scroll horizontally; click a card to fit + animate that route.
|
||||||
|
|
||||||
Live: <https://fleetnow.rahamafresh.com>
|
Live: <https://fleetnow.rahamafresh.com>
|
||||||
|
|
||||||
|
|
|
||||||
171
index.html
171
index.html
|
|
@ -46,7 +46,7 @@
|
||||||
}
|
}
|
||||||
.app {
|
.app {
|
||||||
display: grid; height: 100vh; width: 100vw; overflow: hidden;
|
display: grid; height: 100vh; width: 100vw; overflow: hidden;
|
||||||
grid-template-rows: auto auto 1fr;
|
grid-template-rows: auto auto minmax(0, 1fr) auto; /* header · context · map · bottom dock */
|
||||||
grid-template-columns: minmax(0, 1fr); /* clamp so the trip-card scroller can't widen the page */
|
grid-template-columns: minmax(0, 1fr); /* clamp so the trip-card scroller can't widen the page */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,55 +121,52 @@
|
||||||
.clock .label { font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; }
|
.clock .label { font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; }
|
||||||
.clock b { font-weight: 600; }
|
.clock b { font-weight: 600; }
|
||||||
|
|
||||||
/* ── Body: map + fixed right filter dock + fixed bottom trips bar ───── */
|
/* ── Layout: header · context · map · two-tier bottom dock ───────────── */
|
||||||
header { grid-row: 1; }
|
header { grid-row: 1; }
|
||||||
.tripbar { grid-row: 2; }
|
.tripbar { grid-row: 2; }
|
||||||
.body {
|
#map { grid-row: 3; position: relative; min-height: 0; }
|
||||||
grid-row: 3; min-height: 0;
|
|
||||||
display: grid;
|
|
||||||
/* minmax(0,…) so the horizontal trip-card scroller can't blow out the track */
|
|
||||||
grid-template-columns: minmax(0, 1fr) 224px; /* map | slim filter dock */
|
|
||||||
grid-template-rows: minmax(0, 1fr) auto; /* map | fixed trips bar */
|
|
||||||
}
|
|
||||||
#map { grid-column: 1; grid-row: 1; position: relative; min-height: 0; }
|
|
||||||
.placeholder {
|
.placeholder {
|
||||||
position: absolute; inset: 0; display: grid; place-items: center;
|
position: absolute; inset: 0; display: grid; place-items: center;
|
||||||
color: var(--muted); font-size: 13px; text-align: center; padding: 32px;
|
color: var(--muted); font-size: 13px; text-align: center; padding: 32px;
|
||||||
pointer-events: none; z-index: 1;
|
pointer-events: none; z-index: 1;
|
||||||
}
|
}
|
||||||
|
.dockbar {
|
||||||
|
grid-row: 4; min-width: 0; display: flex; flex-direction: column;
|
||||||
|
background: var(--panel); border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
/* Slim, content-height filter card — docked in the right column (top-aligned,
|
/* Filter tier — expanded form (live / editing) ⇄ collapsed summary (trips) */
|
||||||
so no dead space below the button), part of the layout (won't drift). */
|
.filter-summary { display: none; align-items: center; gap: 12px; padding: 9px 16px; }
|
||||||
.filters {
|
.filter-tier.collapsed .filter-summary { display: flex; }
|
||||||
grid-column: 2; grid-row: 1 / span 2; align-self: start;
|
.filter-tier.collapsed .filter-form { display: none; }
|
||||||
margin: 12px 12px 0 0; max-height: calc(100% - 24px); overflow-y: auto;
|
.fs-label { font-size: 10px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); font-weight: 600; }
|
||||||
background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
|
.fs-text { color: var(--text); font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
padding: 13px 13px 14px;
|
.fs-edit {
|
||||||
|
margin-left: auto; background: var(--panel-2); color: var(--accent);
|
||||||
|
border: 1px solid var(--border); border-radius: 6px; padding: 5px 12px;
|
||||||
|
font: 600 12px system-ui; cursor: pointer; white-space: nowrap;
|
||||||
}
|
}
|
||||||
.filters h3 {
|
.fs-edit:hover { border-color: var(--accent); }
|
||||||
font-size: 11px; text-transform: uppercase; letter-spacing: .6px;
|
|
||||||
color: var(--muted); margin: 0 0 12px; font-weight: 600;
|
.filter-form { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 12px; padding: 10px 16px 12px; }
|
||||||
}
|
.ff-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
||||||
.field { display: flex; flex-direction: column; margin-bottom: 11px; }
|
.ff-field > label {
|
||||||
.field label {
|
font-size: 10px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
|
||||||
font-size: 10px; text-transform: uppercase; letter-spacing: .5px;
|
|
||||||
color: var(--muted); margin-bottom: 4px;
|
|
||||||
}
|
}
|
||||||
|
.ff-plate { width: 230px; }
|
||||||
select, input[type=date] {
|
select, input[type=date] {
|
||||||
padding: 7px 9px; background: var(--bg); color: var(--text);
|
padding: 7px 9px; background: var(--bg); color: var(--text);
|
||||||
border: 1px solid var(--border); border-radius: 6px; font: 13px system-ui; width: 100%;
|
border: 1px solid var(--border); border-radius: 6px; font: 13px system-ui; width: 100%;
|
||||||
}
|
}
|
||||||
select:focus, input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
select:focus, input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
||||||
.hint { font-size: 9.5px; color: var(--muted); margin-top: 3px; line-height: 1.3; }
|
.ff-field.custom { display: none; }
|
||||||
.custom { display: none; }
|
.ff-field.custom.show { display: flex; }
|
||||||
.custom.show { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
.ff-go { width: auto; margin: 0; align-self: flex-end; padding: 8px 18px; }
|
||||||
.custom .field { margin-bottom: 0; }
|
|
||||||
.custom input[type=date] { padding: 6px 6px; font-size: 11.5px; }
|
|
||||||
|
|
||||||
/* Searchable plate combobox + chips (replaces the tall multi-select) */
|
/* Searchable plate combobox + chips — dropdown opens UPWARD (bar is at bottom) */
|
||||||
.plate-box { position: relative; }
|
.plate-box { position: relative; }
|
||||||
.plate-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 5px; }
|
.plate-chips { display: flex; flex-wrap: wrap; gap: 4px; }
|
||||||
.plate-chips:empty { display: none; }
|
.plate-chips:not(:empty) { margin-bottom: 5px; }
|
||||||
.plate-chip {
|
.plate-chip {
|
||||||
display: inline-flex; align-items: center; gap: 5px;
|
display: inline-flex; align-items: center; gap: 5px;
|
||||||
background: var(--accent); color: #1a1009; font: 600 11px system-ui;
|
background: var(--accent); color: #1a1009; font: 600 11px system-ui;
|
||||||
|
|
@ -180,9 +177,9 @@
|
||||||
.plate-search { width: 100%; padding: 7px 9px; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 6px; font: 13px system-ui; }
|
.plate-search { width: 100%; padding: 7px 9px; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 6px; font: 13px system-ui; }
|
||||||
.plate-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
.plate-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
|
||||||
.plate-results {
|
.plate-results {
|
||||||
display: none; position: absolute; z-index: 20; left: 0; right: 0; top: 100%; margin-top: 3px;
|
display: none; position: absolute; z-index: 20; left: 0; right: 0; bottom: 100%; margin-bottom: 4px;
|
||||||
max-height: 200px; overflow-y: auto; background: var(--panel-2);
|
max-height: 240px; overflow-y: auto; background: var(--panel-2);
|
||||||
border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 22px rgba(0,0,0,.5);
|
border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 -8px 22px rgba(0,0,0,.5);
|
||||||
}
|
}
|
||||||
.plate-results.show { display: block; }
|
.plate-results.show { display: block; }
|
||||||
.plate-opt { padding: 7px 10px; font-size: 12.5px; cursor: pointer; color: var(--text); }
|
.plate-opt { padding: 7px 10px; font-size: 12.5px; cursor: pointer; color: var(--text); }
|
||||||
|
|
@ -191,30 +188,17 @@
|
||||||
.plate-opt .pd { color: var(--muted); font-size: 11px; }
|
.plate-opt .pd { color: var(--muted); font-size: 11px; }
|
||||||
.plate-none { padding: 8px 10px; color: var(--muted); font-size: 11.5px; }
|
.plate-none { padding: 8px 10px; color: var(--muted); font-size: 11.5px; }
|
||||||
.btn {
|
.btn {
|
||||||
width: 100%; padding: 10px; margin-top: 6px; background: var(--accent);
|
padding: 10px; background: var(--accent);
|
||||||
color: #1a1009; border: 0; border-radius: 6px; font: 600 13px system-ui; cursor: pointer;
|
color: #1a1009; border: 0; border-radius: 6px; font: 600 13px system-ui; cursor: pointer;
|
||||||
}
|
}
|
||||||
.btn:hover { background: var(--accent-hover); }
|
.btn:hover { background: var(--accent-hover); }
|
||||||
.btn:disabled { background: #4b5563; color: #cbd5e1; cursor: wait; }
|
.btn:disabled { background: #4b5563; color: #cbd5e1; cursor: wait; }
|
||||||
|
|
||||||
/* Fixed bottom trips bar — horizontal scrolling cards (mirrors the top bar) */
|
/* Card tier (beneath the filter tier; trips mode only) */
|
||||||
.tripsbar {
|
.cards-tier { display: none; flex-direction: column; min-width: 0; border-top: 1px solid var(--border); }
|
||||||
grid-column: 1; grid-row: 2; display: none;
|
.cards-tier.show { display: flex; }
|
||||||
background: var(--panel); border-top: 1px solid var(--border);
|
.cards-tier-head { padding: 7px 16px 0; }
|
||||||
flex-direction: column; min-width: 0;
|
.tripsbar-title { font-size: 11px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); font-weight: 600; }
|
||||||
}
|
|
||||||
.tripsbar.show { display: flex; }
|
|
||||||
.tripsbar-head {
|
|
||||||
display: flex; align-items: center; gap: 14px; padding: 7px 16px 0;
|
|
||||||
}
|
|
||||||
.tripsbar-head .back {
|
|
||||||
display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
|
|
||||||
color: var(--accent); font: 600 12px system-ui; background: none; border: 0; padding: 0;
|
|
||||||
}
|
|
||||||
.tripsbar-head .back:hover { color: var(--accent-hover); }
|
|
||||||
.tripsbar-title {
|
|
||||||
font-size: 11px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); font-weight: 600;
|
|
||||||
}
|
|
||||||
.tripsbar-title span { color: var(--text); }
|
.tripsbar-title span { color: var(--text); }
|
||||||
.trip-cards {
|
.trip-cards {
|
||||||
display: flex; gap: 10px; overflow-x: auto; overflow-y: hidden;
|
display: flex; gap: 10px; overflow-x: auto; overflow-y: hidden;
|
||||||
|
|
@ -384,33 +368,40 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
|
||||||
<div id="map">
|
<div id="map">
|
||||||
<div class="placeholder" id="placeholder">Loading live fleet…</div>
|
<div class="placeholder" id="placeholder">Loading live fleet…</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filters — fixed right dock (always) -->
|
<!-- Bottom dock: two tiers — filter tier (top) + trip-card tier (beneath) -->
|
||||||
<aside class="filters" id="filters">
|
<div class="dockbar" id="dockbar">
|
||||||
<h3>Filters</h3>
|
|
||||||
<div class="field">
|
<!-- Filter tier: collapsed summary (trips) ⇄ expanded form -->
|
||||||
|
<div class="filter-tier" id="filter-tier">
|
||||||
|
<div class="filter-summary" id="filter-summary">
|
||||||
|
<span class="fs-label">Filters</span>
|
||||||
|
<span class="fs-text" id="fs-text">Whole fleet · Today</span>
|
||||||
|
<button class="fs-edit" id="filter-edit" type="button">✎ Edit</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-form" id="filter-form">
|
||||||
|
<div class="ff-field ff-plate">
|
||||||
<label for="plate-search">Number plate</label>
|
<label for="plate-search">Number plate</label>
|
||||||
<div class="plate-box">
|
<div class="plate-box">
|
||||||
<div class="plate-chips" id="plate-chips"></div>
|
<div class="plate-chips" id="plate-chips"></div>
|
||||||
<input type="text" id="plate-search" class="plate-search" placeholder="Search plate…" autocomplete="off">
|
<input type="text" id="plate-search" class="plate-search" placeholder="Search plate…" autocomplete="off">
|
||||||
<div class="plate-results" id="plate-results"></div>
|
<div class="plate-results" id="plate-results"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Source of truth for all filter logic; UI above drives it. -->
|
|
||||||
<select id="f-vehicle" multiple hidden></select>
|
<select id="f-vehicle" multiple hidden></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="ff-field">
|
||||||
<label for="f-cc">Cost centre</label>
|
<label for="f-cc">Cost centre</label>
|
||||||
<select id="f-cc"><option value="">All cost centres</option></select>
|
<select id="f-cc"><option value="">All cost centres</option></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="ff-field">
|
||||||
<label for="f-city">Assigned city</label>
|
<label for="f-city">Assigned city</label>
|
||||||
<select id="f-city"><option value="">All cities</option></select>
|
<select id="f-city"><option value="">All cities</option></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="ff-field">
|
||||||
<label for="f-period">Time</label>
|
<label for="f-period">Time</label>
|
||||||
<select id="f-period">
|
<select id="f-period">
|
||||||
<option value="today" selected>Today</option>
|
<option value="today" selected>Today</option>
|
||||||
|
|
@ -419,17 +410,19 @@
|
||||||
<option value="custom">Custom range</option>
|
<option value="custom">Custom range</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom" id="custom">
|
<div class="ff-field custom" id="custom">
|
||||||
<div class="field"><label for="f-start">Start date</label><input type="date" id="f-start"></div>
|
<label for="f-start">Start</label><input type="date" id="f-start">
|
||||||
<div class="field"><label for="f-end">End date</label><input type="date" id="f-end"></div>
|
</div>
|
||||||
|
<div class="ff-field custom" id="custom-end">
|
||||||
|
<label for="f-end">End</label><input type="date" id="f-end">
|
||||||
|
</div>
|
||||||
|
<button class="btn ff-go" id="show-trips" type="button">Show trips</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn" id="show-trips" type="button">Show trips</button>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- Trips — fixed bottom bar of horizontal cards (trips mode only) -->
|
<!-- Card tier: trips (trips mode only) -->
|
||||||
<div class="tripsbar" id="tripsbar">
|
<div class="cards-tier" id="cards-tier">
|
||||||
<div class="tripsbar-head">
|
<div class="cards-tier-head">
|
||||||
<button class="back" id="trips-back" type="button">◀ Live</button>
|
|
||||||
<span class="tripsbar-title">TRIPS <span id="trip-count"></span></span>
|
<span class="tripsbar-title">TRIPS <span id="trip-count"></span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="trip-cards" id="trip-scroll">
|
<div class="trip-cards" id="trip-scroll">
|
||||||
|
|
@ -1062,8 +1055,29 @@ function updateVehScale() {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('f-period').addEventListener('change', e => {
|
document.getElementById('f-period').addEventListener('change', e => {
|
||||||
document.getElementById('custom').classList.toggle('show', e.target.value === 'custom');
|
const show = e.target.value === 'custom';
|
||||||
|
document.getElementById('custom').classList.toggle('show', show);
|
||||||
|
document.getElementById('custom-end').classList.toggle('show', show);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Filter tier: collapse to a one-line summary (trips) vs the full form.
|
||||||
|
function setFilterExpanded(expanded) {
|
||||||
|
document.getElementById('filter-tier').classList.toggle('collapsed', !expanded);
|
||||||
|
if (map) setTimeout(() => map.resize(), 0); // dock height changed
|
||||||
|
}
|
||||||
|
function renderFilterSummary() {
|
||||||
|
const sel = currentFilterSelection();
|
||||||
|
const parts = [];
|
||||||
|
if (sel.vehicles.length === 1) parts.push(sel.vehicles[0]);
|
||||||
|
else if (sel.vehicles.length > 1) parts.push(`${sel.vehicles.length} vehicles`);
|
||||||
|
if (sel.cost_centre) parts.push(sel.cost_centre);
|
||||||
|
if (sel.assigned_city) parts.push(sel.assigned_city);
|
||||||
|
if (!parts.length) parts.push('Whole fleet');
|
||||||
|
const periodLabel = { today: 'Today', '7d': 'Last 1 week', '30d': 'Last 1 month', custom: `${sel.start || '?'} → ${sel.end || '?'}` }[sel.period] || sel.period;
|
||||||
|
parts.push(periodLabel);
|
||||||
|
document.getElementById('fs-text').textContent = parts.join(' · ');
|
||||||
|
}
|
||||||
|
document.getElementById('filter-edit').addEventListener('click', () => setFilterExpanded(true));
|
||||||
// Selecting a cost centre or assigned city filters the live map immediately.
|
// Selecting a cost centre or assigned city filters the live map immediately.
|
||||||
document.getElementById('f-cc').addEventListener('change', applyLiveFilters);
|
document.getElementById('f-cc').addEventListener('change', applyLiveFilters);
|
||||||
document.getElementById('f-city').addEventListener('change', applyLiveFilters);
|
document.getElementById('f-city').addEventListener('change', applyLiveFilters);
|
||||||
|
|
@ -1138,10 +1152,12 @@ function switchToTripsMode() {
|
||||||
clearTrail(); trailedVehicle = null;
|
clearTrail(); trailedVehicle = null;
|
||||||
liveMarkers.forEach(m => m.getElement().style.display = 'none');
|
liveMarkers.forEach(m => m.getElement().style.display = 'none');
|
||||||
clusterMarkers.forEach(m => m.remove()); clusterMarkers.clear();
|
clusterMarkers.forEach(m => m.remove()); clusterMarkers.clear();
|
||||||
document.getElementById('tripsbar').classList.add('show');
|
document.getElementById('cards-tier').classList.add('show');
|
||||||
document.getElementById('tripbar').classList.add('show');
|
document.getElementById('tripbar').classList.add('show');
|
||||||
document.getElementById('live-pill').classList.add('show');
|
document.getElementById('live-pill').classList.add('show');
|
||||||
document.getElementById('stale-chip').style.display = 'none';
|
document.getElementById('stale-chip').style.display = 'none';
|
||||||
|
renderFilterSummary();
|
||||||
|
setFilterExpanded(false); // collapse filters to the one-line summary
|
||||||
const ph = document.getElementById('placeholder'); if (ph) ph.style.display = 'none';
|
const ph = document.getElementById('placeholder'); if (ph) ph.style.display = 'none';
|
||||||
if (map) setTimeout(() => map.resize(), 0); // map column shrank — let it reflow
|
if (map) setTimeout(() => map.resize(), 0); // map column shrank — let it reflow
|
||||||
}
|
}
|
||||||
|
|
@ -1337,18 +1353,17 @@ function showTripPlaceholder(msg) {
|
||||||
function backToLive() {
|
function backToLive() {
|
||||||
mode = 'live';
|
mode = 'live';
|
||||||
clearAnim(); removeTripLayers();
|
clearAnim(); removeTripLayers();
|
||||||
document.getElementById('tripsbar').classList.remove('show');
|
document.getElementById('cards-tier').classList.remove('show');
|
||||||
document.getElementById('tripbar').classList.remove('show');
|
document.getElementById('tripbar').classList.remove('show');
|
||||||
document.getElementById('live-pill').classList.remove('show');
|
document.getElementById('live-pill').classList.remove('show');
|
||||||
document.getElementById('stale-chip').style.display = '';
|
document.getElementById('stale-chip').style.display = '';
|
||||||
|
setFilterExpanded(true); // back to the full filter form for live
|
||||||
liveMarkers.forEach(m => m.getElement().style.display = '');
|
liveMarkers.forEach(m => m.getElement().style.display = '');
|
||||||
if (map) setTimeout(() => map.resize(), 0); // bottom bar gone — map grows back
|
|
||||||
if (liveFeatures.length) applyLiveFilters(); // rebuild cluster bubbles/pins
|
if (liveFeatures.length) applyLiveFilters(); // rebuild cluster bubbles/pins
|
||||||
const ph = document.getElementById('placeholder'); if (ph) ph.style.display = 'none';
|
const ph = document.getElementById('placeholder'); if (ph) ph.style.display = 'none';
|
||||||
startPolling();
|
startPolling();
|
||||||
}
|
}
|
||||||
document.getElementById('live-pill').addEventListener('click', backToLive);
|
document.getElementById('live-pill').addEventListener('click', backToLive);
|
||||||
document.getElementById('trips-back').addEventListener('click', backToLive);
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Reverse-geocoding (Nominatim) — queued, 1 req/sec, in-memory cache
|
// Reverse-geocoding (Nominatim) — queued, 1 req/sec, in-memory cache
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue