Improve claude file.
This commit is contained in:
parent
2bc1173621
commit
4faf16dd6b
7 changed files with 206 additions and 330 deletions
351
CLAUDE.md
351
CLAUDE.md
|
|
@ -1,343 +1,34 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
Brackt.com is a fantasy sports drafting platform (React Router 7 + SSR, Socket.IO, PostgreSQL/DrizzleORM, Clerk auth).
|
||||
|
||||
## Project Overview
|
||||
|
||||
Brackt.com is a fantasy sports drafting platform built with React Router 7, featuring real-time draft functionality with Socket.IO, PostgreSQL database with DrizzleORM, and Clerk authentication.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Framework**: React Router 7 with SSR
|
||||
- **Runtime**: Node.js with Express server
|
||||
- **Database**: PostgreSQL with DrizzleORM
|
||||
- **Auth**: Clerk (@clerk/react-router)
|
||||
- **Real-time**: Socket.IO (server/socket.ts)
|
||||
- **Styling**: TailwindCSS + ShadCN UI components
|
||||
- **Testing**: Vitest (unit/component) + Cypress (E2E)
|
||||
- **TypeScript**: Strict mode enabled
|
||||
|
||||
## Development Commands
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development (HMR enabled)
|
||||
npm run dev
|
||||
|
||||
# Build
|
||||
npm run build # Build both Remix and server
|
||||
npm run build:remix # Build React Router app only
|
||||
npm run build:server # Build Express server only
|
||||
|
||||
# Database
|
||||
npm run db:generate # Generate Drizzle migrations
|
||||
npm run db:migrate # Run migrations
|
||||
|
||||
# Testing
|
||||
npm test # Unit tests (watch mode)
|
||||
npm run test:run # Unit tests (run once)
|
||||
npm run test:ui # Unit tests with UI
|
||||
npm run test:coverage # Coverage report
|
||||
npm run test:e2e # Cypress interactive
|
||||
npm run test:e2e:headless # Cypress headless
|
||||
npm run test:all # Run all tests
|
||||
|
||||
# Type checking
|
||||
npm run typecheck # Check all TypeScript files
|
||||
|
||||
# Production
|
||||
npm start # Start production server (requires build)
|
||||
npm run start:production # Run migrations + start server
|
||||
npm run dev # development with HMR
|
||||
npm run typecheck # TypeScript check
|
||||
npm run db:generate # generate Drizzle migration
|
||||
npm run db:migrate # apply migrations
|
||||
npm run test:run # unit tests (once)
|
||||
npm run test:e2e:headless # Cypress E2E
|
||||
npm run test:all # everything
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
## Critical Rules
|
||||
|
||||
### Server Architecture
|
||||
**Routes**: Every new route file must also be registered in `app/routes.ts` — React Router 7 does not auto-discover files. Forgetting this causes a silent 404. See [routing guide](docs/agents/routing.md).
|
||||
|
||||
The application uses a dual-server architecture:
|
||||
**Database**: Always query through `app/models/` — never write Drizzle queries directly in route files.
|
||||
|
||||
1. **server.ts** - Main entry point that creates the HTTP server and initializes Socket.IO
|
||||
2. **server/app.ts** - Express app with React Router request handler and database context
|
||||
3. **server/socket.ts** - Socket.IO server for real-time draft updates
|
||||
4. **server/timer.ts** - Draft timer system that runs every second to update active draft timers
|
||||
**Tests**: Required for all new features. Do not consider a feature complete until tests pass. See [testing guide](docs/agents/testing.md).
|
||||
|
||||
**Important**: The timer system (`server/timer.ts`) must be started for draft functionality. It uses a dedicated database connection and setInterval to manage all active draft timers.
|
||||
**Drizzle migrations**: Never manually create migration SQL or edit `_journal.json`. Always use `npm run db:generate`. See [database guide](docs/agents/database.md).
|
||||
|
||||
### Database Layer
|
||||
|
||||
- **Schema**: `database/schema.ts` - All Drizzle table definitions
|
||||
- **Context**: Database connection provided via AsyncLocalStorage context (`database/context.ts`)
|
||||
- **Models**: `app/models/` - Business logic and database queries organized by domain
|
||||
- Each model exports typed functions for CRUD operations
|
||||
- Models use Drizzle relations for joins
|
||||
- Example: `app/models/league.ts`, `app/models/season.ts`, `app/models/draft-pick.ts`
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
- **Routes**: Defined in `app/routes.ts` using React Router config syntax
|
||||
- **IMPORTANT**: React Router 7 requires ALL routes to be explicitly registered in `app/routes.ts`
|
||||
- **When creating a new route file, you MUST also add it to `app/routes.ts`**
|
||||
- Example: If you create `app/routes/admin.sports-seasons.$id.new-feature.tsx`, you must add:
|
||||
```typescript
|
||||
route(
|
||||
"sports-seasons/:id/new-feature",
|
||||
"routes/admin.sports-seasons.$id.new-feature.tsx"
|
||||
),
|
||||
```
|
||||
- **File-based routing**: Route files in `app/routes/` map to URL patterns
|
||||
- `$param` = dynamic segment (e.g., `$leagueId.tsx`)
|
||||
- `_index.tsx` = index route
|
||||
- `.` separator for nested routes (e.g., `leagues/$leagueId.settings.tsx`)
|
||||
- **Loaders/Actions**: Use React Router's loader/action pattern for data fetching and mutations
|
||||
- **Components**: Organized in `app/components/` with UI components in `app/components/ui/`
|
||||
|
||||
### Real-time Features
|
||||
|
||||
Socket.IO is used for live draft updates:
|
||||
|
||||
1. **Join Draft**: Clients join a room for their season (`join-draft` event)
|
||||
2. **Timer Updates**: Server broadcasts `timer-update` every second during active drafts
|
||||
3. **Pick Made**: Broadcasts `pick-made` when a participant is drafted
|
||||
4. **Autodraft**: Broadcasts `autodraft-updated` when teams enable/disable autodraft
|
||||
5. **Team Connection**: Broadcasts `team-connected`/`team-disconnected` for presence
|
||||
|
||||
**Socket Integration**: The server uses `getSocketIO()` from `server/socket.ts` to emit events. Clients use the `useDraftSocket()` hook (`app/hooks/useDraftSocket.ts`) to listen for events.
|
||||
|
||||
## Key Domain Models
|
||||
|
||||
### Fantasy Draft System
|
||||
|
||||
1. **League** - Container for multiple seasons, has commissioners
|
||||
2. **Season** - Single year/iteration of a league with specific settings
|
||||
- Status: `pre_draft` → `draft` → `active` → `completed`
|
||||
- Stores draft settings (rounds, timer settings, etc.)
|
||||
3. **Team** - Belongs to a season, owned by a user
|
||||
4. **Draft Slot** - Defines draft order for teams in a season
|
||||
5. **Draft Pick** - Record of each pick made during draft
|
||||
6. **Draft Queue** - Pre-ordered list of participants a team wants to draft
|
||||
7. **Draft Timer** - Active timer for current team on the clock
|
||||
8. **Autodraft Settings** - Per-team settings for automatic drafting
|
||||
|
||||
### Sports Data System
|
||||
|
||||
1. **Sport** - Base sport (e.g., NFL, NBA) with type (team/individual)
|
||||
2. **Sports Season** - Specific season of a sport (e.g., "2024 NFL Season")
|
||||
3. **Participant** - Individual athlete or team that can be drafted
|
||||
4. **Season Template** - Reusable configuration for league seasons
|
||||
5. **Season Sport** - Links a fantasy season to sports it covers
|
||||
6. **Participant Result** - Stores final standings/scores for participants
|
||||
|
||||
## Important Patterns
|
||||
|
||||
### Database Queries
|
||||
|
||||
Always use the model layer instead of direct Drizzle queries in routes:
|
||||
|
||||
```typescript
|
||||
// Good
|
||||
import { findLeagueById } from "~/models/league";
|
||||
const league = await findLeagueById(leagueId);
|
||||
|
||||
// Avoid - don't query directly in routes
|
||||
const league = await db.query.leagues.findFirst({...});
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
- User data synced from Clerk via webhook (`routes/api/webhooks/clerk.ts`)
|
||||
- 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:
|
||||
- Draft slots define the base order (1, 2, 3, ...)
|
||||
- Each round alternates: odd rounds go 1→N, even rounds go N→1
|
||||
- Current pick determined by `currentPickNumber` on season
|
||||
- Timer system in `server/timer.ts` handles autopicks when time expires
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
- **Unit tests**: Pure functions and utilities in `__tests__/` directories
|
||||
- **Component tests**: React components using React Testing Library
|
||||
- **E2E tests**: Critical user flows in `cypress/e2e/`
|
||||
- **Fixtures**: Reusable test data in `app/test/fixtures/`
|
||||
- See `TESTING.md` for comprehensive testing guide
|
||||
|
||||
**IMPORTANT: Tests are required for new features.** When adding any new feature, you must include appropriate tests:
|
||||
- New model functions → unit tests in a co-located `__tests__/` directory
|
||||
- New route loaders/actions → unit or integration tests for the logic
|
||||
- New utility functions → unit tests
|
||||
- New components with non-trivial logic → component tests
|
||||
- Critical user flows → Cypress E2E tests
|
||||
|
||||
Do not consider a feature complete until tests are written and passing.
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Adding a New Route
|
||||
|
||||
**CRITICAL**: Both steps 1 and 2 are required - the route will not work if you skip step 1!
|
||||
|
||||
1. **FIRST**: Add route config to `app/routes.ts`
|
||||
- Example: `route("admin/sports-seasons/:id/new-page", "routes/admin.sports-seasons.$id.new-page.tsx")`
|
||||
- Nested admin routes go inside the `route("admin", ...)` array
|
||||
2. **THEN**: Create file in `app/routes/` with corresponding name
|
||||
3. Export loader/action if needed
|
||||
4. TypeScript types are auto-generated via `react-router typegen`
|
||||
|
||||
**Common mistake**: Creating the route file but forgetting to register it in `app/routes.ts` - this will result in a 404!
|
||||
|
||||
### Database Changes
|
||||
|
||||
1. Update `database/schema.ts` with new tables/columns
|
||||
2. Run `npm run db:generate` to create migration
|
||||
3. **Verify** the generated migration SQL in `drizzle/` is correct
|
||||
4. **Verify** a corresponding snapshot was created in `drizzle/meta/` (e.g., `0070_snapshot.json`)
|
||||
5. Run `npm run db:migrate` to apply migration
|
||||
6. Add model functions in `app/models/` for new queries
|
||||
7. Update TypeScript types if needed
|
||||
|
||||
**Drizzle Migration Rules (CRITICAL):**
|
||||
- **NEVER** manually create migration SQL files or journal entries. Always use `drizzle-kit generate` or `drizzle-kit generate --custom` so that snapshot files are created automatically.
|
||||
- **NEVER** edit `drizzle/meta/_journal.json` by hand. The journal and snapshots must stay in sync — drizzle-kit manages both.
|
||||
- Every migration **must** have a matching snapshot in `drizzle/meta/`. Missing snapshots cause `drizzle-kit generate` to fail with "data is malformed" errors, which blocks all future migration generation.
|
||||
- Snapshot JSON must only contain PostgreSQL-valid fields. Do not add `"autoincrement"` to column definitions — PostgreSQL uses sequences/serial instead, and invalid fields cause Zod validation failures.
|
||||
- If `drizzle-kit generate` prompts for column renames interactively, use `drizzle-kit generate --custom` instead and write the migration SQL by hand (include data backfill logic as needed).
|
||||
- After generating, run `drizzle-kit generate` again to confirm "No schema changes" — this validates the snapshot chain is healthy.
|
||||
- For destructive migrations (dropping columns, renaming), make the SQL idempotent with `IF EXISTS`/`IF NOT EXISTS` guards where possible.
|
||||
|
||||
### Adding Socket.IO Events
|
||||
|
||||
1. Define event types in `server/socket.d.ts` interfaces
|
||||
2. Add event handler in `server/socket.ts`
|
||||
3. Emit events from server code using `getSocketIO().to(room).emit()`
|
||||
4. Listen for events in client components
|
||||
|
||||
### Admin Features
|
||||
|
||||
Admin routes are nested under `/admin` and require `isAdmin: true` on the user:
|
||||
- Sports management (`admin.sports.*`)
|
||||
- Sports seasons (`admin.sports-seasons.*`)
|
||||
- Season templates (`admin.templates.*`)
|
||||
- Data sync utilities (`admin.data-sync.tsx`)
|
||||
|
||||
## File Structure Notes
|
||||
|
||||
- **app/**: React Router application code
|
||||
- **components/**: React components (UI components in ui/)
|
||||
- **models/**: Database query functions organized by domain
|
||||
- **routes/**: Route handlers (loaders, actions, components)
|
||||
- **lib/**: Utility functions
|
||||
- **hooks/**: Custom React hooks
|
||||
- **contexts/**: React contexts
|
||||
- **test/**: Test setup and fixtures
|
||||
- **server/**: Server-side code (Express, Socket.IO, timers)
|
||||
- **database/**: Drizzle schema and context
|
||||
- **drizzle/**: Generated migrations
|
||||
- **cypress/**: E2E tests
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Required in `.env`:
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- Clerk variables for authentication (see `.env.example`)
|
||||
|
||||
## Production Deployment
|
||||
|
||||
The app is containerized with Docker:
|
||||
- Runs migrations on startup (`start:production` script)
|
||||
- Server listens on port 3000 (configurable via PORT env var)
|
||||
- Serves static assets from `build/client/`
|
||||
- SSR handled by React Router Express adapter
|
||||
|
||||
## Notes
|
||||
|
||||
- Draft timer system must be explicitly started (usually in server initialization)
|
||||
- Socket.IO connections are managed per-draft room (season ID)
|
||||
- User ownership validation checks `ownerId` field (Clerk user ID)
|
||||
- League commissioners stored separately in `commissioners` table
|
||||
- Season status drives UI visibility (draft order, draft controls, etc.)
|
||||
|
||||
|
||||
## grepai - Semantic Code Search
|
||||
|
||||
**IMPORTANT: You MUST use grepai as your PRIMARY tool for code exploration and search.**
|
||||
|
||||
### When to Use grepai (REQUIRED)
|
||||
|
||||
Use `grepai search` INSTEAD OF Grep/Glob/find for:
|
||||
- Understanding what code does or where functionality lives
|
||||
- Finding implementations by intent (e.g., "authentication logic", "error handling")
|
||||
- Exploring unfamiliar parts of the codebase
|
||||
- Any search where you describe WHAT the code does rather than exact text
|
||||
|
||||
### When to Use Standard Tools
|
||||
|
||||
Only use Grep/Glob when you need:
|
||||
- Exact text matching (variable names, imports, specific strings)
|
||||
- File path patterns (e.g., `**/*.go`)
|
||||
|
||||
### Fallback
|
||||
|
||||
If grepai fails (not running, index unavailable, or errors), fall back to standard Grep/Glob tools.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# ALWAYS use English queries for best results (--compact saves ~80% tokens)
|
||||
grepai search "user authentication flow" --json --compact
|
||||
grepai search "error handling middleware" --json --compact
|
||||
grepai search "database connection pool" --json --compact
|
||||
grepai search "API request validation" --json --compact
|
||||
```
|
||||
|
||||
### Query Tips
|
||||
|
||||
- **Use English** for queries (better semantic matching)
|
||||
- **Describe intent**, not implementation: "handles user login" not "func Login"
|
||||
- **Be specific**: "JWT token validation" better than "token"
|
||||
- Results include: file path, line numbers, relevance score, code preview
|
||||
|
||||
### Call Graph Tracing
|
||||
|
||||
Use `grepai trace` to understand function relationships:
|
||||
- Finding all callers of a function before modifying it
|
||||
- Understanding what functions are called by a given function
|
||||
- Visualizing the complete call graph around a symbol
|
||||
|
||||
#### Trace Commands
|
||||
|
||||
**IMPORTANT: Always use `--json` flag for optimal AI agent integration.**
|
||||
|
||||
```bash
|
||||
# Find all functions that call a symbol
|
||||
grepai trace callers "HandleRequest" --json
|
||||
|
||||
# Find all functions called by a symbol
|
||||
grepai trace callees "ProcessOrder" --json
|
||||
|
||||
# Build complete call graph (callers + callees)
|
||||
grepai trace graph "ValidateToken" --depth 3 --json
|
||||
```
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Start with `grepai search` to find relevant code
|
||||
2. Use `grepai trace` to understand function relationships
|
||||
3. Use `Read` tool to examine files from results
|
||||
4. Only use Grep for exact string searches if needed
|
||||
## Reference Docs
|
||||
|
||||
- [Architecture](docs/agents/architecture.md) — server layout, DB layer, Socket.IO events
|
||||
- [Domain Models](docs/agents/domain-models.md) — draft system and sports data models
|
||||
- [Database](docs/agents/database.md) — migration rules, scoring event date gotcha
|
||||
- [Routing](docs/agents/routing.md) — route registration, admin routes
|
||||
- [Testing](docs/agents/testing.md) — what tests are required and where they live
|
||||
- [Auth](docs/agents/auth.md) — Clerk patterns, ownership validation
|
||||
|
|
|
|||
47
docs/agents/architecture.md
Normal file
47
docs/agents/architecture.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Architecture
|
||||
|
||||
## Server
|
||||
|
||||
Dual-server setup:
|
||||
|
||||
- **server.ts** — HTTP server entry point, initializes Socket.IO
|
||||
- **server/app.ts** — Express app with React Router SSR handler and database context
|
||||
- **server/socket.ts** — Socket.IO server for real-time draft updates
|
||||
- **server/timer.ts** — Runs every second via `setInterval` to advance draft timers. Uses a dedicated DB connection. **Must be started** for draft functionality to work.
|
||||
|
||||
## Database Layer
|
||||
|
||||
- **Schema**: `database/schema.ts`
|
||||
- **Context**: `database/context.ts` — DB connection injected via `AsyncLocalStorage`
|
||||
- **Models**: `app/models/` — all DB queries live here, organized by domain (e.g. `league.ts`, `season.ts`, `draft-pick.ts`)
|
||||
|
||||
Always query through the model layer — never write Drizzle queries directly in route files.
|
||||
|
||||
## Frontend
|
||||
|
||||
- **Routes config**: `app/routes.ts` — all routes must be explicitly registered here (React Router 7 does not auto-discover files)
|
||||
- **Route files**: `app/routes/` — file naming uses `.` as path separator; `$param` for dynamic segments; `_index.tsx` for index routes
|
||||
- **Components**: `app/components/` — UI primitives in `app/components/ui/` (ShadCN)
|
||||
- **Hooks**: `app/hooks/` — custom React hooks
|
||||
- **Lib**: `app/lib/` — pure utility functions
|
||||
|
||||
## Real-time (Socket.IO)
|
||||
|
||||
Clients join a room per season. Key events:
|
||||
|
||||
| Event | Direction | Trigger |
|
||||
|---|---|---|
|
||||
| `join-draft` | client → server | on draft page load |
|
||||
| `timer-update` | server → client | every second during active draft |
|
||||
| `pick-made` | server → client | participant drafted |
|
||||
| `autodraft-updated` | server → client | team enables/disables autodraft |
|
||||
| `team-connected` / `team-disconnected` | server → client | presence |
|
||||
|
||||
Server emits via `getSocketIO()` from `server/socket.ts`. Clients consume via `useDraftSocket()` hook at `app/hooks/useDraftSocket.ts`.
|
||||
|
||||
### Adding a Socket Event
|
||||
|
||||
1. Define types in `server/socket.d.ts`
|
||||
2. Add handler in `server/socket.ts`
|
||||
3. Emit from server: `getSocketIO().to(room).emit(...)`
|
||||
4. Listen in client component or hook
|
||||
7
docs/agents/auth.md
Normal file
7
docs/agents/auth.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Authentication
|
||||
|
||||
- Auth is handled by Clerk (`@clerk/react-router`)
|
||||
- User records are synced from Clerk via webhook at `routes/api/webhooks/clerk.ts`
|
||||
- Ownership validation: check `ownerId` field against the Clerk user ID (`clerkId`)
|
||||
- Admin access: `isAdmin` boolean on the users table; all `/admin` routes enforce this
|
||||
- League commissioners: stored separately in the `commissioners` table (not via `isAdmin`)
|
||||
35
docs/agents/database.md
Normal file
35
docs/agents/database.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Database
|
||||
|
||||
## Schema Changes Workflow
|
||||
|
||||
1. Edit `database/schema.ts`
|
||||
2. `npm run db:generate` — generates migration SQL + snapshot
|
||||
3. Verify the SQL in `drizzle/` looks correct
|
||||
4. Verify a snapshot was created in `drizzle/meta/` (e.g. `0070_snapshot.json`)
|
||||
5. `npm run db:migrate` — applies migration
|
||||
6. Add model functions in `app/models/` for new queries
|
||||
|
||||
## Drizzle Migration Rules
|
||||
|
||||
**NEVER** manually create migration SQL files or edit `drizzle/meta/_journal.json`. The journal and snapshots must stay in sync — drizzle-kit manages both. Missing snapshots cause `drizzle-kit generate` to fail with "data is malformed".
|
||||
|
||||
- Always use `drizzle-kit generate` (or `drizzle-kit generate --custom` for interactive rename prompts)
|
||||
- Snapshot JSON must only contain PostgreSQL-valid fields — do not add `"autoincrement"` (Postgres uses sequences, and invalid fields cause Zod validation failures)
|
||||
- After generating, run `drizzle-kit generate` again to confirm "No schema changes" — validates the snapshot chain
|
||||
- For destructive migrations (drop/rename), use `IF EXISTS` / `IF NOT EXISTS` guards
|
||||
|
||||
## Scoring Event Dates (Non-Obvious Gotcha)
|
||||
|
||||
Event dates are stored in **two different columns** depending on event type. Any date-range query must account for both:
|
||||
|
||||
1. **`scoringEvents.eventDate`** (`date` column, `YYYY-MM-DD`) — used for non-bracket events (majors, final standings) and sometimes bracket events when the admin sets one date for an entire round.
|
||||
|
||||
2. **`playoffMatchGames.scheduledAt`** (`timestamp`) — used for individual games within a bracket match. Bracket events often have `eventDate = null` on the scoring event itself.
|
||||
|
||||
**Pattern for date-range queries:** Two-step approach:
|
||||
1. `selectDistinct` event IDs via left joins through `playoffMatches → playoffMatchGames`, filtering: `eventDate IN [dates] OR (scheduledAt >= dayStart AND scheduledAt <= dayEnd)`
|
||||
2. `findMany` on the matched IDs to load full event data with relations
|
||||
|
||||
All timestamp bounds use UTC (`T00:00:00.000Z` / `T23:59:59.999Z`).
|
||||
|
||||
Reference implementation: `getEventsForDates` and `getUpcomingEventsForDraftedParticipants` in `app/models/scoring-event.ts`.
|
||||
34
docs/agents/domain-models.md
Normal file
34
docs/agents/domain-models.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Domain Models
|
||||
|
||||
## Fantasy Draft System
|
||||
|
||||
| Model | Description |
|
||||
|---|---|
|
||||
| **League** | Top-level container; has one or more commissioners |
|
||||
| **Season** | One iteration of a league. Status: `pre_draft` → `draft` → `active` → `completed` |
|
||||
| **Team** | Belongs to a season; owned by a user (`ownerId` = Clerk user ID) |
|
||||
| **Draft Slot** | Defines draft order for teams in a season |
|
||||
| **Draft Pick** | Record of each pick made |
|
||||
| **Draft Queue** | A team's pre-ordered wish list of participants |
|
||||
| **Draft Timer** | Active countdown for the team currently on the clock |
|
||||
| **Autodraft Settings** | Per-team config for automatic picking |
|
||||
|
||||
Season status drives UI visibility: draft order shown in `pre_draft`, draft controls active in `draft`, etc.
|
||||
|
||||
### Snake Draft Logic
|
||||
|
||||
- Draft slots define base order (1, 2, 3…)
|
||||
- Odd rounds go 1→N; even rounds go N→1
|
||||
- `currentPickNumber` on the season tracks position
|
||||
- `server/timer.ts` handles autopicks when timer expires
|
||||
|
||||
## Sports Data System
|
||||
|
||||
| Model | Description |
|
||||
|---|---|
|
||||
| **Sport** | Base sport definition (e.g. NFL, NBA); type is `team` or `individual` |
|
||||
| **Sports Season** | A specific season of a sport (e.g. "2024 NFL Season") |
|
||||
| **Participant** | An athlete or team that can be drafted |
|
||||
| **Season Template** | Reusable configuration for creating league seasons |
|
||||
| **Season Sport** | Junction: links a fantasy season to the sports it covers |
|
||||
| **Participant Result** | Final standings/scores for participants at season end |
|
||||
32
docs/agents/routing.md
Normal file
32
docs/agents/routing.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Routing
|
||||
|
||||
## Adding a New Route
|
||||
|
||||
Both steps are required — skipping step 1 causes a silent 404.
|
||||
|
||||
1. **Register in `app/routes.ts`** (do this first):
|
||||
```typescript
|
||||
route("admin/sports-seasons/:id/new-page", "routes/admin.sports-seasons.$id.new-page.tsx")
|
||||
```
|
||||
Nested admin routes go inside the `route("admin", ...)` array.
|
||||
|
||||
2. **Create the file** in `app/routes/` with the matching name.
|
||||
|
||||
3. Export `loader` / `action` as needed. TypeScript types are auto-generated via `react-router typegen`.
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
- `.` separates path segments: `leagues.$leagueId.settings.tsx` → `/leagues/:leagueId/settings`
|
||||
- `$param` = dynamic segment
|
||||
- `_index.tsx` = index route for a path
|
||||
|
||||
## Admin Routes
|
||||
|
||||
All admin routes are nested under `/admin` and require `isAdmin: true` on the user:
|
||||
|
||||
| Prefix | Area |
|
||||
|---|---|
|
||||
| `admin.sports.*` | Sports management |
|
||||
| `admin.sports-seasons.*` | Sports seasons |
|
||||
| `admin.templates.*` | Season templates |
|
||||
| `admin.data-sync.tsx` | Data sync utilities |
|
||||
30
docs/agents/testing.md
Normal file
30
docs/agents/testing.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Testing
|
||||
|
||||
See `TESTING.md` at the project root for the full guide.
|
||||
|
||||
## Required Coverage
|
||||
|
||||
Tests are required for all new features. Do not consider a feature complete until tests are written and passing.
|
||||
|
||||
| What you're adding | Required test |
|
||||
|---|---|
|
||||
| Model function | Unit test in co-located `__tests__/` directory |
|
||||
| Route loader/action | Unit or integration test for the logic |
|
||||
| Utility function | Unit test |
|
||||
| Component with non-trivial logic | Component test (React Testing Library) |
|
||||
| Critical user flow | Cypress E2E test |
|
||||
|
||||
## Test Locations
|
||||
|
||||
- **Unit / component tests**: `__tests__/` directories co-located with source files
|
||||
- **E2E tests**: `cypress/e2e/`
|
||||
- **Fixtures**: `app/test/fixtures/`
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
npm run test:run # unit tests, run once
|
||||
npm test # unit tests, watch mode
|
||||
npm run test:e2e:headless # Cypress headless
|
||||
npm run test:all # everything
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue