Commit graph

22 commits

Author SHA1 Message Date
david kiania
4f25fae6c8 feat(map): exempt specialist vehicles from clustering
Crane/motorbike/pick-up are held out of the supercluster index and always
rendered as individual markers at every zoom, so they never fold into a
cluster bubble and always stand out. The rest of the fleet clusters as before.
KPIs and legend are unaffected (still computed from the full filtered set).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:59:47 +03:00
david kiania
fc5a7ed31b Merge: coordinated cost-centre colours + collapsible legend 2026-06-08 15:10:14 +03:00
david kiania
e55cfadb1c feat(map): coordinated cost-centre colours + collapsible legend
colorForCostCentre() now maps each cost centre to a deliberate, distinct colour
(ISP/OSP/FDS/… via COST_CENTRE_COLORS, lowercase-normalised) instead of an
arbitrary hash, so same-centre vehicles share a colour and centres are easy to
tell apart. Unmapped centres still fall back to a stable hash. State intensity is
unchanged: full colour moving → pastel when stopped <24h → grey when offline >24h.

Adds a compact, collapsible "Key" legend (bottom-left, collapsed by default) that
lists only the cost centres currently on screen with live counts — rebuilt each
render in applyLiveFilters(). Self-contained (#legend block + .legend CSS +
renderLegend) so it can be removed cleanly if the screen needs to stay minimal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:10:12 +03:00
david kiania
b11d8131a9 Merge: distinct marker icons for specialist vehicles (crane/motorbike/pick-up) 2026-06-08 14:40:59 +03:00
david kiania
38fd7551f9 feat(map): distinct marker icons for specialist vehicles
Crane, Motorbike and Pick-Up now render their own white SVG silhouette inside
the department-coloured pin (via fn_live_positions' new `vehicle_type` field).
Specialists keep full size + colour even when parked so they stay legible and
stand out from the field-service swarm. All other vehicles (field-service +
unassigned) are unchanged — they fall through to the existing arrow/square/dot
marker. Icon-only change; no popup/clustering/filter behaviour altered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:39:46 +03:00
kianiadee
ade9705345 docs: mark v2 feature-frozen (2026-06-07) — two-tier dock + clustering + dedup
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:43:36 +03:00
kianiadee
70928c0b2d 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>
2026-06-07 00:35:34 +03:00
kianiadee
169fc21f36 refactor(ui): slim content-height filter dock + searchable plate combobox
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>
2026-06-07 00:21:16 +03:00
kianiadee
48631b6a38 refactor(ui): fixed filter dock + bottom trip-card bar (no floating panels)
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>
2026-06-07 00:07:05 +03:00
kianiadee
cd627b4f9a feat(live): Folium-style marker clustering (supercluster + DOM pins)
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>
2026-06-06 23:44:09 +03:00
kianiadee
33401415ad feat(live): pair tracker+camera per vehicle — tracker default, camera fallback
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>
2026-06-06 23:23:44 +03:00
kianiadee
525360f204 docs: mark v1 feature-frozen (2026-06-06) + add status/deploy summary
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:56:44 +03:00
kianiadee
76d1c17830 feat(trips): context bar with filter summary + first/last trip bookends
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>
2026-06-06 11:14:16 +03:00
kianiadee
620a82de55 style(markers): parked pastel square at half size (9px vs 18px moving)
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>
2026-06-06 10:57:54 +03:00
kianiadee
ddf7c31b6c style(markers): render parked pastel square ~2/3 size (still zoom-scaled)
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>
2026-06-06 10:42:37 +03:00
kianiadee
d73755af35 style(markers): parked = clean pastel department square, no arrow
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>
2026-06-06 10:28:56 +03:00
kianiadee
74f1ef268f feat(markers): department-colour square + arrow for vehicles active within 24h
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>
2026-06-06 10:18:25 +03:00
kianiadee
2237757369 docs: document filters (plate A→Z, cost centre, assigned city, time) + zoom-scaled markers
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:09:52 +03:00
kianiadee
b930582dc8 feat(filters): sort number plates A→Z + add assigned-city filter
- Number-plate dropdown now sorted alphabetically (natural/numeric, placeholder
  pinned), re-sorted as live-discovered plates are added.
- New 'Assigned city' filter: populated from the API + live feed, filters the
  live map instantly (with cost centre + plate), auto-fills from a picked
  vehicle, and is passed to the trips query (assigned_city).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 23:23:57 +03:00
kianiadee
135253d37d feat(live): zoom-relative marker sizing + live plate/cost-centre filtering
- Markers now scale with zoom (--veh-scale, ~0.42 at z5 → 1.20 at z14) via a
  transform on .veh-inner, so they no longer bloat at country zoom; pins stay
  anchored on their coordinate (verified 0px drift).
- Selecting a plate or cost centre now filters the LIVE markers immediately and
  recomputes the header KPIs (previously the filter card only fed Show trips, so
  selections didn't reflect on the live map). Time period still applies to trips.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 23:01:30 +03:00
kianiadee
50163536e3 fix(markers): stop wiping MapLibre's marker class (markers stacked in flow)
Re-setting el.className each render dropped the maplibregl-marker class
(position:absolute), so live markers fell into document flow and stacked
+32px each — vehicles drifted south, worse when zoomed out, dragging their
plate labels with them. Use classList (preserve maplibregl-marker) and wrap
pin+plate in a .veh-inner positioning context so a class change can't reflow.

Verified: every marker now projects to dX=0,dY=0 on its coordinate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 22:45:41 +03:00
kianiadee
3d420fa82e feat: FleetNow merged live+trips map SPA (nginx/Coolify)
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>
2026-06-05 21:56:01 +03:00