# Task 2.7 — Event picker (subscription driver) **Phase:** 2 — Live monitoring map **Status:** ⬜ Not started **Depends on:** 2.4. **Wiki refs:** `docs/wiki/synthesis/processor-ws-contract.md` §"Subscription model"; `docs/wiki/synthesis/directus-schema-draft.md`. ## Goal Let the operator pick which event to monitor. The picker reads the events the user has access to from Directus (via TanStack Query), drives the WS subscription via `client.subscribe('event:')`, applies the snapshot to the position store, and switches subscriptions cleanly when the user changes events. After this task, the live map is operator-driven — no hardcoded event id, no dev-only sample data. ## Deliverables - **`src/data/events.ts`** — TanStack Query hook + types: ```ts export type EventSummary = { id: string; name: string; slug: string; discipline: 'rally' | 'time-trial' | 'regatta' | 'trail-run' | 'hike'; starts_at: string; ends_at: string; organization_id: string; }; export function useUserEvents(): UseQueryResult; ``` Fetches `/items/events?fields=id,name,slug,discipline,starts_at,ends_at,organization_id&sort=-starts_at`. Directus's RLS handles "what events does this user have access to" via the cookie. Stale time 5 minutes. - **`src/ui/components/event-picker.tsx`** — `` component. Top-of-page dropdown showing the events list. Selected event highlights. On select, calls a callback (passed in by the monitor route). - **`src/routes/_authed/monitor.tsx`** updated — orchestrates the picker → subscribe → snapshot → store flow: ```tsx function MonitorPage() { const activeEventId = usePositionStore((s) => s.activeEventId); const setActiveEvent = useActiveEventOrchestration(); return ( <>
{/* 2.9 */} ); } ``` - **`src/live/active-event.ts`** — `useActiveEventOrchestration()` hook that wraps: 1. Unsubscribe from the previous event's topic if any. 2. Subscribe to the new event's topic. 3. On `subscribed`, call `usePositionStore.applySnapshot(eventId, snapshot)`. 4. On error, surface a toast (or inline alert) — wrong event id, no permission, etc. 5. Persist the selected event id to localStorage so reload returns to the same event. - **`src/live/index.ts`** — re-export `useActiveEventOrchestration`. ## Specification ### Filter for "events the user is meaningfully looking at" The naive query `GET /items/events` returns every event in every org the user belongs to. For a multi-tenant pilot with one org and a handful of events, that's fine — sort by `starts_at DESC` and the most recent shows first. For a future where the user is in multiple orgs with dozens of past events: filter by date window: ```ts const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(); const url = `/api/items/events?filter[ends_at][_gte]=${oneWeekAgo}&fields=...&sort=-starts_at`; ``` Defaults the picker to "events that ended in the last week or are still ongoing." Document the decision; for the dogfood it's overkill but harmless. ### Picker UI A small dropdown / combobox in the top-left of the monitor page. shadcn's `Popover` + `Command` primitives (or a vanilla `