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>
88 lines
2.6 KiB
Vue
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>
|