# 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
<VirtualList :items="playHistory" :item-height="60">
  <template #default="{ item }">
    <PlayItem :play="item" />
  </template>
</VirtualList>
```

**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
<template>
  <div class="mobile-game-view">
    <!-- Sticky header: Score + inning -->
    <div class="sticky top-0 z-20 bg-white shadow-lg">
      <ScoreBoard compact />
    </div>

    <!-- Main content: Single column -->
    <div class="flex flex-col gap-4 p-4">
      <!-- Baseball diamond -->
      <GameBoard size="compact" />

      <!-- Current situation -->
      <CurrentSituation mobile />

      <!-- Decision panel (when active) -->
      <DecisionPanel v-if="isMyTurn" mobile />

      <!-- Play-by-play (collapsible) -->
      <Disclosure title="Recent Plays">
        <PlayByPlay :limit="5" />
      </Disclosure>
    </div>

    <!-- Bottom navigation (optional) -->
    <div class="fixed bottom-0 w-full">
      <BottomNav />
    </div>
  </div>
</template>
```

**Desktop (1024px+)**:
```vue
<template>
  <div class="desktop-game-view">
    <div class="grid grid-cols-2 gap-6 p-6">
      <!-- Left column: Game state -->
      <div class="flex flex-col gap-4">
        <ScoreBoard detailed />
        <GameBoard size="large" />
        <CurrentSituation expanded />
      </div>

      <!-- Right column: Actions + history -->
      <div class="flex flex-col gap-4">
        <DecisionPanel v-if="isMyTurn" />
        <PlayByPlay infinite-scroll />
        <GameStats />
      </div>
    </div>
  </div>
</template>
```

### 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<string, boolean>
  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<GameState>
}

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
<!-- ✅ Good: Composition API with TypeScript -->
<script setup lang="ts">
interface Props {
  gameId: string
  isActive: boolean
}

const props = defineProps<Props>()
const emit = defineEmits<{
  update: [value: string]
}>()

const localState = ref('')
const displayValue = computed(() =>
  props.isActive ? 'Active' : 'Inactive'
)
</script>

<!-- ❌ Bad: Options API without types -->
<script>
export default {
  props: ['gameId', 'isActive'],
  data() {
    return { localState: '' }
  }
}
</script>
```

**Composable Best Practices**:
```typescript
// ✅ Good: Returns reactive refs/computeds
export const useGameState = (gameId: Ref<string>) => {
  const state = ref<GameState | null>(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<GameState | null>(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=<production-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
