feat: task 2.6 MapTrails (bounded ring buffer, polyline rendering)
- src/map/core/map-pref-store.ts: trailMode preference
('none' | 'selected' | 'all', default 'selected') + setter, persisted.
- src/map/layers/map-trails.tsx: <MapTrails /> side-effect-only.
Single GeoJSON source + single line layer. Builds one LineString
Feature per device whose trail has >= 2 points; filtered by mode.
Per-device flat colour via deterministic deviceId hash mod a
6-entry palette.
- src/map/core/trails-toggle.tsx: <TrailsToggle /> floating card
below <BasemapSwitcher />. Three buttons (None / Selected / All).
- src/routes/_authed/monitor.tsx: renders <TrailsToggle /> +
<MapTrails /> *before* <MapPositions /> so the line layer is added
to the map first and renders underneath the symbol layers.
Speed-coloured-per-segment deferred to Phase 3 polish per the task
spec's open-question decision; flat-colour-per-device for v1.
Bundle: main 394KB / 120KB gz — no change from 2.5.
This commit is contained in:
@@ -276,7 +276,7 @@ Three new files plus Schema work:
|
||||
|
||||
**Schema overhaul** in `src/auth/client.ts`:
|
||||
|
||||
- Made `Schema` type SDK-compatible. Each entry is an *array* of row types (e.g. `devices: DeviceRow[]`), not a single row — `RegularCollections<Schema>` filters on `Schema[K] extends ArrayLike` and a non-array value collapses to `never`, which broke `readItems('devices', ...)` with `keyof Schema = never`.
|
||||
- Made `Schema` type SDK-compatible. Each entry is an _array_ of row types (e.g. `devices: DeviceRow[]`), not a single row — `RegularCollections<Schema>` filters on `Schema[K] extends ArrayLike` and a non-array value collapses to `never`, which broke `readItems('devices', ...)` with `keyof Schema = never`.
|
||||
- Spelled out the composed client type explicitly: `DirectusClient<Schema> & RestClient<Schema> & AuthenticationClient<Schema>` instead of `ReturnType<typeof buildClient>`. Without the explicit annotation Schema didn't reliably flow through the chained `.with(...)` calls to inference at `request()` call sites.
|
||||
- Added `DeviceRow` and `EventRow` row types to the Schema; barrel-exported via `@/auth`.
|
||||
|
||||
@@ -289,6 +289,7 @@ Three new files plus Schema work:
|
||||
3. Cluster click handler: `getClusterExpansionZoom` is a `Promise`-returning method in maplibre-gl 5.x (was callback-style in older versions). Used the Promise form.
|
||||
|
||||
**Smoke check (local `pnpm dev`):**
|
||||
|
||||
- `/monitor` loads. `<MapPositions />` mounts. Position store is empty until the event picker (2.7) lands.
|
||||
- In the browser console: `usePositionStore.getState().applyPositions([{deviceId: 'test-1', lat: 41.327, lon: 19.819, ts: Date.now()}])` produces a marker on the map at the seeded coordinates within ~16ms (one rAF flush).
|
||||
- Clicking the marker triggers `selectDevice('test-1')` — verifiable by reading the store after.
|
||||
|
||||
@@ -153,4 +153,22 @@ If operators want a longer-trail mode: a slider in the prefs (`50 / 200 / 500 /
|
||||
|
||||
## Done
|
||||
|
||||
(Filled in when the task lands.)
|
||||
- **`src/map/core/map-pref-store.ts`** — extended with `trailMode: TrailMode` (`'none' | 'selected' | 'all'`, default `'selected'`) and `setTrailMode`. Persisted via the existing zustand `persist` middleware on `'trm-map-prefs'`.
|
||||
- **`src/map/layers/map-trails.tsx`** — `<MapTrails />` side-effect-only component. Single GeoJSON source + single `'line'` layer. Two-effect pattern (setup + setData on store changes). Reads `trailsByDevice`, `selectedDeviceId`, `trailMode`. Builds one `LineString` Feature per device whose trail has ≥ 2 points; filtered by mode (`none` → empty, `selected` → just the selected device, `all` → every device).
|
||||
- **`src/map/core/trails-toggle.tsx`** — `<TrailsToggle />` floating card top-right, below `<BasemapSwitcher />` (`top-32`). Three buttons (None / Selected / All); active one highlighted via `bg-accent`. Has a small "Trails" label up top.
|
||||
- **`src/routes/_authed/monitor.tsx`** — renders `<TrailsToggle />` as a sibling of `<BasemapSwitcher />`, and `<MapTrails />` *before* `<MapPositions />` inside `<MapView>` so the line layer is added to the map first and renders underneath the symbol layers.
|
||||
|
||||
**Per-device colouring:** flat colour from a 6-entry palette (`#2563C8 / #2E8C4A / #6B46C1 / #188C8A / #C9296F / #5A5A53`) keyed by a deterministic hash of the deviceId. Same device = same colour across reloads. Distinct enough that two trails don't blend. Speed-coloured-per-segment deferred to a Phase 3 polish task per the original task spec's open-question decision.
|
||||
|
||||
**Smoke check (local `pnpm dev`):**
|
||||
- `/monitor` shows the trails toggle below the basemap switcher.
|
||||
- Default mode is "Selected" → no trails visible until a device is selected.
|
||||
- Synthetic positions (push 2+ positions for the same deviceId via the console — `usePositionStore.getState().applyPositions(...)`) and the trail polyline appears immediately.
|
||||
- Toggling to "All" shows trails for every device with ≥ 2 points.
|
||||
- Toggling to "None" hides trails.
|
||||
- Switching basemap → trails reappear after the swap (mapReady gate handles the remount).
|
||||
- Reloading the page: trail mode persists.
|
||||
|
||||
**Bundle:** main bundle 394KB / 120KB gz — no change from 2.5. Trails layer is small.
|
||||
|
||||
Landed in `PENDING_SHA`.
|
||||
|
||||
Reference in New Issue
Block a user