Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
/**
|
|
|
|
|
* Tests for getEventsForDates — the admin dashboard "Games to Score" query.
|
|
|
|
|
*/
|
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
|
|
|
|
|
|
// ── DB mock ──────────────────────────────────────────────────────────────────
|
2026-03-25 20:46:54 -07:00
|
|
|
// select returns a fluent chain; findMany is used in step 2.
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
const mockFindMany = vi.fn();
|
|
|
|
|
|
2026-03-25 20:46:54 -07:00
|
|
|
function makeSelectChain(rows: { id: string; eventDate?: string | null; gameDate?: string | null }[]) {
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
const chain = {
|
|
|
|
|
from: vi.fn().mockReturnThis(),
|
|
|
|
|
leftJoin: vi.fn().mockReturnThis(),
|
|
|
|
|
where: vi.fn().mockResolvedValue(rows),
|
|
|
|
|
};
|
|
|
|
|
return chain;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 20:46:54 -07:00
|
|
|
const mockSelect = vi.fn();
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
|
|
|
|
|
const mockDb = {
|
|
|
|
|
query: {
|
|
|
|
|
scoringEvents: { findMany: mockFindMany },
|
|
|
|
|
},
|
2026-03-25 20:46:54 -07:00
|
|
|
select: mockSelect,
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
vi.mock("~/database/context", () => ({
|
|
|
|
|
database: () => mockDb,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
vi.mock("~/database/schema", () => ({
|
|
|
|
|
scoringEvents: {
|
|
|
|
|
id: "se.id",
|
|
|
|
|
eventDate: "se.event_date",
|
|
|
|
|
eventType: "se.event_type",
|
|
|
|
|
eventStartsAt: "se.event_starts_at",
|
|
|
|
|
createdAt: "se.created_at",
|
|
|
|
|
},
|
|
|
|
|
playoffMatches: {
|
|
|
|
|
scoringEventId: "pm.scoring_event_id",
|
|
|
|
|
id: "pm.id",
|
|
|
|
|
},
|
|
|
|
|
playoffMatchGames: {
|
|
|
|
|
playoffMatchId: "pmg.playoff_match_id",
|
|
|
|
|
scheduledAt: "pmg.scheduled_at",
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
vi.mock("drizzle-orm", () => ({
|
|
|
|
|
eq: (col: unknown, val: unknown) => ({ type: "eq", col, val }),
|
|
|
|
|
and: (...args: unknown[]) => ({ type: "and", args }),
|
|
|
|
|
asc: (col: unknown) => ({ type: "asc", col }),
|
|
|
|
|
desc: (col: unknown) => ({ type: "desc", col }),
|
|
|
|
|
gte: (col: unknown, val: unknown) => ({ type: "gte", col, val }),
|
|
|
|
|
lte: (col: unknown, val: unknown) => ({ type: "lte", col, val }),
|
|
|
|
|
or: (...args: unknown[]) => ({ type: "or", args }),
|
|
|
|
|
inArray: (col: unknown, arr: unknown) => ({ type: "inArray", col, arr }),
|
|
|
|
|
isNotNull: (col: unknown) => ({ type: "isNotNull", col }),
|
2026-03-25 20:46:54 -07:00
|
|
|
sql: (strings: TemplateStringsArray, ...values: unknown[]) => ({ type: "sql", strings, values }),
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
import { getEventsForDates } from "../scoring-event";
|
|
|
|
|
|
|
|
|
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
function makeEventRow(overrides: Partial<{
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
eventDate: string | null;
|
|
|
|
|
eventStartsAt: Date | null;
|
|
|
|
|
eventType: string;
|
|
|
|
|
isComplete: boolean;
|
|
|
|
|
completedAt: Date | null;
|
|
|
|
|
sportsSeasonId: string;
|
|
|
|
|
sportsSeason: { name: string; sport: { name: string; slug: string | null } };
|
|
|
|
|
}> = {}) {
|
|
|
|
|
return {
|
|
|
|
|
id: overrides.id ?? "event-1",
|
|
|
|
|
name: overrides.name ?? "Test Event",
|
|
|
|
|
eventDate: overrides.eventDate ?? "2026-03-20",
|
|
|
|
|
eventStartsAt: overrides.eventStartsAt ?? null,
|
|
|
|
|
eventType: overrides.eventType ?? "playoff_game",
|
|
|
|
|
isComplete: overrides.isComplete ?? false,
|
|
|
|
|
completedAt: overrides.completedAt ?? null,
|
|
|
|
|
sportsSeasonId: overrides.sportsSeasonId ?? "season-1",
|
|
|
|
|
sportsSeason: overrides.sportsSeason ?? {
|
|
|
|
|
name: "2026 NFL Playoffs",
|
|
|
|
|
sport: { name: "NFL", slug: "nfl" },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
describe("getEventsForDates", () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(makeSelectChain([]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("returns empty array and skips DB when no dates given", async () => {
|
|
|
|
|
const result = await getEventsForDates([]);
|
|
|
|
|
expect(result).toEqual([]);
|
2026-03-25 20:46:54 -07:00
|
|
|
expect(mockSelect).not.toHaveBeenCalled();
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
expect(mockFindMany).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-25 20:46:54 -07:00
|
|
|
it("returns empty array when select finds no matching event IDs", async () => {
|
|
|
|
|
mockSelect.mockReturnValue(makeSelectChain([]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result).toEqual([]);
|
|
|
|
|
expect(mockFindMany).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("maps database rows to DashboardScoringEvent shape", async () => {
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e1", eventDate: "2026-03-20", gameDate: null }]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
mockFindMany.mockResolvedValueOnce([
|
|
|
|
|
makeEventRow({
|
|
|
|
|
id: "e1",
|
|
|
|
|
name: "Conference Finals",
|
|
|
|
|
eventDate: "2026-03-20",
|
|
|
|
|
eventType: "playoff_game",
|
|
|
|
|
isComplete: false,
|
|
|
|
|
sportsSeasonId: "ss-1",
|
|
|
|
|
sportsSeason: {
|
|
|
|
|
name: "2026 NBA Playoffs",
|
|
|
|
|
sport: { name: "NBA", slug: "nba" },
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result).toHaveLength(1);
|
|
|
|
|
expect(result[0]).toMatchObject({
|
|
|
|
|
id: "e1",
|
|
|
|
|
name: "Conference Finals",
|
|
|
|
|
eventDate: "2026-03-20",
|
|
|
|
|
eventType: "playoff_game",
|
|
|
|
|
isComplete: false,
|
|
|
|
|
sportsSeasonId: "ss-1",
|
|
|
|
|
sportsSeasonName: "2026 NBA Playoffs",
|
|
|
|
|
sportName: "NBA",
|
|
|
|
|
sportSlug: "nba",
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("returns events for multiple dates", async () => {
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(
|
|
|
|
|
makeSelectChain([{ id: "e1", eventDate: "2026-03-20", gameDate: null }, { id: "e2", eventDate: "2026-03-21", gameDate: null }])
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
);
|
|
|
|
|
mockFindMany.mockResolvedValueOnce([
|
|
|
|
|
makeEventRow({ id: "e1", eventDate: "2026-03-20" }),
|
|
|
|
|
makeEventRow({ id: "e2", eventDate: "2026-03-21" }),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20", "2026-03-21"]);
|
|
|
|
|
|
|
|
|
|
expect(result).toHaveLength(2);
|
|
|
|
|
expect(result.map((e) => e.id)).toEqual(["e1", "e2"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("includes completed events with isComplete: true", async () => {
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e-done", eventDate: "2026-03-20", gameDate: null }]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
mockFindMany.mockResolvedValueOnce([
|
|
|
|
|
makeEventRow({
|
|
|
|
|
id: "e-done",
|
|
|
|
|
isComplete: true,
|
|
|
|
|
completedAt: new Date("2026-03-20T18:00:00Z"),
|
|
|
|
|
}),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result[0].isComplete).toBe(true);
|
|
|
|
|
expect(result[0].completedAt).toBeInstanceOf(Date);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("includes eventStartsAt when present", async () => {
|
|
|
|
|
const starts = new Date("2026-03-20T19:30:00Z");
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e1", eventDate: "2026-03-20", gameDate: null }]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
mockFindMany.mockResolvedValueOnce([
|
|
|
|
|
makeEventRow({ id: "e1", eventStartsAt: starts }),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result[0].eventStartsAt).toEqual(starts);
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-25 20:46:54 -07:00
|
|
|
it("uses two DB calls: select then findMany", async () => {
|
|
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e1", eventDate: "2026-03-20", gameDate: null }]));
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1" })]);
|
|
|
|
|
|
|
|
|
|
await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
2026-03-25 20:46:54 -07:00
|
|
|
expect(mockSelect).toHaveBeenCalledOnce();
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
expect(mockFindMany).toHaveBeenCalledOnce();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("passes event IDs from step 1 into findMany in step 2", async () => {
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(
|
|
|
|
|
makeSelectChain([{ id: "abc", eventDate: "2026-03-20", gameDate: null }, { id: "def", eventDate: "2026-03-20", gameDate: null }])
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
);
|
|
|
|
|
mockFindMany.mockResolvedValueOnce([]);
|
|
|
|
|
|
|
|
|
|
await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
const findManyArgs = mockFindMany.mock.calls[0][0];
|
|
|
|
|
const whereStr = JSON.stringify(findManyArgs.where);
|
|
|
|
|
expect(whereStr).toContain("abc");
|
|
|
|
|
expect(whereStr).toContain("def");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("uses leftJoin so bracket events without playoffMatchGames still qualify via eventDate", async () => {
|
|
|
|
|
const chain = makeSelectChain([]);
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(chain);
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
|
|
|
|
|
await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(chain.leftJoin).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("includes both eventDate and scheduledAt timestamp bounds in the where clause", async () => {
|
|
|
|
|
const chain = makeSelectChain([]);
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(chain);
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
|
|
|
|
|
await getEventsForDates(["2026-03-20", "2026-03-21"]);
|
|
|
|
|
|
|
|
|
|
// The where clause is the first (and only) arg to chain.where
|
|
|
|
|
const whereArg = JSON.stringify((chain.where as ReturnType<typeof vi.fn>).mock.calls[0][0]);
|
|
|
|
|
// eventDate inArray check
|
|
|
|
|
expect(whereArg).toContain("2026-03-20");
|
|
|
|
|
expect(whereArg).toContain("2026-03-21");
|
|
|
|
|
// timestamp bounds for scheduledAt
|
|
|
|
|
expect(whereArg).toContain("T00:00:00.000Z");
|
|
|
|
|
expect(whereArg).toContain("T23:59:59.999Z");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("excludes schedule_event type via the where clause", async () => {
|
|
|
|
|
const chain = makeSelectChain([]);
|
2026-03-25 20:46:54 -07:00
|
|
|
mockSelect.mockReturnValue(chain);
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
|
|
|
|
|
await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
const whereArg = JSON.stringify((chain.where as ReturnType<typeof vi.fn>).mock.calls[0][0]);
|
|
|
|
|
expect(whereArg).toContain("playoff_game");
|
|
|
|
|
expect(whereArg).toContain("major_tournament");
|
|
|
|
|
expect(whereArg).toContain("final_standings");
|
|
|
|
|
expect(whereArg).not.toContain("schedule_event");
|
|
|
|
|
});
|
2026-03-25 20:46:54 -07:00
|
|
|
|
|
|
|
|
it("sets displayDate from eventDate when present", async () => {
|
|
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e1", eventDate: "2026-03-20", gameDate: null }]));
|
|
|
|
|
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1", eventDate: "2026-03-20" })]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result[0].displayDate).toBe("2026-03-20");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sets displayDate from gameDate when eventDate is null", async () => {
|
|
|
|
|
mockSelect.mockReturnValue(makeSelectChain([{ id: "e1", eventDate: null, gameDate: "2026-03-20" }]));
|
|
|
|
|
mockFindMany.mockResolvedValueOnce([makeEventRow({ id: "e1", eventDate: null })]);
|
|
|
|
|
|
|
|
|
|
const result = await getEventsForDates(["2026-03-20"]);
|
|
|
|
|
|
|
|
|
|
expect(result[0].displayDate).toBe("2026-03-20");
|
|
|
|
|
});
|
Add "Games to Score" widget to admin dashboard (#183)
* Add "Games to Score" widget to admin dashboard
Shows scoring events for today and tomorrow in a tabbed card, with
status badges (Pending/Scored), a scored/total progress counter, and
direct "Score" / "View" links to each event's results page. Excludes
non-scoring schedule_event types so only actionable events surface.
- app/models/scoring-event.ts: add getEventsForDates() model function
that queries scoringEvents joined with sportsSeasons/sport for a
given list of date strings
- app/routes/admin._index.tsx: call getEventsForDates([today, tomorrow])
in the loader and render the GamesToScore component below the stats row
- app/models/__tests__/scoring-event-dashboard.test.ts: 8 unit tests
covering empty input, shape mapping, multi-date filtering, completed
events, and the inArray where conditions
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Fix getEventsForDates to include bracket events by game scheduledAt
The previous implementation only matched on scoringEvents.eventDate,
which is often null for bracket events where individual game times are
set on playoffMatchGames.scheduledAt instead.
Switched to a two-step query mirroring getUpcomingEventsForDraftedParticipants:
1. selectDistinct event IDs via left joins through playoffMatches →
playoffMatchGames, matching where eventDate IN dates OR any game's
scheduledAt falls within the date range's timestamp bounds
2. findMany on the matched IDs with sportsSeason + sport relations
Updated tests to cover the two-step flow, leftJoin usage, and the
scheduledAt timestamp bounds appearing in the where clause.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
* Document dual game date storage in CLAUDE.md
scoringEvents.eventDate and playoffMatchGames.scheduledAt both hold game
dates depending on the event type. Added a dedicated section under
Important Patterns explaining when each is used and the two-step
selectDistinct + findMany pattern required for correct date queries.
https://claude.ai/code/session_014zKK15o5RJfTaodLvnZpF5
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-19 19:25:37 -07:00
|
|
|
});
|