README: add a "Map overlay layers" section (how the toggleable layer system
works + how to add a layer in ~2 min), document fleet segmentation / department
colours / legend / POIs, refresh the file tree (layers/), and correct the deploy
note (Coolify auto-deploys on push to main).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Right dock was a full-height 270px panel with a tall 6-row plate multi-select
and lots of dead space. Now a slim (~212px) content-height card: plate becomes a
searchable combobox with removable chips (hidden <select> kept as source of truth
so all filter logic is unchanged), cost-centre/city/time single-line, custom
dates side-by-side and only when needed. Better visual balance, more map.
Verified: dock 212x348 (was 270xfull), combobox search/pick/chip/remove all
drive the hidden select + KPIs; no overflow; no errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the floating filter + trip-list cards (which drifted/overlapped the
map) with fixed chrome: filters dock on the right (full body height, same
controls), and trips render as horizontally-scrolling cards in a fixed bottom
bar that mirrors the top bar. Body is a grid (map | filter dock / trips bar);
used minmax(0,1fr) + min-width:0 + .app overflow:hidden so the 150-card scroller
can't widen the page. map.resize() on mode switch. Verified: no overflow
(1440=1170+270), filters right of map, trips bar below map, cards scroll, click
animates. No errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zoomed out, vehicles group into amber count-bubbles (tiered by size); click to
zoom-expand; clusters disband into individual pins at ~z11. Implemented with
supercluster while KEEPING the DOM pin/square/arrow look — the filtered+deduped
fleet is loaded into a cluster index, re-queried on moveend per viewport/zoom.
Clustering is live-mode only; honours plate/cost-centre/city filters; KPIs track
the filtered set. Verified: z6 → 4 bubbles summing to fleet total, z13 → pins,
cluster click zooms in. No console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every vehicle has a GPS tracker (X3/GT06E/AT4) and a JC400P dashcam sharing the
same plate. dedupeLiveFeatures() collapses the pair to one device per normalised
plate: functioning (<24h) beats offline → tracker beats camera → freshest fix.
So a vehicle shows once — tracker by default, camera only when the tracker is
dark. Dropdown + live filter also dedup by normalised plate (merges stray-space
variants like 'KDS 453 Y' vs 'KDS 453Y'). Unit-verified all precedence cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trips view now shows a second bar under the header: the active filter
(vehicle/cost centre/city) plus first-trip and last-trip bookends, each with the
server's reverse-geocoded location + timestamp (first_trip_start_*, last_trip_end_*).
Pinned grid rows so a hidden tripbar doesn't collapse the map.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Parked square scale 0.66 → 0.5 so it renders ~9px against the 18px moving
circle (still compounds with zoom scaling). README legend updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Parked squares now scale to ~0.66 of the moving-now circle (compounding with
the existing zoom scaling) so they read as quieter 'recent activity' markers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per feedback: recently-parked vehicles now render as a pastel tint of their
cost-centre colour (blended toward white) as a plain square — arrow removed.
Moving-now keeps the full-colour circle + arrow; offline stays grey.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
High-level fleet-activity read: parked vehicles (reported within 24h) now take
their cost-centre colour as a SQUARE (same 32px as the circle) with the heading
arrow, instead of a flat grey circle. Moving-now stays a coloured circle; only
offline (>24h silent) stays grey. Verified per-state: active=circle, parked=
square+arrow, offline=grey circle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Single-file MapLibre SPA merging live vehicle positions and historical
trips into one console. Reads the existing dashboard read-API
(fleetapi.rahamafresh.com); served as a static nginx image for Coolify.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>