import { useEffect, useState } from 'react' import { useQuery } from '@tanstack/react-query' import { Link, useNavigate } from '@tanstack/react-router' import { fetchRaceHub, fetchWeekend, fetchWeekendContext } from '../api' import { weekendFocusSearch } from '../lib/routeSearch' import { DatasetStrip } from '../components/DatasetStrip' import { RaceStoryCanvas } from '../components/RaceStoryCanvas' import { TabBar, type Tab } from '../components/TabBar' import { DatasetStatusView } from '../components/DatasetStatusView' import { StrategyView } from '../components/StrategyView' import { LapsView } from '../components/LapsView' import { CompareView } from '../components/CompareView' import { RaceControlView } from '../components/RaceControlView' import { WeatherView } from '../components/WeatherView' import { OverviewView } from '../components/OverviewView' import { PartialAnalysisBanner, PreSessionView, SessionPhaseView, } from '../components/PreSessionView' import { WeekendSwitcher } from '../components/WeekendSwitcher' import { SourceBadge } from '../components/SourceBadge' import { RouteState } from '../components/RouteState' import { countryAccent, countryDecal, formatGpDateRange } from '../lib/gpIdentity' import { sessionTypeAbbrev } from '../lib/coverage' import { formatSessionScheduleTime, sortSessionsByStart } from '../lib/schedule' import { defaultAnalysisSessionKey, isPartialAnalysis, isPreSession, isPreparing, isUnavailable, resolveSessionState, sessionStateDotClass, sessionStateLabel, } from '../lib/sessionState' import type { WeekendSession } from '../types' interface Props { sessionKey: number } export function RaceHubPage({ sessionKey }: Props) { const navigate = useNavigate() const [activeTab, setActiveTab] = useState('overview') const [switcherOpen, setSwitcherOpen] = useState(false) const [showDiagnostics, setShowDiagnostics] = useState(false) const [phaseDiagnostics, setPhaseDiagnostics] = useState(false) const [now, setNow] = useState(() => new Date()) // Keep schedule-adjacent UI (countdown) moving; Live/completion come from context. useEffect(() => { const id = window.setInterval(() => setNow(new Date()), 1000) return () => window.clearInterval(id) }, []) const contextQuery = useQuery({ queryKey: ['weekend-context'], queryFn: ({ signal }) => fetchWeekendContext(signal), staleTime: 15_000, refetchInterval: 30_000, }) const context = contextQuery.data ?? null // Bare `/race-hub` resolves only through canonical Weekend Context. useEffect(() => { if (sessionKey !== 0) return if (!contextQuery.isSuccess || !context) return const target = defaultAnalysisSessionKey(context) if (target) { navigate({ to: '/race-hub', search: { session_key: target }, replace: true }) } }, [sessionKey, contextQuery.isSuccess, context, navigate]) const raceHubQuery = useQuery({ queryKey: ['race-hub', sessionKey], queryFn: ({ signal }) => fetchRaceHub(sessionKey, signal), enabled: sessionKey > 0, staleTime: 30_000, }) const meetingKey = raceHubQuery.data?.meeting?.meeting_key const weekendQuery = useQuery({ queryKey: ['weekend', meetingKey], queryFn: ({ signal }) => fetchWeekend(meetingKey!, signal), 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 const openDiagnostics = () => { setActiveTab('data_status') setShowDiagnostics(true) setPhaseDiagnostics(true) } // ─── No session_key: resolve via Weekend Context ─── if (sessionKey === 0) { if (contextQuery.isLoading) { return (
) } if (contextQuery.isError) { return (
{ if (!contextQuery.isFetching) void contextQuery.refetch() }} retrying={contextQuery.isFetching} retryTestId="rh-retry" >
Back to Weekend
) } if (context && !defaultAnalysisSessionKey(context)) { return (
box-box · race hub

No completed session to analyse yet

Weekend Context has no default analysis session. Pick a past session to review, or check back once a session completes with local analysis.

Back to Weekend
{switcherOpen && ( setSwitcherOpen(false)} /> )}
) } return (
) } // ─── Loading / error for the requested session_key ─── if (raceHubQuery.isLoading) { return (
) } if (raceHubQuery.isError || !data) { const backMeeting = context?.focus_meeting?.meeting_key ?? context?.previous_completed_session?.meeting?.meeting_key const backSearch = weekendFocusSearch(backMeeting, sessionKey) return (
{ if (!raceHubQuery.isFetching) void raceHubQuery.refetch() }} retrying={raceHubQuery.isFetching} retryTestId="rh-retry" >
Back to Weekend
) } 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])) : {} const activeSessionMeta: WeekendSession | undefined = sessionMeta[sessionKey] const activeState = resolveSessionState({ weekendSession: activeSessionMeta, context, now, }) const preSession = isPreSession(activeState) const preparing = isPreparing(activeState) const unavailable = isUnavailable(activeState) const partial = isPartialAnalysis(activeState) return (
Back to Weekend box-box · race hub {data.meeting?.year ? ` · ${data.meeting.year}` : ''}
{switcherOpen && ( setSwitcherOpen(false)} /> )} {data.meeting && (
)} {sessions.length > 0 && ( )} {data.session && (
{data.session.session_name} {formatSessionScheduleTime(data.session.date_start)} key {sessionKey}
)} {preSession && data.session ? ( ) : preparing || unavailable ? ( <> {phaseDiagnostics && (
Diagnostics
)} ) : ( <> {partial && } {activeTab === 'overview' && } {activeTab === 'race_story' && (
)} {activeTab === 'strategy' && (
Race Strategy
)} {activeTab === 'compare' && (
Driver Compare
)} {activeTab === 'lap_data' && (
Lap Data {data.laps.length > 0 && ( {data.laps.length} samples )}
)} {activeTab === 'conditions' && (
Conditions {data.weather.length > 0 && ( {data.weather.length} samples )}
)} {activeTab === 'race_control' && (
Race Control {data.race_control.length > 0 && ( {data.race_control.length} messages )}
)} {activeTab === 'data_status' && (
Diagnostics
{showDiagnostics && (
)}
)} )}
) }