import { useEffect, useState } from 'react' import { useQuery } from '@tanstack/react-query' import { useNavigate } from '@tanstack/react-router' import { fetchLocalMeetings, fetchSeasons, fetchWeekend } from '../api' import { formatCoverageHint } from '../lib/coverage' import { SourceBadge } from './SourceBadge' import { SessionCoverageDots } from './SessionCoverageDots' import { RouteState } from './RouteState' import type { Meeting, WeekendSession } from '../types' function formatMeetingDates(meeting: Meeting): string { const start = meeting.date_start?.slice(0, 10) const end = meeting.date_end?.slice(0, 10) if (start && end && start !== end) return `${start} – ${end}` return start || end || '—' } function sessionSourceBadge(source: WeekendSession['source']) { return } interface Props { onSelectSession?: (sessionKey: number) => void } export function LocalDataNavigator({ onSelectSession }: Props) { const navigate = useNavigate() const [selectedYear, setSelectedYear] = useState(null) const [selectedMeetingKey, setSelectedMeetingKey] = useState(null) const seasonsQuery = useQuery({ queryKey: ['seasons'], queryFn: ({ signal }) => fetchSeasons(signal), }) const meetingsQuery = useQuery({ queryKey: ['meetings', selectedYear], queryFn: ({ signal }) => fetchLocalMeetings(selectedYear!, signal), enabled: selectedYear != null, }) const weekendQuery = useQuery({ queryKey: ['weekend', selectedMeetingKey], queryFn: ({ signal }) => fetchWeekend(selectedMeetingKey!, signal), enabled: selectedMeetingKey != null, }) useEffect(() => { if (seasonsQuery.data?.length && selectedYear == null) { setSelectedYear(seasonsQuery.data[0]) } }, [seasonsQuery.data, selectedYear]) function handleSelectSession(sessionKey: number) { if (onSelectSession) { onSelectSession(sessionKey) return } navigate({ to: '/race-hub', search: { session_key: sessionKey } }) } function handleSelectYear(year: number) { setSelectedYear(year) setSelectedMeetingKey(null) } function handleSelectMeeting(meetingKey: number) { setSelectedMeetingKey((prev) => (prev === meetingKey ? null : meetingKey)) } if (seasonsQuery.isLoading) { return } if (seasonsQuery.isError) { return ( { if (!seasonsQuery.isFetching) void seasonsQuery.refetch() }} retrying={seasonsQuery.isFetching} testId="weekend-error" /> ) } const seasons = seasonsQuery.data ?? [] if (seasons.length === 0) { return (
Local Data
No ingested seasons yet
Ingest a session with box-box --ingest-session <key>, then browse here or enter a session key below.
) } const meetings = meetingsQuery.data ?? [] const weekend = weekendQuery.data return (
Local Data
{seasons.map((year) => ( ))}
{meetingsQuery.isLoading && (
loading meetings…
)} {meetingsQuery.isError && ( { if (!meetingsQuery.isFetching) void meetingsQuery.refetch() }} retrying={meetingsQuery.isFetching} testId="weekend-meetings-error" /> )} {!meetingsQuery.isLoading && !meetingsQuery.isError && meetings.length === 0 && (
No meetings ingested for {selectedYear}.
)} {meetings.length > 0 && (
Meetings {meetings.length}
{meetings.map((meeting) => { const selected = meeting.meeting_key === selectedMeetingKey return ( ) })}
Grand Prix Country Dates Open
{meeting.meeting_name} {meeting.circuit_short_name && meeting.circuit_short_name !== meeting.meeting_name && ( {meeting.circuit_short_name} )} {meeting.country_code || meeting.country_name} {formatMeetingDates(meeting)}
)} {selectedMeetingKey != null && weekendQuery.isLoading && (
loading sessions…
)} {selectedMeetingKey != null && weekendQuery.isError && ( { if (!weekendQuery.isFetching) void weekendQuery.refetch() }} retrying={weekendQuery.isFetching} testId="weekend-error" /> )} {weekend && (
{weekend.meeting.meeting_name} Sessions {weekend.sessions.length}
{weekend.sessions.length === 0 ? (
No sessions stored for this meeting.
) : (
{weekend.sessions.map(({ session, source, datasets }) => { const coverage = formatCoverageHint(datasets) const isDefault = session.session_key === weekend.default_session_key return ( ) })}
Session Type Coverage Source Open
{session.session_name} {isDefault && default} {session.session_key} {session.session_type} {coverage} {sessionSourceBadge(source)}
)}
)}
) } // Re-export helpers used by tests export { countRaceHubDatasets, formatCoverageHint } from '../lib/coverage'