diff --git a/CLAUDE.md b/CLAUDE.md index dcbc641..1af74cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,6 +147,20 @@ const league = await db.query.leagues.findFirst({...}); - Check `clerkId` for ownership validation - Admin checks via `isAdmin` boolean on users table +### Scoring Event Dates + +Game/event dates are stored in **two different places** depending on the event type, and any date-based query must account for both: + +1. **`scoringEvents.eventDate`** (`date` column, `YYYY-MM-DD` string) — set directly on the event. Used for non-bracket events (majors, final standings) and sometimes for bracket events when the admin sets a single date for the whole round. + +2. **`playoffMatchGames.scheduledAt`** (`timestamp` column) — set on individual games within a bracket match. Bracket events (e.g. a playoff series) often have `eventDate = null` on the scoring event itself, with each game's exact time stored here instead. + +**Pattern for date-range queries:** Use a two-step approach: +1. `selectDistinct` event IDs via left joins through `playoffMatches → playoffMatchGames`, filtering with `eventDate IN [dates] OR (scheduledAt >= dayStart AND scheduledAt <= dayEnd)` +2. `findMany` on the matched IDs to load full event data with relations + +See `getEventsForDates` and `getUpcomingEventsForDraftedParticipants` in `app/models/scoring-event.ts` for the reference implementation. All timestamp bounds use UTC (`T00:00:00.000Z` / `T23:59:59.999Z`). + ### Draft Logic The draft system implements snake draft: