From 2dd2b530f8a91d5861bb3983bfa34aa93aefad7c Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 15 Jan 2026 13:58:02 -0600 Subject: [PATCH] CLAUDE: Fix lineup builder bugs and improve player image display Bug fixes: - Fix pitcher filter to recognize SP, RP, CP positions (not just P) - Fix validation to allow 9 players when pitcher bats (no DH games) - Simplify position filters to All/Batters/Pitchers Image display improvements: - Use headshot > vanity_card priority (avoid card images in circles) - Show player initials as fallback (e.g., "AV" for Alex Verdugo) - Handle name suffixes (Jr, Sr, II, III, IV) correctly Co-Authored-By: Claude Opus 4.5 --- frontend-sba/pages/games/lineup/[id].vue | 76 ++++++++++++++---------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/frontend-sba/pages/games/lineup/[id].vue b/frontend-sba/pages/games/lineup/[id].vue index 97624a7..c1ab099 100644 --- a/frontend-sba/pages/games/lineup/[id].vue +++ b/frontend-sba/pages/games/lineup/[id].vue @@ -20,7 +20,7 @@ const activeTab = ref('away') // Away bats first // Search and filter state const searchQuery = ref('') -type PositionFilter = 'all' | 'catchers' | 'infielders' | 'outfielders' | 'pitchers' +type PositionFilter = 'all' | 'batters' | 'pitchers' const positionFilter = ref('all') // Player preview modal @@ -67,10 +67,13 @@ const availableAwayRoster = computed(() => { const currentLineup = computed(() => activeTab.value === 'home' ? homeLineup.value : awayLineup.value) const currentRoster = computed(() => activeTab.value === 'home' ? availableHomeRoster.value : availableAwayRoster.value) -// Slot 10 (pitcher) should be disabled if P is selected in batting order +// Position category helpers (defined early for validation) +const PITCHER_POSITIONS = ['P', 'SP', 'RP', 'CP'] + +// Slot 10 (pitcher) should be disabled if a pitcher is in batting order const pitcherSlotDisabled = computed(() => { const lineup = currentLineup.value - return lineup.slice(0, 9).some(slot => slot.position === 'P') + return lineup.slice(0, 9).some(slot => slot.position && PITCHER_POSITIONS.includes(slot.position)) }) // Validation @@ -102,8 +105,8 @@ const validationErrors = computed(() => { function validateLineup(lineup: LineupSlot[], teamName: string): string[] { const errors: string[] = [] - // Check if P is in batting order - const pitcherInBattingOrder = lineup.slice(0, 9).some(s => s.position === 'P') + // Check if a pitcher is in batting order (no DH game) + const pitcherInBattingOrder = lineup.slice(0, 9).some(s => s.position && PITCHER_POSITIONS.includes(s.position)) const requiredSlots = pitcherInBattingOrder ? 9 : 10 // Check if all slots filled @@ -207,26 +210,17 @@ function clearLineup() { }) } -// Position category helpers -const CATCHER_POSITIONS = ['C'] -const INFIELD_POSITIONS = ['1B', '2B', '3B', 'SS'] -const OUTFIELD_POSITIONS = ['LF', 'CF', 'RF'] -const PITCHER_POSITIONS = ['P'] - function playerHasPositionInCategory(player: SbaPlayer, category: PositionFilter): boolean { if (category === 'all') return true const positions = getPlayerPositions(player) + const isPitcher = positions.some(p => PITCHER_POSITIONS.includes(p)) switch (category) { - case 'catchers': - return positions.some(p => CATCHER_POSITIONS.includes(p)) - case 'infielders': - return positions.some(p => INFIELD_POSITIONS.includes(p)) - case 'outfielders': - return positions.some(p => OUTFIELD_POSITIONS.includes(p)) case 'pitchers': - return positions.some(p => PITCHER_POSITIONS.includes(p)) + return isPitcher + case 'batters': + return !isPitcher default: return true } @@ -269,6 +263,24 @@ function closePlayerPreview() { previewPlayer.value = null } +// Get player preview image with fallback priority: headshot > vanity_card > null +function getPlayerPreviewImage(player: SbaPlayer): string | null { + return player.headshot || player.vanity_card || null +} + +// Get player avatar fallback - use first + last initials (e.g., "Alex Verdugo" -> "AV") +// Ignores common suffixes like Jr, Sr, II, III, IV +function getPlayerFallbackInitial(player: SbaPlayer): string { + const suffixes = ['jr', 'jr.', 'sr', 'sr.', 'ii', 'iii', 'iv', 'v'] + const parts = player.name.trim().split(/\s+/).filter( + part => !suffixes.includes(part.toLowerCase()) + ) + if (parts.length >= 2) { + return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase() + } + return parts[0]?.charAt(0).toUpperCase() || '?' +} + // Fetch game data async function fetchGameData() { try { @@ -493,7 +505,7 @@ onMounted(async () => {
@@ -650,14 +662,14 @@ onMounted(async () => {
- {{ slot.player.name.charAt(0) }} + {{ getPlayerFallbackInitial(slot.player) }}
@@ -773,14 +785,14 @@ onMounted(async () => {
- {{ pitcherPlayer.name.charAt(0) }} + {{ getPlayerFallbackInitial(pitcherPlayer) }}
@@ -872,14 +884,14 @@ onMounted(async () => {
-
- {{ previewPlayer.name.charAt(0) }} +
+ {{ getPlayerFallbackInitial(previewPlayer) }}