claude/cool-lamport-etpwsi (#96)
Co-authored-by: Claude <noreply@anthropic.com> Reviewed-on: #96
This commit is contained in:
parent
08ae2bb88a
commit
496ecba464
3 changed files with 70 additions and 31 deletions
|
|
@ -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,11 +53,14 @@ 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);
|
||||
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,9 +113,13 @@ 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 && (() => {
|
||||
const ts = event.earliestGameTime ?? (event.eventStartsAt as string | null | undefined);
|
||||
if (!ts) return null;
|
||||
const d = new Date(ts);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
return <span>· {d.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" })}</span>;
|
||||
})()}
|
||||
</p>
|
||||
</div>
|
||||
{event.eventType === "schedule_event" ? (
|
||||
|
|
|
|||
|
|
@ -310,10 +310,13 @@ 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
|
||||
|
||||
const [gameTimeRows, groupStageTimeRows] = await Promise.all([
|
||||
db
|
||||
.select({
|
||||
eventId: schema.scoringEvents.id,
|
||||
scheduledAt: schema.playoffMatchGames.scheduledAt,
|
||||
|
|
@ -333,10 +336,28 @@ export async function getUpcomingScoringEvents(
|
|||
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,15 @@ 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()
|
||||
?? e.eventStartsAt?.toISOString()
|
||||
?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -886,7 +886,9 @@ export const tournamentGroups = pgTable("tournament_groups", {
|
|||
.references(() => scoringEvents.id, { onDelete: "cascade" }),
|
||||
groupName: varchar("group_name", { length: 10 }).notNull(), // "A" through "L"
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
});
|
||||
}, (t) => [
|
||||
index("tournament_groups_scoring_event_id_idx").on(t.scoringEventId),
|
||||
]);
|
||||
|
||||
export const tournamentGroupMembers = pgTable("tournament_group_members", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue