# Draft Room Implementation Plan ## Overview Build a real-time draft room using Socket.IO for live updates, with snake draft grid, player selection, per-team queue, and chess clock timers. --- ## Phase 1: Database Schema & Models ### 1.1 New Tables **`draft_picks`** ```sql - id (uuid, primary key) - season_id (uuid, references seasons) - team_id (uuid, references teams) - participant_id (uuid, references participants) - pick_number (integer) - overall pick number - round (integer) - pick_in_round (integer) - picked_by_user_id (varchar) - Clerk user ID - picked_by_type (enum: 'owner', 'commissioner', 'auto') - who made the pick - time_used (integer) - seconds used for this pick - created_at (timestamp) ``` **`draft_queue`** ```sql - id (uuid, primary key) - season_id (uuid, references seasons) - team_id (uuid, references teams) - participant_id (uuid, references participants) - queue_position (integer) - created_at (timestamp) - updated_at (timestamp) ``` **`draft_timers`** ```sql - id (uuid, primary key) - season_id (uuid, references seasons) - team_id (uuid, references teams) - time_remaining (integer) - seconds remaining - updated_at (timestamp) ``` ### 1.2 Schema Updates **Update `seasons` table:** ```sql - draft_initial_time (integer) - seconds, default 120 (2 minutes) - draft_increment_time (integer) - seconds, default 30 - current_pick_number (integer) - track current pick - draft_started_at (timestamp) - draft_paused (boolean, default false) ``` ### 1.3 Model Functions - `createDraftPick()` - `getDraftPicks(seasonId)` - `getCurrentPick(seasonId)` - `addToQueue(teamId, participantId)` - `removeFromQueue(queueId)` - `reorderQueue(teamId, participantIds[])` - `getTeamQueue(teamId)` - `updateTeamTimer(teamId, timeRemaining)` - `getTeamTimers(seasonId)` - `initializeDraftTimers(seasonId)` - set initial time for all teams - `autoPickForTeam(teamId)` - pick from queue or top EV --- ## Phase 2: League Settings Updates ### 2.1 Add Timer Configuration to Settings Page - Add fields to "Draft Rounds" card: - Initial Time (seconds) - default 120 - Increment Time (seconds) - default 30 - Update action handler to save these values - Show in league info panel --- ## Phase 3: Socket.IO Setup ### 3.1 Server Setup - Install `socket.io` - Create `server/socket.ts` - Initialize Socket.IO server alongside Remix - Set up draft room namespaces: `draft-${seasonId}` ### 3.2 Socket Events (Server) **Listen for:** - `join-draft` - user joins draft room - `make-pick` - user makes a pick - `add-to-queue` - add player to queue - `remove-from-queue` - remove from queue - `reorder-queue` - reorder queue - `commissioner-force-pick` - commissioner forces pick - `commissioner-start-draft` - start draft - `commissioner-pause-draft` - pause/resume draft **Emit:** - `pick-made` - broadcast new pick to room - `timer-update` - broadcast timer updates (every second) - `current-pick-changed` - notify whose turn it is - `draft-started` - draft has begun - `draft-paused` - draft paused/resumed - `draft-completed` - all picks made - `queue-updated` - queue changed (only to team owner) ### 3.3 Client Hook - Create `hooks/useDraftSocket.ts` - Handle connection/disconnection - Provide methods to emit events - Provide listeners for events --- ## Phase 4: Draft Room Route & Layout ### 4.1 Route Setup - Create `/app/routes/leagues/$leagueId.draft.tsx` - Loader: - Verify user access (team owner or commissioner) - Load season, teams, draft slots, participants - Load existing draft picks - Load user's team queue - Load timer states - No action needed (all via Socket.IO) ### 4.2 Layout Structure ``` ┌─────────────────────────────────────────────────┐ │ Draft Room Header │ │ League Name | Status | Commissioner Controls │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ Draft Grid (horizontally scrollable) │ │ Team 1 Team 2 Team 3 ... │ │ 5:23 4:12 3:45 ... (timers) │ │ ┌────┐ ┌────┐ ┌────┐ │ │ │1.01│ │1.02│ │1.03│ ... │ │ └────┘ └────┘ └────┘ │ │ ┌────┐ ┌────┐ ┌────┐ │ │ │2.03│ │2.02│ │2.01│ ... (snake) │ │ └────┘ └────┘ └────┘ │ └─────────────────────────────────────────────────┘ ┌──────────────────────┬──────────────────────────┐ │ Available Players │ My Queue │ │ [Search: ____] │ 1. Player A │ │ [x] Hide Drafted │ 2. Player B │ │ │ 3. Player C │ │ □ Player 1 - NFL │ [Clear Queue] │ │ EV: 125 │ │ │ □ Player 2 - NBA │ │ │ EV: 118 │ │ │ ⊠ Player 3 - MLB │ │ │ (DRAFTED) │ │ └──────────────────────┴──────────────────────────┘ ``` ### 4.3 No Navigation Bar - Create standalone layout without main nav - Add "Exit Draft Room" button to return to league page --- ## Phase 5: Draft Grid Component ### 5.1 Calculate Snake Order - Use existing draft slots for team order - Generate pick order: - Round 1: slots in order (1→N) - Round 2: slots reversed (N→1) - Round 3: slots in order (1→N) - etc. ### 5.2 Grid Display - Show team names as column headers - Show timer under each team name - Render grid cells with pick numbers (1.01, 1.02, etc.) - Highlight current pick (border, background color) - Show drafted participant in completed picks - Responsive: horizontal scroll on smaller screens ### 5.3 Pick Cell Component ```tsx ``` --- ## Phase 6: Player List Component ### 6.1 Player List Features - Display all participants from season sports - Show: "Participant Name - Sport" - Show expected value - Sort by: EV (desc), then alphabetical - Search/filter by name - Toggle to hide/show drafted players - Click to add to queue (if not drafted) - Visual indicator for drafted players ### 6.2 Player Item Component ```tsx ``` --- ## Phase 7: Queue Component ### 7.1 Queue Features - Show only current user's team queue - Display in order (1, 2, 3, ...) - Drag & drop to reorder - Remove button for each item - "Clear Queue" button - Real-time sync via Socket.IO ### 7.2 Queue Actions - Add player → emit `add-to-queue` → update DB → emit `queue-updated` to team - Remove → emit `remove-from-queue` → update DB → emit `queue-updated` - Reorder → emit `reorder-queue` → update DB → emit `queue-updated` --- ## Phase 8: Timer System ### 8.1 Timer Display - Show under each team name in grid - Format: - "M:SS" for times under 1 hour (e.g., "5:23") - "H:MM:SS" for times 1 hour or more (e.g., "2:15:30") - Color coding: - Green: > 60s - Yellow: 30-60s - Red: < 30s - Flashing red: < 10s ### 8.2 Timer Logic (Server-Side) - When draft starts: initialize all timers with `draft_initial_time` - During a team's turn: - Their timer counts down every second - Other teams' timers remain paused - When pick is made: - Stop countdown for current team - Add `draft_increment_time` to current team's timer - Move to next pick - Start next team's timer countdown - Emit `timer-update` every second to all clients - When timer reaches 0: trigger `autoPickForTeam()` ### 8.3 Auto-Pick Function ```typescript async function autoPickForTeam(teamId: string, seasonId: string) { // 1. Check queue - pick first item // 2. If queue empty, pick highest EV participant not drafted // 3. Create draft pick with picked_by_type='auto' // 4. Emit pick-made event // 5. Move to next pick } ``` --- ## Phase 9: Draft Actions & Permissions ### 9.1 Making a Pick - Only current team owner or commissioner can pick - Click participant in player list (if current turn) - Validate: participant not already drafted - Create draft pick record - Emit `pick-made` to room - Update current pick - Update timers ### 9.2 Commissioner Controls - Start Draft button (if not started) - Pause/Resume button - Force Pick button (for current team) - Show in header, only visible to commissioners ### 9.3 Draft Start Logic - Can be started manually by commissioner - Or auto-start when `draftDateTime` is reached (background job?) - Change season status to "drafting" - Initialize timers - Emit `draft-started` ### 9.4 Draft Completion - When all picks made (picks = teams × rounds): - Change season status to "active" - Emit `draft-completed` - Stop timers --- ## Phase 10: Real-Time Updates ### 10.1 Client State Management - Use React state for: - Draft picks array - Current pick number - Team timers - User's queue - Available participants - Update state when Socket.IO events received ### 10.2 Optimistic Updates - When user makes pick: update UI immediately - If server rejects: revert and show error - For queue operations: update immediately --- ## Phase 11: Mobile Responsiveness ### 11.1 Mobile Layout Adjustments - Draft grid: horizontal scroll - Stack player list and queue vertically - Collapsible sections for player list/queue - Larger touch targets for buttons - Simplified timer display --- ## Implementation Order 1. **Phase 1**: Database schema & migrations 2. **Phase 2**: League settings timer config 3. **Phase 3**: Socket.IO setup (server + client hook) 4. **Phase 4**: Draft room route & basic layout 5. **Phase 5**: Draft grid component (static first) 6. **Phase 6**: Player list component 7. **Phase 7**: Queue component 8. **Phase 8**: Timer system (server-side logic) 9. **Phase 9**: Draft actions & permissions 10. **Phase 10**: Wire up real-time updates 11. **Phase 11**: Mobile polish --- ## Open Questions / Future Considerations - **Background job for auto-start**: How to trigger draft start at `draftDateTime`? Cron job? Polling? - **Timer persistence**: What if server restarts during draft? Store timer state in DB? - **Undo picks**: Not in initial scope, but may want later - **Draft history/log**: Separate view to see all picks in order? - **Notifications**: Alert users when it's their turn? (email, push, etc.) --- ## Requirements Summary ### User Requirements - Route: `/leagues/$leagueId/draft` - Queue: Per-team, private (stored in database) - Timer: Chess clock (initial time + increment per pick) - Real-time: WebSocket (Socket.IO) - Mobile: Should work on mobile - Commissioner: Can force picks - Participant sorting: By EV (desc), then alphabetical - Search: Filter participants by name - Toggle: Show/hide drafted participants - Teams: 6-16 teams typical (12 average) - Timer config: Configurable per league/season - Auto-pick: From queue, or top EV if queue empty - Draft state: "drafting" status - Start: Auto-start at draft date/time, or manual by commissioner - Pause/Resume: Commissioner can pause/resume - Pick log: Track who made each pick (owner/commissioner/auto) - Grid: Horizontally scrollable - Player display: "Player Name - Sport" - Pre-draft: Room viewable before draft starts - Post-draft: Change status to "active" when complete