Add championship what-if simulator

Fourth view on the championship page: assign P1-P10 per remaining
round and see projected standings update live, with position deltas
and mathematically alive/eliminated title states. Pure projection
logic in lib/simulator.ts (2025 points system, standard GPs only);
scenarios persist to localStorage per season. 169 frontend tests
passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 00:31:09 -04:00
parent e5cb4e4ed2
commit 5cfaed30ba
8 changed files with 1013 additions and 1 deletions

View File

@@ -3,8 +3,9 @@ import { useQuery } from '@tanstack/react-query'
import { fetchChampionshipHub, fetchSeasons } from '../api'
import { teamColor } from '../utils'
import type { ChampHubDriver, ChampionshipHub } from '../types'
import { ChampionshipSimulator } from '../components/ChampionshipSimulator'
type View = 'drivers' | 'constructors' | 'progression'
type View = 'drivers' | 'constructors' | 'progression' | 'simulator'
const GOLD = '#ffd700'
const SILVER = '#c0c0c0'
@@ -208,6 +209,14 @@ function ChampionshipBody({ hub, view, setView }: BodyProps) {
>
Progression
</button>
<button
type="button"
className={`champ-tab${view === 'simulator' ? ' is-active' : ''}`}
onClick={() => setView('simulator')}
data-testid="champ-tab-simulator"
>
Simulator
</button>
</div>
</div>
@@ -228,6 +237,7 @@ function ChampionshipBody({ hub, view, setView }: BodyProps) {
)}
{view === 'constructors' && <ConstructorsView hub={hub} />}
{view === 'progression' && <ProgressionView hub={hub} />}
{view === 'simulator' && <ChampionshipSimulator hub={hub} />}
</div>
)
}