strat-gameplay-webapp/frontend-sba/components/Decisions/DecisionPanel.vue
Cal Corum 2d4dbe82eb CLAUDE: Compact defensive setup UI with glowing ring turn indicator
- Rewrite DefensiveSetup as inline segmented controls (~120px vs ~340px)
- Fix bug: read game state from useGameStore() instead of never-passed prop
- Remove turn indicator banner from DecisionPanel (replaced by green glow ring)
- Emoji fix: shield -> baseball glove
- Confirm button matches Roll Dice style (full-width, prominent)
- Infield options only show IF In/Corners when runner on 3rd
- Outfield row only renders in walk-off scenarios
- Hold pills only render for occupied bases
- 20 tests rewritten with Pinia store integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:44:46 -06:00

175 lines
5.0 KiB
Vue

<template>
<div class="space-y-4">
<!-- Decision Phase Content -->
<div v-if="phase !== 'idle'" class="space-y-4">
<!-- Defensive Phase -->
<template v-if="phase === 'defensive'">
<DefensiveSetup
:game-id="gameId"
:is-active="isMyTurn"
:current-setup="currentDefensiveSetup"
@submit="handleDefensiveSubmit"
/>
</template>
<!-- Offensive Phase -->
<template v-if="phase === 'offensive'">
<!-- Offensive Approach -->
<OffensiveApproach
:game-id="gameId"
:is-active="isMyTurn"
:current-decision="currentOffensiveDecision"
:has-runners-on-base="hasRunnersOnBase"
@submit="handleOffensiveSubmit"
/>
<!-- Stolen Base Attempts (if runners on base) -->
<StolenBaseInputs
v-if="hasRunnersOnBase"
:runners="runners"
:is-active="isMyTurn"
:current-attempts="currentStealAttempts"
@submit="handleStealAttemptsSubmit"
@cancel="handleStealAttemptsCancel"
/>
</template>
<!-- Decision History (Collapsible) -->
<div
v-if="decisionHistory.length > 0"
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden"
>
<button
type="button"
class="w-full px-6 py-3 flex items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
@click="historyExpanded = !historyExpanded"
>
<span class="font-semibold text-gray-900 dark:text-white">
Recent Decisions
</span>
<span class="text-gray-500 dark:text-gray-400">
{{ historyExpanded ? '▼' : '▶' }}
</span>
</button>
<div
v-show="historyExpanded"
class="border-t border-gray-200 dark:border-gray-700 p-4 space-y-2"
>
<div
v-for="(decision, index) in recentDecisions"
:key="index"
class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-3 text-sm"
>
<div class="flex items-start justify-between gap-2">
<div class="flex-1">
<div class="font-medium text-gray-900 dark:text-white">
{{ decision.type }} Decision
</div>
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
{{ decision.summary }}
</div>
</div>
<div class="text-xs text-gray-400 dark:text-gray-500">
{{ decision.timestamp }}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Idle State -->
<div
v-else
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-8 text-center"
>
<div class="text-6xl mb-4">⚾</div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Waiting for Play
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">
No decisions required at this time
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { DefensiveDecision, OffensiveDecision } from '~/types/game'
import DefensiveSetup from './DefensiveSetup.vue'
import OffensiveApproach from './OffensiveApproach.vue'
import StolenBaseInputs from './StolenBaseInputs.vue'
interface DecisionHistoryItem {
type: 'Defensive' | 'Offensive'
summary: string
timestamp: string
}
interface Props {
gameId: string
currentTeam: 'home' | 'away'
isMyTurn: boolean
phase: 'defensive' | 'offensive' | 'idle'
runners?: {
first: number | null
second: number | null
third: number | null
}
currentDefensiveSetup?: DefensiveDecision
currentOffensiveDecision?: Omit<OffensiveDecision, 'steal_attempts'>
currentStealAttempts?: number[]
decisionHistory?: DecisionHistoryItem[]
}
const props = withDefaults(defineProps<Props>(), {
phase: 'idle',
runners: () => ({
first: null,
second: null,
third: null,
}),
currentStealAttempts: () => [],
decisionHistory: () => [],
})
const emit = defineEmits<{
defensiveSubmit: [decision: DefensiveDecision]
offensiveSubmit: [decision: Omit<OffensiveDecision, 'steal_attempts'>]
stealAttemptsSubmit: [attempts: number[]]
}>()
// Local state
const historyExpanded = ref(false)
// Computed
const hasRunnersOnBase = computed(() => {
return props.runners.first !== null ||
props.runners.second !== null ||
props.runners.third !== null
})
const recentDecisions = computed(() => {
return props.decisionHistory.slice(0, 3)
})
// Event handlers
const handleDefensiveSubmit = (decision: DefensiveDecision) => {
emit('defensiveSubmit', decision)
}
const handleOffensiveSubmit = (decision: Omit<OffensiveDecision, 'steal_attempts'>) => {
emit('offensiveSubmit', decision)
}
const handleStealAttemptsSubmit = (attempts: number[]) => {
emit('stealAttemptsSubmit', attempts)
}
const handleStealAttemptsCancel = () => {
// Reset handled by parent component
}
</script>