Phase F1 - Authentication: - OAuth callback handling with token management - Auth guards for protected routes - Account linking composable - Profile page updates Phase F2 - Deck Management: - Collection page with card filtering and display - Decks page with CRUD operations - Deck builder with drag-drop support - Collection and deck Pinia stores Phase F3 - Phaser Integration: - Game bridge composable for Vue-Phaser communication - Game page with Phaser canvas mounting - Socket.io event types for real-time gameplay - Game store with match state management - Phaser scene scaffolding and type definitions Also includes: - New UI components (ConfirmDialog, EmptyState, FilterBar, etc.) - Toast notification system - Game config composable for dynamic rule loading - Comprehensive test coverage for new features
92 lines
2.6 KiB
Vue
92 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Root application component.
|
|
*
|
|
* Handles auth initialization on startup and renders the appropriate layout
|
|
* based on the current route's meta.layout property.
|
|
*
|
|
* Auth initialization:
|
|
* - Shows loading spinner while validating tokens
|
|
* - Refreshes expired tokens automatically
|
|
* - Fetches user profile if authenticated
|
|
* - Blocks navigation until initialization completes
|
|
*/
|
|
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
import LoadingOverlay from '@/components/ui/LoadingOverlay.vue'
|
|
import ToastContainer from '@/components/ui/ToastContainer.vue'
|
|
import { useAuth } from '@/composables/useAuth'
|
|
import { useGameConfig } from '@/composables/useGameConfig'
|
|
|
|
// Lazy-load layouts to reduce initial bundle size
|
|
const DefaultLayout = defineAsyncComponent(() => import('@/layouts/DefaultLayout.vue'))
|
|
const MinimalLayout = defineAsyncComponent(() => import('@/layouts/MinimalLayout.vue'))
|
|
const GameLayout = defineAsyncComponent(() => import('@/layouts/GameLayout.vue'))
|
|
|
|
const route = useRoute()
|
|
const { initialize } = useAuth()
|
|
const { fetchDeckConfig } = useGameConfig()
|
|
|
|
// Track if auth initialization is in progress
|
|
const isAuthInitializing = ref(true)
|
|
|
|
type LayoutType = 'default' | 'minimal' | 'game'
|
|
|
|
const layoutComponents = {
|
|
default: DefaultLayout,
|
|
minimal: MinimalLayout,
|
|
game: GameLayout,
|
|
} as const
|
|
|
|
const currentLayout = computed(() => {
|
|
const layout = (route.meta.layout as LayoutType) || 'default'
|
|
return layoutComponents[layout] || layoutComponents.default
|
|
})
|
|
|
|
// Initialize app on mount
|
|
onMounted(async () => {
|
|
try {
|
|
// Initialize auth and fetch config in parallel
|
|
await Promise.all([
|
|
initialize(),
|
|
fetchDeckConfig(),
|
|
])
|
|
} finally {
|
|
isAuthInitializing.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Show loading spinner while auth initializes -->
|
|
<div
|
|
v-if="isAuthInitializing"
|
|
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-900"
|
|
>
|
|
<div class="flex flex-col items-center gap-4">
|
|
<!-- Spinner -->
|
|
<div class="relative">
|
|
<div class="w-12 h-12 border-4 border-primary/30 rounded-full" />
|
|
<div
|
|
class="absolute inset-0 w-12 h-12 border-4 border-primary border-t-transparent rounded-full animate-spin"
|
|
/>
|
|
</div>
|
|
<p class="text-gray-300 text-sm">
|
|
Loading...
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main app content (only after auth is initialized) -->
|
|
<template v-else>
|
|
<component :is="currentLayout">
|
|
<RouterView />
|
|
</component>
|
|
</template>
|
|
|
|
<!-- Global UI components -->
|
|
<LoadingOverlay />
|
|
<ToastContainer />
|
|
</template>
|