# SBA Frontend - Long-Term Vision & Implementation Roadmap **Project**: Paper Dynasty Real-Time Game Engine - SBA Frontend **Framework**: Vue 3 + Nuxt 3 + TypeScript **Status**: Planning Phase **Created**: 2025-01-10 **Backend Status**: Phase 3 Complete (730/731 tests passing, all WebSocket handlers implemented) --- ## Executive Summary The SBA (Strat-O-Matic Baseball Association) frontend is a **mobile-first, real-time web application** that connects to our production-ready FastAPI backend via WebSockets and REST APIs. With the backend fully operational (15 WebSocket event handlers, complete game engine, 99.9% test coverage), we're ready to build the user-facing interface that brings Paper Dynasty baseball to life. **Key Objectives**: - Replace legacy Google Sheets system with modern web UI - Deliver seamless real-time multiplayer baseball gameplay - Prioritize mobile experience (60%+ target usage) - Support live, async, and AI game modes - Enable spectator viewing of active games - Achieve sub-500ms action latency **Success Metrics**: - 90% player migration within 1 month of launch - 60%+ games played on mobile devices - < 500ms average action latency - System usability scale (SUS) score > 75 - Zero critical bugs requiring rollback --- ## Current State Assessment ### βœ… What We Have **Backend (Production-Ready)**: - Complete game engine with all strategic decisions - 15 WebSocket event handlers (defensive, offensive, substitutions) - Manual outcome workflow (dice rolling + card reading) - Position ratings integration (PD league) - Box score statistics (materialized views) - 730/731 tests passing (99.9% coverage) - PostgreSQL persistence with async operations - Discord OAuth integration ready **Frontend (Basic Skeleton)**: - Nuxt 3 project initialized - Dependencies installed (Pinia, Tailwind, Socket.io, Axios) - Basic configuration (runtime config, TypeScript strict mode) - Tailwind CSS configured - Development environment ready ### πŸ”² What We Need to Build **Core Application** (Phases F1-F4): - Authentication flow (Discord OAuth) - WebSocket connection management - Game state synchronization - Real-time game interface - Decision input workflows - Mobile-optimized UI components - Type definitions matching backend models **Advanced Features** (Phases F5-F6): - Spectator mode - Game history and replay - Polish and animations - Performance optimization --- ## Technical Architecture ### Frontend Stack ``` SBA Frontend (Vue 3 + Nuxt 3) β”œβ”€β”€ Runtime Environment β”‚ β”œβ”€β”€ Nuxt 3 (latest) - Meta framework with SSR/SPA β”‚ β”œβ”€β”€ Vue 3 Composition API - Reactive UI layer β”‚ └── TypeScript (strict mode) - Type safety β”‚ β”œβ”€β”€ State Management β”‚ β”œβ”€β”€ Pinia - Application state stores β”‚ β”œβ”€β”€ Composables - Reusable logic hooks β”‚ └── WebSocket sync - Real-time state updates β”‚ β”œβ”€β”€ Communication Layer β”‚ β”œβ”€β”€ Socket.io-client - WebSocket for real-time events β”‚ β”œβ”€β”€ Axios - REST API calls (game creation, history) β”‚ └── Discord OAuth - Authentication flow β”‚ β”œβ”€β”€ UI/UX Layer β”‚ β”œβ”€β”€ Tailwind CSS - Utility-first styling β”‚ β”œβ”€β”€ Mobile-first design - Portrait optimization β”‚ β”œβ”€β”€ Touch gestures - Swipe navigation β”‚ └── Responsive breakpoints - xs/sm/md/lg/xl β”‚ └── Development Tools β”œβ”€β”€ Vue DevTools - Component inspection β”œβ”€β”€ Nuxt DevTools - Framework debugging β”œβ”€β”€ ESLint + Prettier - Code quality └── TypeScript compiler - Type checking ``` ### Data Flow Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SBA Frontend (Nuxt 3) β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Pages │───▢│ Composables │───▢│ Pinia Stores β”‚ β”‚ β”‚ β”‚ (Routes) β”‚ β”‚ (Logic) β”‚ β”‚ (State) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ └─────────▢│ Components β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ (UI/UX) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WebSocket API β”‚ β”‚ REST API β”‚ β”‚ (Socket.io) β”‚ β”‚ (FastAPI) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Game Backend (FastAPI) β”‚ β”‚ β”‚ β”‚ β€’ Game Engine (in-memory) β”‚ β”‚ β€’ PostgreSQL (persistence) β”‚ β”‚ β€’ WebSocket Handlers (15) β”‚ β”‚ β€’ Discord OAuth β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### WebSocket Event Flow **Client β†’ Server (Actions)**: ```typescript // Defensive decisions socket.emit('set_defense', { game_id, positioning }) socket.emit('set_pitcher_focus', { game_id, focus }) // Offensive decisions socket.emit('set_stolen_base_attempt', { game_id, runner, attempt }) socket.emit('set_offensive_approach', { game_id, approach }) // Manual workflow socket.emit('request_manual_roll', { game_id }) socket.emit('submit_manual_outcome', { game_id, outcome_data }) // Substitutions socket.emit('substitute_player', { game_id, ...sub_data }) socket.emit('change_pitcher', { game_id, new_pitcher_id }) ``` **Server β†’ Client (Updates)**: ```typescript // State updates socket.on('game_state_update', (state: GameState) => {}) socket.on('decision_required', (decision: DecisionPrompt) => {}) socket.on('play_completed', (play: PlayResult) => {}) // Manual workflow socket.on('manual_roll_ready', (roll_data: RollData) => {}) socket.on('awaiting_outcome_input', (prompt: OutcomePrompt) => {}) // Game events socket.on('inning_change', (inning: number, half: string) => {}) socket.on('game_ended', (result: GameResult) => {}) // Errors socket.on('invalid_action', (error: ActionError) => {}) socket.on('connection_error', (error: ConnectionError) => {}) ``` --- ## Implementation Phases ### Phase F1: Core Infrastructure (Week 1) **Goal**: Establish foundation for all future development **Deliverables**: - [ ] Project structure (pages, components, composables, stores) - [ ] TypeScript type definitions (game models, API responses) - [ ] Discord OAuth authentication flow - [ ] WebSocket connection management composable - [ ] REST API client utility (Axios wrapper) - [ ] Pinia stores (auth, game, ui) - [ ] Basic routing (home, login, game view) - [ ] Environment configuration (.env setup) **Key Files**: ``` types/ β”œβ”€β”€ game.ts # GameState, PlayResult, etc. β”œβ”€β”€ player.ts # SbaPlayer, Lineup β”œβ”€β”€ api.ts # API request/response types └── websocket.ts # Socket event types composables/ β”œβ”€β”€ useAuth.ts # Auth state & Discord OAuth β”œβ”€β”€ useWebSocket.ts # Socket connection management β”œβ”€β”€ useGameState.ts # Game state synchronization └── useApi.ts # REST API calls store/ β”œβ”€β”€ auth.ts # Authentication state β”œβ”€β”€ game.ts # Active game state └── ui.ts # UI state (modals, toasts) pages/ β”œβ”€β”€ index.vue # Landing/dashboard β”œβ”€β”€ auth/ β”‚ β”œβ”€β”€ login.vue # Login page β”‚ └── callback.vue # OAuth callback handler └── games/ └── [id].vue # Game view (placeholder) ``` **Acceptance Criteria**: - User can log in via Discord OAuth - WebSocket connects successfully to backend - TypeScript compilation succeeds with no errors - All stores properly typed and functional - Environment variables loaded correctly - **Unit tests written and passing (20+ tests for composables and stores)** --- ### Phase F2: Game State Display (Week 2) **Goal**: Display real-time game state with proper UI components **Deliverables**: - [ ] Game board component (baseball diamond) - [ ] Scoreboard component (score, inning, count) - [ ] Current situation display (batter, pitcher, runners) - [ ] Play-by-play feed component - [ ] Game state synchronization logic - [ ] Real-time WebSocket updates - [ ] Mobile-responsive layout - [ ] Loading and error states - [ ] **Unit tests for all components and state sync logic** **Key Components**: ``` components/Game/ β”œβ”€β”€ GameBoard.vue # Baseball diamond visualization β”œβ”€β”€ ScoreBoard.vue # Score + inning + count β”œβ”€β”€ CurrentBatter.vue # Batter card display β”œβ”€β”€ CurrentPitcher.vue # Pitcher card display β”œβ”€β”€ RunnersDisplay.vue # Base runners visual β”œβ”€β”€ PlayByPlay.vue # Play history feed └── GameStatus.vue # Game status indicator components/UI/ β”œβ”€β”€ PlayerCard.vue # Simple SBA player card β”œβ”€β”€ LoadingSpinner.vue # Loading state └── ErrorAlert.vue # Error display ``` **Acceptance Criteria**: - Game state displays correctly from backend - WebSocket updates reflected in real-time (< 200ms) - Baseball diamond shows runners accurately - Play-by-play updates smoothly - Responsive on mobile (portrait mode) - Handles connection loss gracefully - **Unit tests passing for all components (10+ tests)** --- ### Phase F3: Decision Input Workflow (Week 3) **Goal**: Enable players to make strategic decisions during gameplay **Deliverables**: - [ ] Defensive positioning input - [ ] Pitcher focus input - [ ] Stolen base attempt inputs - [ ] Offensive approach selection - [ ] Decision validation logic - [ ] Turn indicator (whose turn) - [ ] Decision timeout warnings - [ ] Mobile-friendly input forms - [ ] **Unit tests for decision components and validation logic** **Key Components**: ``` components/Decisions/ β”œβ”€β”€ DefensiveSetup.vue # Positioning selection β”œβ”€β”€ PitcherFocusInput.vue # Pitcher focus options β”œβ”€β”€ StolenBaseInputs.vue # Per-runner SB attempts β”œβ”€β”€ OffensiveApproach.vue # Swing/bunt/hit-and-run β”œβ”€β”€ DecisionPanel.vue # Container for decision UI └── TurnIndicator.vue # Whose turn indicator components/UI/ β”œβ”€β”€ ActionButton.vue # Primary action button β”œβ”€β”€ ButtonGroup.vue # Radio-style button group └── DecisionTimer.vue # Countdown for decisions ``` **WebSocket Integration**: ```typescript // composables/useGameActions.ts export const useGameActions = () => { const { socket } = useWebSocket() const setDefense = async (positioning: string) => { socket.emit('set_defense', { game_id: gameId.value, positioning }) } const setOffensiveApproach = async (approach: string) => { socket.emit('set_offensive_approach', { game_id: gameId.value, approach }) } // ... other actions } ``` **Acceptance Criteria**: - All decision inputs emit correct WebSocket events - Backend validates and accepts decisions - Mobile touch targets are 44x44px minimum - Decision UI shows when it's player's turn - Clear feedback when waiting for opponent - Timeout warnings display appropriately - **Unit tests passing for decision validation and components (8+ tests)** --- ### Phase F4: Manual Outcome Workflow (Week 4) **Goal**: Implement dice rolling and outcome reading interface **Deliverables**: - [ ] Dice roll button and animation - [ ] Roll result display - [ ] Outcome input form (hit type, bases, runs) - [ ] Play result submission - [ ] Dice roll history display - [ ] Manual workflow state management - [ ] Error handling for invalid outcomes - [ ] Mobile-optimized outcome input - [ ] **Unit tests for manual workflow and outcome validation** **Key Components**: ``` components/Manual/ β”œβ”€β”€ DiceRollButton.vue # Request roll button β”œβ”€β”€ DiceRollDisplay.vue # Roll result with animation β”œβ”€β”€ OutcomeInputForm.vue # Manual outcome entry β”œβ”€β”€ OutcomePreview.vue # Preview before submit └── RollHistory.vue # Recent rolls display components/UI/ β”œβ”€β”€ DiceAnimation.vue # Animated dice roll β”œβ”€β”€ NumberInput.vue # Numeric input (bases, runs) └── OutcomeSelect.vue # Outcome type dropdown ``` **WebSocket Flow**: ```typescript // 1. Request dice roll socket.emit('request_manual_roll', { game_id }) // 2. Receive roll result socket.on('manual_roll_ready', (data) => { // Display: d6_1=3, d6_2a=4, d6_2b=2, d20_chaos=15, d20_resolution=8 showRollResult(data) }) // 3. Await outcome input socket.on('awaiting_outcome_input', (prompt) => { // Show form: hit type, bases advanced, runs scored showOutcomeForm(prompt) }) // 4. Submit outcome socket.emit('submit_manual_outcome', { game_id, outcome_data: { hit_type: 'SINGLE_1', bases_advanced: {...}, runs_scored: 1, outs_recorded: 0 } }) // 5. Play completed socket.on('play_completed', (result) => { // Update game state, show result updateGameState(result) }) ``` **Acceptance Criteria**: - Dice roll request triggers backend roll generation - Roll results display with clear visual feedback - Outcome form validates input before submission - Play result reflects in game state immediately - Mobile form input is intuitive (large touch targets) - Error messages clear when invalid outcome submitted - **Unit tests passing for workflow and validation (8+ tests)** --- ### Phase F5: Substitutions & Advanced Actions (Week 5) **Goal**: Enable player substitutions and pitching changes **Deliverables**: - [ ] Pinch hitter substitution UI - [ ] Pinch runner substitution UI - [ ] Defensive replacement UI - [ ] Pitching change interface - [ ] Bench/bullpen display - [ ] Substitution validation - [ ] Lineup position display - [ ] Player availability indicators - [ ] **Unit tests for substitution logic and components** **Key Components**: ``` components/Substitutions/ β”œβ”€β”€ SubstitutionModal.vue # Main substitution interface β”œβ”€β”€ PinchHitterSelect.vue # Select pinch hitter β”œβ”€β”€ PinchRunnerSelect.vue # Select pinch runner β”œβ”€β”€ DefensiveReplacementSelect.vue # Defensive sub β”œβ”€β”€ PitchingChangeModal.vue # Bullpen selection β”œβ”€β”€ BenchDisplay.vue # Available bench players β”œβ”€β”€ BullpenDisplay.vue # Available pitchers └── LineupDisplay.vue # Current lineup status components/UI/ β”œβ”€β”€ PlayerList.vue # Scrollable player list β”œβ”€β”€ PlayerAvailability.vue # Availability indicator └── Modal.vue # Reusable modal component ``` **WebSocket Integration**: ```typescript // Pinch hitter socket.emit('substitute_player', { game_id, substitution_type: 'pinch_hitter', lineup_spot_id: 123, new_player_id: 456, batting_order_position: 3 }) // Pitching change socket.emit('change_pitcher', { game_id, new_pitcher_lineup_id: 789 }) // Defensive replacement socket.emit('substitute_player', { game_id, substitution_type: 'defensive_replacement', lineup_spot_id: 234, new_player_id: 567, position: '2B' }) ``` **Acceptance Criteria**: - Substitution modals display available players - Backend validates substitution eligibility - Lineup updates reflect substitution immediately - Substituted players marked as inactive - Mobile interface allows easy player selection - Clear indication of player availability - **Unit tests passing for substitution validation (8+ tests)** --- ### Phase F6: Game Management & History (Week 6) **Goal**: Complete game lifecycle from creation to history **Deliverables**: - [ ] Game creation form - [ ] Game lobby/pre-game interface - [ ] Active games list - [ ] Completed games history - [ ] Game detail view (spectator-style) - [ ] Play-by-play history export - [ ] Game search and filtering - [ ] Mobile-optimized lists - [ ] **Unit tests for game management and API integration** **Key Pages**: ``` pages/ β”œβ”€β”€ games/ β”‚ β”œβ”€β”€ create.vue # Create new game form β”‚ β”œβ”€β”€ lobby/[id].vue # Pre-game lobby β”‚ β”œβ”€β”€ active.vue # List of active games β”‚ β”œβ”€β”€ history.vue # Completed games list β”‚ └── [id]/ β”‚ β”œβ”€β”€ index.vue # Live game view β”‚ β”œβ”€β”€ history.vue # Play-by-play history β”‚ └── stats.vue # Box score stats └── spectate/ └── [id].vue # Spectator view ``` **Key Components**: ``` components/GameManagement/ β”œβ”€β”€ GameCreationForm.vue # Create game form β”œβ”€β”€ GameLobby.vue # Pre-game interface β”œβ”€β”€ GamesList.vue # List of games β”œβ”€β”€ GameCard.vue # Game summary card β”œβ”€β”€ GameFilters.vue # Filter controls └── PlayByPlayHistory.vue # Full play history components/Stats/ β”œβ”€β”€ BoxScore.vue # Full box score display β”œβ”€β”€ BattingStats.vue # Batting statistics table β”œβ”€β”€ PitchingStats.vue # Pitching statistics table └── GameSummary.vue # Game summary card ``` **REST API Integration**: ```typescript // composables/useGameApi.ts export const useGameApi = () => { const api = useApi() const createGame = async (gameData: CreateGameRequest) => { return await api.post('/api/games', gameData) } const getActiveGames = async () => { return await api.get('/api/games/active') } const getGameHistory = async (filters?: GameFilters) => { return await api.get('/api/games/completed', { params: filters }) } const getBoxScore = async (gameId: string) => { return await api.get(`/api/games/${gameId}/box_score`) } } ``` **Acceptance Criteria**: - Users can create new games with configuration - Game lobby shows both lineups before start - Active games list updates in real-time - Game history loads with pagination - Box scores display correctly after games - Mobile lists scroll smoothly - Search and filter work correctly - **Unit tests passing for game API and components (10+ tests)** --- ### Phase F7: Spectator Mode (Week 7) **Goal**: Enable real-time spectating of active games **Deliverables**: - [ ] Spectator view (read-only game interface) - [ ] Spectator WebSocket connection - [ ] Spectator count display - [ ] Join spectator mode from games list - [ ] Spectator-specific UI elements - [ ] Real-time updates for spectators - [ ] Mobile-optimized spectator view - [ ] **Unit tests for spectator mode logic** **Key Features**: ``` pages/spectate/ └── [id].vue # Spectator game view components/Spectator/ β”œβ”€β”€ SpectatorGameView.vue # Read-only game display β”œβ”€β”€ SpectatorIndicator.vue # "Spectating" badge β”œβ”€β”€ SpectatorCount.vue # Number of spectators └── SpectatorControls.vue # Leave, fullscreen, etc. ``` **WebSocket Integration**: ```typescript // Join as spectator socket.emit('join_game', { game_id, role: 'spectator' }) // Receive same updates as players socket.on('game_state_update', (state) => {}) socket.on('play_completed', (play) => {}) socket.on('inning_change', (inning) => {}) // Spectator-specific events socket.on('spectator_joined', (count) => {}) socket.on('spectator_left', (count) => {}) ``` **Acceptance Criteria**: - Spectators can view active games in real-time - No decision inputs shown to spectators - Spectator count visible to players (optional) - Updates arrive with same latency as players - Mobile spectator view clear and readable - Can spectate multiple games (browser tabs) - **Unit tests passing for spectator permissions (5+ tests)** --- ### Phase F8: Polish & Animation (Week 8) **Goal**: Enhance UX with animations, transitions, and polish **Deliverables**: - [ ] Dice roll animation (3D or 2D) - [ ] Play result animations - [ ] Page transitions - [ ] Loading skeletons - [ ] Toast notifications - [ ] Error state illustrations - [ ] Confetti for home runs (optional) - [ ] Sound effects (optional) - [ ] **Unit tests for animation triggers and timing** **Key Components**: ``` components/Animations/ β”œβ”€β”€ DiceRoll3D.vue # 3D dice animation β”œβ”€β”€ PlayResultAnimation.vue # Hit/out animations β”œβ”€β”€ RunnerMovement.vue # Animated base running β”œβ”€β”€ ScoreIncrement.vue # Score change animation └── Confetti.vue # Home run celebration components/UI/ β”œβ”€β”€ SkeletonLoader.vue # Loading skeleton β”œβ”€β”€ Toast.vue # Toast notification β”œβ”€β”€ Transition.vue # Page transition wrapper └── EmptyState.vue # Empty state illustration ``` **Animation Libraries** (potential): ```typescript // Consideration: Add animation libraries // - @vueuse/motion - Vue animation utilities // - gsap - Advanced animations (dice roll) // - lottie-vue - Lottie animations ``` **Acceptance Criteria**: - Dice roll feels satisfying and realistic - Play results animate smoothly - Transitions enhance UX without delay - Loading states prevent confusion - Toasts provide clear feedback - Mobile animations perform well (60fps) - **Unit tests passing for animation logic (5+ tests)** --- ### Phase F9: Performance Optimization (Week 9) **Goal**: Optimize for fast load times and smooth interactions **Deliverables**: - [ ] Code splitting optimization - [ ] Lazy loading for heavy components - [ ] Image optimization (player cards) - [ ] Bundle size analysis and reduction - [ ] Lighthouse performance audit - [ ] WebSocket message throttling - [ ] State update debouncing - [ ] Memory leak prevention - [ ] **E2E tests with Playwright (full user flows)** - [ ] **Performance benchmarks and automated audits** **Optimization Tasks**: ```typescript // 1. Lazy load heavy components const GameBoard = defineAsyncComponent(() => import('~/components/Game/GameBoard.vue') ) // 2. Throttle WebSocket updates const throttledUpdate = useThrottle((state) => { gameStore.setGameState(state) }, 100) // 3. Memoize expensive computations const scoreDiff = computed(() => { return gameState.value.home_score - gameState.value.away_score }) // 4. Virtual scrolling for long lists ``` **Performance Targets**: - Initial page load: < 3 seconds on 3G - Time to interactive: < 5 seconds - Lighthouse score: > 90 - Bundle size: < 500KB (gzipped) - WebSocket latency: < 200ms - Action response: < 500ms - Memory usage: < 100MB sustained **Acceptance Criteria**: - Lighthouse audit passes all core metrics - Bundle size reduced by 30%+ from baseline - No memory leaks detected - Smooth scrolling on mobile (60fps) - WebSocket updates don't block UI - **E2E tests passing for critical user flows (10+ tests)** - **Performance benchmarks meet or exceed targets** --- ## Mobile-First Design Principles ### Layout Strategy **Portrait Mobile (375px - 640px)**: ```vue ``` **Desktop (1024px+)**: ```vue ``` ### Touch Interaction Guidelines 1. **Minimum Touch Targets**: 44x44px for all interactive elements 2. **Swipe Gestures**: - Swipe left/right to navigate between plays - Pull to refresh game state 3. **Bottom Sheets**: Use for modals on mobile (easier one-handed use) 4. **Haptic Feedback**: Vibrate on important actions (optional) 5. **Avoid Hover**: Don't rely on hover states for functionality --- ## Type Safety & API Contracts ### Core Type Definitions ```typescript // types/game.ts export interface GameState { game_id: string league_id: string status: 'pending' | 'active' | 'completed' // Game situation inning: number half: 'top' | 'bottom' outs: number balls: number strikes: number // Score home_score: number away_score: number // Runners (lineup IDs) runners: { first: number | null second: number | null third: number | null } // Current players current_batter_lineup_id: number | null current_pitcher_lineup_id: number | null // Decisions defensive_positioning: string | null pitcher_focus: string | null stolen_base_attempts: Record offensive_approach: string | null // Teams home_team_id: number away_team_id: number } export interface Lineup { id: number game_id: string team_id: number card_id: number position: string batting_order: number | null is_starter: boolean is_active: boolean player: SbaPlayer } export interface SbaPlayer { id: number name: string image: string team?: string manager?: string } export interface PlayResult { play_number: number outcome: string description: string runs_scored: number outs_recorded: number new_state: Partial } export interface DecisionPrompt { phase: 'defense' | 'stolen_base' | 'offensive_approach' | 'manual_outcome' role: 'home' | 'away' timeout_seconds: number options?: string[] } export interface RollData { roll_id: string d6_one: number d6_two_a: number d6_two_b: number chaos_d20: number resolution_d20: number } ``` ### API Response Types ```typescript // types/api.ts export interface CreateGameRequest { league_id: string home_team_id: number away_team_id: number game_mode: 'live' | 'async' | 'vs_ai' visibility: 'public' | 'private' } export interface CreateGameResponse { game_id: string status: string created_at: string } export interface GameListItem { game_id: string home_team_id: number away_team_id: number status: string current_inning: number home_score: number away_score: number started_at: string } export interface BoxScoreResponse { game_stats: { home_runs: number away_runs: number home_hits: number away_hits: number linescore_home: number[] linescore_away: number[] } batting_stats: BattingStatLine[] pitching_stats: PitchingStatLine[] } ``` ### WebSocket Event Types ```typescript // types/websocket.ts export type SocketEventMap = { // Client β†’ Server 'join_game': (data: { game_id: string, role: 'home' | 'away' | 'spectator' }) => void 'set_defense': (data: { game_id: string, positioning: string }) => void 'set_pitcher_focus': (data: { game_id: string, focus: string }) => void 'set_stolen_base_attempt': (data: { game_id: string, runner: string, attempt: boolean }) => void 'set_offensive_approach': (data: { game_id: string, approach: string }) => void 'request_manual_roll': (data: { game_id: string }) => void 'submit_manual_outcome': (data: { game_id: string, outcome_data: OutcomeData }) => void // Server β†’ Client 'game_state_update': (state: GameState) => void 'decision_required': (decision: DecisionPrompt) => void 'play_completed': (play: PlayResult) => void 'manual_roll_ready': (roll: RollData) => void 'awaiting_outcome_input': (prompt: OutcomePrompt) => void 'inning_change': (data: { inning: number, half: string }) => void 'game_ended': (result: GameResult) => void 'invalid_action': (error: ActionError) => void 'connection_error': (error: ConnectionError) => void } ``` --- ## Testing Strategy **Philosophy**: Unit tests are written **in each phase** alongside feature development, not deferred to Phase F9. This ensures immediate feedback, prevents regressions, and maintains code quality throughout the project. ### Unit Testing (Phases F1-F8) Each phase includes comprehensive unit tests for all new code: ```bash # Run all unit tests npm run test # Watch mode for development npm run test:watch # Coverage report npm run test:coverage # Target: > 80% coverage ``` **What to Test Each Phase**: - **Composables**: Logic, validation, error handling, state management - **Stores**: State mutations, computed properties, actions, persistence - **Components**: Props, emits, rendering, user interactions - **Utilities**: Pure functions, formatters, validators **Testing Tools**: - **Vitest**: Fast unit test runner with Vue support - **@vue/test-utils**: Official Vue testing library - **happy-dom**: Lightweight DOM implementation **Example Component Test**: ```typescript import { mount } from '@vue/test-utils' import { describe, it, expect } from 'vitest' import ScoreBoard from '~/components/Game/ScoreBoard.vue' describe('ScoreBoard', () => { it('displays correct score', () => { const wrapper = mount(ScoreBoard, { props: { homeScore: 5, awayScore: 3, inning: 7, half: 'bottom' } }) expect(wrapper.text()).toContain('5') expect(wrapper.text()).toContain('3') expect(wrapper.text()).toContain('Bottom 7') }) }) ``` ### E2E Testing (Phase F9) End-to-end tests validate complete user flows across the entire application: ```bash # E2E tests with Playwright npm run test:e2e # Headed mode (see browser) npm run test:e2e:headed # Specific browser npm run test:e2e --project=chromium # Test scenarios: # - Full game flow (create β†’ play β†’ complete) # - WebSocket reconnection and recovery # - Mobile responsive behavior # - Multi-user synchronization # - Authentication flow ``` **Example E2E Test**: ```typescript import { test, expect } from '@playwright/test' test('can create and join game', async ({ page }) => { // Login await page.goto('/auth/login') await page.click('button:has-text("Login with Discord")') // Create game await page.goto('/games/create') await page.selectOption('#home-team', '123') await page.selectOption('#away-team', '456') await page.click('button:has-text("Create Game")') // Verify game view loaded await expect(page.locator('.game-board')).toBeVisible() }) ``` ### Manual Testing Checklist **Mobile Testing** (Real Devices): - [ ] iPhone 12/13/14 (Safari) - [ ] Samsung Galaxy S21/S22 (Chrome) - [ ] iPad (Safari) - [ ] Various screen sizes (375px - 768px) **Desktop Testing**: - [ ] Chrome (latest) - [ ] Firefox (latest) - [ ] Safari (latest) - [ ] Edge (latest) **Network Conditions**: - [ ] Fast 3G - [ ] Slow 3G - [ ] Offline behavior - [ ] Connection loss/recovery --- ## Development Guidelines ### Code Quality Standards **Vue/TypeScript Best Practices**: ```vue ``` **Composable Best Practices**: ```typescript // βœ… Good: Returns reactive refs/computeds export const useGameState = (gameId: Ref) => { const state = ref(null) const isLoading = ref(false) const currentInning = computed(() => state.value?.inning ?? 1 ) const loadState = async () => { isLoading.value = true // ... fetch state isLoading.value = false } return { state: readonly(state), isLoading: readonly(isLoading), currentInning, loadState } } // ❌ Bad: Returns non-reactive values export const useGameState = (gameId: string) => { let state = null // Not reactive! return { state } } ``` **Store Best Practices**: ```typescript // βœ… Good: Composition API store with types export const useGameStore = defineStore('game', () => { const state = ref(null) const currentInning = computed(() => state.value?.inning ?? 1) const setState = (newState: GameState) => { state.value = newState } return { state: readonly(state), currentInning, setState } }) // ❌ Bad: Options API store without types export const useGameStore = defineStore('game', { state: () => ({ state: null }), actions: { setState(state) { this.state = state } } }) ``` ### Git Workflow **Branch Strategy**: ```bash # Feature branches from main git checkout -b frontend/auth-flow git checkout -b frontend/game-board git checkout -b frontend/decision-inputs # Commit prefix: "FRONTEND:" git commit -m "FRONTEND: Implement Discord OAuth login flow" git commit -m "FRONTEND: Add GameBoard component with responsive layout" ``` **Pull Request Template**: ```markdown ## Changes - Added Discord OAuth authentication flow - Created login page and callback handler - Implemented auth store with Pinia ## Testing - [x] Tested on mobile (iPhone 13) - [x] Tested on desktop (Chrome) - [x] TypeScript compilation passes - [x] Component tests pass ## Screenshots [Mobile login screen] [Desktop login screen] ``` --- ## Deployment & DevOps ### Build Process ```bash # Production build npm run build # Output: .output/ directory # Contains: # - Server files (Nitro) # - Static assets # - Pre-rendered pages (if any) ``` ### Environment Variables ```bash # .env.production NUXT_PUBLIC_API_URL=https://api.paperdynasty.com NUXT_PUBLIC_WS_URL=https://api.paperdynasty.com NUXT_PUBLIC_DISCORD_CLIENT_ID= NUXT_PUBLIC_DISCORD_REDIRECT_URI=https://sba.paperdynasty.com/auth/callback ``` ### Hosting Options **Option 1: VPS with Docker** ```dockerfile # Dockerfile FROM node:20-alpine WORKDIR /app COPY . . RUN npm ci RUN npm run build EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] ``` **Option 2: Vercel/Netlify** - Zero-config deployment - Automatic HTTPS - CDN edge caching - Preview deployments for PRs **Recommendation**: Start with Vercel for simplicity, migrate to VPS if needed for control. --- ## Success Metrics & KPIs ### Launch Success (First Month) | Metric | Target | Measurement | |--------|--------|-------------| | Player Migration | 90% | Users active on web vs Sheets | | Mobile Usage | 60%+ | Mobile vs desktop sessions | | Action Latency | < 500ms | p95 WebSocket response time | | Page Load Time | < 3s | Lighthouse audit on 3G | | System Uptime | 99.5% | Uptime monitoring | | Critical Bugs | 0 | Zero rollback-required bugs | ### User Experience (Ongoing) | Metric | Target | Measurement | |--------|--------|-------------| | SUS Score | > 75 | User survey (10 questions) | | Net Promoter Score | > 50 | "How likely to recommend?" | | Game Completion Rate | > 90% | Games completed vs started | | Average Session | 30-60 min | Analytics tracking | | Support Tickets | < 5/week | Support system | ### Technical Performance (Ongoing) | Metric | Target | Measurement | |--------|--------|-------------| | Lighthouse Score | > 90 | Automated audit | | Bundle Size | < 500KB | Build analysis | | WebSocket Latency | < 200ms | Network monitoring | | Memory Usage | < 100MB | Browser DevTools | | Error Rate | < 0.1% | Error tracking (Sentry) | --- ## Risk Mitigation ### Technical Risks | Risk | Mitigation | |------|------------| | WebSocket disconnections | Auto-reconnect with exponential backoff, state recovery from backend | | Type mismatches with backend | Generate types from backend OpenAPI spec, integration tests | | Mobile performance issues | Performance budget, bundle size limits, lazy loading | | Browser compatibility | Polyfills, feature detection, graceful degradation | | State desynchronization | Periodic state refresh, conflict resolution | ### User Experience Risks | Risk | Mitigation | |------|------------| | Confusing UI for Sheets users | Onboarding tutorial, help documentation, familiar terminology | | Mobile input difficulties | Large touch targets, mobile-first design, user testing | | Slow adoption | Beta testing period, migration guide, user training sessions | | Connection issues misunderstood | Clear connection status indicator, helpful error messages | --- ## Next Steps ### Immediate Actions (This Session) 1. **Review Backend WebSocket Specifications** - Read `backend/app/websocket/handlers.py` - Review event payloads and responses - Understand decision workflow sequences 2. **Create Core Type Definitions** - Start with `types/game.ts` - Match backend Pydantic models exactly - Add JSDoc comments for clarity 3. **Set Up Basic Auth Flow** - Create Discord OAuth configuration - Implement login page - Test callback handling 4. **Initialize Pinia Stores** - Auth store with Discord user - UI store for modals/toasts - Game store skeleton 5. **Create First Component** - Simple ScoreBoard component - Test with mock data - Ensure TypeScript types work ### Week 1 Priorities - [ ] Complete Phase F1 (Core Infrastructure) - [ ] WebSocket connection working - [ ] Type system fully defined - [ ] Auth flow functional - [ ] Basic routing in place ### Long-Term Milestones - **Week 2**: Phase F2 complete (Game state display working) - **Week 3**: Phase F3 complete (Decision inputs functional) - **Week 4**: Phase F4 complete (Manual workflow end-to-end) - **Week 5**: Phase F5 complete (Substitutions working) - **Week 6**: Phase F6 complete (Full game lifecycle) - **Week 7**: Phase F7 complete (Spectator mode) - **Week 8**: Phase F8 complete (Polish & animations) - **Week 9**: Phase F9 complete (Performance optimized) **Target Launch**: 9-10 weeks from start (Beta), +2 weeks for production hardening --- ## Appendix ### Useful Resources **Nuxt 3**: - Documentation: https://nuxt.com/docs - Examples: https://nuxt.com/docs/examples - Modules: https://nuxt.com/modules **Vue 3**: - Composition API: https://vuejs.org/guide/extras/composition-api-faq.html - TypeScript: https://vuejs.org/guide/typescript/overview.html - Best Practices: https://vuejs.org/style-guide/ **Socket.io**: - Client docs: https://socket.io/docs/v4/client-api/ - TypeScript: https://socket.io/docs/v4/typescript/ **Tailwind CSS**: - Documentation: https://tailwindcss.com/docs - Customization: https://tailwindcss.com/docs/configuration ### Backend API Reference **REST Endpoints**: ``` POST /api/games # Create new game GET /api/games/:id # Get game details GET /api/games/active # List active games GET /api/games/completed # List completed games GET /api/games/:id/box_score # Get box score ``` **WebSocket Events** (Full list in backend docs): - Connection: `join_game`, `leave_game` - Decisions: `set_defense`, `set_stolen_base_attempt`, `set_offensive_approach` - Manual: `request_manual_roll`, `submit_manual_outcome` - Substitutions: `substitute_player`, `change_pitcher` --- **Document Version**: 1.0 **Last Updated**: 2025-01-10 **Next Review**: After Phase F1 completion