strat-gameplay-webapp/frontend-sba/components/Schedule/GameCard.vue
Cal Corum fbbb1cc5da CLAUDE: Add SBA schedule integration with weekly matchup display
Implements schedule viewing from SBA production API with week navigation
and game creation from scheduled matchups. Groups games by team matchup
horizontally with games stacked vertically for space efficiency.

Backend:
- Add schedule routes (/api/schedule/current, /api/schedule/games)
- Add SBA API client methods for schedule data
- Fix multi-worker state isolation (single worker for in-memory state)
- Add Redis migration TODO for future scalability
- Support custom team IDs in quick-create endpoint

Frontend:
- Add Schedule tab as default on home page
- Week navigation with prev/next and "Current Week" jump
- Horizontal group layout (2-6 columns responsive)
- Completed games show score + "Final" badge (no Play button)
- Incomplete games show "Play" button to create webapp game

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 23:39:31 -06:00

88 lines
2.6 KiB
Vue

<template>
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition p-4 border border-gray-200">
<!-- Game Badge -->
<div class="flex justify-between items-start mb-3">
<span class="px-2 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded">
{{ game.season_type || 'Regular' }}
</span>
<span v-if="hasScore" class="text-xs text-green-600 font-medium">
Completed
</span>
</div>
<!-- Matchup Display -->
<div class="space-y-2 mb-4">
<!-- Away Team -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-xs text-gray-400 w-10">Away</span>
<span class="font-semibold text-gray-900">
{{ game.away_team.abbrev }}
</span>
<span class="text-sm text-gray-600 hidden sm:inline">
{{ game.away_team.sname }}
</span>
</div>
<span v-if="game.away_score !== null" class="text-lg font-bold tabular-nums">
{{ game.away_score }}
</span>
</div>
<!-- Home Team -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-xs text-gray-400 w-10">Home</span>
<span class="font-semibold text-gray-900">
{{ game.home_team.abbrev }}
</span>
<span class="text-sm text-gray-600 hidden sm:inline">
{{ game.home_team.sname }}
</span>
</div>
<span v-if="game.home_score !== null" class="text-lg font-bold tabular-nums">
{{ game.home_score }}
</span>
</div>
</div>
<!-- Play This Game Button -->
<button
@click="handlePlayGame"
:disabled="isCreating"
class="w-full px-4 py-2 bg-primary hover:bg-blue-700 disabled:bg-gray-400 text-white font-medium rounded-lg transition disabled:cursor-not-allowed"
>
{{ isCreating ? 'Creating...' : 'Play This Game' }}
</button>
</div>
</template>
<script setup lang="ts">
import type { SbaScheduledGame } from '~/types'
interface Props {
game: SbaScheduledGame
}
const props = defineProps<Props>()
const emit = defineEmits<{
play: [homeTeamId: number, awayTeamId: number]
}>()
const isCreating = ref(false)
// Check if game has scores (completed)
const hasScore = computed(() => {
return props.game.away_score !== null && props.game.home_score !== null
})
function handlePlayGame() {
emit('play', props.game.home_team.id, props.game.away_team.id)
}
// Expose isCreating for parent to control
defineExpose({
setCreating: (value: boolean) => { isCreating.value = value }
})
</script>