strat-gameplay-webapp/frontend-sba/pages/games/create.vue
Cal Corum 581bc33f15 CLAUDE: Move games list to index page for better UX
SBA users are already league members - they don't need a marketing page.
Moving the games list to "/" reduces friction by eliminating a redirect.

Changes:
- pages/index.vue: Now shows games list (was marketing/dashboard)
- pages/games/index.vue: Redirects to / for backwards compatibility
- Updated all internal links from /games to /
- Auth callback redirects to / after login

User flow is now:
- Not logged in: / → auth middleware → Discord OAuth → /
- Logged in: / shows games list directly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 20:59:34 -06:00

191 lines
5.6 KiB
Vue
Executable File

<template>
<div class="max-w-2xl mx-auto">
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 mb-2">Create New Game</h1>
<p class="text-gray-600">
Set up a new game to play with friends
</p>
</div>
<!-- Create Game Form -->
<form class="bg-white rounded-lg shadow-md p-8" @submit.prevent="handleCreateGame">
<!-- Game Name -->
<div class="mb-6">
<label for="gameName" class="block text-sm font-medium text-gray-700 mb-2">
Game Name
</label>
<input
id="gameName"
v-model="formData.name"
type="text"
required
placeholder="Enter a name for this game"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition"
>
</div>
<!-- Home Team -->
<div class="mb-6">
<label for="homeTeam" class="block text-sm font-medium text-gray-700 mb-2">
Home Team
</label>
<select
id="homeTeam"
v-model="formData.homeTeamId"
:disabled="isLoadingTeams"
required
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition disabled:bg-gray-100 disabled:cursor-not-allowed"
>
<option value="" disabled>
{{ isLoadingTeams ? 'Loading teams...' : 'Select home team' }}
</option>
<option
v-for="team in availableTeams"
:key="team.id"
:value="team.id"
>
{{ team.lname }} ({{ team.abbrev }})
</option>
</select>
</div>
<!-- Away Team -->
<div class="mb-6">
<label for="awayTeam" class="block text-sm font-medium text-gray-700 mb-2">
Away Team
</label>
<select
id="awayTeam"
v-model="formData.awayTeamId"
:disabled="isLoadingTeams"
required
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition disabled:bg-gray-100 disabled:cursor-not-allowed"
>
<option value="" disabled>
{{ isLoadingTeams ? 'Loading teams...' : 'Select away team' }}
</option>
<option
v-for="team in availableTeams"
:key="team.id"
:value="team.id"
:disabled="team.id === formData.homeTeamId"
>
{{ team.lname }} ({{ team.abbrev }})
</option>
</select>
</div>
<!-- Error Message -->
<div
v-if="error"
class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg text-red-800 text-sm"
>
{{ error }}
</div>
<!-- Actions -->
<div class="flex items-center justify-between">
<NuxtLink
to="/"
class="px-6 py-3 text-gray-700 hover:text-gray-900 font-medium transition"
>
Cancel
</NuxtLink>
<button
type="submit"
:disabled="isLoading || isLoadingTeams"
class="px-6 py-3 bg-primary hover:bg-blue-700 disabled:bg-gray-400 text-white font-semibold rounded-lg transition disabled:cursor-not-allowed"
>
{{ isLoadingTeams ? 'Loading...' : isLoading ? 'Creating...' : 'Create Game' }}
</button>
</div>
</form>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '~/store/auth'
import type { SbaTeam } from '~/types/api'
definePageMeta({
middleware: ['auth'], // Require authentication
})
const authStore = useAuthStore()
const router = useRouter()
const config = useRuntimeConfig()
const isLoading = ref(false)
const isLoadingTeams = ref(true)
const error = ref<string | null>(null)
const availableTeams = ref<SbaTeam[]>([])
const formData = ref({
name: '',
homeTeamId: '',
awayTeamId: '',
})
// Fetch teams on component mount
onMounted(async () => {
try {
isLoadingTeams.value = true
const teams = await $fetch<SbaTeam[]>(`${config.public.apiUrl}/api/teams/`, {
params: { season: 3 },
})
availableTeams.value = teams
} catch (err: any) {
console.error('Failed to load teams:', err)
error.value = 'Failed to load teams. Please refresh the page.'
} finally {
isLoadingTeams.value = false
}
})
const handleCreateGame = async () => {
try {
isLoading.value = true
error.value = null
// Validate form
if (!formData.value.name || !formData.value.homeTeamId || !formData.value.awayTeamId) {
error.value = 'Please fill in all required fields'
return
}
if (formData.value.homeTeamId === formData.value.awayTeamId) {
error.value = 'Home and away teams must be different'
return
}
// Call API to create game
const response = await $fetch<{ game_id: string; message: string; status: string }>(
`${config.public.apiUrl}/api/games/`,
{
method: 'POST',
body: {
name: formData.value.name,
home_team_id: formData.value.homeTeamId,
away_team_id: formData.value.awayTeamId,
is_ai_opponent: false, // SBA only supports human vs human
season: 3,
league_id: 'sba',
},
}
)
// Redirect to lineup builder
router.push(`/games/lineup/${response.game_id}`)
} catch (err: any) {
error.value = err.data?.detail || err.message || 'Failed to create game'
console.error('Create game error:', err)
} finally {
isLoading.value = false
}
}
</script>
<style scoped>
/* Additional component styles if needed */
</style>