brackt/plans/completed/draft-room-redesign.md
Chris Parsons 35fe84a1dd
Update claude.md to push more tests. (#84)
* Update claude.md to push more tests.

* Archiving old plans.

* fix: run DB migrations programmatically on server startup

Replace unreliable drizzle-kit CLI migration with drizzle-orm's built-in
migrator running in the server process before accepting connections. This
ensures migrations are always applied on deploy and fails fast if they error.

- Add runMigrations() to server.ts using drizzle-orm/postgres-js/migrator
- Skip migrations in development (handled manually via db:migrate)
- Add DATABASE_URL guard with a clear error message
- Remove start:production script (now identical to start)
- Update Dockerfile CMD to use npm run start

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 22:31:04 -08:00

11 KiB
Raw Permalink Blame History

Draft Room Redesign Plan

Status: Complete - All Features Implemented & Tested Created: 2025-10-24 Last Updated: 2025-10-24

Overview

Redesigning the draft room to use a full-screen layout with a collapsible left sidebar and tabbed main content area.


Requirements

Layout Changes

1. Full-Screen Layout (No Page Scroll)

  • Page fills entire viewport (100vh)
  • Individual sections scroll internally
  • Fixed header at top
  • Content area uses remaining height

2. Collapsible Left Sidebar

  • Default State: Expanded
  • Width:
    • Expanded: 350px
    • Collapsed: 50px
  • Contents (Accordion-style sections):
    • My Queue (top section) - Collapsible
      • Eligibility status banner
      • Queue list (ordered, scrollable)
      • Autodraft settings
      • Clear queue button
    • Available Participants (bottom section) - Collapsible
      • Search bar
      • Sport filter dropdown
      • Show/hide drafted toggle
      • Participants table (scrollable)
      • Action buttons (Pick/Add to Queue)
  • Section Behavior:
    • Both sections can be expanded or collapsed independently
    • When both expanded: 50/50 vertical split
    • When one collapsed: Expanded section takes 100% of space
    • At least one section must remain expanded
  • Responsive:
    • Desktop (≥1024px): Fixed sidebar on left
    • Mobile (<1024px): Full-screen modal

3. Tabbed Main Content Area

Three tabs with toggle functionality:

Tab 1: Draft Board (Default)

  • Current draft grid display
  • Team headers with timers and autodraft status
  • Snake draft grid (scrollable)
  • Context menu for commissioners
  • Reuse existing DraftGrid component or inline implementation

Tab 2: Recent Picks

  • List of recent picks (latest 10-20)
  • Same format as current Recent Picks section
  • Scrollable list
  • Shows: Pick number, team, participant, sport

Tab 3: Teams Drafted (NEW)

  • Grid layout:
    • Rows: Sports (from season sports)
    • Columns: Teams (in draft order)
    • Cells: Participants drafted by that team in that sport
  • Highlighting: Visual indicator when team has multiple participants in same sport
  • Flex Display: Show "X of Y flexes used" somewhere with team data
  • Scrollable: Both horizontal and vertical as needed

Design Decisions

Teams Drafted Grid:

  1. Cell content: Show participant names (e.g., "Patrick Mahomes, Travis Kelce")
  2. Cell interactivity: No clickability needed (existing commissioner context menu on draft board remains)
  3. Empty cells: Leave empty (just border, no text)
  4. Flex display: In team column header, below team name (similar to timer positioning)

Tab UI: 5. Tab style: ShadCN Tabs component 6. Recent Picks data: Same 10 most recent picks as current implementation

Sidebar: 7. Section sizing: Accordion-style - both sections collapsible independently

  • Both expanded: 50/50 split
  • One collapsed: Expanded section takes 100%
  • At least one must remain expanded

Current Implementation Analysis

File Structure

Main Route: /app/routes/leagues/$leagueId.draft.$seasonId.tsx

  • All draft room logic currently in this single file
  • ~1,300 lines of code
  • Inline rendering of Queue and Available Participants (not extracted components)

Related Components:

  • /app/components/DraftGrid.tsx - Reusable draft grid (not currently used in draft room)
  • /app/components/AutodraftSettings.tsx - Autodraft toggle and settings

Current Layout:

Header (full width, fixed)
├─ Title and metadata
├─ Control buttons (start/pause/resume)
└─ Connection status

Draft Grid (full width, scrollable)
├─ Team headers
└─ Pick grid

3-Column Grid (lg:grid-cols-3)
├─ My Queue (conditional if user has team)
│  ├─ Eligibility status
│  ├─ Queue list (max-h-96)
│  ├─ Autodraft settings
│  └─ Clear button
├─ Recent Picks
│  └─ Last 10 picks (max-h-96)
└─ Available Participants
   ├─ Search and filters
   └─ Participants table (max-h-[500px])

State Management

Component state (useState):

  • picks - All draft picks
  • currentPick - Current pick number
  • searchQuery - Participant search term
  • hideDrafted - Toggle drafted visibility
  • sportFilter - Active sport filter
  • queue - User's queue items
  • teamTimers - Timer for each team
  • autodraftStatus - Autodraft state per team
  • connectedTeams - Set of connected team IDs
  • forcePickDialogOpen - Commissioner dialog state

Real-time updates via Socket.IO:

  • pick-made
  • timer-update
  • draft-paused / draft-resumed
  • draft-completed
  • autodraft-updated
  • team-connected / team-disconnected
  • participant-removed-from-queues

Data Flow

  • Loader provides initial data
  • Socket.IO provides real-time updates
  • Form actions handle queue management and draft actions
  • Draft eligibility calculated client-side via calculateDraftEligibility()

Proposed Architecture

New Component Structure

leagues/$leagueId.draft.$seasonId.tsx (Main Route)
├─ Full-screen flex container (100vh)
├─ Header (fixed, flex: 0 0 auto)
├─ Content Area (flex: 1 1 auto, flex row)
│  ├─ DraftSidebar (NEW component)
│  │  ├─ Collapse toggle button
│  │  ├─ QueueSection (scrollable)
│  │  └─ AvailableParticipantsSection (scrollable)
│  └─ MainContent (flex: 1 1 auto)
│     ├─ TabSelector (Draft Board | Recent Picks | Teams Drafted)
│     └─ TabContent (scrollable)
│        ├─ DraftBoardTab (uses DraftGrid component)
│        ├─ RecentPicksTab
│        └─ TeamsDraftedTab (NEW)

Files to Create

  1. /app/components/DraftSidebar.tsx (NEW)

    • Collapsible sidebar container
    • Props: collapsed state, queue data, participants data, handlers
    • Two internal sections with separate scrolling
  2. /app/components/draft/QueueSection.tsx (NEW, optional)

    • Extracted queue rendering logic
    • Props: queue, eligibility, autodraft settings, handlers
  3. /app/components/draft/AvailableParticipantsSection.tsx (NEW, optional)

    • Extracted participants table logic
    • Props: participants, filters, handlers
  4. /app/components/draft/TeamsDraftedGrid.tsx (NEW)

    • Grid showing teams × sports with drafted participants
    • Props: teams, sports, picks, flex usage
    • Highlighting logic for multiple drafts per sport
  5. /app/components/draft/DraftTabs.tsx (NEW, optional)

    • Tab selector and content container
    • Props: active tab, tab change handler, children

Files to Modify

  1. /app/routes/leagues/$leagueId.draft.$seasonId.tsx

    • Add full-screen layout container
    • Add sidebar collapsed state
    • Add active tab state
    • Import and render DraftSidebar
    • Render tabbed main content
    • Remove 3-column grid layout
    • Remove Recent Picks section (moved to tab)
  2. /app/components/DraftGrid.tsx

    • Ensure it works in flex container
    • May need height/overflow adjustments

State Additions

Add to main route component:

const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
  // Check localStorage for user preference
  const stored = localStorage.getItem('draftSidebarCollapsed');
  return stored ? JSON.parse(stored) : false; // Default: expanded
});

const [activeTab, setActiveTab] = useState<'board' | 'picks' | 'teams'>('board');

CSS/Styling Strategy

Layout CSS

/* Main container */
.draft-room-container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Header */
.draft-room-header {
  flex: 0 0 auto;
}

/* Content area */
.draft-room-content {
  flex: 1 1 auto;
  display: flex;
  overflow: hidden;
}

/* Sidebar */
.draft-sidebar {
  flex: 0 0 auto;
  width: 350px; /* Expanded */
  transition: width 0.3s ease, transform 0.3s ease;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.draft-sidebar.collapsed {
  width: 50px;
}

/* Sidebar sections */
.sidebar-section {
  flex: 0 0 auto; /* Queue: take what you need */
  overflow-y: auto;
}

.sidebar-section.grow {
  flex: 1 1 auto; /* Available Participants: take remaining */
}

/* Main content */
.draft-main-content {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

/* Tab content */
.draft-tab-content {
  flex: 1 1 auto;
  overflow-y: auto;
}

Responsive Breakpoints

/* Mobile (<1024px) */
@media (max-width: 1023px) {
  .draft-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    z-index: 50;
    transform: translateX(-100%);
  }

  .draft-sidebar.open {
    transform: translateX(0);
  }
}

Teams Drafted Grid Styling

.teams-grid {
  display: grid;
  grid-template-columns: 150px repeat(auto-fit, minmax(120px, 1fr));
  overflow: auto;
}

.teams-grid-cell {
  border: 1px solid var(--border);
  padding: 0.5rem;
  min-height: 60px;
}

.teams-grid-cell.multiple-picks {
  background-color: var(--accent);
  font-weight: 600;
}

Implementation Steps

Phase 1: Setup and Layout Structure

  • Create plans directory and documentation
  • Add sidebar collapsed state with localStorage persistence
  • Add active tab state
  • Create full-screen layout container in main route
  • Update header to work with new layout
  • Test basic layout structure

Phase 2: Sidebar Component

  • Create DraftSidebar.tsx component shell
  • Add collapse/expand toggle button
  • Move Queue section rendering to sidebar
  • Move Available Participants section to sidebar
  • Add internal scrolling for both sections
  • Test sidebar functionality and transitions
  • Add mobile modal behavior

Phase 3: Tab System

  • Create tab selector UI (choose style based on user feedback)
  • Add Draft Board tab (use existing grid)
  • Add Recent Picks tab (move existing section)
  • Create TeamsDraftedGrid component
  • Implement Teams Drafted grid logic
  • Add flex usage display
  • Add highlighting for multiple picks
  • Test tab switching and content rendering

Phase 4: Refinement

  • Adjust spacing and padding throughout
  • Test all Socket.IO real-time updates
  • Test all form actions (queue management, making picks)
  • Test responsive behavior (desktop, tablet, mobile)
  • Add keyboard shortcuts (optional)
  • Performance testing with large datasets
  • Accessibility audit (ARIA labels, focus management)

Phase 5: Polish

  • Animation tuning
  • Dark mode verification
  • Cross-browser testing
  • User acceptance testing

Notes

  • Preserve all existing Socket.IO functionality
  • Maintain commissioner-specific features (force pick, context menus)
  • Keep eligibility calculation logic unchanged
  • Ensure autodraft integration remains functional
  • Test with various screen sizes and data volumes

Success Criteria

  • Draft room fills viewport (no page scroll)
  • Sidebar collapses/expands smoothly
  • All three tabs render correctly
  • Teams Drafted grid shows accurate data
  • Queue and Available Participants work as before
  • All real-time updates continue to work
  • Mobile experience is usable
  • No regressions in existing functionality
  • Performance is acceptable (no lag during picks/updates)