mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
feat(#76): bound primary-route fetches and local-first driver summary
Add shared apiFetch timeouts/abort/typed errors with in-flight dedupe, a RouteState UI for loading/empty/timeout/error+retry, and wire it through Weekend (Data Health), Championship, Driver Profile, Briefing, Live, and Race Hub. Driver summary is local-first with bounded optional OpenF1 enrichment so a hung remote call cannot block the profile. Spike note: parseSourceMode defaults to openf1; driver summary now treats omitted ?source= as auto so local season data is preferred. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import type { ChampHubDriver, ChampionshipHub } from '../types'
|
||||
import { ChampionshipSimulator } from '../components/ChampionshipSimulator'
|
||||
import { RivalryCompare } from '../components/RivalryCompare'
|
||||
import { Meaning } from '../components/Meaning'
|
||||
import { RouteState } from '../components/RouteState'
|
||||
import { TeammateH2H } from '../components/TeammateH2H'
|
||||
import { teammatePairs } from '../lib/h2h'
|
||||
import { pointsGapMeaning } from '../lib/meaning'
|
||||
@@ -81,23 +82,55 @@ function teamSplit(teamName: string, drivers: ChampHubDriver[]): TeamSplit {
|
||||
export function ChampionshipPage() {
|
||||
const [view, setView] = useState<View>('drivers')
|
||||
|
||||
const seasonsQuery = useQuery({ queryKey: ['seasons'], queryFn: fetchSeasons })
|
||||
const seasonsQuery = useQuery({
|
||||
queryKey: ['seasons'],
|
||||
queryFn: ({ signal }) => fetchSeasons(signal),
|
||||
})
|
||||
const latestSeason = seasonsQuery.data?.[0] ?? null
|
||||
|
||||
const hubQuery = useQuery({
|
||||
queryKey: ['championship-hub', latestSeason],
|
||||
queryFn: () => fetchChampionshipHub(latestSeason ?? undefined),
|
||||
queryFn: ({ signal }) => fetchChampionshipHub(latestSeason ?? undefined, signal),
|
||||
enabled: latestSeason != null,
|
||||
staleTime: 5 * 60_000,
|
||||
})
|
||||
|
||||
if (seasonsQuery.isLoading || hubQuery.isLoading) {
|
||||
return <div className="page loading-state">loading championship…</div>
|
||||
return (
|
||||
<div className="page">
|
||||
<RouteState kind="loading" title="loading championship…" testId="championship-loading" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (seasonsQuery.isError) {
|
||||
return (
|
||||
<div className="page">
|
||||
<RouteState
|
||||
kind="error"
|
||||
title="Championship unavailable"
|
||||
error={seasonsQuery.error}
|
||||
onRetry={() => {
|
||||
if (!seasonsQuery.isFetching) void seasonsQuery.refetch()
|
||||
}}
|
||||
retrying={seasonsQuery.isFetching}
|
||||
testId="championship-error"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (hubQuery.isError) {
|
||||
return (
|
||||
<div className="page error-box">
|
||||
{hubQuery.error instanceof Error ? hubQuery.error.message : 'Failed to load championship'}
|
||||
<div className="page">
|
||||
<RouteState
|
||||
kind="error"
|
||||
title="Championship unavailable"
|
||||
error={hubQuery.error}
|
||||
onRetry={() => {
|
||||
if (!hubQuery.isFetching) void hubQuery.refetch()
|
||||
}}
|
||||
retrying={hubQuery.isFetching}
|
||||
testId="championship-error"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user