mantimon-tcg/frontend/src/App.vue
Cal Corum 059536a42b Implement frontend phases F1-F3: auth, deck management, Phaser integration
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
2026-01-31 15:43:56 -06:00

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>