Refactor BSC Score to Astro, TypeScript, and modular architecture

This commit is contained in:
Cursor Agent
2025-06-24 11:44:19 +00:00
parent bcf793b9e3
commit 6f626c9977
30 changed files with 1836 additions and 497 deletions

65
src/hooks/useGameState.ts Normal file
View File

@@ -0,0 +1,65 @@
import { useState, useEffect, useCallback } from 'preact/hooks';
import type { Game, NewGameData, GameFilter } from '../types/game';
import { GameService } from '../services/gameService';
export function useGameState() {
const [games, setGames] = useState<Game[]>([]);
const [filter, setFilter] = useState<GameFilter>('all');
// Load games from localStorage on mount
useEffect(() => {
const savedGames = GameService.loadGames();
setGames(savedGames);
}, []);
// Save games to localStorage whenever games change
useEffect(() => {
GameService.saveGames(games);
}, [games]);
const addGame = useCallback((gameData: NewGameData): number => {
const newGame = GameService.createGame(gameData);
setGames(prevGames => [newGame, ...prevGames]);
return newGame.id;
}, []);
const updateGame = useCallback((gameId: number, updatedGame: Game) => {
setGames(prevGames =>
prevGames.map(game => game.id === gameId ? updatedGame : game)
);
}, []);
const deleteGame = useCallback((gameId: number) => {
setGames(prevGames => prevGames.filter(game => game.id !== gameId));
}, []);
const getGameById = useCallback((gameId: number): Game | undefined => {
return games.find(game => game.id === gameId);
}, [games]);
const getFilteredGames = useCallback((): Game[] => {
return games
.filter(game => {
if (filter === 'active') return game.status === 'active';
if (filter === 'completed') return game.status === 'completed';
return true;
})
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
}, [games, filter]);
const getPlayerNameHistory = useCallback((): string[] => {
return GameService.getPlayerNameHistory(games);
}, [games]);
return {
games,
filter,
setFilter,
addGame,
updateGame,
deleteGame,
getGameById,
getFilteredGames,
getPlayerNameHistory,
};
}

60
src/hooks/useModal.ts Normal file
View File

@@ -0,0 +1,60 @@
import { useState, useCallback } from 'preact/hooks';
import type { ModalState, ValidationState, CompletionModalState } from '../types/ui';
import type { Game } from '../types/game';
export function useModal() {
const [modal, setModal] = useState<ModalState>({ open: false, gameId: null });
const openModal = useCallback((gameId?: number) => {
setModal({ open: true, gameId });
}, []);
const closeModal = useCallback(() => {
setModal({ open: false, gameId: null });
}, []);
return {
modal,
openModal,
closeModal,
};
}
export function useValidationModal() {
const [validation, setValidation] = useState<ValidationState>({ open: false, message: '' });
const showValidation = useCallback((message: string) => {
setValidation({ open: true, message });
}, []);
const closeValidation = useCallback(() => {
setValidation({ open: false, message: '' });
}, []);
return {
validation,
showValidation,
closeValidation,
};
}
export function useCompletionModal() {
const [completionModal, setCompletionModal] = useState<CompletionModalState>({
open: false,
game: null
});
const openCompletionModal = useCallback((game: Game) => {
setCompletionModal({ open: true, game });
}, []);
const closeCompletionModal = useCallback(() => {
setCompletionModal({ open: false, game: null });
}, []);
return {
completionModal,
openCompletionModal,
closeCompletionModal,
};
}

View File

@@ -0,0 +1,82 @@
import { useState, useCallback } from 'preact/hooks';
import type { NewGameStep, NewGameData } from '../types/game';
type Screen = 'game-list' | 'new-game' | 'game-detail';
export function useNavigation() {
const [screen, setScreen] = useState<Screen>('game-list');
const [currentGameId, setCurrentGameId] = useState<number | null>(null);
const showGameList = useCallback(() => {
setScreen('game-list');
setCurrentGameId(null);
}, []);
const showNewGame = useCallback(() => {
setScreen('new-game');
setCurrentGameId(null);
}, []);
const showGameDetail = useCallback((gameId: number) => {
setCurrentGameId(gameId);
setScreen('game-detail');
}, []);
return {
screen,
currentGameId,
showGameList,
showNewGame,
showGameDetail,
};
}
export function useNewGameWizard() {
const [newGameStep, setNewGameStep] = useState<NewGameStep>(null);
const [newGameData, setNewGameData] = useState<NewGameData>({
player1: '',
player2: '',
player3: '',
gameType: '',
raceTo: '',
});
const startWizard = useCallback(() => {
setNewGameStep('player1');
setNewGameData({
player1: '',
player2: '',
player3: '',
gameType: '',
raceTo: '',
});
}, []);
const resetWizard = useCallback(() => {
setNewGameStep(null);
setNewGameData({
player1: '',
player2: '',
player3: '',
gameType: '',
raceTo: '',
});
}, []);
const updateGameData = useCallback((data: Partial<NewGameData>) => {
setNewGameData(prev => ({ ...prev, ...data }));
}, []);
const nextStep = useCallback((step: NewGameStep) => {
setNewGameStep(step);
}, []);
return {
newGameStep,
newGameData,
startWizard,
resetWizard,
updateGameData,
nextStep,
};
}