feat(phase4): Complete frontend with themes, replay, and results

Visual Effects:
- Add effects.css with neon glow, CRT scanlines, glitch, sparkles, RGB shift
- Animations work with all 5 themes (DRRR, Retro, Minimal, RGB, Anime)

Game Finished Handler:
- Add gameResult state to gameStore (winner, scores, replay, achievements)
- Handle game_finished WebSocket event in useSocket
- Store achievements unlocked by all players

Results Page:
- Show winner with animation (or tie)
- Display final scores with staggered animation
- List achievements unlocked per player
- Buttons for replay and new game

Replay Player:
- Fetch replay from API by code
- Auto-playback with configurable speed (1x, 2x, 4x)
- Play/Pause and timeline controls
- Events sidebar for navigation
- Animated event transitions

Lobby Updates:
- Load categories from API on mount
- Display available categories with icons
- Backend generates board (no hardcoded data)

TypeScript Fixes:
- Add vite-env.d.ts for import.meta.env types
- Fix ringColor style issues
- Remove unused imports

Build verified: npm run build succeeds

Phase 4 tasks completed:
- F4.1: CSS effects for themes
- F4.2: Results page with achievements
- F4.3: Replay player
- F4.4: game_finished handler
- F4.5: Lobby API integration
- F4.6: Build verification and fixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 08:40:36 +00:00
parent 0141153653
commit 3e91305e46
13 changed files with 862 additions and 296 deletions

View File

@@ -1,5 +1,5 @@
import { create } from 'zustand'
import type { GameRoom, Player, Question, ChatMessage, Achievement } from '../types'
import type { GameRoom, Question, ChatMessage, Achievement } from '../types'
interface GameState {
// Room state
@@ -44,6 +44,19 @@ interface GameState {
showStealPrompt: boolean
setShowStealPrompt: (show: boolean) => void
// Game result
gameResult: {
winner: 'A' | 'B' | null
finalScores: { A: number; B: number }
replayCode: string | null
achievementsUnlocked: Array<{
player_name: string
team: 'A' | 'B'
achievement: Achievement
}>
} | null
setGameResult: (result: GameState['gameResult']) => void
// Reset
resetGame: () => void
}
@@ -92,6 +105,9 @@ export const useGameStore = create<GameState>((set) => ({
showStealPrompt: false,
setShowStealPrompt: (showStealPrompt) => set({ showStealPrompt }),
gameResult: null,
setGameResult: (gameResult) => set({ gameResult }),
resetGame: () =>
set({
room: null,
@@ -100,5 +116,6 @@ export const useGameStore = create<GameState>((set) => ({
messages: [],
stats: initialStats,
showStealPrompt: false,
gameResult: null,
}),
}))