Critical fixes in game_engine.py: - Fix silent error swallowing in _batch_save_inning_rolls (re-raise) - Add per-game asyncio.Lock for race condition prevention - Add _cleanup_game_resources() for memory leak prevention - All 739 tests passing Documentation refactoring: - Created CODE_REVIEW_GAME_ENGINE.md documenting 24 identified issues - Trimmed backend/app/core/CLAUDE.md from 1371 to 143 lines - Trimmed frontend-sba/CLAUDE.md from 696 to 110 lines - Created focused subdirectory CLAUDE.md files: - frontend-sba/components/CLAUDE.md (105 lines) - frontend-sba/composables/CLAUDE.md (79 lines) - frontend-sba/store/CLAUDE.md (116 lines) - frontend-sba/types/CLAUDE.md (95 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
3.1 KiB
3.1 KiB
Components Directory
Vue 3 components organized by feature domain. All use <script setup lang="ts"> with Composition API.
Directory Structure
Game/ - Core Game Display
| Component | Purpose | Key Props |
|---|---|---|
ScoreBoard.vue |
Sticky header with inning/score/count | gameState |
GameBoard.vue |
Baseball diamond with runners | gameState |
CurrentSituation.vue |
Pitcher vs Batter cards | currentPitcher, currentBatter |
PlayByPlay.vue |
Animated play history feed | plays |
Data Pattern: These receive LineupPlayerState from gameStore, then use findPlayerInLineup() to get full player data (name, headshot).
Decisions/ - Strategic Decision Input
| Component | Purpose | Emits |
|---|---|---|
DecisionPanel.vue |
Container for decision workflow | - |
DefensiveSetup.vue |
Infield/outfield positioning | submit |
OffensiveApproach.vue |
Batting action selection | submit |
StolenBaseInputs.vue |
Per-runner steal attempts | submit |
Data Pattern: Read currentDecisionPrompt from store, emit decisions to parent which calls useGameActions.
Substitutions/ - Player Replacement
| Component | Purpose | Emits |
|---|---|---|
SubstitutionPanel.vue |
Main substitution container | close |
PinchHitterSelector.vue |
Replace batter | substitute |
PitchingChangeSelector.vue |
Replace pitcher | substitute |
DefensiveReplacementSelector.vue |
Position switch | substitute |
Data Pattern: Filter homeLineup/awayLineup for active vs bench players.
UI/ - Reusable Primitives
| Component | Purpose |
|---|---|
ActionButton.vue |
Styled action button with loading state |
ButtonGroup.vue |
Radio-button style group selector |
ToggleSwitch.vue |
Boolean toggle with labels |
Component Standards
<template>
<!-- Always wrap in single root -->
<div class="component-name">
<!-- Content -->
</div>
</template>
<script setup lang="ts">
// 1. Imports
import { computed } from 'vue'
import { useGameStore } from '~/store/game'
// 2. Props/Emits with TypeScript
interface Props {
gameId: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
submit: [data: SomeType]
}>()
// 3. Store access
const gameStore = useGameStore()
// 4. Computed/methods
</script>
<style scoped>
/* Tailwind @apply preferred */
</style>
Common Patterns
Two-Step Player Lookup
// GameState has LineupPlayerState (minimal)
const batterState = computed(() => gameStore.currentBatter)
// Get full Lineup with player details
const batterLineup = computed(() => {
if (!batterState.value) return null
return gameStore.findPlayerInLineup(batterState.value.lineup_id)
})
// Access player data
const batterName = computed(() => batterLineup.value?.player.name ?? 'Unknown')
const batterHeadshot = computed(() => batterLineup.value?.player.headshot)
Conditional Rendering by Team
const isMyTurn = computed(() => {
// Check if current user's team needs to act
return gameStore.battingTeamId === myTeamId
})