CLAUDE: Fix game recovery to load team display info and add score text outline
Backend: - Add game_metadata to load_game_state() return dict in DatabaseOperations - Populate team display fields (name, color, thumbnail) in _rebuild_state_from_data() so recovered games show team colors/names Frontend: - Add text-outline CSS for score visibility on any background (light logos, gradients) - Handle thumbnail 404 with @error event, show enhanced shadow when no thumbnail - Apply consistent outline across mobile and desktop layouts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d60b7a2d60
commit
64325d7163
@ -389,6 +389,11 @@ class StateManager:
|
||||
if current_pitcher and current_catcher:
|
||||
break
|
||||
|
||||
# Extract team display info from game_metadata (stored at game creation)
|
||||
game_metadata = game.get("game_metadata") or {}
|
||||
home_meta = game_metadata.get("home_team", {})
|
||||
away_meta = game_metadata.get("away_team", {})
|
||||
|
||||
state = GameState(
|
||||
game_id=game["id"],
|
||||
league_id=game["league_id"],
|
||||
@ -405,6 +410,15 @@ class StateManager:
|
||||
current_batter=current_batter_placeholder,
|
||||
current_pitcher=current_pitcher,
|
||||
current_catcher=current_catcher,
|
||||
# Team display info from metadata
|
||||
home_team_name=home_meta.get("lname"),
|
||||
home_team_abbrev=home_meta.get("abbrev"),
|
||||
home_team_color=home_meta.get("color"),
|
||||
home_team_thumbnail=home_meta.get("thumbnail"),
|
||||
away_team_name=away_meta.get("lname"),
|
||||
away_team_abbrev=away_meta.get("abbrev"),
|
||||
away_team_color=away_meta.get("color"),
|
||||
away_team_thumbnail=away_meta.get("thumbnail"),
|
||||
)
|
||||
|
||||
# Get last completed play to recover runner state and batter indices
|
||||
|
||||
@ -525,6 +525,7 @@ class DatabaseOperations:
|
||||
"current_half": game.current_half,
|
||||
"home_score": game.home_score,
|
||||
"away_score": game.away_score,
|
||||
"game_metadata": game.game_metadata, # Team display info
|
||||
},
|
||||
"lineups": [
|
||||
{
|
||||
|
||||
@ -16,14 +16,21 @@
|
||||
<!-- Away Team -->
|
||||
<div class="flex-1 text-center relative">
|
||||
<img
|
||||
v-if="awayTeamThumbnail"
|
||||
v-if="awayTeamThumbnail && !awayThumbnailFailed"
|
||||
:src="awayTeamThumbnail"
|
||||
alt=""
|
||||
class="absolute inset-0 w-full h-full object-contain opacity-15 pointer-events-none"
|
||||
@error="awayThumbnailFailed = true"
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="text-xs font-medium text-blue-100 mb-1">AWAY</div>
|
||||
<div class="text-4xl font-bold tabular-nums">{{ awayScore }}</div>
|
||||
<div
|
||||
class="text-xs font-medium mb-1 text-outline"
|
||||
:class="showAwayShadow ? 'text-white text-outline-strong' : 'text-blue-100'"
|
||||
>AWAY</div>
|
||||
<div
|
||||
class="text-4xl font-bold tabular-nums text-outline"
|
||||
:class="showAwayShadow ? 'text-outline-strong' : ''"
|
||||
>{{ awayScore }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -87,14 +94,21 @@
|
||||
<!-- Home Team -->
|
||||
<div class="flex-1 text-center relative">
|
||||
<img
|
||||
v-if="homeTeamThumbnail"
|
||||
v-if="homeTeamThumbnail && !homeThumbnailFailed"
|
||||
:src="homeTeamThumbnail"
|
||||
alt=""
|
||||
class="absolute inset-0 w-full h-full object-contain opacity-15 pointer-events-none"
|
||||
@error="homeThumbnailFailed = true"
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="text-xs font-medium text-blue-100 mb-1">HOME</div>
|
||||
<div class="text-4xl font-bold tabular-nums">{{ homeScore }}</div>
|
||||
<div
|
||||
class="text-xs font-medium mb-1 text-outline"
|
||||
:class="showHomeShadow ? 'text-white text-outline-strong' : 'text-blue-100'"
|
||||
>HOME</div>
|
||||
<div
|
||||
class="text-4xl font-bold tabular-nums text-outline"
|
||||
:class="showHomeShadow ? 'text-outline-strong' : ''"
|
||||
>{{ homeScore }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,14 +120,21 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-center min-w-[100px] relative">
|
||||
<img
|
||||
v-if="awayTeamThumbnail"
|
||||
v-if="awayTeamThumbnail && !awayThumbnailFailed"
|
||||
:src="awayTeamThumbnail"
|
||||
alt=""
|
||||
class="absolute inset-0 w-full h-full object-contain opacity-15 pointer-events-none"
|
||||
@error="awayThumbnailFailed = true"
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="text-sm font-medium text-blue-100">AWAY</div>
|
||||
<div class="text-5xl font-bold tabular-nums">{{ awayScore }}</div>
|
||||
<div
|
||||
class="text-sm font-medium text-outline"
|
||||
:class="showAwayShadow ? 'text-white text-outline-strong' : 'text-blue-100'"
|
||||
>AWAY</div>
|
||||
<div
|
||||
class="text-5xl font-bold tabular-nums text-outline"
|
||||
:class="showAwayShadow ? 'text-outline-strong' : ''"
|
||||
>{{ awayScore }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -179,14 +200,21 @@
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-center min-w-[100px] relative">
|
||||
<img
|
||||
v-if="homeTeamThumbnail"
|
||||
v-if="homeTeamThumbnail && !homeThumbnailFailed"
|
||||
:src="homeTeamThumbnail"
|
||||
alt=""
|
||||
class="absolute inset-0 w-full h-full object-contain opacity-15 pointer-events-none"
|
||||
@error="homeThumbnailFailed = true"
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="text-sm font-medium text-blue-100">HOME</div>
|
||||
<div class="text-5xl font-bold tabular-nums">{{ homeScore }}</div>
|
||||
<div
|
||||
class="text-sm font-medium text-outline"
|
||||
:class="showHomeShadow ? 'text-white text-outline-strong' : 'text-blue-100'"
|
||||
>HOME</div>
|
||||
<div
|
||||
class="text-5xl font-bold tabular-nums text-outline"
|
||||
:class="showHomeShadow ? 'text-outline-strong' : ''"
|
||||
>{{ homeScore }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -196,7 +224,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { InningHalf } from '~/types/game'
|
||||
|
||||
interface Props {
|
||||
@ -229,6 +257,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
homeTeamThumbnail: undefined
|
||||
})
|
||||
|
||||
// Track thumbnail load failures
|
||||
const awayThumbnailFailed = ref(false)
|
||||
const homeThumbnailFailed = ref(false)
|
||||
|
||||
// Show enhanced shadow effect when no thumbnail (missing or failed to load)
|
||||
// Even with thumbnails, we use a subtle outline for readability
|
||||
const showAwayShadow = computed(() => !props.awayTeamThumbnail || awayThumbnailFailed.value)
|
||||
const showHomeShadow = computed(() => !props.homeTeamThumbnail || homeThumbnailFailed.value)
|
||||
|
||||
// Generate gradient style from team colors
|
||||
// Uses Option 7: Solid blocks with center blend (away 30% -> dark center 50% -> home 70%)
|
||||
const gradientStyle = computed(() => {
|
||||
@ -248,6 +285,25 @@ const gradientStyle = computed(() => {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Text outline for readability on any background (light logos, gradients) */
|
||||
.text-outline {
|
||||
text-shadow:
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.5),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.5),
|
||||
-1px 1px 0 rgba(0, 0, 0, 0.5),
|
||||
1px 1px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Enhanced outline when no thumbnail - more prominent shadow */
|
||||
.text-outline-strong {
|
||||
text-shadow:
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.8),
|
||||
-1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
0 2px 8px rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
/* Pulse animation for runners */
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user