mantimon-tcg/.claude/frontend-poc/PROJECT_PLAN_FRONTEND.json
Cal Corum 8685e3e16e Archive frontend POC to .claude/frontend-poc/
Preserves the working F3 Phaser demo implementation before resetting
the main frontend/ directory for a fresh start. The POC demonstrates:
- Vue 3 + Phaser 3 integration
- Real card rendering with images
- Vue-Phaser state sync via gameBridge
- Card interactions and damage counters

To restore: copy .claude/frontend-poc/ back to frontend/ and run npm install
2026-01-31 22:00:51 -06:00

990 lines
34 KiB
JSON

{
"meta": {
"version": "1.1.0",
"created": "2026-01-30",
"lastUpdated": "2026-01-31",
"planType": "master",
"projectName": "Mantimon TCG - Frontend",
"description": "Vue 3 + Phaser 3 frontend for pocket.manticorum.com - real-time multiplayer TCG with campaign mode",
"totalPhases": 8,
"completedPhases": 4,
"status": "Phase F3 COMPLETE - Ready for Phase F4 (Live Gameplay)"
},
"techStack": {
"framework": "Vue 3 (Composition API + <script setup>)",
"gameEngine": "Phaser 3",
"language": "TypeScript",
"stateManagement": "Pinia",
"styling": "Tailwind CSS",
"realtime": "Socket.io-client",
"buildTool": "Vite",
"testing": "Vitest + Vue Test Utils",
"e2e": "Playwright (future)"
},
"architectureDecisions": {
"vuePhaser": {
"pattern": "Phaser mounts as Vue component, communication via event bridge",
"rationale": "Keep Vue for UI/state, Phaser for game canvas rendering only",
"communication": "EventEmitter pattern - Vue emits intentions, Phaser emits completion"
},
"stateManagement": {
"pattern": "Pinia stores as single source of truth, Phaser reads from stores",
"stores": ["auth", "user", "game", "deck", "collection", "ui"]
},
"apiLayer": {
"pattern": "Composables for API calls, automatic token refresh",
"structure": "useApi() base, useAuth(), useDecks(), useGames() domain composables"
},
"socketIO": {
"pattern": "Singleton connection manager with Pinia integration",
"reconnection": "Auto-reconnect with exponential backoff, queue actions during disconnect"
},
"routing": {
"guards": "Auth guard redirects to login, game guard ensures valid game state",
"lazyLoading": "Route-level code splitting for Phaser scenes"
}
},
"sitePlan": {
"designApproach": "Mobile-first, desktop is first-class citizen",
"navigation": {
"desktop": "Left sidebar",
"mobile": "Bottom tabs",
"items": [
{"icon": "home", "label": "Home", "route": "/"},
{"icon": "cards", "label": "Collection", "route": "/collection"},
{"icon": "deck", "label": "Decks", "route": "/decks"},
{"icon": "play", "label": "Play", "route": "/play"},
{"icon": "user", "label": "Profile", "route": "/profile"}
]
},
"layouts": {
"minimal": {
"routes": ["/login", "/auth/callback"],
"description": "No navigation, centered content"
},
"game": {
"routes": ["/game/:id"],
"description": "Full viewport for Phaser, no nav"
},
"default": {
"routes": ["*"],
"description": "Sidebar (desktop) / bottom tabs (mobile)"
}
},
"routes": [
{"path": "/login", "name": "Login", "auth": "guest", "layout": "minimal", "notes": "OAuth buttons, redirect if logged in"},
{"path": "/auth/callback", "name": "AuthCallback", "auth": "none", "layout": "minimal", "notes": "Silent token handling"},
{"path": "/starter", "name": "StarterSelection", "auth": "required", "layout": "minimal", "notes": "Redirect here if no starter deck"},
{"path": "/", "name": "Dashboard", "auth": "required", "layout": "default", "notes": "Home with widgets (future)"},
{"path": "/collection", "name": "Collection", "auth": "required", "layout": "default", "notes": "Card grid with filters"},
{"path": "/decks", "name": "DeckList", "auth": "required", "layout": "default", "notes": "List + create button"},
{"path": "/decks/new", "name": "NewDeck", "auth": "required", "layout": "default", "notes": "Deck builder (empty)"},
{"path": "/decks/:id", "name": "EditDeck", "auth": "required", "layout": "default", "notes": "Deck builder (existing)"},
{"path": "/play", "name": "PlayMenu", "auth": "required", "layout": "default", "notes": "Game mode selection"},
{"path": "/game/:id", "name": "Game", "auth": "required", "layout": "game", "notes": "Phaser canvas, full viewport"},
{"path": "/profile", "name": "Profile", "auth": "required", "layout": "default", "notes": "Display name, avatar, linked accounts, logout"}
],
"authFlow": {
"loggedOut": "Redirect to /login",
"loggedInNoStarter": "Redirect to /starter",
"loggedIn": "Allow access to protected routes"
},
"futureRoutes": [
{"path": "/campaign", "backendDep": "Phase 5"},
{"path": "/campaign/club/:id", "backendDep": "Phase 5"},
{"path": "/matchmaking", "backendDep": "Phase 6"},
{"path": "/history", "backendDep": "Phase 6"},
{"path": "/packs", "backendDep": "Phase 5"}
],
"dashboardWidgets": {
"planned": [
"Active games (resume)",
"Quick play button",
"Recent matches",
"Collection stats",
"Daily rewards (Phase 5+)",
"News/announcements"
],
"v1": ["Quick play button", "Active games"]
},
"profilePage": {
"v1Features": [
"Avatar display",
"Display name (editable)",
"Linked accounts list",
"Logout button"
]
}
},
"phases": [
{
"id": "PHASE_F0",
"name": "Project Foundation",
"status": "COMPLETE",
"completedDate": "2026-01-30",
"description": "Scaffolding, tooling, core infrastructure, API client setup",
"estimatedDays": "3-5",
"dependencies": [],
"backendDependencies": ["PHASE_2"],
"deliverables": [
"Vite + Vue 3 + TypeScript project structure",
"Tailwind CSS configuration",
"Vue Router with lazy loading",
"Pinia store scaffolding",
"API client with auth header injection",
"Socket.IO client wrapper",
"Environment configuration (dev/staging/prod)",
"App shell with responsive layout",
"ESLint + Prettier configuration"
],
"tasks": [
{
"id": "F0-001",
"name": "Initialize Vite project",
"description": "Create Vue 3 + TypeScript project with Vite",
"files": ["package.json", "vite.config.ts", "tsconfig.json"],
"details": [
"npm create vite@latest . -- --template vue-ts",
"Configure path aliases (@/ for src/)",
"Set up TypeScript strict mode"
]
},
{
"id": "F0-002",
"name": "Install and configure Tailwind",
"description": "Set up Tailwind CSS with custom theme",
"files": ["tailwind.config.js", "src/assets/main.css"],
"details": [
"Install tailwindcss, postcss, autoprefixer",
"Configure content paths",
"Add Mantimon color palette (Pokemon-inspired)"
]
},
{
"id": "F0-003",
"name": "Set up Vue Router",
"description": "Configure routing with guards and lazy loading",
"files": ["src/router/index.ts", "src/router/guards.ts"],
"details": [
"Define route structure",
"Create auth guard placeholder",
"Configure lazy loading for heavy routes"
]
},
{
"id": "F0-004",
"name": "Set up Pinia stores",
"description": "Create store structure with persistence",
"files": ["src/stores/auth.ts", "src/stores/user.ts", "src/stores/ui.ts"],
"details": [
"Install pinia and pinia-plugin-persistedstate",
"Create auth store skeleton",
"Create user store skeleton",
"Create UI store (loading states, toasts)"
]
},
{
"id": "F0-005",
"name": "Create API client",
"description": "HTTP client with auth token injection and refresh",
"files": ["src/api/client.ts", "src/api/types.ts"],
"details": [
"Create fetch/axios wrapper",
"Inject Authorization header from auth store",
"Handle 401 responses with token refresh",
"Type API responses"
]
},
{
"id": "F0-006",
"name": "Create Socket.IO client",
"description": "WebSocket connection manager",
"files": ["src/socket/client.ts", "src/socket/types.ts"],
"details": [
"Install socket.io-client",
"Create connection manager singleton",
"Configure auth token in handshake",
"Set up reconnection with exponential backoff",
"Create typed event emitters"
]
},
{
"id": "F0-007",
"name": "Create app shell",
"description": "Basic layout with navigation",
"files": ["src/App.vue", "src/layouts/DefaultLayout.vue", "src/components/NavBar.vue"],
"details": [
"Responsive header/nav",
"Main content area with router-view",
"Loading overlay component",
"Toast notification area"
]
},
{
"id": "F0-008",
"name": "Environment configuration",
"description": "Configure environment variables",
"files": [".env.development", ".env.production", "src/config.ts"],
"details": [
"VITE_API_BASE_URL",
"VITE_WS_URL",
"VITE_OAUTH_REDIRECT_URI",
"Type-safe config access"
]
}
]
},
{
"id": "PHASE_F1",
"name": "Authentication",
"status": "COMPLETE",
"completedDate": "2026-01-30",
"description": "OAuth login flow, token management, protected routes",
"estimatedDays": "2-3",
"dependencies": ["PHASE_F0"],
"backendDependencies": ["PHASE_2"],
"deliverables": [
"Login page with OAuth buttons",
"OAuth callback handling",
"Token storage and auto-refresh",
"Auth store implementation",
"Protected route guards",
"User profile display",
"Logout functionality"
],
"tasks": [
{
"id": "F1-001",
"name": "Create login page",
"description": "OAuth login buttons for Google and Discord",
"files": ["src/pages/LoginPage.vue"],
"details": [
"Google OAuth button with icon",
"Discord OAuth button with icon",
"Redirect to backend /api/auth/{provider}",
"Handle loading states"
]
},
{
"id": "F1-002",
"name": "Handle OAuth callback",
"description": "Parse tokens from URL fragment after OAuth redirect",
"files": ["src/pages/AuthCallbackPage.vue", "src/composables/useAuth.ts"],
"details": [
"Extract access_token, refresh_token from URL fragment",
"Store tokens in auth store",
"Redirect to intended destination or home",
"Handle OAuth errors"
]
},
{
"id": "F1-003",
"name": "Implement token refresh",
"description": "Auto-refresh tokens before expiry",
"files": ["src/stores/auth.ts", "src/api/client.ts"],
"details": [
"Track token expiry time",
"Refresh proactively before expiry",
"Handle refresh failures (logout)",
"Queue requests during refresh"
]
},
{
"id": "F1-004",
"name": "Implement auth guards",
"description": "Protect routes requiring authentication",
"files": ["src/router/guards.ts"],
"details": [
"requireAuth guard - redirect to login",
"requireGuest guard - redirect authenticated users away from login",
"Store intended destination for post-login redirect"
]
},
{
"id": "F1-005",
"name": "Create user profile display",
"description": "Show logged-in user info in nav",
"files": ["src/components/UserMenu.vue", "src/stores/user.ts"],
"details": [
"Fetch user from /api/users/me on login",
"Display avatar and name in nav",
"Dropdown with profile link and logout"
]
},
{
"id": "F1-006",
"name": "Implement logout",
"description": "Clear tokens and redirect to login",
"files": ["src/stores/auth.ts"],
"details": [
"Call /api/auth/logout to revoke refresh token",
"Clear local token storage",
"Disconnect WebSocket",
"Redirect to login page"
]
}
]
},
{
"id": "PHASE_F2",
"name": "Deck Management",
"status": "COMPLETE",
"completedDate": "2026-01-31",
"auditStatus": "PASSED",
"auditDate": "2026-01-31",
"description": "Collection viewing, deck building, starter deck selection",
"estimatedDays": "5-7",
"dependencies": ["PHASE_F1"],
"backendDependencies": ["PHASE_3"],
"deliverables": [
"Starter deck selection flow",
"Collection grid view",
"Card display component",
"Deck list page",
"Deck builder with drag-and-drop",
"Real-time validation feedback",
"Card detail modal"
],
"tasks": [
{
"id": "F2-001",
"name": "Create card component",
"description": "Reusable card display component",
"files": ["src/components/cards/CardDisplay.vue", "src/components/cards/CardImage.vue"],
"details": [
"Display card image from CDN/local",
"Show card name, HP, type",
"Type-colored border/badge",
"Hover state with subtle animation",
"Support multiple sizes (thumbnail, medium, large)"
]
},
{
"id": "F2-002",
"name": "Create card detail modal",
"description": "Full card view with all details",
"files": ["src/components/cards/CardDetailModal.vue"],
"details": [
"Large card image",
"Full attack descriptions",
"Weakness/resistance/retreat",
"Owned quantity display",
"Close on backdrop click or Escape"
]
},
{
"id": "F2-003",
"name": "Implement starter deck selection",
"description": "First-time user flow to pick starter",
"files": ["src/pages/StarterSelectionPage.vue", "src/composables/useStarter.ts"],
"details": [
"Check starter status on app load",
"Display 5 starter deck options with preview",
"POST /api/users/me/starter-deck on selection",
"Redirect to main app after selection",
"Cannot proceed without selecting"
]
},
{
"id": "F2-004",
"name": "Create collection page",
"description": "Grid view of owned cards",
"files": ["src/pages/CollectionPage.vue", "src/stores/collection.ts"],
"details": [
"Fetch collection from /api/collections/me",
"Filterable grid (by type, rarity, set)",
"Search by card name",
"Show quantity owned",
"Click to open detail modal"
]
},
{
"id": "F2-005",
"name": "Create deck list page",
"description": "View all user decks",
"files": ["src/pages/DecksPage.vue", "src/stores/deck.ts"],
"details": [
"Fetch decks from /api/decks",
"Display deck cards with validation status",
"Create new deck button",
"Edit/delete deck actions",
"Show starter deck badge"
]
},
{
"id": "F2-006",
"name": "Create deck builder",
"description": "Add/remove cards from deck",
"files": ["src/pages/DeckBuilderPage.vue", "src/components/deck/DeckEditor.vue"],
"details": [
"Two-panel layout: collection picker + deck contents",
"Drag-and-drop or click to add/remove",
"Energy deck configuration",
"Live validation with error messages",
"Save/cancel buttons",
"Confirm discard unsaved changes"
]
},
{
"id": "F2-007",
"name": "Implement deck validation feedback",
"description": "Real-time validation as user edits",
"files": ["src/composables/useDeckValidation.ts"],
"details": [
"Debounced validation on changes",
"Call /api/decks/validate endpoint",
"Display errors inline",
"Disable save if invalid",
"Show card count progress (40/40)"
]
}
]
},
{
"id": "PHASE_F3",
"name": "Phaser Integration",
"status": "COMPLETE",
"completedDate": "2026-01-31",
"description": "Game rendering foundation - Phaser setup, board layout, card objects",
"estimatedDays": "7-10",
"dependencies": ["PHASE_F2"],
"backendDependencies": ["PHASE_4"],
"deliverables": [
"Phaser mounted as Vue component",
"Vue-Phaser event bridge",
"Scene structure (Preload, Match)",
"Asset loading system",
"Game board layout with all zones",
"Card game objects with interactions",
"Responsive canvas scaling"
],
"tasks": [
{
"id": "F3-001",
"name": "Install and configure Phaser",
"description": "Add Phaser 3 to project",
"files": ["package.json", "src/game/config.ts"],
"details": [
"Install phaser",
"Create Phaser game configuration",
"Configure WebGL with canvas fallback",
"Set up asset base path"
]
},
{
"id": "F3-002",
"name": "Create Phaser Vue component",
"description": "Mount Phaser game in Vue component",
"files": ["src/components/game/PhaserGame.vue"],
"details": [
"Create/destroy Phaser game on mount/unmount",
"Pass game instance to parent via ref",
"Handle resize events",
"Emit ready event when game initialized"
]
},
{
"id": "F3-003",
"name": "Create Vue-Phaser event bridge",
"description": "Bidirectional communication system",
"files": ["src/game/bridge.ts", "src/composables/useGameBridge.ts"],
"details": [
"Vue -> Phaser: game.events.emit('card:play', data)",
"Phaser -> Vue: game.events.on('animation:complete', cb)",
"Type-safe event definitions",
"Automatic cleanup on unmount"
]
},
{
"id": "F3-004",
"name": "Create scene structure",
"description": "Phaser scenes for game flow",
"files": ["src/game/scenes/PreloadScene.ts", "src/game/scenes/MatchScene.ts"],
"details": [
"PreloadScene: Load all assets with progress bar",
"MatchScene: Main game rendering",
"Scene transitions",
"Scene data passing"
]
},
{
"id": "F3-005",
"name": "Implement asset loading",
"description": "Load card images and UI assets",
"files": ["src/game/assets/loader.ts", "src/game/assets/manifest.ts"],
"details": [
"Card image loading (lazy load as needed)",
"UI sprite atlas",
"Board background",
"Type icons",
"Loading progress display"
]
},
{
"id": "F3-006",
"name": "Create board layout",
"description": "Position all game zones",
"files": ["src/game/objects/Board.ts", "src/game/layout.ts"],
"details": [
"Player zones: active, bench (5 slots), deck, discard, prizes (6)",
"Opponent zones: mirrored layout",
"Hand area (bottom)",
"Zone highlighting for valid targets",
"Responsive positioning"
]
},
{
"id": "F3-007",
"name": "Create card game objects",
"description": "Interactive card sprites",
"files": ["src/game/objects/Card.ts", "src/game/objects/CardBack.ts"],
"details": [
"Card face with image",
"Card back for hidden cards",
"Hover effects (scale, glow)",
"Click/tap handling",
"Drag support for hand cards",
"Damage counters display"
]
},
{
"id": "F3-008",
"name": "Implement responsive scaling",
"description": "Canvas adapts to screen size",
"files": ["src/game/scale.ts"],
"details": [
"Maintain aspect ratio",
"Scale to fit container",
"Handle window resize",
"Mobile-friendly touch areas"
]
}
]
},
{
"id": "PHASE_F4",
"name": "Live Gameplay",
"status": "NOT_STARTED",
"description": "WebSocket integration, game state sync, action handling, complete game flow",
"estimatedDays": "10-14",
"dependencies": ["PHASE_F3"],
"backendDependencies": ["PHASE_4"],
"deliverables": [
"Game creation flow",
"WebSocket connection with auth",
"Game state rendering",
"Hand and card interactions",
"Attack selection UI",
"Turn phase management",
"Action dispatch to server",
"Opponent state display",
"Game over handling"
],
"tasks": [
{
"id": "F4-001",
"name": "Create game lobby page",
"description": "Start or join a game",
"files": ["src/pages/GameLobbyPage.vue"],
"details": [
"Create new game with deck selection",
"Show active games (for rejoining)",
"Simple invite flow (share game ID for now)",
"POST /api/games to create"
]
},
{
"id": "F4-002",
"name": "Implement WebSocket game connection",
"description": "Connect to game via Socket.IO",
"files": ["src/socket/gameSocket.ts", "src/stores/game.ts"],
"details": [
"Connect with JWT auth",
"Emit game:join on connect",
"Handle game:state events",
"Handle game:error events",
"Store game state in Pinia"
]
},
{
"id": "F4-003",
"name": "Render game state to Phaser",
"description": "Sync Pinia state to Phaser objects",
"files": ["src/game/sync/StateRenderer.ts"],
"details": [
"Watch game store for changes",
"Update card positions",
"Show/hide cards based on visibility",
"Update HP, status, damage counters",
"Highlight active Pokemon"
]
},
{
"id": "F4-004",
"name": "Implement hand interactions",
"description": "Play cards from hand",
"files": ["src/game/interactions/HandManager.ts"],
"details": [
"Fan cards in hand area",
"Drag to play Pokemon to bench",
"Click to play Trainer cards",
"Attach energy to Pokemon",
"Visual feedback for valid/invalid plays"
]
},
{
"id": "F4-005",
"name": "Implement attack selection",
"description": "UI for choosing attacks",
"files": ["src/components/game/AttackMenu.vue", "src/game/ui/AttackOverlay.ts"],
"details": [
"Show available attacks for active Pokemon",
"Display energy cost, damage, effect text",
"Disable attacks without enough energy",
"Target selection for attacks that require it",
"Confirm attack action"
]
},
{
"id": "F4-006",
"name": "Implement turn phase UI",
"description": "Show current phase and valid actions",
"files": ["src/components/game/TurnIndicator.vue", "src/components/game/PhaseActions.vue"],
"details": [
"Display current phase (Draw, Main, Attack)",
"Show whose turn it is",
"End Turn button",
"Retreat button",
"Phase-appropriate action hints"
]
},
{
"id": "F4-007",
"name": "Implement action dispatch",
"description": "Send actions to server",
"files": ["src/composables/useGameActions.ts"],
"details": [
"Emit game:action for each action type",
"Handle action results",
"Optimistic UI updates (optional)",
"Error handling and retry",
"Action confirmation for important moves"
]
},
{
"id": "F4-008",
"name": "Display opponent state",
"description": "Render opponent's visible information",
"files": ["src/game/objects/OpponentArea.ts"],
"details": [
"Show opponent active and bench",
"Display card backs for hand (count only)",
"Show deck and discard counts",
"Prize card display (face down)",
"Opponent name and avatar"
]
},
{
"id": "F4-009",
"name": "Implement forced action handling",
"description": "Handle required actions (select prize, new active)",
"files": ["src/components/game/ForcedActionModal.vue"],
"details": [
"Prize card selection after KO",
"New active Pokemon selection when active KO'd",
"Discard selection for certain effects",
"Block other actions until resolved"
]
},
{
"id": "F4-010",
"name": "Implement game over screen",
"description": "Display results and return to menu",
"files": ["src/components/game/GameOverModal.vue"],
"details": [
"Victory/Defeat display",
"Show win reason (prizes, deck out, etc.)",
"Game statistics (turns, cards played)",
"Return to lobby button",
"Rematch option (future)"
]
}
]
},
{
"id": "PHASE_F5",
"name": "Polish & UX",
"status": "NOT_STARTED",
"description": "Reconnection handling, animations, turn timer, error states",
"estimatedDays": "5-7",
"dependencies": ["PHASE_F4"],
"backendDependencies": ["PHASE_4"],
"deliverables": [
"Reconnection with state recovery",
"Turn timer display",
"Opponent connection status",
"Card play animations",
"Attack animations",
"Loading and error states",
"Sound effects (optional)"
],
"tasks": [
{
"id": "F5-001",
"name": "Implement reconnection flow",
"description": "Handle disconnects gracefully",
"files": ["src/socket/reconnection.ts", "src/components/game/ReconnectingOverlay.vue"],
"details": [
"Detect disconnect",
"Show reconnecting overlay",
"Auto-reconnect with backoff",
"Restore game state on reconnect",
"Handle failed reconnection"
]
},
{
"id": "F5-002",
"name": "Implement turn timer display",
"description": "Visual countdown for turn time",
"files": ["src/components/game/TurnTimer.vue"],
"details": [
"Countdown display from game state",
"Warning color at thresholds",
"Pulse animation when low",
"Handle timeout notification"
]
},
{
"id": "F5-003",
"name": "Show opponent connection status",
"description": "Indicate if opponent is connected",
"files": ["src/components/game/OpponentStatus.vue"],
"details": [
"Online/offline indicator",
"Handle game:opponent_connected events",
"Show 'waiting for opponent' if disconnected"
]
},
{
"id": "F5-004",
"name": "Add card play animations",
"description": "Animate cards moving between zones",
"files": ["src/game/animations/CardAnimations.ts"],
"details": [
"Draw card animation (deck to hand)",
"Play to bench animation",
"Evolve animation",
"Attach energy animation",
"Discard animation"
]
},
{
"id": "F5-005",
"name": "Add attack animations",
"description": "Visual feedback for attacks",
"files": ["src/game/animations/AttackAnimations.ts"],
"details": [
"Attack motion (active toward opponent)",
"Damage numbers",
"KO animation (fade out)",
"Status effect indicators",
"Type-specific visual effects (optional)"
]
},
{
"id": "F5-006",
"name": "Add loading and error states",
"description": "Feedback for async operations",
"files": ["src/components/ui/LoadingSpinner.vue", "src/components/ui/Toast.vue"],
"details": [
"Skeleton loaders for lists",
"Button loading states",
"Error toast notifications",
"Retry prompts for failed requests"
]
},
{
"id": "F5-007",
"name": "Add sound effects (optional)",
"description": "Audio feedback for game events",
"files": ["src/game/audio/SoundManager.ts"],
"details": [
"Card play sound",
"Attack sound",
"Turn change chime",
"Victory/defeat music",
"Volume controls",
"Mute option"
]
}
]
},
{
"id": "PHASE_F6",
"name": "Campaign Mode",
"status": "NOT_STARTED",
"description": "Single-player campaign UI - clubs, NPCs, progression, rewards",
"estimatedDays": "7-10",
"dependencies": ["PHASE_F5"],
"backendDependencies": ["PHASE_5"],
"blocked": true,
"blockedReason": "Requires backend Phase 5 (Campaign Mode)",
"deliverables": [
"Campaign world map",
"Club selection",
"NPC opponent display",
"Medal/progression tracking",
"Reward notifications",
"Difficulty indicators"
]
},
{
"id": "PHASE_F7",
"name": "Multiplayer & Matchmaking",
"status": "NOT_STARTED",
"description": "PvP matchmaking queue, invite links, match history",
"estimatedDays": "5-7",
"dependencies": ["PHASE_F5"],
"backendDependencies": ["PHASE_6"],
"blocked": true,
"blockedReason": "Requires backend Phase 6 (Multiplayer)",
"deliverables": [
"Matchmaking queue UI",
"Queue status and cancel",
"Invite link generation",
"Join via invite link",
"Match history page",
"Player stats display"
]
},
{
"id": "PHASE_F8",
"name": "Pack Opening & Rewards",
"status": "NOT_STARTED",
"description": "Animated pack opening experience",
"estimatedDays": "5-7",
"dependencies": ["PHASE_F6"],
"backendDependencies": ["PHASE_5"],
"blocked": true,
"blockedReason": "Requires backend Phase 5 reward system",
"deliverables": [
"Pack opening Phaser scene",
"Card reveal animations",
"Rarity celebration effects",
"New card indicators in collection"
]
}
],
"criticalPath": ["PHASE_F0", "PHASE_F1", "PHASE_F3", "PHASE_F4"],
"parallelDevelopment": {
"note": "F0-F5 can proceed while backend completes Phase 5-6",
"tracks": [
{
"name": "Frontend Core",
"phases": ["PHASE_F0", "PHASE_F1", "PHASE_F2", "PHASE_F3", "PHASE_F4", "PHASE_F5"]
},
{
"name": "Backend Campaign",
"phases": ["PHASE_5"]
},
{
"name": "Backend Multiplayer",
"phases": ["PHASE_6"]
}
],
"joinPoints": [
{
"frontend": "PHASE_F6",
"backend": "PHASE_5",
"description": "Campaign UI requires campaign backend"
},
{
"frontend": "PHASE_F7",
"backend": "PHASE_6",
"description": "Matchmaking UI requires multiplayer backend"
}
]
},
"designSystem": {
"colors": {
"note": "Pokemon type-inspired palette",
"types": {
"grass": "#78C850",
"fire": "#F08030",
"water": "#6890F0",
"lightning": "#F8D030",
"psychic": "#F85888",
"fighting": "#C03028",
"darkness": "#705848",
"metal": "#B8B8D0",
"fairy": "#EE99AC",
"dragon": "#7038F8",
"colorless": "#A8A878"
},
"ui": {
"primary": "#3B82F6",
"secondary": "#6366F1",
"success": "#22C55E",
"warning": "#F59E0B",
"error": "#EF4444",
"background": "#1F2937",
"surface": "#374151",
"text": "#F9FAFB"
}
},
"typography": {
"headings": "Press Start 2P or similar pixel font for retro feel",
"body": "Inter or system font stack"
}
},
"testingStrategy": {
"unit": {
"tool": "Vitest",
"scope": "Composables, stores, utilities",
"location": "src/**/*.test.ts"
},
"component": {
"tool": "Vitest + Vue Test Utils",
"scope": "Vue components in isolation",
"location": "src/**/*.test.ts"
},
"e2e": {
"tool": "Playwright",
"scope": "Full user flows",
"location": "e2e/",
"deferred": "After F4 complete"
}
},
"risks": [
{
"risk": "Phaser learning curve",
"mitigation": "Start F3 with simple prototypes, iterate",
"priority": "high"
},
{
"risk": "Vue-Phaser state sync complexity",
"mitigation": "Establish clear patterns in F3, document bridge API",
"priority": "high"
},
{
"risk": "Mobile touch interactions",
"mitigation": "Design for touch from start, test on real devices",
"priority": "medium"
},
{
"risk": "Asset loading performance",
"mitigation": "Lazy load cards, use sprite atlases, implement caching",
"priority": "medium"
}
]
}