2026-05-25 12:35:08 -04:00
|
|
|
|
import { useEffect, useMemo, useState } from 'react'
|
2026-05-25 01:10:44 -04:00
|
|
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
|
|
|
import { useNavigate } from '@tanstack/react-router'
|
2026-05-25 12:35:08 -04:00
|
|
|
|
import {
|
|
|
|
|
|
fetchLocalMeetings,
|
|
|
|
|
|
fetchRaceHub,
|
|
|
|
|
|
fetchSeasons,
|
|
|
|
|
|
fetchWeekend,
|
|
|
|
|
|
} from '../api'
|
2026-05-25 01:10:44 -04:00
|
|
|
|
import { DatasetStrip } from '../components/DatasetStrip'
|
2026-05-25 13:10:47 -04:00
|
|
|
|
import { RaceStoryCanvas } from '../components/RaceStoryCanvas'
|
2026-05-25 01:20:04 -04:00
|
|
|
|
import { TabBar, type Tab } from '../components/TabBar'
|
|
|
|
|
|
import { DatasetStatusView } from '../components/DatasetStatusView'
|
|
|
|
|
|
import { StrategyView } from '../components/StrategyView'
|
2026-05-25 02:56:06 -04:00
|
|
|
|
import { LapsView } from '../components/LapsView'
|
2026-07-04 00:18:10 -04:00
|
|
|
|
import { CompareView } from '../components/CompareView'
|
2026-05-25 02:56:06 -04:00
|
|
|
|
import { RaceControlView } from '../components/RaceControlView'
|
|
|
|
|
|
import { WeatherView } from '../components/WeatherView'
|
2026-05-25 12:35:08 -04:00
|
|
|
|
import { OverviewView } from '../components/OverviewView'
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
import { PreSessionView } from '../components/PreSessionView'
|
2026-05-25 12:35:08 -04:00
|
|
|
|
import { WeekendSwitcher } from '../components/WeekendSwitcher'
|
|
|
|
|
|
import { SourceBadge } from '../components/SourceBadge'
|
|
|
|
|
|
import { countryAccent, countryDecal, formatGpDateRange } from '../lib/gpIdentity'
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
import { sessionTypeAbbrev } from '../lib/coverage'
|
2026-05-25 12:35:08 -04:00
|
|
|
|
import {
|
|
|
|
|
|
formatSessionScheduleTime,
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
pickAnalysisFocusMeeting,
|
|
|
|
|
|
sessionStartTime,
|
2026-05-25 12:35:08 -04:00
|
|
|
|
sortSessionsByStart,
|
|
|
|
|
|
} from '../lib/schedule'
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
import {
|
|
|
|
|
|
isPreSession,
|
|
|
|
|
|
sessionState,
|
|
|
|
|
|
sessionStateDotClass,
|
|
|
|
|
|
sessionStateLabel,
|
|
|
|
|
|
} from '../lib/sessionState'
|
2026-05-25 12:35:08 -04:00
|
|
|
|
import type { Weekend, WeekendSession } from '../types'
|
2026-05-25 01:10:44 -04:00
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
sessionKey: number
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Resolve the session a bare `/race-hub` should open. Prefers the canonical
|
|
|
|
|
|
* Weekend Context `default_analysis_session` (which never points at a future
|
|
|
|
|
|
* session), then any completed session with the richest coverage. Returns
|
|
|
|
|
|
* `undefined` when every session is still upcoming so the caller can fall back
|
|
|
|
|
|
* to the switcher instead of opening empty analysis.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function pickAnalysisSession(weekend: Weekend | undefined, now: Date): number | undefined {
|
2026-05-25 12:35:08 -04:00
|
|
|
|
if (!weekend) return undefined
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
if (weekend.default_analysis_session && weekend.default_analysis_session > 0) {
|
|
|
|
|
|
return weekend.default_analysis_session
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const started = weekend.sessions.filter((s) => {
|
|
|
|
|
|
const start = sessionStartTime(s.session)
|
|
|
|
|
|
return !start || start <= now
|
|
|
|
|
|
})
|
|
|
|
|
|
if (started.length === 0) return undefined
|
|
|
|
|
|
|
|
|
|
|
|
const local = started.filter((s) => s.source === 'local')
|
|
|
|
|
|
const partial = started.filter((s) => s.source === 'partial')
|
|
|
|
|
|
const pool = local.length > 0 ? local : partial.length > 0 ? partial : started
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const race = pool.find((s) => s.session.session_type?.toLowerCase().includes('race'))
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
if (race) return race.session.session_key
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const qual = pool.find((s) => s.session.session_type?.toLowerCase().includes('qualifying'))
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
if (qual) return qual.session.session_key
|
|
|
|
|
|
return pool[pool.length - 1]?.session.session_key
|
2026-05-25 12:35:08 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-25 01:10:44 -04:00
|
|
|
|
export function RaceHubPage({ sessionKey }: Props) {
|
|
|
|
|
|
const navigate = useNavigate()
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const [activeTab, setActiveTab] = useState<Tab>('overview')
|
|
|
|
|
|
const [switcherOpen, setSwitcherOpen] = useState(false)
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
const now = useMemo(() => new Date(), [])
|
2026-05-25 12:35:08 -04:00
|
|
|
|
|
|
|
|
|
|
// ─── Auto-redirect when no session_key is supplied ───
|
|
|
|
|
|
const seasonsQuery = useQuery({
|
|
|
|
|
|
queryKey: ['seasons'],
|
|
|
|
|
|
queryFn: fetchSeasons,
|
|
|
|
|
|
enabled: sessionKey === 0,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const latestSeason = seasonsQuery.data?.[0] ?? null
|
|
|
|
|
|
|
|
|
|
|
|
const meetingsQuery = useQuery({
|
|
|
|
|
|
queryKey: ['meetings', latestSeason],
|
|
|
|
|
|
queryFn: () => fetchLocalMeetings(latestSeason!),
|
|
|
|
|
|
enabled: sessionKey === 0 && latestSeason != null,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const focusMeeting = useMemo(() => {
|
|
|
|
|
|
if (sessionKey !== 0 || !meetingsQuery.data) return null
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
return pickAnalysisFocusMeeting(meetingsQuery.data, now)
|
|
|
|
|
|
}, [sessionKey, meetingsQuery.data, now])
|
2026-05-25 12:35:08 -04:00
|
|
|
|
|
|
|
|
|
|
const fallbackWeekendQuery = useQuery({
|
|
|
|
|
|
queryKey: ['weekend', focusMeeting?.meeting_key],
|
|
|
|
|
|
queryFn: () => fetchWeekend(focusMeeting!.meeting_key),
|
|
|
|
|
|
enabled: sessionKey === 0 && focusMeeting != null,
|
|
|
|
|
|
})
|
2026-05-25 01:10:44 -04:00
|
|
|
|
|
2026-05-25 02:31:35 -04:00
|
|
|
|
useEffect(() => {
|
2026-05-25 12:35:08 -04:00
|
|
|
|
if (sessionKey !== 0) return
|
|
|
|
|
|
const weekend = fallbackWeekendQuery.data
|
|
|
|
|
|
if (!weekend) return
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
const target = pickAnalysisSession(weekend, now)
|
2026-05-25 12:35:08 -04:00
|
|
|
|
if (target) {
|
|
|
|
|
|
navigate({ to: '/race-hub', search: { session_key: target }, replace: true })
|
|
|
|
|
|
}
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
}, [sessionKey, fallbackWeekendQuery.data, navigate, now])
|
2026-05-25 02:31:35 -04:00
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
// ─── Active session payload ───
|
|
|
|
|
|
const raceHubQuery = useQuery({
|
2026-05-25 01:10:44 -04:00
|
|
|
|
queryKey: ['race-hub', sessionKey],
|
|
|
|
|
|
queryFn: () => fetchRaceHub(sessionKey),
|
|
|
|
|
|
enabled: sessionKey > 0,
|
|
|
|
|
|
staleTime: 30_000,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const meetingKey = raceHubQuery.data?.meeting?.meeting_key
|
|
|
|
|
|
const weekendQuery = useQuery({
|
|
|
|
|
|
queryKey: ['weekend', meetingKey],
|
|
|
|
|
|
queryFn: () => fetchWeekend(meetingKey!),
|
|
|
|
|
|
enabled: meetingKey != null && meetingKey > 0,
|
|
|
|
|
|
staleTime: 60_000,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const data = raceHubQuery.data
|
|
|
|
|
|
const weekend = weekendQuery.data
|
|
|
|
|
|
const accent = countryAccent(data?.meeting ?? null)
|
|
|
|
|
|
const accentStyle = { '--gp-accent': accent } as React.CSSProperties
|
|
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
const [showDiagnostics, setShowDiagnostics] = useState(false)
|
|
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
// ─── No session_key: show resolving state, fall back to switcher if no local data ───
|
|
|
|
|
|
if (sessionKey === 0) {
|
|
|
|
|
|
if (seasonsQuery.isLoading || meetingsQuery.isLoading || fallbackWeekendQuery.isLoading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="rh-page" style={accentStyle}>
|
|
|
|
|
|
<div className="loading-state">resolving latest local weekend…</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
2026-05-25 01:10:44 -04:00
|
|
|
|
}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const seasons = seasonsQuery.data ?? []
|
|
|
|
|
|
if (seasons.length === 0) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="rh-page rh-empty" data-testid="race-hub-empty" style={accentStyle}>
|
|
|
|
|
|
<div className="rh-empty-band">
|
|
|
|
|
|
<span className="rh-empty-eyebrow mono">box-box · race hub</span>
|
|
|
|
|
|
<h1 className="rh-empty-title">No local sessions yet</h1>
|
|
|
|
|
|
<p className="rh-empty-sub">
|
|
|
|
|
|
The Race Hub reads from local ingest only. Once a weekend is ingested
|
|
|
|
|
|
it will open here automatically.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="rh-empty-actions">
|
|
|
|
|
|
<a href="/admin" className="rh-empty-action">Open Admin · Data Health</a>
|
|
|
|
|
|
<a href="/" className="rh-empty-action">Back to Command Center</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
// Weekend resolved but every session is upcoming — offer the switcher instead
|
|
|
|
|
|
// of silently opening empty analysis.
|
|
|
|
|
|
if (fallbackWeekendQuery.data && !pickAnalysisSession(fallbackWeekendQuery.data, now)) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="rh-page rh-empty" data-testid="race-hub-no-analysis" style={accentStyle}>
|
|
|
|
|
|
<div className="rh-empty-band">
|
|
|
|
|
|
<span className="rh-empty-eyebrow mono">box-box · race hub</span>
|
|
|
|
|
|
<h1 className="rh-empty-title">No completed session to analyse yet</h1>
|
|
|
|
|
|
<p className="rh-empty-sub">
|
|
|
|
|
|
The next weekend hasn’t run. Pick a past session to review, or check
|
|
|
|
|
|
back once it’s complete.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="rh-empty-actions">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className="rh-empty-action"
|
|
|
|
|
|
onClick={() => setSwitcherOpen(true)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Browse Weekends
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<a href="/" className="rh-empty-action">Back to Command Center</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{switcherOpen && (
|
|
|
|
|
|
<WeekendSwitcher
|
|
|
|
|
|
currentMeetingKey={fallbackWeekendQuery.data.meeting_key}
|
|
|
|
|
|
onClose={() => setSwitcherOpen(false)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="rh-page" style={accentStyle}>
|
|
|
|
|
|
<div className="loading-state">resolving latest local weekend…</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
// ─── Loading / error for the requested session_key (retry + back to weekend) ───
|
2026-05-25 12:35:08 -04:00
|
|
|
|
if (raceHubQuery.isLoading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="rh-page" style={accentStyle}>
|
|
|
|
|
|
<div className="loading-state">loading session {sessionKey}…</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (raceHubQuery.isError || !data) {
|
|
|
|
|
|
return (
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
<div className="rh-page" data-testid="race-hub-error" style={accentStyle}>
|
|
|
|
|
|
<div className="rh-recover">
|
|
|
|
|
|
<div className="error-box">
|
|
|
|
|
|
{raceHubQuery.error instanceof Error
|
|
|
|
|
|
? raceHubQuery.error.message
|
|
|
|
|
|
: `Failed to load session ${sessionKey}.`}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="rh-recover-actions">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className="rh-recover-btn primary"
|
|
|
|
|
|
onClick={() => raceHubQuery.refetch()}
|
|
|
|
|
|
data-testid="rh-retry"
|
|
|
|
|
|
>
|
|
|
|
|
|
Retry
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<a href="/race-hub" className="rh-recover-btn" data-testid="rh-back-weekend">
|
|
|
|
|
|
Back to Weekend
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
2026-05-25 12:35:08 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
2026-05-25 01:10:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
const decal = countryDecal(data.meeting ?? null)
|
|
|
|
|
|
const sessions = weekend ? sortSessionsByStart(weekend.sessions.map((w) => w.session)) : []
|
|
|
|
|
|
const sessionMeta = weekend
|
|
|
|
|
|
? Object.fromEntries(weekend.sessions.map((w) => [w.session.session_key, w]))
|
|
|
|
|
|
: {}
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
const activeSessionMeta: WeekendSession | undefined = sessionMeta[sessionKey]
|
|
|
|
|
|
const activeState = activeSessionMeta ? sessionState(activeSessionMeta, now) : undefined
|
|
|
|
|
|
const preSession = activeState != null && isPreSession(activeState)
|
2026-05-25 12:35:08 -04:00
|
|
|
|
|
2026-05-25 01:10:44 -04:00
|
|
|
|
return (
|
2026-05-25 12:35:08 -04:00
|
|
|
|
<div className="rh-page" data-testid="race-hub" style={accentStyle}>
|
|
|
|
|
|
{/* Topbar */}
|
|
|
|
|
|
<div className="rh-topbar">
|
|
|
|
|
|
<span className="rh-topbar-label mono">
|
|
|
|
|
|
box-box · race hub
|
|
|
|
|
|
{data.meeting?.year ? ` · ${data.meeting.year}` : ''}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span className="rh-topbar-spacer" />
|
|
|
|
|
|
<SourceBadge source={data.source} />
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className={`rh-switcher-toggle${switcherOpen ? ' active' : ''}`}
|
|
|
|
|
|
onClick={() => setSwitcherOpen((v) => !v)}
|
|
|
|
|
|
aria-expanded={switcherOpen}
|
|
|
|
|
|
data-testid="rh-switch-weekend"
|
|
|
|
|
|
>
|
|
|
|
|
|
{switcherOpen ? 'Close' : 'Switch Weekend'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{switcherOpen && (
|
|
|
|
|
|
<WeekendSwitcher
|
|
|
|
|
|
currentMeetingKey={meetingKey}
|
|
|
|
|
|
currentSessionKey={sessionKey}
|
|
|
|
|
|
onClose={() => setSwitcherOpen(false)}
|
2026-05-25 01:10:44 -04:00
|
|
|
|
/>
|
2026-05-25 12:35:08 -04:00
|
|
|
|
)}
|
2026-05-25 01:10:44 -04:00
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
{/* GP Identity band */}
|
|
|
|
|
|
{data.meeting && (
|
|
|
|
|
|
<section className="rh-identity" data-testid="rh-identity">
|
|
|
|
|
|
<div className="rh-identity-accent" aria-hidden="true" />
|
|
|
|
|
|
<div className="rh-identity-body">
|
|
|
|
|
|
<span className="rh-identity-decal mono">{decal}</span>
|
|
|
|
|
|
<div className="rh-identity-titles">
|
|
|
|
|
|
<h1 className="rh-identity-name">{data.meeting.meeting_name}</h1>
|
|
|
|
|
|
<div className="rh-identity-sub mono">
|
|
|
|
|
|
{[data.meeting.location, data.meeting.circuit_short_name]
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.join(' · ')}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="rh-identity-sub mono rh-identity-dates">
|
|
|
|
|
|
{formatGpDateRange(data.meeting)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
)}
|
2026-05-25 01:10:44 -04:00
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
{/* Session rail */}
|
|
|
|
|
|
{sessions.length > 0 && (
|
|
|
|
|
|
<nav className="rh-session-rail" aria-label="Weekend sessions" data-testid="rh-session-rail">
|
|
|
|
|
|
{sessions.map((session) => {
|
|
|
|
|
|
const meta = sessionMeta[session.session_key]
|
|
|
|
|
|
const active = session.session_key === sessionKey
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
const state = meta ? sessionState(meta, now) : undefined
|
2026-05-25 12:35:08 -04:00
|
|
|
|
return (
|
|
|
|
|
|
<button
|
|
|
|
|
|
key={session.session_key}
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className={`rh-session-chip${active ? ' active' : ''}`}
|
|
|
|
|
|
onClick={() =>
|
|
|
|
|
|
navigate({
|
|
|
|
|
|
to: '/race-hub',
|
|
|
|
|
|
search: { session_key: session.session_key },
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
aria-current={active ? 'page' : undefined}
|
|
|
|
|
|
data-testid={`rh-session-${session.session_key}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span className="rh-session-abbrev mono">
|
|
|
|
|
|
{sessionTypeAbbrev(session.session_type, session.session_name)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span className="rh-session-name">{session.session_name}</span>
|
|
|
|
|
|
<span className="rh-session-time mono">
|
|
|
|
|
|
{formatSessionScheduleTime(session.date_start)}
|
|
|
|
|
|
</span>
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{state && (
|
2026-05-25 12:35:08 -04:00
|
|
|
|
<span className="rh-session-cov mono">
|
|
|
|
|
|
<span
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
className={`cc-cov-dot ${sessionStateDotClass(state)}`}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
/>
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{sessionStateLabel(state)}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
)
|
|
|
|
|
|
})}
|
|
|
|
|
|
</nav>
|
2026-05-25 01:10:44 -04:00
|
|
|
|
)}
|
|
|
|
|
|
|
2026-05-25 12:35:08 -04:00
|
|
|
|
{/* Active session sub-bar */}
|
|
|
|
|
|
{data.session && (
|
|
|
|
|
|
<div className="rh-active-bar" data-testid="rh-active-bar">
|
|
|
|
|
|
<span className="rh-active-name">{data.session.session_name}</span>
|
|
|
|
|
|
<span className="rh-active-meta mono">
|
|
|
|
|
|
{formatSessionScheduleTime(data.session.date_start)}
|
|
|
|
|
|
</span>
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeState && (
|
|
|
|
|
|
<span className="rh-active-cov mono" data-testid="rh-active-state">
|
2026-05-25 12:35:08 -04:00
|
|
|
|
<span
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
className={`cc-cov-dot ${sessionStateDotClass(activeState)}`}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
aria-hidden="true"
|
|
|
|
|
|
/>
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{sessionStateLabel(activeState)}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
<span className="rh-active-key mono">key {sessionKey}</span>
|
2026-05-25 01:10:44 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{preSession && data.session ? (
|
|
|
|
|
|
<PreSessionView session={data.session} sessionName={data.session.session_name} />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<TabBar active={activeTab} onChange={setActiveTab} />
|
2026-05-25 01:10:44 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'overview' && <OverviewView data={data} />}
|
2026-05-25 01:20:04 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'race_story' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<RaceStoryCanvas data={data} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-25 01:20:04 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'strategy' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Race Strategy</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<StrategyView
|
|
|
|
|
|
results={data.results}
|
|
|
|
|
|
stints={data.stints}
|
|
|
|
|
|
pit_stops={data.pit_stops}
|
|
|
|
|
|
hasStints={data.datasets['stints']?.status === 'available'}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-25 02:56:06 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'compare' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Driver Compare</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<CompareView
|
|
|
|
|
|
sessionKey={sessionKey}
|
|
|
|
|
|
results={data.results}
|
|
|
|
|
|
drivers={data.drivers}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-07-04 00:18:10 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'lap_data' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Lap Data</span>
|
|
|
|
|
|
{data.laps.length > 0 && (
|
|
|
|
|
|
<span className="sec-meta mono">{data.laps.length} samples</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<LapsView laps={data.laps} drivers={data.drivers} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-25 02:56:06 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'conditions' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Conditions</span>
|
|
|
|
|
|
{data.weather.length > 0 && (
|
|
|
|
|
|
<span className="sec-meta mono">{data.weather.length} samples</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<WeatherView weather={data.weather} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-25 02:56:06 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'race_control' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Race Control</span>
|
|
|
|
|
|
{data.race_control.length > 0 && (
|
|
|
|
|
|
<span className="sec-meta mono">{data.race_control.length} messages</span>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<RaceControlView messages={data.race_control} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-25 12:35:08 -04:00
|
|
|
|
|
feat(race-hub): trustworthy defaults and fan-facing analysis hierarchy (#75)
Make bare /race-hub resolve to a completed session and never open empty
post-session analysis for a future race.
- Backend: add `default_analysis_session` to the Weekend context. It never
resolves to a future session (picks the richest completed session, ties
toward the later one; 0 when everything is upcoming). Existing
`default_session_key` and deep links are unchanged.
- Frontend default resolution prefers the most recently completed weekend
(`pickAnalysisFocusMeeting`) and consumes `default_analysis_session`, falling
back to the switcher when only upcoming sessions exist.
- New `sessionState` lib maps timing + coverage to user language
(upcoming/live/preparing/partial/ready/cancelled); the session rail, active
sub-bar, and WeekendSwitcher now label states instead of raw x/11 counts.
- Future sessions render a purpose-built PreSessionView (expected availability +
countdown) instead of empty Winner/Podium/Pole/Strategy/Compare cards.
- Analysis navigation regrouped into Story / Analysis / Data & Context; every
existing tab is preserved. Diagnostics (renamed from Data Status) is now a
secondary action and the raw dataset strip is hidden behind an explicit
toggle, so operational coverage no longer precedes fan content.
- Loading/error states offer Retry and a path back to Weekend.
Tests: Go query tests for future-exclusion; Vitest for default selection,
future pre-session, partial state, error/retry, grouped nav, and sessionState;
hermetic Playwright for bare/completed/future/return-to-Weekend; new
race-hub-future visual snapshots. Seed adds a far-future session inside the
Monaco meeting (kept in-meeting so Command Center focus is unaffected).
Note: `default_analysis_session` is an additive field on the existing
`/weekend` contract (no new endpoint), per the spec's "context contract" scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 18:29:34 -04:00
|
|
|
|
{activeTab === 'data_status' && (
|
|
|
|
|
|
<div className="data-section">
|
|
|
|
|
|
<div className="sec-header">
|
|
|
|
|
|
<span className="sec-title">Diagnostics</span>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
className={`rh-diagnostics-toggle${showDiagnostics ? ' active' : ''}`}
|
|
|
|
|
|
onClick={() => setShowDiagnostics((v) => !v)}
|
|
|
|
|
|
aria-expanded={showDiagnostics}
|
|
|
|
|
|
data-testid="rh-diagnostics-toggle"
|
|
|
|
|
|
>
|
|
|
|
|
|
{showDiagnostics ? 'Hide dataset coverage' : 'Show dataset coverage'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<DatasetStatusView datasets={data.datasets} />
|
|
|
|
|
|
{showDiagnostics && (
|
|
|
|
|
|
<div style={{ marginTop: 'var(--s5)' }} data-testid="rh-dataset-strip">
|
|
|
|
|
|
<DatasetStrip datasets={data.datasets} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</>
|
2026-05-25 01:10:44 -04:00
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|