Add game-over state and pre-populate names on new game
Highlights the winning player's column in green when the game ends, changes the footer button to "New Game" (reusing the existing confirm dialog), and pre-populates player names from the previous game when starting a new one. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-1
@@ -23,8 +23,10 @@ function clearGame() {
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
const [game, setGame] = useState<GameState | null>(loadGame)
|
const [game, setGame] = useState<GameState | null>(loadGame)
|
||||||
const [page, setPage] = useState<Page>(() => (game ? 'scoresheet' : 'title'))
|
const [page, setPage] = useState<Page>(() => (game ? 'scoresheet' : 'title'))
|
||||||
|
const [previousPlayers, setPreviousPlayers] = useState<string[] | undefined>(undefined)
|
||||||
|
|
||||||
function handleNewGame() {
|
function handleNewGame() {
|
||||||
|
setPreviousPlayers(game?.players)
|
||||||
clearGame()
|
clearGame()
|
||||||
setGame(null)
|
setGame(null)
|
||||||
setPage('setup')
|
setPage('setup')
|
||||||
@@ -45,7 +47,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (page === 'title') return <TitleScreen onNewGame={handleNewGame} />
|
if (page === 'title') return <TitleScreen onNewGame={handleNewGame} />
|
||||||
if (page === 'setup') return <PlayerSetup onStart={handleStart} />
|
if (page === 'setup') return <PlayerSetup onStart={handleStart} initialNames={previousPlayers} />
|
||||||
if (page === 'scoresheet') return <ScoreSheet game={game!} onScoreRound={() => setPage('enterscores')} onNewGame={handleNewGame} />
|
if (page === 'scoresheet') return <ScoreSheet game={game!} onScoreRound={() => setPage('enterscores')} onNewGame={handleNewGame} />
|
||||||
if (page === 'enterscores') return <EnterScores game={game!} onSubmit={handleSubmitScores} />
|
if (page === 'enterscores') return <EnterScores game={game!} onSubmit={handleSubmitScores} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import styles from './PlayerSetup.module.css'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onStart: (players: string[]) => void
|
onStart: (players: string[]) => void
|
||||||
|
initialNames?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize(s: string) {
|
function normalize(s: string) {
|
||||||
return s.trim().replace(/\s+/g, ' ')
|
return s.trim().replace(/\s+/g, ' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PlayerSetup({ onStart }: Props) {
|
export default function PlayerSetup({ onStart, initialNames }: Props) {
|
||||||
const [names, setNames] = useState<string[]>(['', '', ''])
|
const [names, setNames] = useState<string[]>(initialNames ?? ['', '', ''])
|
||||||
|
|
||||||
function updateName(i: number, value: string) {
|
function updateName(i: number, value: string) {
|
||||||
setNames(n => n.map((v, idx) => (idx === i ? value : v)))
|
setNames(n => n.map((v, idx) => (idx === i ? value : v)))
|
||||||
|
|||||||
@@ -49,6 +49,10 @@
|
|||||||
border-top: 1px solid var(--color-parchment-border);
|
border-top: 1px solid var(--color-parchment-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.winnerCell {
|
||||||
|
background: rgba(74, 158, 106, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
/* Small red "New Game" button in header top-right */
|
/* Small red "New Game" button in header top-right */
|
||||||
.newGameBtn {
|
.newGameBtn {
|
||||||
background: #d94f4f;
|
background: #d94f4f;
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ export default function ScoreSheet({ game, onScoreRound, onNewGame }: Props) {
|
|||||||
|
|
||||||
const totals = computeRunningTotals(game.rounds)
|
const totals = computeRunningTotals(game.rounds)
|
||||||
const gameOver = isGameOver(totals)
|
const gameOver = isGameOver(totals)
|
||||||
|
const lastTotals = totals[totals.length - 1] ?? []
|
||||||
|
const winnerIndex = gameOver ? lastTotals.indexOf(Math.max(...lastTotals)) : -1
|
||||||
|
|
||||||
const newGameButton = (
|
const newGameButton = (
|
||||||
<button className={styles.newGameBtn} onClick={() => setConfirmingNewGame(true)}>
|
<button className={styles.newGameBtn} onClick={() => setConfirmingNewGame(true)}>
|
||||||
@@ -42,7 +44,11 @@ export default function ScoreSheet({ game, onScoreRound, onNewGame }: Props) {
|
|||||||
title="Score Sheet"
|
title="Score Sheet"
|
||||||
headerAction={newGameButton}
|
headerAction={newGameButton}
|
||||||
overlay={confirmingNewGame ? confirmOverlay : undefined}
|
overlay={confirmingNewGame ? confirmOverlay : undefined}
|
||||||
footer={<PrimaryButton onClick={onScoreRound} disabled={gameOver}>Score Round</PrimaryButton>}
|
footer={
|
||||||
|
<PrimaryButton onClick={gameOver ? () => setConfirmingNewGame(true) : onScoreRound}>
|
||||||
|
{gameOver ? 'New Game' : 'Score Round'}
|
||||||
|
</PrimaryButton>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className={styles.tableWrapper}>
|
<div className={styles.tableWrapper}>
|
||||||
<div
|
<div
|
||||||
@@ -51,21 +57,21 @@ export default function ScoreSheet({ game, onScoreRound, onNewGame }: Props) {
|
|||||||
>
|
>
|
||||||
<div className={styles.labelCell} />
|
<div className={styles.labelCell} />
|
||||||
{game.players.map((name, j) => (
|
{game.players.map((name, j) => (
|
||||||
<div key={j} className={styles.headerCell}>{name}</div>
|
<div key={j} className={styles.headerCell + (j === winnerIndex ? ' ' + styles.winnerCell : '')}>{name}</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{game.rounds.map((roundScores, i) => (
|
{game.rounds.map((roundScores, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
<div className={styles.labelCell}>Round {i + 1}</div>
|
<div className={styles.labelCell}>Round {i + 1}</div>
|
||||||
{roundScores.map((score, j) => (
|
{roundScores.map((score, j) => (
|
||||||
<div key={j} className={styles.scoreCell}>{score}</div>
|
<div key={j} className={styles.scoreCell + (j === winnerIndex ? ' ' + styles.winnerCell : '')}>{score}</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{i > 0 && (
|
{i > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className={`${styles.labelCell} ${styles.divider}`} />
|
<div className={`${styles.labelCell} ${styles.divider}`} />
|
||||||
{totals[i].map((total, j) => (
|
{totals[i].map((total, j) => (
|
||||||
<div key={j} className={`${styles.totalCell} ${styles.divider}`}>{total}</div>
|
<div key={j} className={styles.totalCell + ' ' + styles.divider + (j === winnerIndex ? ' ' + styles.winnerCell : '')}>{total}</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user