Show cancelled schedule status

This commit is contained in:
2026-07-03 02:57:18 -04:00
parent 85a8b6ad44
commit c973c03689
20 changed files with 173 additions and 29 deletions

View File

@@ -10,6 +10,7 @@ interface Props {
function SourceBadge({ source }: { source: RaceHub['source'] }) {
if (source === 'local') return <span className="badge badge-local">Local</span>
if (source === 'partial') return <span className="badge badge-partial">Partial</span>
if (source === 'cancelled') return <span className="badge badge-cancelled">Cancelled</span>
return <span className="badge badge-none">No data</span>
}

View File

@@ -1,4 +1,4 @@
type Source = 'local' | 'partial' | 'none'
type Source = 'local' | 'partial' | 'none' | 'cancelled'
interface Props {
source: Source
@@ -11,6 +11,8 @@ export function SourceBadge({ source, label }: Props) {
return <span className="badge badge-local">{label ?? 'Local'}</span>
case 'partial':
return <span className="badge badge-partial">{label ?? 'Partial'}</span>
case 'cancelled':
return <span className="badge badge-cancelled">{label ?? 'Cancelled'}</span>
default:
return <span className="badge badge-none">{label ?? 'None'}</span>
}
@@ -22,6 +24,8 @@ export function weekendStatusLabel(source: Source): string {
return 'Full'
case 'partial':
return 'Partial'
case 'cancelled':
return 'Cancelled'
default:
return 'Missing'
}

View File

@@ -51,6 +51,7 @@ export function countWeekendStats(weekends: (Weekend | undefined)[]) {
let full = 0
let partial = 0
let missing = 0
let cancelled = 0
for (const weekend of weekends) {
if (!weekend || weekend.sessions.length === 0) {
@@ -64,15 +65,19 @@ export function countWeekendStats(weekends: (Weekend | undefined)[]) {
case 'partial':
partial++
break
case 'cancelled':
cancelled++
break
default:
missing++
}
}
return { full, partial, missing, total: weekends.length }
return { full, partial, cancelled, missing, total: weekends.length }
}
export function sessionIconClass(session: WeekendSession): string {
if (session.source === 'cancelled') return 'si-cancelled'
if (session.source === 'none') return 'si-missing'
if (isSessionComplete(session.datasets)) return 'si-full'
return 'si-partial'

View File

@@ -137,6 +137,9 @@ export function DataLibraryPage() {
<span>
<em className="dl-stat-partial">{stats.partial}</em> partial
</span>
<span>
<em className="dl-stat-cancelled">{stats.cancelled}</em> cancelled
</span>
<span>
<em>{stats.missing}</em> missing
</span>
@@ -177,6 +180,10 @@ export function DataLibraryPage() {
<span className="dl-stat-label">Partial</span>
<span className="dl-stat-val dl-stat-partial">{stats.partial}</span>
</div>
<div className="dl-stat">
<span className="dl-stat-label">Cancelled</span>
<span className="dl-stat-val dl-stat-cancelled">{stats.cancelled}</span>
</div>
<div className="dl-stat">
<span className="dl-stat-label">Missing</span>
<span className="dl-stat-val">{stats.missing}</span>
@@ -294,7 +301,9 @@ export function DataLibraryPage() {
)}
</td>
<td className="hide-mobile mono" style={{ color: 'var(--text-2)' }}>
{weekend && weekend.sessions.length > 0
{weekend?.source === 'cancelled'
? 'cancelled'
: weekend && weekend.sessions.length > 0
? `${weekend.sessions.filter((s) => s.source === 'local').length}/${weekend.sessions.length} full`
: '—'}
</td>

View File

@@ -361,6 +361,7 @@ a { color: inherit; text-decoration: none; }
}
.badge-local { background: rgba(57,199,58,.12); color: var(--green); border: 1px solid rgba(57,199,58,.25); }
.badge-partial { background: rgba(255,214,0,.12); color: var(--yellow); border: 1px solid rgba(255,214,0,.25); }
.badge-cancelled { background: rgba(255,107,53,.12); color: #ff8a5c; border: 1px solid rgba(255,107,53,.28); }
.badge-none { background: rgba(80,80,80,.12); color: var(--text-3); border: 1px solid var(--border); }
/* ── Dataset strip ── */
@@ -1178,6 +1179,7 @@ a { color: inherit; text-decoration: none; }
}
.dl-banner-stats em.dl-stat-full { color: var(--green); }
.dl-banner-stats em.dl-stat-partial { color: var(--yellow); }
.dl-banner-stats em.dl-stat-cancelled { color: #ff8a5c; }
.dl-footer-link {
display: flex;
@@ -1259,6 +1261,7 @@ a { color: inherit; text-decoration: none; }
}
.dl-stat-full { color: var(--green); }
.dl-stat-partial { color: var(--yellow); }
.dl-stat-cancelled { color: #ff8a5c; }
.dl-content {
display: flex;
@@ -1328,6 +1331,7 @@ a { color: inherit; text-decoration: none; }
}
.session-icon.si-full { background: rgba(57,199,58,0.2); color: var(--green); }
.session-icon.si-partial { background: rgba(255,214,0,0.2); color: var(--yellow); }
.session-icon.si-cancelled { background: rgba(255,107,53,0.18); color: #ff8a5c; }
.session-icon.si-missing { background: rgba(50,50,50,0.5); color: var(--text-3); }
.dl-detail-wrap {
@@ -4440,4 +4444,3 @@ a { color: inherit; text-decoration: none; }
background: #f82f34;
transform: translateY(-1px);
}

View File

@@ -59,11 +59,18 @@ describe('coverage helpers', () => {
meeting: {} as Weekend['meeting'],
sessions: [{ session: {} as Weekend['sessions'][0]['session'], source: 'partial', datasets: {} }],
}
expect(countWeekendStats([local, partial, undefined])).toEqual({
const cancelled: Weekend = {
source: 'cancelled',
meeting_key: 3,
meeting: {} as Weekend['meeting'],
sessions: [{ session: {} as Weekend['sessions'][0]['session'], source: 'cancelled', datasets: {} }],
}
expect(countWeekendStats([local, partial, cancelled, undefined])).toEqual({
full: 1,
partial: 1,
cancelled: 1,
missing: 1,
total: 3,
total: 4,
})
})
})

View File

@@ -16,6 +16,7 @@ export interface Meeting {
date_start: string
date_end: string
year: number
is_cancelled?: boolean
}
export interface Session {
@@ -26,6 +27,7 @@ export interface Session {
date_start: string
date_end: string
gmt_offset: string
is_cancelled?: boolean
}
export interface Driver {
@@ -73,7 +75,7 @@ export interface EnrichedGrid {
}
export interface RaceHub {
source: 'local' | 'partial' | 'none'
source: 'local' | 'partial' | 'none' | 'cancelled'
session_key: number
datasets: Record<string, DatasetInfo>
meeting?: Meeting
@@ -158,12 +160,12 @@ export interface Lap {
export interface WeekendSession {
session: Session
source: 'local' | 'partial' | 'none'
source: 'local' | 'partial' | 'none' | 'cancelled'
datasets: Record<string, DatasetInfo>
}
export interface Weekend {
source: 'local' | 'partial' | 'none'
source: 'local' | 'partial' | 'none' | 'cancelled'
meeting_key: number
meeting: Meeting
sessions: WeekendSession[]