From 3bd169c55ca02c78df3489af165f4a3736e13d00 Mon Sep 17 00:00:00 2001 From: AmanTahiliani Date: Mon, 25 May 2026 12:35:08 -0400 Subject: [PATCH] Rework Race Hub as weekend workspace --- README.md | 9 +- .../refactor/26-phase-19-weekend-workspace.md | 128 ++++ .../refactor/28-orchestrator-handoff.md | 117 +--- documentations/refactor/README.md | 6 + frontend/src/components/DatasetStatusView.tsx | 51 +- frontend/src/components/OverviewView.tsx | 281 ++++++++ frontend/src/components/TabBar.tsx | 22 +- frontend/src/components/WeekendSwitcher.tsx | 140 ++++ frontend/src/pages/RaceHubPage.tsx | 473 ++++++++++---- frontend/src/styles/app.css | 614 ++++++++++++++++++ frontend/src/test/DatasetStatusView.test.tsx | 91 ++- frontend/src/test/RaceHubPage.test.tsx | 260 ++++++++ frontend/src/test/TabBar.test.tsx | 33 +- frontend/src/test/setup.ts | 5 + tests/command-center.spec.ts | 8 +- tests/data-library.spec.ts | 3 +- tests/production-smoke.spec.ts | 7 +- tests/race-hub.spec.ts | 78 ++- .../visual/__snapshots__/desktop/race-hub.png | Bin 30171 -> 57168 bytes .../visual/__snapshots__/mobile/race-hub.png | Bin 21071 -> 56809 bytes .../visual/__snapshots__/tablet/race-hub.png | Bin 29356 -> 57634 bytes tests/visual/helpers.ts | 6 +- 22 files changed, 2019 insertions(+), 313 deletions(-) create mode 100644 documentations/refactor/26-phase-19-weekend-workspace.md create mode 100644 frontend/src/components/OverviewView.tsx create mode 100644 frontend/src/components/WeekendSwitcher.tsx create mode 100644 frontend/src/test/RaceHubPage.test.tsx diff --git a/README.md b/README.md index 3f0ca31..6c8e9c7 100644 --- a/README.md +++ b/README.md @@ -110,16 +110,16 @@ BOXBOX_API_PORT=18080 npm run dev --prefix frontend | Route | Purpose | | --- | --- | | `/` | **Command Center** — fan-facing race-weekend home with GP identity, live status, session schedule, and analysis links | -| `/race-hub?session_key=` | **Race Hub** — classification, grid, strategy, positions, laps, race control, weather (from local DB) | +| `/race-hub?session_key=` | **Race Hub** — weekend workspace with session rail and Overview / Race Story / Strategy / Lap Data / Conditions / Race Control / Data Status tabs. Bare `/race-hub` auto-resolves to the focus session. | | `/admin` | **Admin / Data Health** — ingestion coverage, local data status, and suggested CLI commands | | `/data-library` | Legacy alias for Admin / Data Health | | `/live` | **Live Timing** — timing tower and race control via SSE when a session is live | Example after seeding: `http://localhost:5173/race-hub?session_key=9472` -## Ingest historical data (OpenF1 → domain DB) +## Ingest historical data and briefing feeds -Ingestion is a **CLI mode** on the same binary. Only one of `--ingest-year`, `--ingest-meeting`, or `--ingest-session` may be set per run. +Ingestion is a **CLI mode** on the same binary. Only one of `--ingest-year`, `--ingest-meeting`, `--ingest-session`, or `--ingest-news` may be set per run. ```bash # Season: discover and store meeting metadata (2023+) @@ -134,6 +134,9 @@ go run ./cmd/main.go --ingest-session 9472 # Preview without writing go run ./cmd/main.go --dry-run --ingest-meeting 1229 +# Refresh Paddock Briefing RSS/Atom feeds +go run ./cmd/main.go --ingest-news + # Custom DB path (default: ~/.local/share/box-box/boxbox.db) go run ./cmd/main.go --ingest-meeting 1229 --db /tmp/boxbox.db ``` diff --git a/documentations/refactor/26-phase-19-weekend-workspace.md b/documentations/refactor/26-phase-19-weekend-workspace.md new file mode 100644 index 0000000..5d03efc --- /dev/null +++ b/documentations/refactor/26-phase-19-weekend-workspace.md @@ -0,0 +1,128 @@ +# Phase 19: Weekend Workspace / Race Hub Flow V1 + +## Goal + +Rework `/race-hub` from an "admin-style table on top, analysis below" page +into a Weekend Workspace that feels like a modern session companion: a +compact GP identity band, a horizontal session rail, an Overview snapshot, +and fan-oriented analysis tabs. Keep `/race-hub?session_key=…` working, +keep ingestion/admin concerns out of the fan surface, and make mobile/iPad +first-class. + +## Completed Scope + +- Replaced the legacy Race Hub layout with a Weekend Workspace: + - **Topbar**: `box-box · race hub · ` eyebrow, weekend source badge, + and a `Switch Weekend` toggle that opens an inline switcher panel. + - **GP identity band**: country decal, GP name, location · circuit, date + range, with a left-edge `--gp-accent` strip derived from + `frontend/src/lib/gpIdentity.ts`. + - **Session rail**: horizontal segmented strip of session cards (FP1, + FP2, FP3, Q, Sprint, R …) showing abbreviation, name, time, source dot, + and coverage hint. Switching is a single click; selected chip pulses + with the GP accent. + - **Active session sub-bar**: compact line with session name, scheduled + time, coverage label, and `key ` for power users. +- New tab grouping (`frontend/src/components/TabBar.tsx`): + Overview · Race Story · Strategy · Lap Data · Conditions · Race Control · + Data Status. Race Story bundles classification, starting grid, and + position evolution behind a sub-segmented control so the operational + feel is preserved without exploding the top-level tab list. +- New **Overview** tab (`components/OverviewView.tsx`): operational stat + cards (Winner / Pole / Fastest Lap / Podium) plus compact panels for + Conditions, latest Race Control messages, and a Local Coverage meter + that links to the relevant Data Status tab. +- Inline **Weekend Switcher** (`components/WeekendSwitcher.tsx`): season + tabs, meeting cards with country decals, and expandable per-meeting + session lists that navigate via `useNavigate`. Replaces the old fullscreen + `LocalDataNavigator` table on the Race Hub surface. +- Auto-resolution when `/race-hub` is opened without `session_key`: the + page resolves the focus weekend via the same `pickFocusMeeting` helper + Command Center uses and `navigate(replace: true)` to the focus + session (race → qualifying → first local session). +- **Data Status** tab now points at `/admin` for missing datasets instead of + inlining CLI commands. Admin remains the home for ingestion guidance. +- GP accent is plumbed through CSS custom property `--gp-accent`, used by + session chips, story sub-control underline, overview stat cards, and the + topbar `Switch Weekend` border. + +## Route Behavior + +- `/race-hub?session_key=9472` — unchanged contract; loads the workspace + for that session and opens Overview by default. +- `/race-hub` (no key) — resolves locally via `fetchSeasons` → + `fetchLocalMeetings` → `pickFocusMeeting` → `fetchWeekend`, then + `navigate({ replace: true })` to the focus session's race/qualifying. +- `/data-library` and `/admin` remain untouched. + +## What Did Not Change + +- Backend APIs (`/api/v1/race-hub`, `/api/v1/seasons`, `/api/v1/meetings`, + `/api/v1/weekend`). +- Live SignalR bridge, `/live` page, TUI live mode. +- Command Center, Admin / Data Health flows. +- Existing chart and table components (`ClassificationTable`, + `StartingGridTable`, `StrategyView`, `PositionEvolutionView`, `LapsView`, + `RaceControlView`, `WeatherView`) are reused inside the new shell. +- The legacy `LocalDataNavigator` component is kept (still unit-tested) so + any future surfaces can reuse it, but it is no longer mounted on + `/race-hub`. + +## Tests and Visual Coverage + +Updated: + +- `frontend/src/test/TabBar.test.tsx` — new tab list (Overview / Race Story + / Strategy / Lap Data / Conditions / Race Control / Data Status). +- `frontend/src/test/DatasetStatusView.test.tsx` — rewritten against the + fan-facing dataset list (11/11), the new `Manage ingestion → /admin` + link, and the removal of inline CLI hints. +- `frontend/src/test/RaceHubPage.test.tsx` — new test file covering the + identity band, session rail, Race Story sub-controls, Data Status admin + link, and the inline weekend switcher. +- `tests/race-hub.spec.ts` — rewritten E2E spec covering Overview default, + Race Story sub-views, Strategy and Positions missing-data notices, + weekend switcher toggle, Data Status admin link, and the bare + `/race-hub` redirect. +- `tests/command-center.spec.ts`, `tests/data-library.spec.ts`, + `tests/production-smoke.spec.ts` — updated assertions to land on the + new workspace shell rather than the old "Final Classification" headline. +- `tests/visual/helpers.ts` — `gotoRaceHubReady` now waits for + `race-hub` + `rh-identity` + `rh-session-` + `rh-overview`. +- `tests/visual/__snapshots__/{desktop,tablet,mobile}/race-hub.png` — + regenerated. Command Center, Admin, and Live snapshots untouched. + +## Verification + +```bash +npm --prefix frontend test -- --run # 98 unit tests pass +npm --prefix frontend run build # tsc + vite, clean +npm run test:e2e # 18 E2E pass +npm run test:e2e:prod # 6 prod smoke pass +npm run test:visual # 12 baseline pass after regen +npm run test:visual:prod # 12 prod baseline pass after regen +``` + +## Limitations and Follow-ups + +- The Race Story tab keeps three legacy datasets behind a sub-segmented + control. A future pass could merge classification + position evolution + into a single scrollable "story" canvas. +- Overview's "Fastest Lap" picks the minimum non-pit-out `lap_duration` from + the ingested laps payload. Sessions that don't ingest laps show "No data + ingested" — accurate, but a future phase could fall back to OpenF1's + `fastest_lap` field if/when that lands locally. +- The inline weekend switcher fetches the active meeting's `/weekend` + payload only when expanded. Switching seasons or browsing many + meetings does not pre-warm sibling weekend queries; this is intentional + to avoid the N×weekend fan-out that Command Center already pays. +- Visual baselines are regenerated against the current seeded e2e DB. If + the seeded session list grows, the desktop snapshot will widen. +- Country accents in `gpIdentity.ts` remain a hand-tuned subset; unknown + codes fall back to a neutral gray (same behavior as Command Center). + +## Related + +- [21 MVP Completion Checklist](21-mvp-completion-checklist.md) +- [22 Phase 14 Visual Regression](22-phase-14-visual-regression.md) +- [25 Phase 18 Fan Command Center](25-phase-18-fan-command-center.md) diff --git a/documentations/refactor/28-orchestrator-handoff.md b/documentations/refactor/28-orchestrator-handoff.md index fc5f94d..8ed39ea 100644 --- a/documentations/refactor/28-orchestrator-handoff.md +++ b/documentations/refactor/28-orchestrator-handoff.md @@ -62,24 +62,22 @@ Known test note: do not run Playwright suites that share the same seeded SQLite ## Recent Commits -- `571edb9 Add visual regression coverage` -- `e539abc Add command center screen` -- `9532206 Polish live timing UI` - `a0f135a Update operator documentation` - `79b0b9f Rework command center for race weekends` - `84a8827 Add paddock briefing RSS backend spike` +- `ee88a07 Add paddock briefing feed ingestion` +- `Rework Race Hub as weekend workspace` (latest Phase 19 commit) -## Current Uncommitted Work +## Current State -Two phases are currently uncommitted and need review/integration. Review and -commit Phase 19 first, then Phase 20, or stage hunks carefully if separating the -shared README/refactor README edits. +Phase 19 and Phase 20 have been reviewed and committed. Start new work from a +clean tree unless `git status --short` shows user changes made after this +handoff. -### Phase 19: Weekend Workspace / Race Hub Flow V1 +## Completed Phase 19: Weekend Workspace / Race Hub Flow V1 -Claude has completed Phase 19. It is currently uncommitted and needs review. - -Claude reported these Phase 19 changes: +Claude implemented, and Codex reviewed/committed, a Race Hub redesign that turns +`/race-hub` into a Weekend Workspace. - New: - `frontend/src/components/OverviewView.tsx` @@ -97,7 +95,7 @@ Claude reported these Phase 19 changes: - root `README.md` - `documentations/refactor/README.md` -Claude reported these UX changes: +UX changes: - Race Hub is now a Weekend Workspace. - Compact GP identity band with country decal/accent strip. @@ -111,32 +109,34 @@ Claude reported these UX changes: - `/race-hub?session_key=9472` still works and loads Bahrain GP 2024 seeded session. - Bare `/race-hub` now resolves to a focus weekend/session via `pickFocusMeeting` and navigation replace. -Claude reported these tests: +Verification run by Codex before commit: -- `npm --prefix frontend test -- --run` passed, 98 tests. -- `npm --prefix frontend run build` passed. -- `npm run test:e2e` passed, 18 tests. -- `npm run test:e2e:prod` passed, 6 tests. -- `npm run test:visual` passed, 12 screenshots after regenerating race-hub baselines. -- `npm run test:visual:prod` passed, 12 screenshots. +- `npm --prefix frontend test -- --run` +- `npm --prefix frontend run build` +- `npm run test:e2e` +- `npm run test:e2e:prod` +- `npm run test:visual` +- `npm run test:visual:prod` -### Phase 20: Paddock Briefing Ingestion CLI +Small review fix included: `frontend/src/test/setup.ts` stubs +`window.scrollTo` so TanStack Router scroll restoration does not spam jsdom test +stderr. -A backend subagent implemented Phase 20 after the RSS backend spike. It is also -currently uncommitted and needs review. +## Completed Phase 20: Paddock Briefing Ingestion CLI -Reported Phase 20 changes: +A backend subagent implemented, and Codex reviewed/committed, Phase 20 after the +RSS backend spike. + +Phase 20 changes: - Modified: - `cmd/main.go` - - `README.md` - - `documentations/refactor/README.md` - New: - `internal/news/refresh.go` - `internal/news/refresh_test.go` - `documentations/refactor/29-phase-20-paddock-briefing-ingestion.md` -Implemented behavior: +Behavior: - Adds `--ingest-news` as a CLI mode. - Keeps it mutually exclusive with `--ingest-year`, `--ingest-meeting`, and @@ -159,81 +159,24 @@ go run ./cmd/main.go --dry-run --ingest-news go run ./cmd/main.go --ingest-news --db /tmp/boxbox.db ``` -Phase 20 verification already run by Codex: +Verification run by Codex before commit: ```bash go test ./cmd/... ./internal/news ./internal/store -go test ./internal/web ./internal/query -go test ./... git diff --check ``` ## Immediate Task -Start by reviewing and committing Phase 19. Then review and commit Phase 20. -Do not start new implementation until both are accepted and committed. - -1. Inspect working tree: +Start with a quick sync: ```bash git status --short git diff --stat -git diff --name-only ``` -2. Review Claude’s Phase 19 work quickly but responsibly: - -- Check `RaceHubPage.tsx`, `OverviewView.tsx`, `WeekendSwitcher.tsx`, `TabBar.tsx`, `DatasetStatusView.tsx`, `app.css`, route/test updates, docs. -- Make sure no admin/CLI guidance leaked back into Race Hub. -- Make sure `/race-hub?session_key=9472` compatibility is preserved. -- Make sure `/admin` remains the admin/data-health surface. -- Confirm visual tests and docs match the changed UX. - -3. Run a focused verification pass. At minimum: - -```bash -npm --prefix frontend test -- --run -npm --prefix frontend run build -npm run test:e2e -npm run test:visual -``` - -If time allows or if production behavior changed: - -```bash -npm run test:e2e:prod -npm run test:visual:prod -``` - -4. Patch only small issues if found. -5. Stage only Phase 19 files. -6. Commit with a message like: - -```bash -git commit -m "Rework Race Hub as weekend workspace" -``` - -Then review Phase 20: - -1. Check `cmd/main.go`, `internal/news/refresh.go`, - `internal/news/refresh_test.go`, - `documentations/refactor/29-phase-20-paddock-briefing-ingestion.md`, and the - README/refactor README hunks. -2. Confirm the CLI mode does not interfere with OpenF1 ingestion modes or web/TUI - startup. -3. Confirm no live internet tests were added. -4. Re-run targeted backend tests if needed: - -```bash -go test ./cmd/... ./internal/news ./internal/store -go test ./... -``` - -5. Stage Phase 20 files/hunks and commit with a message like: - -```bash -git commit -m "Add paddock briefing feed ingestion" -``` +Then continue with the next requested phase. The most natural next phase is +Phase 21: Paddock Briefing UI, unless the user wants to deepen Race Story first. ## RSS / Paddock Briefing Context diff --git a/documentations/refactor/README.md b/documentations/refactor/README.md index d700375..10edbd8 100644 --- a/documentations/refactor/README.md +++ b/documentations/refactor/README.md @@ -92,9 +92,15 @@ not implementation tickets yet. - [25 Phase 18 Fan Command Center](25-phase-18-fan-command-center.md): reworks `/` around race-weekend identity and splits ingestion/admin concerns into the new `/admin` (Data Health) route. +- [26 Phase 19 Weekend Workspace](26-phase-19-weekend-workspace.md): rebuilds + `/race-hub` as a session-card-rail workspace with Overview / Race Story / + Strategy / Lap Data / Conditions / Race Control / Data Status tabs, + an inline weekend switcher, and GP-accent identity treatment. - [27 Phase 19B Paddock Briefing RSS](27-phase-19b-paddock-briefing-rss.md): backend spike for publisher-owned RSS/Atom feeds, local SQLite caching, and a future fan-facing briefing API. +- [29 Phase 20 Paddock Briefing Ingestion](29-phase-20-paddock-briefing-ingestion.md): + backend CLI slice for refreshing RSS/Atom feeds into the local news cache. ## External References diff --git a/frontend/src/components/DatasetStatusView.tsx b/frontend/src/components/DatasetStatusView.tsx index c24c6da..70c5d26 100644 --- a/frontend/src/components/DatasetStatusView.tsx +++ b/frontend/src/components/DatasetStatusView.tsx @@ -1,37 +1,54 @@ +import { Link } from '@tanstack/react-router' +import { RACE_HUB_DATASETS } from '../lib/coverage' import type { DatasetInfo } from '../types' interface Props { datasets: Record } -const KNOWN_DATASETS: { key: string; label: string }[] = [ - { key: 'meeting', label: 'Meeting' }, - { key: 'session', label: 'Session' }, - { key: 'drivers', label: 'Drivers' }, - { key: 'results', label: 'Results' }, - { key: 'starting_grid', label: 'Starting Grid' }, -] +const DATASET_LABELS: Record = { + meeting: 'Meeting', + session: 'Session', + drivers: 'Drivers', + results: 'Results', + starting_grid: 'Starting Grid', + stints: 'Stints', + pit_stops: 'Pit Stops', + positions: 'Positions', + race_control: 'Race Control', + weather: 'Weather', + laps: 'Laps', +} export function DatasetStatusView({ datasets }: Props) { - const entries = KNOWN_DATASETS.map(({ key, label }) => ({ + const entries = RACE_HUB_DATASETS.map((key) => ({ key, - label, + label: DATASET_LABELS[key] ?? key, info: datasets[key] as DatasetInfo | undefined, })) - const available = entries.filter((e) => e.info?.status === 'available').length const total = entries.length + const missing = total - available return ( -
+
+