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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="location-group">
|
<div v-if="showOutfieldLocations" class="location-group">
|
||||||
<div class="location-group-label">Outfield</div>
|
<div class="location-group-label">Outfield</div>
|
||||||
<div class="location-buttons">
|
<div class="location-buttons">
|
||||||
<button
|
<button
|
||||||
@ -105,7 +105,7 @@ import { ref, computed } from 'vue'
|
|||||||
import type { PlayOutcome, RollData } from '~/types'
|
import type { PlayOutcome, RollData } from '~/types'
|
||||||
|
|
||||||
// Import centralized outcome constants
|
// 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 {
|
interface Props {
|
||||||
rollData: RollData | null
|
rollData: RollData | null
|
||||||
@ -144,6 +144,12 @@ const needsHitLocation = computed(() => {
|
|||||||
return props.hasRunners
|
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(() => {
|
const canSubmitForm = computed(() => {
|
||||||
if (!selectedOutcome.value) return false
|
if (!selectedOutcome.value) return false
|
||||||
if (needsHitLocation.value && !selectedHitLocation.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)) {
|
if (!(outcomesNeedingHitLocation as readonly string[]).includes(outcome)) {
|
||||||
selectedHitLocation.value = null
|
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) => {
|
const selectHitLocation = (location: string) => {
|
||||||
|
|||||||
@ -84,6 +84,16 @@ export const OUTCOMES_REQUIRING_HIT_LOCATION = [
|
|||||||
'error',
|
'error',
|
||||||
] as const satisfies readonly PlayOutcome[]
|
] 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
|
* Hit location options
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -87,7 +87,7 @@
|
|||||||
:last-play-result="lastPlayResult"
|
:last-play-result="lastPlayResult"
|
||||||
:can-submit-outcome="canSubmitOutcome"
|
:can-submit-outcome="canSubmitOutcome"
|
||||||
:outs="gameState?.outs ?? 0"
|
: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"
|
@roll-dice="handleRollDice"
|
||||||
@submit-outcome="handleSubmitOutcome"
|
@submit-outcome="handleSubmitOutcome"
|
||||||
@dismiss-result="handleDismissResult"
|
@dismiss-result="handleDismissResult"
|
||||||
@ -140,7 +140,7 @@
|
|||||||
:last-play-result="lastPlayResult"
|
:last-play-result="lastPlayResult"
|
||||||
:can-submit-outcome="canSubmitOutcome"
|
:can-submit-outcome="canSubmitOutcome"
|
||||||
:outs="gameState?.outs ?? 0"
|
: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"
|
@roll-dice="handleRollDice"
|
||||||
@submit-outcome="handleSubmitOutcome"
|
@submit-outcome="handleSubmitOutcome"
|
||||||
@dismiss-result="handleDismissResult"
|
@dismiss-result="handleDismissResult"
|
||||||
@ -555,65 +555,20 @@ const updateScoreBoardHeight = () => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
console.log('[Game Page] Mounted for game:', gameId.value)
|
console.log('[Game Page] Mounted for game:', gameId.value)
|
||||||
|
|
||||||
// Check if we have valid auth
|
// Verify authentication via cookies
|
||||||
console.log('[Game Page] Auth check - isAuthenticated:', authStore.isAuthenticated, 'isTokenValid:', authStore.isTokenValid)
|
const isAuthed = await authStore.checkAuth()
|
||||||
|
console.log('[Game Page] Auth check - isAuthenticated:', authStore.isAuthenticated)
|
||||||
|
|
||||||
if (!authStore.isAuthenticated || !authStore.isTokenValid) {
|
if (!isAuthed) {
|
||||||
console.warn('[Game Page] No valid authentication - fetching test token from backend')
|
console.warn('[Game Page] Not authenticated - redirecting to login')
|
||||||
// Clear any old auth first
|
navigateTo('/auth/login?redirect=' + encodeURIComponent(route.fullPath))
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('[Game Page] Using existing valid token')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to WebSocket
|
console.log('[Game Page] Authenticated as:', authStore.currentUser?.username)
|
||||||
if (!isConnected.value) {
|
|
||||||
console.log('[Game Page] Attempting WebSocket connection...')
|
|
||||||
connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) => {
|
watch(isConnected, async (connected) => {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
connectionStatus.value = 'connected'
|
connectionStatus.value = 'connected'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user