- Added loader and action functions for managing expected values in sports seasons. - Implemented UI for recalculating probabilities based on participant results. - Created a service to update probabilities after results are finalized, including handling finished and unfinished participants. - Developed tests for the probability updater service to ensure correct functionality. - Introduced a preview feature to show potential changes before applying updates.
239 lines
No EOL
9.3 KiB
Markdown
239 lines
No EOL
9.3 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## 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
|
|
|
|
```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
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
### Server Architecture
|
|
|
|
The application uses a dual-server architecture:
|
|
|
|
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
|
|
|
|
**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.
|
|
|
|
### 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 `useSocket()` hook (if implemented) 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
|
|
|
|
### 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
|
|
|
|
## 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. Run `npm run db:migrate` to apply migration
|
|
4. Add model functions in `app/models/` for new queries
|
|
5. Update TypeScript types if needed
|
|
|
|
### 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.) |