- Add player_positions JSONB column to roster_links (migration 006)
- Add player_data JSONB column to cache name/image/headshot (migration 007)
- Add is_pitcher/is_batter computed properties for two-way player support
- Update lineup submission to populate RosterLink with all players + positions
- Update get_bench handler to use cached data (no runtime API calls)
- Add BenchPlayer type to frontend with proper filtering
- Add new Lineup components: InlineSubstitutionPanel, LineupSlotRow,
PositionSelector, UnifiedLineupTab
- Add integration tests for get_bench_players
Bench players now load instantly without API dependency, and properly
filter batters vs pitchers (including CP closer position).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Refactor game page into tab container with Game, Lineups, Stats tabs
- Extract GamePlay, LineupBuilder, GameStats components from page
- Add manager-aware default tab logic (pending + manager → Lineups tab)
- Implement per-team lineup submission (each team submits independently)
- Add lineup-status endpoint with actual lineup data for recovery
- Fix lineup persistence across tab switches and page refreshes
- Query database directly for pending games (avoids GameState validation)
- Add userTeamIds to auth store for manager detection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Frontend UX improvements:
- Single-click Discord OAuth from home page (no intermediate /auth page)
- Auto-redirect authenticated users from home to /games
- Fixed Nuxt layout system - app.vue now wraps NuxtPage with NuxtLayout
- Games page now has proper card container with shadow/border styling
- Layout header includes working logout with API cookie clearing
Games list enhancements:
- Display team names (lname) instead of just team IDs
- Show current score for each team
- Show inning indicator (Top/Bot X) for active games
- Responsive header with wrapped buttons on mobile
Backend improvements:
- Added team caching to SbaApiClient (1-hour TTL)
- Enhanced GameListItem with team names, scores, inning data
- Games endpoint now enriches response with SBA API team data
Docker optimizations:
- Optimized Dockerfile using --chown flag on COPY (faster than chown -R)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced O(n²) deduplication check per play with O(1) array replacement:
- Added setPlayHistory() for game_state_sync (replaces entire array)
- Simplified addPlayToHistory() for live play_resolved events (just push)
This separates sync operations (replace) from live events (append),
eliminating the need for deduplication checks during gameplay.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend changes:
- Modified request_game_state handler to fetch plays from database
- Convert Play DB models to frontend-compatible PlayResult dicts
- Emit game_state_sync event with state + recent_plays array
Frontend changes:
- Added deduplication by play_number in addPlayToHistory()
- Prevents duplicate plays when game_state_sync is received
Field mapping from Play model:
- hit_type -> outcome
- result_description -> description
- batter_id -> batter_lineup_id
- batter_final -> batter_result
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auth Improvements:
- auth.ts: Enhanced authentication store with better error handling
- auth.ts middleware: Improved redirect logic
- login.vue: Better login flow and error display
Game Display:
- PlayByPlay.vue: Enhanced play-by-play feed with player name display
- game.ts types: Additional type definitions for runner advancement
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Frontend alignment with backend WebSocket protocol:
**Type System Fixes**:
- types/game.ts: Changed DecisionPhase to use 'awaiting_*' convention matching backend
- types/game.ts: Fixed PlayOutcome enum values to match backend string values (e.g., 'strikeout' not 'STRIKEOUT')
- types/game.ts: Added comprehensive play outcome types (groundball_a/b/c, flyout variants, x_check)
**Decision Detection**:
- store/game.ts: Updated decision detection to check both decision prompt AND gameState.decision_phase
- components: Updated all decision phase checks to use 'awaiting_defensive', 'awaiting_offensive', 'awaiting_stolen_base'
**WebSocket Enhancements**:
- useWebSocket.ts: Added game_joined event handler with success toast
- useWebSocket.ts: Fixed dice roll data - now receives d6_two_a and d6_two_b from server
- useWebSocket.ts: Request fresh game state after decision submissions to sync decision_phase
**New Constants**:
- constants/outcomes.ts: Created centralized PlayOutcome enum with display labels and descriptions
**Testing**:
- Updated test expectations for new decision phase naming
- All component tests passing
**Why**:
Eliminates confusion from dual naming conventions. Frontend now uses same vocabulary as backend.
Fixes runtime type errors from enum value mismatches.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend fixes:
- state_manager: Properly recover current_pitcher and current_catcher from
fielding team during game state recovery (fixes pitcher badge not showing)
- handlers: Add headshot field to lineup data, use lineup_service for proper
player data loading on cache miss
- lineup_service: Minor adjustments for headshot support
Frontend fixes:
- player.ts: Update Lineup type to match WebSocket event format
- lineup_id (was 'id'), card_id fields
- player.headshot for UI circles
- Optional fields for event variations
- CurrentSituation.vue: Adapt to updated type structure
- Substitution selectors: Use updated Lineup type fields
This fixes the issue where pitcher badge wouldn't show after game recovery
because current_pitcher was being set from batting team instead of fielding team.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>