diff --git a/frontend-sba/components/Decisions/DecisionPanel.vue b/frontend-sba/components/Decisions/DecisionPanel.vue index e05e7b9..57d09cd 100644 --- a/frontend-sba/components/Decisions/DecisionPanel.vue +++ b/frontend-sba/components/Decisions/DecisionPanel.vue @@ -19,19 +19,8 @@ :game-id="gameId" :is-active="isMyTurn" :current-decision="currentOffensiveDecision" - :has-runners-on-base="hasRunnersOnBase" @submit="handleOffensiveSubmit" /> - - - @@ -84,7 +73,6 @@ v-else class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-8 text-center" > -
โšพ

Waiting for Play

@@ -100,7 +88,6 @@ 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' @@ -120,7 +107,6 @@ interface Props { } currentDefensiveSetup?: DefensiveDecision currentOffensiveDecision?: Omit - currentStealAttempts?: number[] decisionHistory?: DecisionHistoryItem[] } @@ -131,26 +117,18 @@ const props = withDefaults(defineProps(), { second: null, third: null, }), - currentStealAttempts: () => [], decisionHistory: () => [], }) const emit = defineEmits<{ defensiveSubmit: [decision: DefensiveDecision] offensiveSubmit: [decision: Omit] - 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) }) @@ -164,11 +142,4 @@ const handleOffensiveSubmit = (decision: Omit { - emit('stealAttemptsSubmit', attempts) -} - -const handleStealAttemptsCancel = () => { - // Reset handled by parent component -} diff --git a/frontend-sba/components/Decisions/DefensiveSetup.vue b/frontend-sba/components/Decisions/DefensiveSetup.vue index 02d6924..0195b9a 100644 --- a/frontend-sba/components/Decisions/DefensiveSetup.vue +++ b/frontend-sba/components/Decisions/DefensiveSetup.vue @@ -6,8 +6,7 @@
-

- ๐Ÿงค +

Defense

diff --git a/frontend-sba/components/Decisions/OffensiveApproach.vue b/frontend-sba/components/Decisions/OffensiveApproach.vue index d766166..74d3673 100644 --- a/frontend-sba/components/Decisions/OffensiveApproach.vue +++ b/frontend-sba/components/Decisions/OffensiveApproach.vue @@ -2,8 +2,7 @@
-

- โš”๏ธ +

Offensive Action

- {{ option.icon }}
{{ option.label }}
- {{ option.disabled ? option.disabledReason : option.description }} + {{ option.description }}
@@ -48,22 +45,6 @@
- -
-

- Current Strategy -

-
-
- Action: - {{ currentActionLabel }} -
-
- {{ specialHandlingNote }} -
-
-
- - hasRunnersOnBase?: boolean - runnerOnFirst?: boolean - runnerOnSecond?: boolean - runnerOnThird?: boolean - basesLoaded?: boolean - outs?: number } const props = withDefaults(defineProps(), { isActive: false, - hasRunnersOnBase: false, - runnerOnFirst: false, - runnerOnSecond: false, - runnerOnThird: false, - basesLoaded: false, - outs: 0, }) const emit = defineEmits<{ submit: [decision: Omit] }>() +const gameStore = useGameStore() + // Local state const submitting = ref(false) const localDecision = ref>({ action: props.currentDecision?.action || 'swing_away', }) -// Action options with smart filtering -const availableActions = computed(() => { - const twoOuts = props.outs >= 2 +// Read game state from store (fixes bug where props were never passed from DecisionPanel) +const storeGameState = computed(() => gameStore.gameState) - return [ +const hasRunnersOnBase = computed(() => { + const gs = storeGameState.value + if (!gs) return false + return !!(gs.on_first || gs.on_second || gs.on_third) +}) + +const runnerOnFirst = computed(() => !!storeGameState.value?.on_first) +const runnerOnThird = computed(() => !!storeGameState.value?.on_third) +const outs = computed(() => storeGameState.value?.outs ?? 0) + +// Action options: only include actions whose conditions are met (hidden, not disabled) +const availableActions = computed(() => { + const actions: ActionOption[] = [ { value: 'swing_away', label: 'Swing Away', - icon: 'โšพ', description: 'Normal swing, no special tactics', - disabled: false, - }, - { - value: 'steal', - label: 'Steal', - icon: '๐Ÿƒ', - description: 'Attempt to steal base(s) - configure on steal inputs tab', - disabled: false, - }, - { - value: 'check_jump', - label: 'Check Jump', - icon: '๐Ÿ‘€', - description: 'Lead runner checks jump at start of delivery', - disabled: !props.hasRunnersOnBase, - disabledReason: 'Requires runner on base', - }, - { - value: 'hit_and_run', - label: 'Hit and Run', - icon: '๐Ÿ’จ', - description: 'Runner(s) take off as pitcher delivers; batter must make contact', - disabled: !props.hasRunnersOnBase, - disabledReason: 'Requires runner on base', - }, - { - value: 'sac_bunt', - label: 'Sacrifice Bunt', - icon: '๐ŸŽฏ', - description: 'Bunt to advance runners, batter likely out', - disabled: twoOuts, - disabledReason: twoOuts ? 'Cannot bunt with 2 outs' : undefined, - }, - { - value: 'squeeze_bunt', - label: 'Squeeze Bunt', - icon: '๐Ÿ”ฅ', - description: 'Runner on 3rd breaks for home as pitcher delivers', - disabled: !props.runnerOnThird || twoOuts, - disabledReason: twoOuts - ? 'Cannot squeeze with 2 outs' - : !props.runnerOnThird - ? 'Requires runner on third' - : undefined, }, ] + + // Check jump: requires any runner on base + if (hasRunnersOnBase.value) { + actions.push({ + value: 'check_jump', + label: 'Check Jump', + description: 'Lead runner checks jump at start of delivery', + }) + } + + // Hit and run: requires runner on first and/or third (NOT second only) + if (runnerOnFirst.value || runnerOnThird.value) { + actions.push({ + value: 'hit_and_run', + label: 'Hit and Run', + description: 'Runner(s) take off as pitcher delivers; batter must make contact', + }) + } + + // Sac bunt: requires < 2 outs AND runners on base + if (outs.value < 2 && hasRunnersOnBase.value) { + actions.push({ + value: 'sac_bunt', + label: 'Sacrifice Bunt', + description: 'Bunt to advance runners, batter likely out', + }) + } + + // Squeeze bunt: requires < 2 outs AND runner on third + if (outs.value < 2 && runnerOnThird.value) { + actions.push({ + value: 'squeeze_bunt', + label: 'Squeeze Bunt', + description: 'Runner on 3rd breaks for home as pitcher delivers', + }) + } + + return actions }) // Computed -const currentActionLabel = computed(() => { - const option = availableActions.value.find(opt => opt.value === localDecision.value.action) - return option?.label || 'Swing Away' -}) - -const actionRequiresSpecialHandling = computed(() => { - return localDecision.value.action === 'steal' || localDecision.value.action === 'squeeze_bunt' -}) - -const specialHandlingNote = computed(() => { - if (localDecision.value.action === 'steal') { - return 'Configure which bases to steal on the Stolen Base Inputs tab' - } - if (localDecision.value.action === 'squeeze_bunt') { - return 'R3 will break for home as pitcher delivers - high risk, high reward!' - } - return '' -}) - const hasChanges = computed(() => { if (!props.currentDecision) return true return localDecision.value.action !== props.currentDecision.action @@ -217,22 +171,13 @@ const submitButtonText = computed(() => { // Methods const selectAction = (action: OffensiveDecision['action']) => { if (!props.isActive) return - - // Check if action is disabled - const option = availableActions.value.find(opt => opt.value === action) - if (option?.disabled) return - localDecision.value.action = action } -const getActionButtonClasses = (action: OffensiveDecision['action'], disabled: boolean) => { +const getActionButtonClasses = (action: OffensiveDecision['action']) => { const isSelected = localDecision.value.action === action const base = 'w-full p-4 rounded-lg border-2 transition-all duration-200 disabled:cursor-not-allowed' - if (disabled) { - return `${base} bg-gray-100 dark:bg-gray-800 text-gray-400 dark:text-gray-500 border-gray-200 dark:border-gray-700 opacity-60` - } - if (isSelected) { return `${base} bg-gradient-to-r from-blue-600 to-blue-700 text-white border-blue-700 shadow-lg` } else { @@ -258,12 +203,12 @@ watch(() => props.currentDecision, (newDecision) => { } }, { deep: true }) -// Auto-reset to swing_away if current action becomes invalid +// Auto-reset to swing_away if current action is no longer available watch(() => availableActions.value, (actions) => { const currentAction = localDecision.value.action - const currentOption = actions.find(opt => opt.value === currentAction) + const stillAvailable = actions.some(opt => opt.value === currentAction) - if (currentOption?.disabled) { + if (!stillAvailable) { localDecision.value.action = 'swing_away' } }, { deep: true }) diff --git a/frontend-sba/components/Game/RunnerCard.vue b/frontend-sba/components/Game/RunnerCard.vue index 85d0b44..356e6c3 100644 --- a/frontend-sba/components/Game/RunnerCard.vue +++ b/frontend-sba/components/Game/RunnerCard.vue @@ -26,9 +26,8 @@
{{ base }}
- +