Show events on multiple dates when games span requested date range (#254)
* Fix admin Games to Score panel missing bracket events The panel was silently dropping bracket events in two cases: 1. When a bracket event had `scoringEvents.eventDate` set to a future round date (e.g. "April 5") but an individual game's `scheduledAt` was today, the old dedup code picked `eventDate` first and the component filter `displayDate === today` never matched. 2. When an event had games on both today and tomorrow, whichever row the unordered step-1 query returned first "won" — potentially hiding a today game from the today tab entirely. Fix: replace the `Map<id, firstDate>` deduplication with `Map<id, Set<date>>` that collects every requested date an event qualifies for (via either `eventDate` or `gameDate`). The return uses `flatMap` to emit one entry per (event, date) pair, so events with games on multiple days now appear correctly in both tabs. https://claude.ai/code/session_01QkFxCF4xmKjdVCNPysWDML * Fix lint errors: replace .sort() with .toSorted() and remove non-null assertion - Replace Array#sort() with Array#toSorted() in two places (unicorn rule) - Remove non-null assertion on Map#get(); use early continue instead https://claude.ai/code/session_01QkFxCF4xmKjdVCNPysWDML --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6df66e9cc6
commit
c5ccee2225
2 changed files with 97 additions and 19 deletions
|
|
@ -270,4 +270,55 @@ describe("getEventsForDates", () => {
|
|||
|
||||
expect(result[0].displayDate).toBe("2026-03-20");
|
||||
});
|
||||
|
||||
it("uses gameDate over eventDate when eventDate is outside requested dates", async () => {
|
||||
// Bracket event: eventDate is the round's overall date (not in range),
|
||||
// but an individual game is scheduled on the requested date.
|
||||
mockSelect.mockReturnValue(
|
||||
makeSelectChain([{ id: "e1", eventDate: "2026-04-05", gameDate: "2026-03-20" }])
|
||||
);
|
||||
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1", eventDate: "2026-04-05" })]);
|
||||
|
||||
const result = await getEventsForDates(["2026-03-20"]);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].displayDate).toBe("2026-03-20");
|
||||
});
|
||||
|
||||
it("returns two entries for an event with games on both requested dates", async () => {
|
||||
// Same event has a game today AND a game tomorrow — should appear in both tabs.
|
||||
mockSelect.mockReturnValue(
|
||||
makeSelectChain([
|
||||
{ id: "e1", eventDate: null, gameDate: "2026-03-20" },
|
||||
{ id: "e1", eventDate: null, gameDate: "2026-03-21" },
|
||||
])
|
||||
);
|
||||
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1", eventDate: null })]);
|
||||
|
||||
const result = await getEventsForDates(["2026-03-20", "2026-03-21"]);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
const displayDates = result.map((e) => e.displayDate).toSorted();
|
||||
expect(displayDates).toEqual(["2026-03-20", "2026-03-21"]);
|
||||
// Both entries share the same event id
|
||||
expect(result[0].id).toBe("e1");
|
||||
expect(result[1].id).toBe("e1");
|
||||
});
|
||||
|
||||
it("deduplicates when multiple matches in same bracket have games on the same day", async () => {
|
||||
// Two rows for the same event on the same gameDate (e.g. two matches in today's bracket)
|
||||
mockSelect.mockReturnValue(
|
||||
makeSelectChain([
|
||||
{ id: "e1", eventDate: null, gameDate: "2026-03-20" },
|
||||
{ id: "e1", eventDate: null, gameDate: "2026-03-20" },
|
||||
])
|
||||
);
|
||||
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1", eventDate: null })]);
|
||||
|
||||
const result = await getEventsForDates(["2026-03-20"]);
|
||||
|
||||
// Should appear only once, not twice
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].displayDate).toBe("2026-03-20");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -665,14 +665,33 @@ export async function getEventsForDates(
|
|||
|
||||
if (rows.length === 0) return [];
|
||||
|
||||
// Deduplicate: keep first matched row per event to derive displayDate.
|
||||
const displayDateMap = new Map<string, string | null>();
|
||||
// Collect ALL dates each event qualifies for. For bracket events, the
|
||||
// game's scheduledAt date (gameDate) takes priority over the event-level
|
||||
// eventDate — the event-level date might be set to the overall round date
|
||||
// (e.g. "April 5") while an individual game is actually scheduled today.
|
||||
// Using a Set per event also de-duplicates naturally when multiple matches
|
||||
// in the same bracket have games on the same day.
|
||||
const eventDatesMap = new Map<string, Set<string>>();
|
||||
for (const row of rows) {
|
||||
if (!displayDateMap.has(row.id)) {
|
||||
displayDateMap.set(row.id, row.eventDate ?? row.gameDate ?? null);
|
||||
if (!eventDatesMap.has(row.id)) {
|
||||
eventDatesMap.set(row.id, new Set());
|
||||
}
|
||||
const dateSet = eventDatesMap.get(row.id);
|
||||
if (!dateSet) continue;
|
||||
if (row.eventDate && dates.includes(row.eventDate)) {
|
||||
dateSet.add(row.eventDate);
|
||||
}
|
||||
if (row.gameDate && dates.includes(row.gameDate)) {
|
||||
dateSet.add(row.gameDate);
|
||||
}
|
||||
// Fallback: row passed the WHERE clause so at least one date is relevant;
|
||||
// if neither mapped to a requested date (shouldn't happen), keep something.
|
||||
if (dateSet.size === 0) {
|
||||
const fallback = row.gameDate ?? row.eventDate ?? null;
|
||||
if (fallback) dateSet.add(fallback);
|
||||
}
|
||||
}
|
||||
const eventIds = [...displayDateMap.keys()];
|
||||
const eventIds = [...eventDatesMap.keys()];
|
||||
|
||||
// Step 2: fetch the matched events with sport season + sport info.
|
||||
const events = await db.query.scoringEvents.findMany({
|
||||
|
|
@ -691,20 +710,28 @@ export async function getEventsForDates(
|
|||
},
|
||||
});
|
||||
|
||||
return events.map((e) => ({
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
eventDate: e.eventDate,
|
||||
displayDate: displayDateMap.get(e.id) ?? e.eventDate ?? null,
|
||||
eventStartsAt: e.eventStartsAt,
|
||||
eventType: e.eventType,
|
||||
isComplete: e.isComplete,
|
||||
completedAt: e.completedAt,
|
||||
sportsSeasonId: e.sportsSeasonId,
|
||||
sportsSeasonName: e.sportsSeason.name,
|
||||
sportName: e.sportsSeason.sport.name,
|
||||
sportSlug: e.sportsSeason.sport.slug,
|
||||
}));
|
||||
// Return one entry per (event, date) pair so that an event with games on
|
||||
// both today and tomorrow appears in both tabs of the dashboard widget.
|
||||
return events.flatMap((e) => {
|
||||
const matchingDates = [...(eventDatesMap.get(e.id) ?? [])].toSorted();
|
||||
const base = {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
eventDate: e.eventDate,
|
||||
eventStartsAt: e.eventStartsAt,
|
||||
eventType: e.eventType,
|
||||
isComplete: e.isComplete,
|
||||
completedAt: e.completedAt,
|
||||
sportsSeasonId: e.sportsSeasonId,
|
||||
sportsSeasonName: e.sportsSeason.name,
|
||||
sportName: e.sportsSeason.sport.name,
|
||||
sportSlug: e.sportsSeason.sport.slug,
|
||||
};
|
||||
if (matchingDates.length === 0) {
|
||||
return [{ ...base, displayDate: e.eventDate ?? null }];
|
||||
}
|
||||
return matchingDates.map((date) => ({ ...base, displayDate: date }));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue