mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-08 04:06:18 -04:00
feat(frontend): annotate key numbers with meaning (#18)
Add shared Meaning primitive and pure interpretation helpers for interval, tyre age, and championship gap columns on the live tower, tyre deg panel, and championship hub. Thresholds are exported consts (undercut window tied to PIT_LOSS_SECONDS from #13). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,6 +12,9 @@ import {
|
||||
tyreLabel,
|
||||
} from '../../lib/live'
|
||||
import type { GapHistoryMap } from '../../lib/gapHistory'
|
||||
import { parseIntervalSeconds } from '../../lib/gapHistory'
|
||||
import { intervalMeaning } from '../../lib/meaning'
|
||||
import { Meaning } from '../Meaning'
|
||||
import { GapSparkline } from './GapSparkline'
|
||||
import { StintHistory } from './StintHistory'
|
||||
import { Pin } from 'lucide-react'
|
||||
@@ -100,7 +103,11 @@ export function TimingTower({
|
||||
const showCutoffAfter = row.Position === sessionDisplay.cutoffPosition
|
||||
|
||||
const gapText = gapMode === 'interval' && isRace ? (driver.Interval || driver.GapToLeader) : driver.GapToLeader
|
||||
|
||||
const intervalAnnotation =
|
||||
gapMode === 'interval' && isRace && row.Position > 1
|
||||
? intervalMeaning(parseIntervalSeconds(gapText))
|
||||
: null
|
||||
|
||||
const renderSector = (idx: number) => {
|
||||
const sec = driver.Sectors?.[idx]
|
||||
if (!sec) return '-'
|
||||
@@ -149,7 +156,14 @@ export function TimingTower({
|
||||
<td className={driver.LastLapOB ? 'mono lap-ob' : driver.LastLapPB ? 'mono lap-pb' : 'mono'}>
|
||||
{driver.LastLapTime || '-'}
|
||||
</td>
|
||||
<td className="mono">{gapText || '-'}</td>
|
||||
<td className="mono">
|
||||
<Meaning
|
||||
value={gapText || '-'}
|
||||
meaning={intervalAnnotation?.caption}
|
||||
title={intervalAnnotation?.title}
|
||||
tone={intervalAnnotation?.tone}
|
||||
/>
|
||||
</td>
|
||||
|
||||
{isRace && (
|
||||
<td className="spark-cell">
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
recordStintSamples,
|
||||
stintInputFromRow,
|
||||
} from '../../lib/tyredeg'
|
||||
import { tyreAgeMeaning } from '../../lib/meaning'
|
||||
import { Meaning } from '../Meaning'
|
||||
import '../../styles/tyredeg.css'
|
||||
|
||||
const TOP_DRIVER_COUNT = 10
|
||||
@@ -92,12 +94,20 @@ export function TyreDegPanel({ rows, sessionType, pinned }: Props) {
|
||||
{visible.map((row) => {
|
||||
const model = degradationModel(stints[row.RacingNumber]?.samples ?? [])
|
||||
const rejoin = isRace ? estimatePitRejoin(rows, row.RacingNumber) : null
|
||||
const ageAnnotation = tyreAgeMeaning(row.Tyre?.Compound, row.Tyre?.Age)
|
||||
return (
|
||||
<div className="tyredeg-row" key={row.RacingNumber} data-testid="tyredeg-row">
|
||||
<span className="tyredeg-pos mono">P{row.Position}</span>
|
||||
<span className="drv-bar" style={{ background: teamColor(row.Info?.TeamColour) }} />
|
||||
<span className="drv-code">{driverCode(row)}</span>
|
||||
<span className={`tyre-badge ${compoundClass(row.Tyre?.Compound)}`}>{tyreLabel(row.Tyre)}</span>
|
||||
<span className={`tyre-badge ${compoundClass(row.Tyre?.Compound)}`}>
|
||||
<Meaning
|
||||
value={tyreLabel(row.Tyre)}
|
||||
meaning={ageAnnotation?.caption}
|
||||
title={ageAnnotation?.title}
|
||||
tone={ageAnnotation?.tone}
|
||||
/>
|
||||
</span>
|
||||
{model ? (
|
||||
<>
|
||||
<span className={`tyredeg-trend tyredeg-trend-${model.trend}`}>
|
||||
|
||||
Reference in New Issue
Block a user