Compare commits

...

2 commits

Author SHA1 Message Date
Claude
9a04e25c20
Also fetch group stage match times for sport-season event schedule
The previous fix only queried playoffMatchGames.scheduledAt for bracket
events. FIFA World Cup group stage matches live in groupStageMatches
(linked via tournamentGroups), not playoffMatchGames. Now
getUpcomingScoringEvents fetches both tables in parallel and uses
the earliest scheduledAt from either source as earliestGameTime,
matching what UpcomingCalendarPanel already shows on the Upcoming Events page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KiVGo8gSBXe3WuRniVNKsd
2026-06-17 16:57:40 +00:00
Claude
363413cd38
Fix league sport-season page to show correct game times for FIFA World Cup
getUpcomingScoringEvents already fetched per-game scheduledAt times from
playoffMatchGames for sorting, but discarded them from the return value.
EventSchedule was showing eventStartsAt (event-level) instead of the
accurate per-game times. Now getUpcomingScoringEvents includes earliestGameTime
in each returned event, and EventSchedule prefers earliestGameTime over
eventStartsAt — matching how UpcomingCalendarPanel works on the Upcoming Events page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KiVGo8gSBXe3WuRniVNKsd
2026-06-17 16:47:17 +00:00
2 changed files with 60 additions and 29 deletions

View file

@ -10,6 +10,7 @@ interface ScheduleEvent {
name: string;
eventDate: string | null;
eventStartsAt?: Date | string | null;
earliestGameTime?: string | null;
eventType: string;
isComplete: boolean;
completedAt?: Date | string | null;
@ -52,10 +53,13 @@ function getEventTypeLabel(eventType: string): string {
export function EventSchedule({ upcomingEvents, recentEvents, leagueId, sportsSeasonId }: EventScheduleProps) {
const isMounted = useHasMounted();
function getDisplayDate(event: Pick<ScheduleEvent, "eventDate" | "eventStartsAt">): string {
if (isMounted && event.eventStartsAt) {
const d = new Date(event.eventStartsAt as string);
if (!isNaN(d.getTime())) return format(d, "MMM d, yyyy");
function getDisplayDate(event: Pick<ScheduleEvent, "eventDate" | "eventStartsAt" | "earliestGameTime">): string {
if (isMounted) {
const ts = event.earliestGameTime ?? (event.eventStartsAt as string | null | undefined);
if (ts) {
const d = new Date(ts);
if (!isNaN(d.getTime())) return format(d, "MMM d, yyyy");
}
}
return formatEventDate(event.eventDate);
}
@ -109,8 +113,8 @@ export function EventSchedule({ upcomingEvents, recentEvents, leagueId, sportsSe
<p className="text-xs text-muted-foreground flex items-center gap-1 mt-0.5">
<Calendar className="h-3 w-3" />
<span>{getDisplayDate(event)}</span>
{isMounted && event.eventStartsAt && (
<span>· {format(new Date(event.eventStartsAt), "h:mm a")}</span>
{isMounted && (event.earliestGameTime ?? event.eventStartsAt) && (
<span>· {new Date(event.earliestGameTime ?? (event.eventStartsAt as string)).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" })}</span>
)}
</p>
</div>

View file

@ -310,33 +310,54 @@ export async function getUpcomingScoringEvents(
if (events.length === 0) return [];
// Find earliest upcoming game time per event from bracket match games.
// Find earliest upcoming game time per event from both bracket match games
// and group stage matches (FIFA World Cup style).
const eventIds = events.map((e) => e.id);
const now = new Date();
const gameTimeRows = await db
.select({
eventId: schema.scoringEvents.id,
scheduledAt: schema.playoffMatchGames.scheduledAt,
})
.from(schema.scoringEvents)
.innerJoin(
schema.playoffMatches,
eq(schema.playoffMatches.scoringEventId, schema.scoringEvents.id)
)
.innerJoin(
schema.playoffMatchGames,
eq(schema.playoffMatchGames.playoffMatchId, schema.playoffMatches.id)
)
.where(
and(
inArray(schema.scoringEvents.id, eventIds),
isNotNull(schema.playoffMatchGames.scheduledAt),
gte(schema.playoffMatchGames.scheduledAt, now)
const [gameTimeRows, groupStageTimeRows] = await Promise.all([
db
.select({
eventId: schema.scoringEvents.id,
scheduledAt: schema.playoffMatchGames.scheduledAt,
})
.from(schema.scoringEvents)
.innerJoin(
schema.playoffMatches,
eq(schema.playoffMatches.scoringEventId, schema.scoringEvents.id)
)
);
.innerJoin(
schema.playoffMatchGames,
eq(schema.playoffMatchGames.playoffMatchId, schema.playoffMatches.id)
)
.where(
and(
inArray(schema.scoringEvents.id, eventIds),
isNotNull(schema.playoffMatchGames.scheduledAt),
gte(schema.playoffMatchGames.scheduledAt, now)
)
),
db
.select({
eventId: schema.tournamentGroups.scoringEventId,
scheduledAt: schema.groupStageMatches.scheduledAt,
})
.from(schema.groupStageMatches)
.innerJoin(
schema.tournamentGroups,
eq(schema.groupStageMatches.tournamentGroupId, schema.tournamentGroups.id)
)
.where(
and(
inArray(schema.tournamentGroups.scoringEventId, eventIds),
isNotNull(schema.groupStageMatches.scheduledAt),
gte(schema.groupStageMatches.scheduledAt, now)
)
),
]);
const earliestGameById = new Map<string, Date>();
for (const row of gameTimeRows) {
for (const row of [...gameTimeRows, ...groupStageTimeRows]) {
if (!row.scheduledAt) continue;
const prev = earliestGameById.get(row.eventId);
if (!prev || row.scheduledAt < prev) {
@ -353,7 +374,13 @@ export async function getUpcomingScoringEvents(
return candidates.length > 0 ? Math.min(...candidates) : Infinity;
};
return events.toSorted((a, b) => getEffectiveMs(a) - getEffectiveMs(b)).slice(0, limit);
return events
.toSorted((a, b) => getEffectiveMs(a) - getEffectiveMs(b))
.slice(0, limit)
.map((e) => ({
...e,
earliestGameTime: earliestGameById.get(e.id)?.toISOString() ?? null,
}));
}
/**