mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-08 04:06:18 -04:00
fix(race-hub): consume canonical weekend context for #75 review
Address PR #82 review blockers: drop the competing /weekend default_analysis_session resolver, land bare /race-hub via /api/v1/weekend-context, derive Live/preparing/partial/unavailable from authoritative context with a moving clock, hide Local Coverage behind Diagnostics, isolate the future-session fixture from the shared seed, and strengthen return-to-Weekend context coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,18 +1,14 @@
|
||||
import type { WeekendSession } from '../types'
|
||||
import type { ContextSession, WeekendContext, WeekendSession } from '../types'
|
||||
import { isSessionComplete } from './coverage'
|
||||
import { sessionEndTime, sessionStartTime } from './schedule'
|
||||
import { sessionStartTime } from './schedule'
|
||||
|
||||
/**
|
||||
* User-facing lifecycle state for a weekend session. Combines the schedule
|
||||
* (has it started / finished) with local dataset coverage so the UI can speak
|
||||
* in fan language instead of raw `x/11` coverage counts.
|
||||
* User-facing lifecycle state for a weekend session.
|
||||
*
|
||||
* - `upcoming` — starts in the future; render a pre-session view.
|
||||
* - `live` — currently running (started, not yet finished).
|
||||
* - `preparing` — finished (or unknown timing) but no local analysis yet.
|
||||
* - `partial` — finished with some, but not all, local datasets.
|
||||
* - `ready` — finished with full local coverage; analysis is trustworthy.
|
||||
* - `cancelled` — session was cancelled.
|
||||
* Live comes only from Weekend Context's FIA-backed active identity — never from
|
||||
* the scheduled start/end window alone. Preparing / partial / ready / unavailable
|
||||
* come from structured `availability.local_analysis` when a context ref exists,
|
||||
* otherwise from local dataset coverage after the scheduled start.
|
||||
*/
|
||||
export type SessionState =
|
||||
| 'upcoming'
|
||||
@@ -20,21 +16,91 @@ export type SessionState =
|
||||
| 'preparing'
|
||||
| 'partial'
|
||||
| 'ready'
|
||||
| 'unavailable'
|
||||
| 'cancelled'
|
||||
|
||||
export function sessionState(session: WeekendSession, now: Date): SessionState {
|
||||
if (session.source === 'cancelled') return 'cancelled'
|
||||
export interface SessionStateInput {
|
||||
weekendSession?: WeekendSession
|
||||
context?: WeekendContext | null
|
||||
now: Date
|
||||
}
|
||||
|
||||
const start = sessionStartTime(session.session)
|
||||
const end = sessionEndTime(session.session)
|
||||
function contextRefFor(
|
||||
context: WeekendContext | null | undefined,
|
||||
sessionKey: number | undefined,
|
||||
): ContextSession | undefined {
|
||||
if (!context || !sessionKey) return undefined
|
||||
const refs = [
|
||||
context.active_session,
|
||||
context.default_analysis_session,
|
||||
context.previous_completed_session,
|
||||
context.next_session,
|
||||
]
|
||||
return refs.find((ref) => ref?.session.session_key === sessionKey)
|
||||
}
|
||||
|
||||
function fromAvailability(ref: ContextSession): SessionState | undefined {
|
||||
const { schedule, live_session, local_analysis } = ref.availability
|
||||
if (live_session === 'active') return 'live'
|
||||
if (schedule === 'unavailable' || local_analysis === 'unavailable') return 'unavailable'
|
||||
if (local_analysis === 'not_applicable') return 'upcoming'
|
||||
if (local_analysis === 'pending') return 'preparing'
|
||||
if (local_analysis === 'partial') return 'partial'
|
||||
if (local_analysis === 'complete') return 'ready'
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve fan-facing session state. Prefer Weekend Context availability; never
|
||||
* assert Live from wall-clock schedule alone.
|
||||
*/
|
||||
export function resolveSessionState({
|
||||
weekendSession,
|
||||
context,
|
||||
now,
|
||||
}: SessionStateInput): SessionState {
|
||||
if (weekendSession?.source === 'cancelled') return 'cancelled'
|
||||
|
||||
const sessionKey = weekendSession?.session.session_key
|
||||
const active = context?.active_session
|
||||
if (
|
||||
active &&
|
||||
sessionKey &&
|
||||
active.session.session_key === sessionKey &&
|
||||
active.availability.live_session === 'active'
|
||||
) {
|
||||
return 'live'
|
||||
}
|
||||
|
||||
const ref = contextRefFor(context, sessionKey)
|
||||
if (ref) {
|
||||
const fromCtx = fromAvailability(ref)
|
||||
if (fromCtx) return fromCtx
|
||||
}
|
||||
|
||||
// Settling temporal state with no analysis yet — even without a matching ref.
|
||||
if (
|
||||
context?.temporal_state === 'session_settling' &&
|
||||
weekendSession &&
|
||||
weekendSession.source === 'none'
|
||||
) {
|
||||
return 'preparing'
|
||||
}
|
||||
|
||||
if (!weekendSession) return 'unavailable'
|
||||
|
||||
const start = sessionStartTime(weekendSession.session)
|
||||
if (start && start > now) return 'upcoming'
|
||||
if (start && end && now >= start && now < end) return 'live'
|
||||
|
||||
// Session has started/finished (or timing unknown) — describe it by coverage.
|
||||
if (isSessionComplete(session.datasets)) return 'ready'
|
||||
if (session.source === 'none') return 'preparing'
|
||||
return 'partial'
|
||||
if (isSessionComplete(weekendSession.datasets)) return 'ready'
|
||||
if (weekendSession.source === 'none') return 'preparing'
|
||||
if (weekendSession.source === 'partial') return 'partial'
|
||||
return 'ready'
|
||||
}
|
||||
|
||||
/** @deprecated Prefer resolveSessionState with Weekend Context. */
|
||||
export function sessionState(session: WeekendSession, now: Date): SessionState {
|
||||
return resolveSessionState({ weekendSession: session, now })
|
||||
}
|
||||
|
||||
/** Short label suitable for chips and the session switcher. */
|
||||
@@ -50,6 +116,8 @@ export function sessionStateLabel(state: SessionState): string {
|
||||
return 'Partial'
|
||||
case 'ready':
|
||||
return 'Ready'
|
||||
case 'unavailable':
|
||||
return 'Unavailable'
|
||||
case 'cancelled':
|
||||
return 'Cancelled'
|
||||
}
|
||||
@@ -63,11 +131,13 @@ export function sessionStateDescription(state: SessionState): string {
|
||||
case 'live':
|
||||
return 'Session is running now.'
|
||||
case 'preparing':
|
||||
return 'Analysis is being prepared — no local data ingested yet.'
|
||||
return 'Analysis is being prepared — local data is still settling.'
|
||||
case 'partial':
|
||||
return 'Partial analysis available — some datasets are still missing.'
|
||||
case 'ready':
|
||||
return 'Full analysis is ready.'
|
||||
case 'unavailable':
|
||||
return 'Analysis is not available for this session.'
|
||||
case 'cancelled':
|
||||
return 'This session was cancelled.'
|
||||
}
|
||||
@@ -87,6 +157,8 @@ export function sessionStateDotClass(state: SessionState): string {
|
||||
return 'rh-state-live'
|
||||
case 'upcoming':
|
||||
return 'rh-state-upcoming'
|
||||
case 'unavailable':
|
||||
return 'rh-state-unavailable'
|
||||
case 'cancelled':
|
||||
return 'rh-state-cancelled'
|
||||
default:
|
||||
@@ -98,3 +170,25 @@ export function sessionStateDotClass(state: SessionState): string {
|
||||
export function isPreSession(state: SessionState): boolean {
|
||||
return state === 'upcoming'
|
||||
}
|
||||
|
||||
/** Settling / empty post-session — not yet analysable. */
|
||||
export function isPreparing(state: SessionState): boolean {
|
||||
return state === 'preparing'
|
||||
}
|
||||
|
||||
export function isUnavailable(state: SessionState): boolean {
|
||||
return state === 'unavailable' || state === 'cancelled'
|
||||
}
|
||||
|
||||
/** Show fan analysis with a partial banner; keep available capabilities. */
|
||||
export function isPartialAnalysis(state: SessionState): boolean {
|
||||
return state === 'partial'
|
||||
}
|
||||
|
||||
/** Canonical default analysis session key from Weekend Context, if any. */
|
||||
export function defaultAnalysisSessionKey(
|
||||
context: WeekendContext | null | undefined,
|
||||
): number | undefined {
|
||||
const key = context?.default_analysis_session?.session.session_key
|
||||
return key && key > 0 ? key : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user