CLAUDE: Fix hasRunners detection and hide outfield for groundballs
Bug fix: - Fixed hasRunners prop using wrong property path (gameState.runners.first instead of gameState.on_first) - hit location selector was never showing Optimization: - Hide outfield positions (LF, CF, RF) for groundball outcomes since groundballs by definition stay in the infield - Auto-clear outfield selection when switching to groundball outcome 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9f88317b79
commit
c27a652e54
@ -58,7 +58,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="location-group">
|
||||
<div v-if="showOutfieldLocations" class="location-group">
|
||||
<div class="location-group-label">Outfield</div>
|
||||
<div class="location-buttons">
|
||||
<button
|
||||
@ -105,7 +105,7 @@ import { ref, computed } from 'vue'
|
||||
import type { PlayOutcome, RollData } from '~/types'
|
||||
|
||||
// Import centralized outcome constants
|
||||
import { OUTCOME_CATEGORIES, OUTCOMES_REQUIRING_HIT_LOCATION, HIT_LOCATIONS } from '~/constants/outcomes'
|
||||
import { OUTCOME_CATEGORIES, OUTCOMES_REQUIRING_HIT_LOCATION, INFIELD_ONLY_OUTCOMES, HIT_LOCATIONS } from '~/constants/outcomes'
|
||||
|
||||
interface Props {
|
||||
rollData: RollData | null
|
||||
@ -144,6 +144,12 @@ const needsHitLocation = computed(() => {
|
||||
return props.hasRunners
|
||||
})
|
||||
|
||||
// Hide outfield positions for groundball outcomes (they stay in the infield)
|
||||
const showOutfieldLocations = computed(() => {
|
||||
if (!selectedOutcome.value) return true
|
||||
return !(INFIELD_ONLY_OUTCOMES as readonly string[]).includes(selectedOutcome.value)
|
||||
})
|
||||
|
||||
const canSubmitForm = computed(() => {
|
||||
if (!selectedOutcome.value) return false
|
||||
if (needsHitLocation.value && !selectedHitLocation.value) return false
|
||||
@ -157,6 +163,13 @@ const selectOutcome = (outcome: PlayOutcome) => {
|
||||
if (!(outcomesNeedingHitLocation as readonly string[]).includes(outcome)) {
|
||||
selectedHitLocation.value = null
|
||||
}
|
||||
// Clear outfield hit location if switching to groundball (infield only)
|
||||
if ((INFIELD_ONLY_OUTCOMES as readonly string[]).includes(outcome)) {
|
||||
const outfield = HIT_LOCATIONS.outfield as readonly string[]
|
||||
if (selectedHitLocation.value && outfield.includes(selectedHitLocation.value)) {
|
||||
selectedHitLocation.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectHitLocation = (location: string) => {
|
||||
|
||||
@ -84,6 +84,16 @@ export const OUTCOMES_REQUIRING_HIT_LOCATION = [
|
||||
'error',
|
||||
] as const satisfies readonly PlayOutcome[]
|
||||
|
||||
/**
|
||||
* Outcomes that only allow infield hit locations (no outfield)
|
||||
* Groundballs by definition stay in the infield
|
||||
*/
|
||||
export const INFIELD_ONLY_OUTCOMES = [
|
||||
'groundball_a',
|
||||
'groundball_b',
|
||||
'groundball_c',
|
||||
] as const satisfies readonly PlayOutcome[]
|
||||
|
||||
/**
|
||||
* Hit location options
|
||||
*/
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
:last-play-result="lastPlayResult"
|
||||
:can-submit-outcome="canSubmitOutcome"
|
||||
:outs="gameState?.outs ?? 0"
|
||||
:has-runners="!!(gameState?.runners?.first || gameState?.runners?.second || gameState?.runners?.third)"
|
||||
:has-runners="!!(gameState?.on_first || gameState?.on_second || gameState?.on_third)"
|
||||
@roll-dice="handleRollDice"
|
||||
@submit-outcome="handleSubmitOutcome"
|
||||
@dismiss-result="handleDismissResult"
|
||||
@ -140,7 +140,7 @@
|
||||
:last-play-result="lastPlayResult"
|
||||
:can-submit-outcome="canSubmitOutcome"
|
||||
:outs="gameState?.outs ?? 0"
|
||||
:has-runners="!!(gameState?.runners?.first || gameState?.runners?.second || gameState?.runners?.third)"
|
||||
:has-runners="!!(gameState?.on_first || gameState?.on_second || gameState?.on_third)"
|
||||
@roll-dice="handleRollDice"
|
||||
@submit-outcome="handleSubmitOutcome"
|
||||
@dismiss-result="handleDismissResult"
|
||||
@ -555,65 +555,20 @@ const updateScoreBoardHeight = () => {
|
||||
onMounted(async () => {
|
||||
console.log('[Game Page] Mounted for game:', gameId.value)
|
||||
|
||||
// Check if we have valid auth
|
||||
console.log('[Game Page] Auth check - isAuthenticated:', authStore.isAuthenticated, 'isTokenValid:', authStore.isTokenValid)
|
||||
// Verify authentication via cookies
|
||||
const isAuthed = await authStore.checkAuth()
|
||||
console.log('[Game Page] Auth check - isAuthenticated:', authStore.isAuthenticated)
|
||||
|
||||
if (!authStore.isAuthenticated || !authStore.isTokenValid) {
|
||||
console.warn('[Game Page] No valid authentication - fetching test token from backend')
|
||||
// Clear any old auth first
|
||||
authStore.clearAuth()
|
||||
|
||||
try {
|
||||
// Fetch a valid JWT token from backend
|
||||
const response = await fetch('http://localhost:8000/api/auth/token', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
user_id: 'test-user-1',
|
||||
username: 'TestPlayer',
|
||||
discord_id: 'test-discord-id'
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get token: ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log('[Game Page] Received token from backend')
|
||||
|
||||
// Set auth with real JWT token
|
||||
authStore.setAuth({
|
||||
access_token: data.access_token,
|
||||
refresh_token: 'test-refresh-token',
|
||||
expires_in: 604800, // 7 days in seconds
|
||||
user: {
|
||||
id: 'test-user-1',
|
||||
discord_id: 'test-discord-id',
|
||||
username: 'TestPlayer',
|
||||
discriminator: '0001',
|
||||
avatar: null,
|
||||
email: 'test@example.com',
|
||||
}
|
||||
})
|
||||
console.log('[Game Page] Test token set - isAuthenticated:', authStore.isAuthenticated, 'isTokenValid:', authStore.isTokenValid)
|
||||
} catch (error) {
|
||||
console.error('[Game Page] Failed to fetch test token:', error)
|
||||
connectionStatus.value = 'disconnected'
|
||||
isLoading.value = false
|
||||
return
|
||||
}
|
||||
} else {
|
||||
console.log('[Game Page] Using existing valid token')
|
||||
if (!isAuthed) {
|
||||
console.warn('[Game Page] Not authenticated - redirecting to login')
|
||||
navigateTo('/auth/login?redirect=' + encodeURIComponent(route.fullPath))
|
||||
return
|
||||
}
|
||||
|
||||
// Connect to WebSocket
|
||||
if (!isConnected.value) {
|
||||
console.log('[Game Page] Attempting WebSocket connection...')
|
||||
connect()
|
||||
}
|
||||
console.log('[Game Page] Authenticated as:', authStore.currentUser?.username)
|
||||
|
||||
// Wait for connection, then join game
|
||||
// WebSocket auto-connects via watch on isAuthenticated in useWebSocket composable
|
||||
// Just wait for connection, then join game
|
||||
watch(isConnected, async (connected) => {
|
||||
if (connected) {
|
||||
connectionStatus.value = 'connected'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user