diff --git a/src/components/App.jsx b/src/components/App.jsx index a15375a..9450644 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -69,22 +69,42 @@ const App = () => { const handleCreateGame = useCallback(({ player1, player2, player3, gameType, raceTo }) => { const newGame = { id: Date.now(), - player1, - player2, - player3, - score1: 0, - score2: 0, - score3: 0, gameType, - raceTo, + raceTo: parseInt(raceTo, 10), status: 'active', createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() + updatedAt: new Date().toISOString(), + history: [], }; + + if (gameType === '14/1 endlos') { + const players = [{ name: player1, score: 0, consecutiveFouls: 0 }, { name: player2, score: 0, consecutiveFouls: 0 }]; + if (player3) { + players.push({ name: player3, score: 0, consecutiveFouls: 0 }); + } + newGame.players = players; + newGame.currentPlayer = null; // Set to null, will be chosen in GameDetail141 + newGame.ballsOnTable = 15; + } else { + newGame.player1 = player1; + newGame.player2 = player2; + newGame.score1 = 0; + newGame.score2 = 0; + if (player3) { + newGame.player3 = player3; + newGame.score3 = 0; + } + } + setGames(g => [newGame, ...g]); return newGame.id; }, []); + // Game update for 14.1 + const handleUpdateGame = useCallback((updatedGame) => { + setGames(games => games.map(game => (game.id === currentGameId ? updatedGame : game))); + }, [currentGameId]); + // Score update const handleUpdateScore = useCallback((player, change) => { setGames(games => games.map(game => { @@ -168,7 +188,7 @@ const App = () => { setNewGameStep('gameType'); }; const handleGameTypeNext = (type) => { - setNewGameData(data => ({ ...data, gameType: type })); + setNewGameData(data => ({ ...data, gameType: type, raceTo: type === '14/1 endlos' ? '150' : '50' })); setNewGameStep('raceTo'); }; const handleRaceToNext = (raceTo) => { @@ -250,8 +270,9 @@ const App = () => {
g.id === currentGameId)} - onFinishGame={handleFinishGame} onUpdateScore={handleUpdateScore} + onUpdate={handleUpdateGame} + onFinishGame={handleFinishGame} onBack={showGameList} />
diff --git a/src/components/GameDetail.jsx b/src/components/GameDetail.jsx index 84ac5f6..086b376 100644 --- a/src/components/GameDetail.jsx +++ b/src/components/GameDetail.jsx @@ -1,5 +1,6 @@ import { h } from 'preact'; import styles from './GameDetail.module.css'; +import GameDetail141 from './GameDetail141.jsx'; /** * Game detail view for a single game. @@ -7,11 +8,17 @@ import styles from './GameDetail.module.css'; * @param {object} props.game * @param {Function} props.onFinishGame * @param {Function} props.onUpdateScore + * @param {Function} props.onUpdate * @param {Function} props.onBack * @returns {import('preact').VNode|null} */ -const GameDetail = ({ game, onFinishGame, onUpdateScore, onBack }) => { +const GameDetail = ({ game, onFinishGame, onUpdateScore, onUpdate, onBack }) => { if (!game) return null; + + if (game.gameType === '14/1 endlos') { + return ; + } + const isCompleted = game.status === 'completed'; const playerNames = [game.player1, game.player2, game.player3].filter(Boolean); const scores = [game.score1, game.score2, game.score3].filter((_, i) => playerNames[i]); diff --git a/src/components/GameDetail.module.css b/src/components/GameDetail.module.css index bb8bdf5..2f736e5 100644 --- a/src/components/GameDetail.module.css +++ b/src/components/GameDetail.module.css @@ -122,4 +122,54 @@ margin: 40px 0 0 0; width: 100%; justify-content: center; +} +.franky .player-name { + font-weight: bold; + color: #ff8c00; /* Example color */ +} +.active-player { + border: 2px solid #4caf50; + box-shadow: 0 0 10px #4caf50; +} +.turn-indicator { + margin: 20px 0; + font-size: 1.2rem; + text-align: center; +} +.potted-balls-container { + margin-top: 2rem; + padding: 1rem; + background: #2a2a2a; + border-radius: 8px; +} +.potted-balls-header { + text-align: center; + font-size: 1.1rem; + margin-bottom: 1rem; + font-weight: 600; +} +.potted-balls-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); + gap: 10px; +} +.potted-ball-btn { + padding: 1rem; + font-size: 1.2rem; + font-weight: bold; + border-radius: 8px; + border: 1px solid #444; + background-color: #333; + color: #fff; + cursor: pointer; + transition: background-color 0.2s, transform 0.2s; +} +.potted-ball-btn:hover:not(:disabled) { + background-color: #45a049; + transform: translateY(-2px); +} +.potted-ball-btn:disabled { + background-color: #222; + color: #555; + cursor: not-allowed; } \ No newline at end of file diff --git a/src/components/GameDetail141.jsx b/src/components/GameDetail141.jsx new file mode 100644 index 0000000..1f690c5 --- /dev/null +++ b/src/components/GameDetail141.jsx @@ -0,0 +1,113 @@ +import { h } from 'preact'; +import { useState } from 'preact/hooks'; +import styles from './GameDetail.module.css'; +import modalStyles from './PlayerSelectModal.module.css'; + +const StartingPlayerModal = ({ players, onSelect, onCancel }) => ( +
+
e.stopPropagation()}> +
+

Welcher Spieler fängt an?

+ {/* A cancel button isn't strictly needed if a choice is mandatory */} +
+
+ {players.map((player, index) => ( + + ))} +
+
+
+); + +const GameDetail141 = ({ game, onUpdate, onBack }) => { + const handleSelectStartingPlayer = (playerIndex) => { + onUpdate({ + ...game, + currentPlayer: playerIndex, + }); + }; + + // If no player is selected yet, show the modal + if (game.currentPlayer === null || game.currentPlayer === undefined) { + return ; + } + + const currentPlayer = game.players[game.currentPlayer]; + + const handleTurnEnd = (remainingBalls) => { + if (remainingBalls > game.ballsOnTable) { + console.error("Cannot leave more balls than are on the table."); + return; + } + + const ballsPotted = game.ballsOnTable - remainingBalls; + const newScore = currentPlayer.score + ballsPotted; + + const updatedPlayers = game.players.map((p, index) => + index === game.currentPlayer ? { ...p, score: newScore } : p + ); + + const nextPlayer = (game.currentPlayer + 1) % game.players.length; + + onUpdate({ + ...game, + players: updatedPlayers, + ballsOnTable: remainingBalls, + currentPlayer: nextPlayer, + history: [...game.history, { player: currentPlayer.name, ballsPotted, newScore, ballsOnTable: remainingBalls }], + }); + }; + + return ( +
+
+ 14/1 endlos | Race to {game.raceTo} +
+ +
+ {game.players.map((p, idx) => ( +
+ {p.name} + {p.score} +
+ ))} +
+ +
+ Aktueller Spieler: {currentPlayer.name} ({game.ballsOnTable} Bälle auf dem Tisch) +
+ +
+

Bälle am Ende der Aufnahme:

+
+ {Array.from({ length: 16 }, (_, i) => i).map(num => ( + + ))} +
+
+ + {/* Placeholder for future buttons */} +
+

Fouls & Re-Racks (nächste Phase)

+
+ +
+ +
+
+ ); +}; + +export default GameDetail141; \ No newline at end of file