CLAUDE: Add team color gradient to scoreboard and fix sticky tabs
- ScoreBoard: Dynamic gradient using team colors (away left, home right) with dark center blend and 20% overlay for text readability - Fetch team colors from API using cached season from schedule state - Fix sticky tabs by removing overflow-auto from game layout main - Move play-by-play below gameplay panel on mobile layout Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3a91a5d477
commit
ff3f1746d6
@ -75,15 +75,6 @@
|
||||
:current-pitcher="gameState?.current_pitcher"
|
||||
/>
|
||||
|
||||
<!-- Play-by-Play Feed -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-md">
|
||||
<PlayByPlay
|
||||
:plays="playHistory"
|
||||
:limit="5"
|
||||
:compact="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Decision Panel (Phase F3) -->
|
||||
<DecisionPanel
|
||||
v-if="showDecisions"
|
||||
@ -116,6 +107,15 @@
|
||||
@submit-outcome="handleSubmitOutcome"
|
||||
@dismiss-result="handleDismissResult"
|
||||
/>
|
||||
|
||||
<!-- Play-by-Play Feed (below gameplay on mobile) -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-md">
|
||||
<PlayByPlay
|
||||
:plays="playHistory"
|
||||
:limit="5"
|
||||
:compact="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Layout (Grid) -->
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<div class="bg-gradient-to-r from-primary to-blue-600 text-white shadow-lg">
|
||||
<div class="container mx-auto px-3 py-4">
|
||||
<div class="relative text-white shadow-lg overflow-hidden">
|
||||
<!-- Team colors gradient background -->
|
||||
<div
|
||||
class="absolute inset-0"
|
||||
:style="gradientStyle"
|
||||
/>
|
||||
<!-- Dark overlay for text readability -->
|
||||
<div class="absolute inset-0 bg-black/20" />
|
||||
<!-- Content -->
|
||||
<div class="relative container mx-auto px-3 py-4">
|
||||
<!-- Mobile Layout (default) -->
|
||||
<div class="lg:hidden">
|
||||
<!-- Score Display with Game Situation -->
|
||||
@ -156,6 +164,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { InningHalf } from '~/types/game'
|
||||
|
||||
interface Props {
|
||||
@ -169,6 +178,8 @@ interface Props {
|
||||
second: boolean
|
||||
third: boolean
|
||||
}
|
||||
awayTeamColor?: string
|
||||
homeTeamColor?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@ -177,7 +188,21 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
inning: 1,
|
||||
half: 'top',
|
||||
outs: 0,
|
||||
runners: () => ({ first: false, second: false, third: false })
|
||||
runners: () => ({ first: false, second: false, third: false }),
|
||||
awayTeamColor: undefined,
|
||||
homeTeamColor: undefined
|
||||
})
|
||||
|
||||
// Generate gradient style from team colors
|
||||
// Uses Option 7: Solid blocks with center blend (away 30% -> dark center 50% -> home 70%)
|
||||
const gradientStyle = computed(() => {
|
||||
const awayColor = props.awayTeamColor || '#1e40af' // Default: SBA blue
|
||||
const homeColor = props.homeTeamColor || '#1e40af' // Default: SBA blue
|
||||
const centerColor = '#1f2937' // gray-800
|
||||
|
||||
return {
|
||||
background: `linear-gradient(to right, ${awayColor} 30%, ${centerColor} 50%, ${homeColor} 70%)`
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
</header>
|
||||
|
||||
<!-- Game Content (Full Width, No Container) -->
|
||||
<main class="flex-1 overflow-auto">
|
||||
<main class="flex-1">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
<!-- Sticky Header: ScoreBoard + Tabs -->
|
||||
<div class="sticky top-0 z-30">
|
||||
<!-- ScoreBoard -->
|
||||
<!-- ScoreBoard (scrolls with content) -->
|
||||
<ScoreBoard
|
||||
:home-score="gameState?.home_score"
|
||||
:away-score="gameState?.away_score"
|
||||
@ -10,10 +8,12 @@
|
||||
:half="gameState?.half"
|
||||
:outs="gameState?.outs"
|
||||
:runners="runnersState"
|
||||
:away-team-color="awayTeamColor"
|
||||
:home-team-color="homeTeamColor"
|
||||
/>
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<div class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||
<!-- Tab Navigation (sticky below header) -->
|
||||
<div class="sticky top-[52px] z-30 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex">
|
||||
<button
|
||||
@ -37,7 +37,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content">
|
||||
@ -68,6 +67,7 @@
|
||||
import { useGameStore } from '~/store/game'
|
||||
import { useAuthStore } from '~/store/auth'
|
||||
import { useUiStore } from '~/store/ui'
|
||||
import type { SbaTeam } from '~/types/api'
|
||||
import ScoreBoard from '~/components/Game/ScoreBoard.vue'
|
||||
import GamePlay from '~/components/Game/GamePlay.vue'
|
||||
import LineupBuilder from '~/components/Game/LineupBuilder.vue'
|
||||
@ -78,12 +78,18 @@ definePageMeta({
|
||||
middleware: ['auth'],
|
||||
})
|
||||
|
||||
// Config
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
// Stores
|
||||
const route = useRoute()
|
||||
const gameStore = useGameStore()
|
||||
const authStore = useAuthStore()
|
||||
const uiStore = useUiStore()
|
||||
|
||||
// Team data for colors
|
||||
const teamsMap = ref<Map<number, SbaTeam>>(new Map())
|
||||
|
||||
// Game ID from route
|
||||
const gameId = computed(() => route.params.id as string)
|
||||
|
||||
@ -115,6 +121,63 @@ const runnersState = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// Team colors for ScoreBoard gradient
|
||||
const awayTeamColor = computed(() => {
|
||||
if (!gameState.value) return undefined
|
||||
return teamsMap.value.get(gameState.value.away_team_id)?.color
|
||||
})
|
||||
|
||||
const homeTeamColor = computed(() => {
|
||||
if (!gameState.value) return undefined
|
||||
return teamsMap.value.get(gameState.value.home_team_id)?.color
|
||||
})
|
||||
|
||||
// Get season from schedule state (already fetched on /games page)
|
||||
const selectedSeason = useState<number>('schedule-season')
|
||||
|
||||
// Fetch team data when game state becomes available
|
||||
watch(gameState, async (state) => {
|
||||
if (!state) return
|
||||
|
||||
const teamIds = [state.home_team_id, state.away_team_id]
|
||||
|
||||
// Only fetch teams we don't have yet
|
||||
const missingIds = teamIds.filter(id => !teamsMap.value.has(id))
|
||||
if (missingIds.length === 0) return
|
||||
|
||||
try {
|
||||
// Get season - use cached value or fetch current
|
||||
let season = selectedSeason.value
|
||||
if (!season) {
|
||||
console.log('[Game Page] No cached season, fetching current...')
|
||||
const currentInfo = await $fetch<{ season: number; week: number }>(`${config.public.apiUrl}/api/schedule/current`, {
|
||||
credentials: 'include'
|
||||
})
|
||||
season = currentInfo.season
|
||||
selectedSeason.value = season // Cache it
|
||||
}
|
||||
|
||||
console.log('[Game Page] Fetching teams for season', season)
|
||||
|
||||
// Fetch all teams for the season and filter to the ones we need
|
||||
const teams = await $fetch<SbaTeam[]>(`${config.public.apiUrl}/api/teams/`, {
|
||||
credentials: 'include',
|
||||
query: { season }
|
||||
})
|
||||
|
||||
console.log('[Game Page] Got teams, looking for IDs:', teamIds)
|
||||
|
||||
for (const team of teams) {
|
||||
if (teamIds.includes(team.id)) {
|
||||
console.log('[Game Page] Found team:', team.id, team.sname, 'color:', team.color)
|
||||
teamsMap.value.set(team.id, team)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Game Page] Failed to fetch team data for colors:', error)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// Check if user is a manager of either team in this game
|
||||
const isUserManager = computed(() => {
|
||||
if (!gameState.value) return false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user