feat: task 2.7 event picker (subscription driver)
- src/data/events.ts: useUserEvents() TanStack Query (5-min stale, sort -starts_at). EventSummary type is a Pick of EventRow. - src/live/active-event.ts: useActiveEventOrchestration() returns the swap fn — unsubscribe previous + clearForEvent + subscribe new + applySnapshot on success + persist to localStorage. Out-of-order safety via per-call version counter. Plus readSavedActiveEventId(). - src/ui/components/event-picker.tsx: <EventPicker> dropdown. useState + click-outside; rows show name + date + discipline. - src/live/index.ts: re-exports active-event helpers. - src/routes/_authed/monitor.tsx: auto-select effect (one-shot via initializedRef, gated on events loaded + WS connected); renders <EventPicker> wired to setActiveEvent. Deviations: 1. Vanilla div + useState dropdown instead of shadcn Popover — no new shadcn primitive add; easy to swap later for keyboard nav. 2. Auto-select gated on connectionStatus === 'connected' so the subscribe call gets the snapshot path (not 'not-connected'). 3. Logout-clears-saved-event-id deferred to a small Phase 1.8 follow-up; documented in task risks. Bundle: 395KB / 120KB gz (~1KB up from 2.6).
This commit is contained in:
@@ -179,4 +179,37 @@ So the user lands on `/monitor` and the picker auto-fills with the last-used eve
|
||||
|
||||
## Done
|
||||
|
||||
(Filled in when the task lands.)
|
||||
Three new files plus orchestration in the monitor route:
|
||||
|
||||
- **`src/data/events.ts`** — `useUserEvents()` TanStack Query hook returning `EventSummary[]` (subset of `EventRow`). 5-min stale time, sorted by `-starts_at`. `EventSummary` is a `Pick` of the columns the picker actually needs.
|
||||
- **`src/live/active-event.ts`** — `useActiveEventOrchestration()` returns a function that swaps the active event:
|
||||
1. Unsubscribe previous topic (best-effort).
|
||||
2. `clearForEvent` on the position store.
|
||||
3. `subscribe('event:<new>')`.
|
||||
4. On success → `applySnapshot(eventId, snapshot)` + persist to `localStorage`.
|
||||
5. On failure → `console.warn` and leave previous state alone.
|
||||
Out-of-order safety via per-call version counter; `null` clears the active event.
|
||||
Plus `readSavedActiveEventId()` for callers that want to know what the persisted choice is.
|
||||
- **`src/ui/components/event-picker.tsx`** — `<EventPicker activeEventId, onChange>`. Click-to-open dropdown with click-outside-to-close. Shows the events list sorted most-recent-first; each row displays name + date + discipline. Disabled state when loading / errored / empty.
|
||||
- **`src/live/index.ts`** — re-exports `useActiveEventOrchestration` + `readSavedActiveEventId`.
|
||||
- **`src/routes/_authed/monitor.tsx`** — orchestration:
|
||||
- Reads `activeEventId` from the position store, `connectionStatus` from the connection store.
|
||||
- Auto-select effect (one-shot via `initializedRef`): waits for `events.data` AND `connectionStatus === 'connected'`, then prefers the persisted event id, else the most recent.
|
||||
- Renders `<EventPicker>` top-left wired to `setActiveEvent`.
|
||||
|
||||
**Deviations from spec:**
|
||||
|
||||
1. Spec referenced shadcn's `Popover` + `Command` primitives. Used a vanilla `<div>` with `useState` + click-outside `useEffect` instead — keeps the deps small (no extra shadcn primitive add) and the popover behaviour is dead simple. Easy to swap to `Popover` later if we want keyboard navigation / typeahead.
|
||||
2. The auto-select effect is gated on `connectionStatus === 'connected'`. Without the gate, `subscribe()` returns immediately with `not-connected` (the subscription is still queued for replay on connect, but the snapshot path is lost). Gating means the user sees the snapshot rendered as soon as the WS is up.
|
||||
3. Logout-clears-saved-event-id: documented as a Phase 1 follow-up in this task's risks section. Not implemented here — `performLogout` (Phase 1.8) doesn't currently clear localStorage. A small follow-up patch will pull `localStorage.removeItem('trm-active-event-id')` into the logout flow.
|
||||
|
||||
**Smoke check (local `pnpm dev`):**
|
||||
- `/monitor` shows the event picker top-left. Clicking it opens the dropdown with the seeded events.
|
||||
- On a fresh page load (with stage Directus reachable + a connected WS): the dogfood event auto-selects within ~1s; the snapshot of seeded positions appears as map markers.
|
||||
- Switching events: previous event's markers disappear, new event's markers appear, no leftover state.
|
||||
- Hard refresh: the previously selected event is still active.
|
||||
- `localStorage.trm-active-event-id` reflects the current selection (visible in DevTools Application → Local Storage).
|
||||
|
||||
**Bundle:** main bundle 395KB / 120KB gz — small bump (~1KB) from 2.6 for the data hook and orchestration.
|
||||
|
||||
Landed in `PENDING_SHA`.
|
||||
|
||||
Reference in New Issue
Block a user