diff --git a/docs/superpowers/plans/2026-04-15-phase-2d-signalr-wiring.md b/docs/superpowers/plans/2026-04-15-phase-2d-signalr-wiring.md new file mode 100644 index 00000000..35b93968 --- /dev/null +++ b/docs/superpowers/plans/2026-04-15-phase-2d-signalr-wiring.md @@ -0,0 +1,84 @@ +# Phase 2D — SignalR Wiring + +> **Parent:** `2026-04-14-phase-2-online-board-master.md` § 2D +> +> **Goal:** Wire the generic `useLiveFlights` hook (1E) to TrackerHub channels for the Online Board's search and details pages. Two thin composition hooks, each tested. + +--- + +## Prerequisites + +| Artifact | Source | +|---|---| +| `useLiveFlights` | `src/shared/hooks/useLiveFlights.ts` (1E) | +| `SignalRConnection`, `ConnectionStatus` | `src/shared/signalr/connection.ts` (1E) | +| `useOnlineBoard` (refresh callback) | `src/features/online-board/hooks/useOnlineBoard.ts` (2C) | +| `useFlightDetails` | `src/features/online-board/hooks/useFlightDetails.ts` (2C) | +| `ISimpleFlight`, `IFlightId` | `src/features/online-board/types.ts` (2A) | +| `getEnv().SIGNALR_HUB_URL` | `src/env/index.ts` (1A) | + +--- + +## Tasks + +### T1 — Create `useLiveBoardSearch` hook + +**File:** `src/features/online-board/hooks/useLiveBoardSearch.ts` + +- Accepts `{ date: string; departure?: string; arrival?: string }` params and `initialFlights: ISimpleFlight[]`. +- Composes `useLiveFlights` with config: + - `hubUrl` from `getEnv().SIGNALR_HUB_URL` + - `channelKey` builds `board:${date}:${departure ?? ""}:${arrival ?? ""}` +- Returns `{ flights: ISimpleFlight[]; connectionStatus: ConnectionStatus }`. +- Client-only: `useLiveFlights` already handles SSR guard. + +### T2 — Create `useLiveFlightDetails` hook + +**File:** `src/features/online-board/hooks/useLiveFlightDetails.ts` + +- Accepts `{ carrier: string; flightNumber: string; suffix?: string; date: string }` (matches `IParsedFlightId`) and `initialFlight: ISimpleFlight | null`. +- Composes `useLiveFlights` with config: + - `hubUrl` from `getEnv().SIGNALR_HUB_URL` + - `channelKey` builds `flight:${carrier}${flightNumber}${suffix ?? ""}@${date}` +- Returns `{ flight: ISimpleFlight | null; connectionStatus: ConnectionStatus }`. + +### T3 — Write tests for `useLiveBoardSearch` + +**File:** `src/features/online-board/hooks/useLiveBoardSearch.test.ts` + +- **Channel key construction:** date-only, date+departure, date+departure+arrival all produce correct keys. +- **Data passthrough:** initial data flows through from `useLiveFlights`. +- **Live update:** simulated SignalR message replaces flight data. +- **SSR:** returns initial data with idle status when `window` is undefined. + +### T4 — Write tests for `useLiveFlightDetails` + +**File:** `src/features/online-board/hooks/useLiveFlightDetails.test.ts` + +- **Channel key construction:** with and without suffix. +- **Data passthrough:** initial flight (or null) flows through. +- **Live update:** simulated message replaces single flight. +- **SSR:** idle status, no SignalR import. + +### T5 — Export from barrel + +**File:** `src/features/online-board/index.ts` + +- Add exports for both hooks and their result types. + +### T6 — Verification + +- `pnpm typecheck && pnpm lint && pnpm test` + +--- + +## Exit gates + +| Gate | Verification | +|---|---| +| Channel keys correct | Unit tests for key construction | +| Live updates flow | Mock SignalR push updates state | +| SSR safe | Returns initial data without touching SignalR | +| No regressions | All existing tests still pass | +| Types clean | `pnpm typecheck` passes | +| Lint clean | `pnpm lint` passes |