mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
Add season calendar to command center
This commit is contained in:
@@ -26,6 +26,15 @@ export async function fetchLocalMeetings(year: number): Promise<Meeting[]> {
|
|||||||
return Array.isArray(meetings) ? meetings : []
|
return Array.isArray(meetings) ? meetings : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchSeasonMeetings(year: number): Promise<Meeting[]> {
|
||||||
|
const res = await fetch(`/api/v1/meetings?year=${year}&source=openf1`)
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`API ${res.status}: ${res.statusText}`)
|
||||||
|
}
|
||||||
|
const meetings = await res.json()
|
||||||
|
return Array.isArray(meetings) ? meetings : []
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchWeekend(meetingKey: number): Promise<Weekend> {
|
export async function fetchWeekend(meetingKey: number): Promise<Weekend> {
|
||||||
const res = await fetch(`/api/v1/weekend?meeting_key=${meetingKey}`)
|
const res = await fetch(`/api/v1/weekend?meeting_key=${meetingKey}`)
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const COUNTRY_ACCENTS: Record<string, string> = {
|
|||||||
DEU: '#cdc7c2',
|
DEU: '#cdc7c2',
|
||||||
BEL: '#e0a01b',
|
BEL: '#e0a01b',
|
||||||
NLD: '#ef7c1a',
|
NLD: '#ef7c1a',
|
||||||
|
NED: '#ef7c1a',
|
||||||
HUN: '#3f8a4b',
|
HUN: '#3f8a4b',
|
||||||
AUT: '#c61f1f',
|
AUT: '#c61f1f',
|
||||||
AZE: '#1f7eaf',
|
AZE: '#1f7eaf',
|
||||||
@@ -20,8 +21,11 @@ const COUNTRY_ACCENTS: Record<string, string> = {
|
|||||||
SGP: '#c8202c',
|
SGP: '#c8202c',
|
||||||
BRA: '#2fa84f',
|
BRA: '#2fa84f',
|
||||||
BHR: '#a8132b',
|
BHR: '#a8132b',
|
||||||
|
BRN: '#a8132b',
|
||||||
SAU: '#0a7a3b',
|
SAU: '#0a7a3b',
|
||||||
|
KSA: '#0a7a3b',
|
||||||
ARE: '#4d6b2a',
|
ARE: '#4d6b2a',
|
||||||
|
UAE: '#4d6b2a',
|
||||||
QAT: '#7a1e3e',
|
QAT: '#7a1e3e',
|
||||||
CHN: '#c8202c',
|
CHN: '#c8202c',
|
||||||
MEX: '#1f7a4d',
|
MEX: '#1f7a4d',
|
||||||
@@ -43,6 +47,46 @@ export function countryDecal(meeting: Meeting | undefined | null): string {
|
|||||||
return name.slice(0, 3).toUpperCase() || '—'
|
return name.slice(0, 3).toUpperCase() || '—'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function countryFlag(meeting: Meeting | undefined | null): string {
|
||||||
|
if (meeting?.country_flag && !/^https?:\/\//i.test(meeting.country_flag)) return meeting.country_flag
|
||||||
|
const code = meeting?.country_code?.toUpperCase()
|
||||||
|
const iso2 = code ? CODE3_TO_2[code] : ''
|
||||||
|
if (!iso2) return ''
|
||||||
|
const offset = 0x1f1e6 - 65
|
||||||
|
return String.fromCodePoint(iso2.charCodeAt(0) + offset, iso2.charCodeAt(1) + offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CODE3_TO_2: Record<string, string> = {
|
||||||
|
AUS: 'AU',
|
||||||
|
AUT: 'AT',
|
||||||
|
AZE: 'AZ',
|
||||||
|
BEL: 'BE',
|
||||||
|
BHR: 'BH',
|
||||||
|
BRN: 'BH',
|
||||||
|
BRA: 'BR',
|
||||||
|
CAN: 'CA',
|
||||||
|
CHN: 'CN',
|
||||||
|
DEU: 'DE',
|
||||||
|
GER: 'DE',
|
||||||
|
ESP: 'ES',
|
||||||
|
FRA: 'FR',
|
||||||
|
GBR: 'GB',
|
||||||
|
HUN: 'HU',
|
||||||
|
ITA: 'IT',
|
||||||
|
JPN: 'JP',
|
||||||
|
MEX: 'MX',
|
||||||
|
MON: 'MC',
|
||||||
|
NED: 'NL',
|
||||||
|
NLD: 'NL',
|
||||||
|
QAT: 'QA',
|
||||||
|
SAU: 'SA',
|
||||||
|
KSA: 'SA',
|
||||||
|
SGP: 'SG',
|
||||||
|
UAE: 'AE',
|
||||||
|
ARE: 'AE',
|
||||||
|
USA: 'US',
|
||||||
|
}
|
||||||
|
|
||||||
export function formatGpDateRange(meeting: Meeting | undefined | null): string {
|
export function formatGpDateRange(meeting: Meeting | undefined | null): string {
|
||||||
if (!meeting) return ''
|
if (!meeting) return ''
|
||||||
const start = meeting.date_start?.slice(0, 10)
|
const start = meeting.date_start?.slice(0, 10)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useQueries, useQuery } from '@tanstack/react-query'
|
import { useQueries, useQuery } from '@tanstack/react-query'
|
||||||
import { Link } from '@tanstack/react-router'
|
import { Link } from '@tanstack/react-router'
|
||||||
import { fetchLiveState, fetchLocalMeetings, fetchSeasons, fetchWeekend } from '../api'
|
import { fetchLiveState, fetchLocalMeetings, fetchSeasonMeetings, fetchSeasons, fetchWeekend } from '../api'
|
||||||
import { countWeekendStats, formatCoverageHint, sessionTypeAbbrev } from '../lib/coverage'
|
import { countWeekendStats, formatCoverageHint, sessionTypeAbbrev } from '../lib/coverage'
|
||||||
import {
|
import {
|
||||||
currentAndNextSession,
|
currentAndNextSession,
|
||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
sessionStartTime,
|
sessionStartTime,
|
||||||
sortSessionsByStart,
|
sortSessionsByStart,
|
||||||
} from '../lib/schedule'
|
} from '../lib/schedule'
|
||||||
import { countryAccent, countryDecal, formatGpDateRange } from '../lib/gpIdentity'
|
import { countryAccent, countryDecal, countryFlag, formatGpDateRange } from '../lib/gpIdentity'
|
||||||
import type { Meeting, Session, Weekend, WeekendSession } from '../types'
|
import type { Meeting, Session, Weekend, WeekendSession } from '../types'
|
||||||
import { PaddockBriefing } from '../components/PaddockBriefing'
|
import { PaddockBriefing } from '../components/PaddockBriefing'
|
||||||
|
|
||||||
@@ -28,21 +28,10 @@ function classifySessionStatus(session: Session, now: Date): 'live' | 'done' | '
|
|||||||
return 'upcoming'
|
return 'upcoming'
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectRecentWeekends(weekends: (Weekend | undefined)[], focusKey: number | null, limit = 6) {
|
function meetingStatus(meeting: Meeting, focusKey: number | undefined, now: Date) {
|
||||||
const rows: Weekend[] = []
|
if (meeting.meeting_key === focusKey) return 'focus'
|
||||||
for (const weekend of weekends) {
|
if (meetingHasStarted(meeting, now)) return 'past'
|
||||||
if (!weekend) continue
|
return 'future'
|
||||||
if (focusKey != null && weekend.meeting_key === focusKey) continue
|
|
||||||
if (weekend.source === 'none') continue
|
|
||||||
rows.push(weekend)
|
|
||||||
}
|
|
||||||
return rows
|
|
||||||
.sort((a, b) => {
|
|
||||||
const left = Date.parse(a.meeting.date_start ?? '')
|
|
||||||
const right = Date.parse(b.meeting.date_start ?? '')
|
|
||||||
return right - left
|
|
||||||
})
|
|
||||||
.slice(0, limit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pickAnalysisSession(weekend: Weekend | undefined): WeekendSession | undefined {
|
function pickAnalysisSession(weekend: Weekend | undefined): WeekendSession | undefined {
|
||||||
@@ -73,7 +62,15 @@ export function CommandCenterPage() {
|
|||||||
enabled: latestSeason != null,
|
enabled: latestSeason != null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const meetings = meetingsQuery.data ?? []
|
const seasonMeetingsQuery = useQuery({
|
||||||
|
queryKey: ['season-meetings', latestSeason],
|
||||||
|
queryFn: () => fetchSeasonMeetings(latestSeason!),
|
||||||
|
enabled: latestSeason != null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const localMeetings = meetingsQuery.data ?? []
|
||||||
|
const seasonMeetings = seasonMeetingsQuery.data?.length ? seasonMeetingsQuery.data : localMeetings
|
||||||
|
const meetings = localMeetings
|
||||||
|
|
||||||
const weekendQueries = useQueries({
|
const weekendQueries = useQueries({
|
||||||
queries: meetings.map((meeting) => ({
|
queries: meetings.map((meeting) => ({
|
||||||
@@ -116,7 +113,6 @@ export function CommandCenterPage() {
|
|||||||
: []
|
: []
|
||||||
const { current: currentSession, next: nextSession } = currentAndNextSession(focusSessions, nowDate)
|
const { current: currentSession, next: nextSession } = currentAndNextSession(focusSessions, nowDate)
|
||||||
|
|
||||||
const recentWeekends = collectRecentWeekends(weekendList, focusMeeting?.meeting_key ?? null)
|
|
||||||
const analysisSession = pickAnalysisSession(focusWeekend)
|
const analysisSession = pickAnalysisSession(focusWeekend)
|
||||||
const analysisSessionKey =
|
const analysisSessionKey =
|
||||||
analysisSession?.session.session_key ??
|
analysisSession?.session.session_key ??
|
||||||
@@ -314,45 +310,51 @@ export function CommandCenterPage() {
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(weekendsLoading || recentWeekends.length > 0) && (
|
{seasonMeetings.length > 0 && (
|
||||||
<section className="cc-recent">
|
<section className="cc-season-calendar" data-testid="cc-season-calendar">
|
||||||
<div className="sec-header">
|
<div className="sec-header">
|
||||||
<span className="sec-title">Recent Local Weekends</span>
|
<span className="sec-title">Season Calendar</span>
|
||||||
<span className="sec-meta mono">{recentWeekends.length}</span>
|
<span className="sec-meta mono">
|
||||||
|
{seasonMeetings.length} rounds · {meetingStats.full}/{meetingStats.total || 0} local
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{weekendsLoading && recentWeekends.length === 0 ? (
|
<div className="cc-calendar-grid" role="list">
|
||||||
<div className="loading-state">loading weekends…</div>
|
{seasonMeetings.map((meeting, index) => {
|
||||||
) : recentWeekends.length === 0 ? (
|
const weekend = weekendsByKey.get(meeting.meeting_key)
|
||||||
<div className="cc-side-empty">No additional local weekends.</div>
|
const target = pickAnalysisSession(weekend)
|
||||||
) : (
|
const targetKey = target?.session.session_key ?? weekend?.default_session_key
|
||||||
<div className="cc-recent-strip" role="list">
|
const status = meetingStatus(meeting, focusMeeting?.meeting_key, nowDate)
|
||||||
{recentWeekends.map((weekend) => {
|
const cardAccent = countryAccent(meeting)
|
||||||
const finished = meetingHasStarted(weekend.meeting, nowDate)
|
return (
|
||||||
const target = pickAnalysisSession(weekend)
|
<Link
|
||||||
const target_key = target?.session.session_key ?? weekend.default_session_key
|
key={meeting.meeting_key}
|
||||||
return (
|
to="/race-hub"
|
||||||
<Link
|
search={targetKey ? { session_key: targetKey } : {}}
|
||||||
key={weekend.meeting_key}
|
className={`cc-calendar-card cc-calendar-${status}`}
|
||||||
to="/race-hub"
|
data-testid={`cc-calendar-${meeting.meeting_key}`}
|
||||||
search={target_key ? { session_key: target_key } : {}}
|
role="listitem"
|
||||||
className="cc-recent-card"
|
style={{ '--gp-card-accent': cardAccent } as React.CSSProperties}
|
||||||
data-testid={`cc-weekend-${weekend.meeting_key}`}
|
>
|
||||||
role="listitem"
|
<div className="cc-calendar-accent" aria-hidden="true" />
|
||||||
>
|
<div className="cc-calendar-top mono">
|
||||||
<div className="cc-recent-head mono">
|
<span className="cc-calendar-round">R{String(index + 1).padStart(2, '0')}</span>
|
||||||
<span className="cc-recent-decal">{countryDecal(weekend.meeting)}</span>
|
<span className={`cc-cov-dot cc-cov-${weekend?.source ?? 'none'}`} aria-hidden="true" />
|
||||||
<span className={`cc-cov-dot cc-cov-${weekend.source}`} aria-hidden="true" />
|
</div>
|
||||||
</div>
|
<div className="cc-calendar-id">
|
||||||
<div className="cc-recent-name">{weekend.meeting.meeting_name}</div>
|
<span className="cc-calendar-decal mono">{countryDecal(meeting)}</span>
|
||||||
<div className="cc-recent-meta mono">
|
{countryFlag(meeting) && <span className="cc-calendar-flag">{countryFlag(meeting)}</span>}
|
||||||
{formatGpDateRange(weekend.meeting)}
|
</div>
|
||||||
{' · '}
|
<div className="cc-calendar-copy">
|
||||||
{finished ? 'Past' : 'Upcoming'}
|
<div className="cc-calendar-name">{meeting.meeting_name}</div>
|
||||||
</div>
|
<div className="cc-calendar-circuit mono">{meeting.circuit_short_name || meeting.location}</div>
|
||||||
</Link>
|
<div className="cc-calendar-date mono">{formatGpDateRange(meeting)}</div>
|
||||||
)
|
</div>
|
||||||
})}
|
</Link>
|
||||||
</div>
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{seasonMeetingsQuery.isError && (
|
||||||
|
<div className="cc-side-empty">Using local meetings because the full calendar could not load.</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1296,7 +1296,7 @@ a { color: inherit; text-decoration: none; }
|
|||||||
|
|
||||||
.cc-band-accent {
|
.cc-band-accent {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
background: var(--gp-accent);
|
background: linear-gradient(180deg, var(--gp-accent), rgba(255,255,255,0.28), var(--gp-accent));
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1304,9 +1304,25 @@ a { color: inherit; text-decoration: none; }
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
padding: var(--s5);
|
padding: var(--s5);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, color-mix(in srgb, var(--gp-accent) 18%, transparent), transparent 54%),
|
||||||
|
var(--surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-band-body::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(110deg, transparent 0 22px, rgba(255,255,255,0.025) 22px 23px);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cc-band-row {
|
.cc-band-row {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var(--s6);
|
gap: var(--s6);
|
||||||
@@ -1551,6 +1567,133 @@ a { color: inherit; text-decoration: none; }
|
|||||||
.cc-cov-none { background: var(--text-3); opacity: 0.5; }
|
.cc-cov-none { background: var(--text-3); opacity: 0.5; }
|
||||||
|
|
||||||
/* Recent weekends strip */
|
/* Recent weekends strip */
|
||||||
|
.cc-season-calendar { display: flex; flex-direction: column; gap: var(--s3); }
|
||||||
|
|
||||||
|
.cc-calendar-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
|
||||||
|
gap: var(--s3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-card {
|
||||||
|
--gp-card-accent: var(--red);
|
||||||
|
position: relative;
|
||||||
|
min-height: 148px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--s4);
|
||||||
|
padding: var(--s4);
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
linear-gradient(145deg, color-mix(in srgb, var(--gp-card-accent) 22%, transparent), transparent 58%),
|
||||||
|
var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: transform 0.12s, border-color 0.12s, background 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 82% 18%, color-mix(in srgb, var(--gp-card-accent) 26%, transparent), transparent 32%),
|
||||||
|
repeating-linear-gradient(115deg, transparent 0 18px, rgba(255,255,255,0.025) 18px 19px);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-color: color-mix(in srgb, var(--gp-card-accent) 58%, var(--border));
|
||||||
|
background:
|
||||||
|
linear-gradient(145deg, color-mix(in srgb, var(--gp-card-accent) 28%, transparent), transparent 60%),
|
||||||
|
var(--surface-h);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-card > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-accent {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--gp-card-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-focus {
|
||||||
|
border-color: color-mix(in srgb, var(--gp-card-accent) 66%, var(--border));
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gp-card-accent) 22%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-past {
|
||||||
|
opacity: 0.58;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-top,
|
||||||
|
.cc-calendar-id {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--s3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-round {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-decal {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
line-height: 0.95;
|
||||||
|
color: var(--gp-card-accent);
|
||||||
|
filter: brightness(1.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-flag {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-copy {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-name {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-circuit,
|
||||||
|
.cc-calendar-date {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-circuit {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-date {
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--text-3);
|
||||||
|
}
|
||||||
|
|
||||||
.cc-recent { display: flex; flex-direction: column; gap: var(--s3); }
|
.cc-recent { display: flex; flex-direction: column; gap: var(--s3); }
|
||||||
|
|
||||||
.cc-recent-strip {
|
.cc-recent-strip {
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import type { DatasetInfo, Meeting, Weekend } from '../types'
|
|||||||
vi.mock('../api', () => ({
|
vi.mock('../api', () => ({
|
||||||
fetchSeasons: vi.fn(),
|
fetchSeasons: vi.fn(),
|
||||||
fetchLocalMeetings: vi.fn(),
|
fetchLocalMeetings: vi.fn(),
|
||||||
|
fetchSeasonMeetings: vi.fn(),
|
||||||
fetchWeekend: vi.fn(),
|
fetchWeekend: vi.fn(),
|
||||||
fetchLiveState: vi.fn(),
|
fetchLiveState: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
import { fetchSeasons, fetchLocalMeetings, fetchWeekend, fetchLiveState } from '../api'
|
import { fetchSeasons, fetchLocalMeetings, fetchSeasonMeetings, fetchWeekend, fetchLiveState } from '../api'
|
||||||
|
|
||||||
const mockFetchSeasons = vi.mocked(fetchSeasons)
|
const mockFetchSeasons = vi.mocked(fetchSeasons)
|
||||||
const mockFetchLocalMeetings = vi.mocked(fetchLocalMeetings)
|
const mockFetchLocalMeetings = vi.mocked(fetchLocalMeetings)
|
||||||
|
const mockFetchSeasonMeetings = vi.mocked(fetchSeasonMeetings)
|
||||||
const mockFetchWeekend = vi.mocked(fetchWeekend)
|
const mockFetchWeekend = vi.mocked(fetchWeekend)
|
||||||
const mockFetchLiveState = vi.mocked(fetchLiveState)
|
const mockFetchLiveState = vi.mocked(fetchLiveState)
|
||||||
|
|
||||||
@@ -113,6 +115,7 @@ describe('CommandCenterPage', () => {
|
|||||||
it('shows weekend identity band and schedule when data exists', async () => {
|
it('shows weekend identity band and schedule when data exists', async () => {
|
||||||
mockFetchSeasons.mockResolvedValue([2025])
|
mockFetchSeasons.mockResolvedValue([2025])
|
||||||
mockFetchLocalMeetings.mockResolvedValue([meeting])
|
mockFetchLocalMeetings.mockResolvedValue([meeting])
|
||||||
|
mockFetchSeasonMeetings.mockResolvedValue([meeting])
|
||||||
mockFetchWeekend.mockResolvedValue(weekend)
|
mockFetchWeekend.mockResolvedValue(weekend)
|
||||||
|
|
||||||
renderPage()
|
renderPage()
|
||||||
@@ -126,6 +129,8 @@ describe('CommandCenterPage', () => {
|
|||||||
})
|
})
|
||||||
expect(screen.getByTestId('cc-focus')).toHaveTextContent('Monaco')
|
expect(screen.getByTestId('cc-focus')).toHaveTextContent('Monaco')
|
||||||
expect(screen.getByTestId('cc-focus')).toHaveTextContent('MON')
|
expect(screen.getByTestId('cc-focus')).toHaveTextContent('MON')
|
||||||
|
expect(screen.getByTestId('cc-season-calendar')).toHaveTextContent('Season Calendar')
|
||||||
|
expect(screen.getByTestId('cc-calendar-1229')).toHaveTextContent('R01')
|
||||||
expect(screen.getByText('No live session')).toBeInTheDocument()
|
expect(screen.getByText('No live session')).toBeInTheDocument()
|
||||||
expect(screen.getByTestId('cc-action-race-hub')).toHaveTextContent('Race')
|
expect(screen.getByTestId('cc-action-race-hub')).toHaveTextContent('Race')
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user