Refactor BSC Score to Astro, TypeScript, and modular architecture
This commit is contained in:
244
src/components/App.tsx
Normal file
244
src/components/App.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
import { h } from 'preact';
|
||||
import { useEffect } from 'preact/hooks';
|
||||
|
||||
import { useGameState } from '../hooks/useGameState';
|
||||
import { useNavigation, useNewGameWizard } from '../hooks/useNavigation';
|
||||
import { useModal, useValidationModal, useCompletionModal } from '../hooks/useModal';
|
||||
|
||||
import { GameService } from '../services/gameService';
|
||||
import type { StandardGame, EndlosGame } from '../types/game';
|
||||
|
||||
import { Layout } from './ui/Layout';
|
||||
import GameListScreen from './screens/GameListScreen';
|
||||
import NewGameScreen from './screens/NewGameScreen';
|
||||
import GameDetailScreen from './screens/GameDetailScreen';
|
||||
import Modal from './Modal';
|
||||
import ValidationModal from './ValidationModal';
|
||||
import GameCompletionModal from './GameCompletionModal';
|
||||
import FullscreenToggle from './FullscreenToggle';
|
||||
|
||||
/**
|
||||
* Main App component for BSC Score
|
||||
*/
|
||||
export default function App() {
|
||||
// State management hooks
|
||||
const gameState = useGameState();
|
||||
const navigation = useNavigation();
|
||||
const newGameWizard = useNewGameWizard();
|
||||
const modal = useModal();
|
||||
const validationModal = useValidationModal();
|
||||
const completionModal = useCompletionModal();
|
||||
|
||||
// Game lifecycle handlers
|
||||
const handleCreateGame = (gameData: any) => {
|
||||
const gameId = gameState.addGame(gameData);
|
||||
newGameWizard.resetWizard();
|
||||
navigation.showGameDetail(gameId);
|
||||
};
|
||||
|
||||
const handleUpdateScore = (player: number, change: number) => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const game = gameState.getGameById(navigation.currentGameId);
|
||||
if (!game || game.status === 'completed' || 'players' in game) return;
|
||||
|
||||
const updatedGame = GameService.updateGameScore(game as StandardGame, player, change);
|
||||
gameState.updateGame(navigation.currentGameId, updatedGame);
|
||||
|
||||
// Check for completion
|
||||
if (GameService.isGameCompleted(updatedGame)) {
|
||||
completionModal.openCompletionModal(updatedGame);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGameAction = (updatedGame: EndlosGame) => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const originalGame = gameState.getGameById(navigation.currentGameId);
|
||||
if (!originalGame) return;
|
||||
|
||||
// Add undo state
|
||||
const gameWithHistory = {
|
||||
...updatedGame,
|
||||
undoStack: [...(originalGame.undoStack || []), originalGame],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
gameState.updateGame(navigation.currentGameId, gameWithHistory);
|
||||
|
||||
// Check for completion
|
||||
if (GameService.isGameCompleted(gameWithHistory)) {
|
||||
completionModal.openCompletionModal(gameWithHistory);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUndo = () => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const game = gameState.getGameById(navigation.currentGameId);
|
||||
if (!game?.undoStack?.length) return;
|
||||
|
||||
const lastState = game.undoStack[game.undoStack.length - 1];
|
||||
const newUndoStack = game.undoStack.slice(0, -1);
|
||||
|
||||
gameState.updateGame(navigation.currentGameId, {
|
||||
...lastState,
|
||||
undoStack: newUndoStack,
|
||||
});
|
||||
};
|
||||
|
||||
const handleForfeit = () => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const game = gameState.getGameById(navigation.currentGameId);
|
||||
if (!game || !('players' in game)) return;
|
||||
|
||||
const currentPlayerIndex = game.currentPlayer;
|
||||
if (currentPlayerIndex === null) return;
|
||||
|
||||
const winner = game.players.find((_, idx) => idx !== currentPlayerIndex);
|
||||
const forfeitedGame = {
|
||||
...game,
|
||||
status: 'completed' as const,
|
||||
winner: winner?.name,
|
||||
forfeitedBy: game.players[currentPlayerIndex].name,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
gameState.updateGame(navigation.currentGameId, forfeitedGame);
|
||||
completionModal.openCompletionModal(forfeitedGame);
|
||||
};
|
||||
|
||||
const handleFinishGame = () => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const game = gameState.getGameById(navigation.currentGameId);
|
||||
if (!game) return;
|
||||
|
||||
completionModal.openCompletionModal(game);
|
||||
};
|
||||
|
||||
const handleConfirmCompletion = () => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
gameState.updateGame(navigation.currentGameId, {
|
||||
...gameState.getGameById(navigation.currentGameId)!,
|
||||
status: 'completed',
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
completionModal.closeCompletionModal();
|
||||
};
|
||||
|
||||
const handleRematch = () => {
|
||||
if (!navigation.currentGameId) return;
|
||||
|
||||
const completedGame = gameState.getGameById(navigation.currentGameId);
|
||||
if (!completedGame) return;
|
||||
|
||||
let gameData;
|
||||
if ('players' in completedGame) {
|
||||
gameData = {
|
||||
player1: completedGame.players[0]?.name || '',
|
||||
player2: completedGame.players[1]?.name || '',
|
||||
player3: completedGame.players[2]?.name || '',
|
||||
gameType: completedGame.gameType,
|
||||
raceTo: completedGame.raceTo.toString(),
|
||||
};
|
||||
} else {
|
||||
gameData = {
|
||||
player1: completedGame.player1,
|
||||
player2: completedGame.player2,
|
||||
player3: completedGame.player3 || '',
|
||||
gameType: completedGame.gameType,
|
||||
raceTo: completedGame.raceTo.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
const newGameId = gameState.addGame(gameData);
|
||||
completionModal.closeCompletionModal();
|
||||
navigation.showGameDetail(newGameId);
|
||||
};
|
||||
|
||||
const handleDeleteGame = (gameId: number) => {
|
||||
modal.openModal(gameId);
|
||||
};
|
||||
|
||||
const handleConfirmDelete = () => {
|
||||
if (modal.modal.gameId) {
|
||||
gameState.deleteGame(modal.modal.gameId);
|
||||
}
|
||||
modal.closeModal();
|
||||
navigation.showGameList();
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
{navigation.screen === 'game-list' && (
|
||||
<GameListScreen
|
||||
games={gameState.getFilteredGames()}
|
||||
filter={gameState.filter}
|
||||
onFilterChange={gameState.setFilter}
|
||||
onShowGameDetail={navigation.showGameDetail}
|
||||
onDeleteGame={handleDeleteGame}
|
||||
onShowNewGame={() => {
|
||||
newGameWizard.startWizard();
|
||||
navigation.showNewGame();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{navigation.screen === 'new-game' && (
|
||||
<NewGameScreen
|
||||
step={newGameWizard.newGameStep}
|
||||
data={newGameWizard.newGameData}
|
||||
playerHistory={gameState.getPlayerNameHistory()}
|
||||
onStepChange={newGameWizard.nextStep}
|
||||
onDataChange={newGameWizard.updateGameData}
|
||||
onCreateGame={handleCreateGame}
|
||||
onCancel={() => {
|
||||
newGameWizard.resetWizard();
|
||||
navigation.showGameList();
|
||||
}}
|
||||
onShowValidation={validationModal.showValidation}
|
||||
/>
|
||||
)}
|
||||
|
||||
{navigation.screen === 'game-detail' && navigation.currentGameId && (
|
||||
<GameDetailScreen
|
||||
game={gameState.getGameById(navigation.currentGameId)}
|
||||
onUpdateScore={handleUpdateScore}
|
||||
onFinishGame={handleFinishGame}
|
||||
onUpdateGame={handleGameAction}
|
||||
onUndo={handleUndo}
|
||||
onForfeit={handleForfeit}
|
||||
onBack={navigation.showGameList}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
open={modal.modal.open}
|
||||
title="Spiel löschen"
|
||||
message="Möchten Sie das Spiel wirklich löschen?"
|
||||
onCancel={modal.closeModal}
|
||||
onConfirm={handleConfirmDelete}
|
||||
/>
|
||||
|
||||
<ValidationModal
|
||||
open={validationModal.validation.open}
|
||||
message={validationModal.validation.message}
|
||||
onClose={validationModal.closeValidation}
|
||||
/>
|
||||
|
||||
<GameCompletionModal
|
||||
open={completionModal.completionModal.open}
|
||||
game={completionModal.completionModal.game}
|
||||
onConfirm={handleConfirmCompletion}
|
||||
onClose={completionModal.closeCompletionModal}
|
||||
onRematch={handleRematch}
|
||||
/>
|
||||
|
||||
<FullscreenToggle />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user