# 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<TParams, TData>` | `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 |
