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>
191 lines
5.6 KiB
Vue
Executable File
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>
|