diff --git a/frontend/src/components/WeekendSwitcher.tsx b/frontend/src/components/WeekendSwitcher.tsx
index edbeb07..aa024fc 100644
--- a/frontend/src/components/WeekendSwitcher.tsx
+++ b/frontend/src/components/WeekendSwitcher.tsx
@@ -47,7 +47,7 @@ export function WeekendSwitcher({ currentMeetingKey, currentSessionKey, onClose
}
return (
-
+
Switch Weekend
diff --git a/frontend/src/pages/RaceHubPage.tsx b/frontend/src/pages/RaceHubPage.tsx
index 3e7ec26..fb84eeb 100644
--- a/frontend/src/pages/RaceHubPage.tsx
+++ b/frontend/src/pages/RaceHubPage.tsx
@@ -102,7 +102,16 @@ export function RaceHubPage({ sessionKey }: Props) {
)
}
if (preSession && preSessionRef) {
- return
+ return (
+
setSwitcherOpen((open) => !open)}
+ onCloseSwitcher={() => setSwitcherOpen(false)}
+ />
+ )
}
if (!selectedSessionKey) {
return (
@@ -350,7 +359,21 @@ export function RaceHubPage({ sessionKey }: Props) {
)
}
-function RaceHubPreSession({ session, weekend, now }: { session: ContextSession; weekend?: Weekend; now: number }) {
+function RaceHubPreSession({
+ session,
+ weekend,
+ now,
+ switcherOpen,
+ onToggleSwitcher,
+ onCloseSwitcher,
+}: {
+ session: ContextSession
+ weekend?: Weekend
+ now: number
+ switcherOpen: boolean
+ onToggleSwitcher: () => void
+ onCloseSwitcher: () => void
+}) {
const meeting = session.meeting
const sessions = sortSessionsByStart((weekend?.sessions ?? []).map((entry) => entry.session))
const target = new Date(session.session.date_start)
@@ -359,6 +382,29 @@ function RaceHubPreSession({ session, weekend, now }: { session: ContextSession;
return (
+
+ box-box · race hub
+
+
+
+
+ {switcherOpen && (
+
+ )}
+
box-box · race hub
{meeting?.meeting_name ?? 'Next race weekend'}
diff --git a/frontend/src/test/RaceHubPage.test.tsx b/frontend/src/test/RaceHubPage.test.tsx
index 8eeca29..cbadde5 100644
--- a/frontend/src/test/RaceHubPage.test.tsx
+++ b/frontend/src/test/RaceHubPage.test.tsx
@@ -311,6 +311,30 @@ describe('RaceHubPage', () => {
expect(mockFetchRaceHub).not.toHaveBeenCalled()
})
+ it('opens the weekend switcher from pre-session and navigates to the selected explicit session', async () => {
+ mockFetchWeekendContext.mockResolvedValue({
+ ...analysisContext,
+ race_hub_default_session: {
+ session: { ...raceSession, session_key: 9473, session_name: 'Practice 1', session_type: 'Practice', date_start: '2099-05-23T13:00:00Z' },
+ meeting,
+ availability,
+ },
+ race_hub_pre_session: true,
+ race_hub_refresh_at: '2099-05-23T13:00:00Z',
+ })
+
+ renderRaceHub(0)
+
+ const switchWeekend = await screen.findByTestId('rh-switch-weekend')
+ expect(switchWeekend).toHaveAttribute('aria-expanded', 'false')
+ fireEvent.click(switchWeekend)
+ expect(await screen.findByTestId('rh-switcher')).toBeInTheDocument()
+ expect(switchWeekend).toHaveAttribute('aria-expanded', 'true')
+
+ fireEvent.click(await screen.findByTestId('rh-switcher-session-9471'))
+ await waitFor(() => expect(window.location.search).toBe('?session_key=9471'))
+ })
+
it('shows recovery instead of selecting an empty future session', async () => {
mockFetchWeekendContext.mockResolvedValue({
...analysisContext,
diff --git a/tests/race-hub.spec.ts b/tests/race-hub.spec.ts
index c27458e..dbf9fe1 100644
--- a/tests/race-hub.spec.ts
+++ b/tests/race-hub.spec.ts
@@ -175,6 +175,28 @@ test.describe('Race Hub Weekend Workspace', () => {
expect(raceHubRequests).not.toContain(9473)
})
+ test('pre-session state opens the weekend switcher and navigates to an explicit session', async ({ page }) => {
+ await page.route('**/api/v1/weekend-context', (route) =>
+ route.fulfill({
+ contentType: 'application/json',
+ body: JSON.stringify(pendingContext('2030-01-01T00:00:16Z')),
+ }),
+ )
+
+ await page.goto('/race-hub')
+ await expect(page.getByTestId('race-hub-pre-session')).toBeVisible()
+
+ const switchWeekend = page.getByTestId('rh-switch-weekend')
+ await expect(switchWeekend).toHaveAttribute('aria-expanded', 'false')
+ await switchWeekend.click()
+ await expect(page.getByTestId('rh-switcher')).toBeVisible()
+ await expect(switchWeekend).toHaveAttribute('aria-expanded', 'true')
+
+ await page.getByTestId(`rh-switcher-session-${FULL_SESSION}`).click()
+ await expect(page).toHaveURL(new RegExp(`/race-hub\\?session_key=${FULL_SESSION}`))
+ await expect(page.getByTestId('race-hub')).toBeVisible()
+ })
+
test('bare /race-hub recovers when no completed local analysis exists', async ({ page }) => {
await page.route('**/api/v1/weekend-context', (route) =>
route.fulfill({