Implement scoresheet page with scoring logic and confirmation dialog
- Add src/game/scoring.ts with computeRunningTotals and isGameOver - Build ScoreSheet grid: player columns, per-round scores, running totals after round 2+ - Small red "New Game" button in header top-right with full-screen confirmation overlay - Extend PageFrame with headerAction and overlay props - Move press animation/shadow from .pressable class to global button selector Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,78 @@
|
||||
import { Fragment, useState, type CSSProperties } from 'react'
|
||||
import { type GameState } from '../types'
|
||||
import { computeRunningTotals, isGameOver } from '../game/scoring'
|
||||
import PageFrame from '../components/PageFrame'
|
||||
import PrimaryButton from '../components/PrimaryButton'
|
||||
import styles from './ScoreSheet.module.css'
|
||||
|
||||
interface Props {
|
||||
game: GameState
|
||||
onScoreRound: () => void
|
||||
onNewGame: () => void
|
||||
}
|
||||
|
||||
export default function ScoreSheet({ onScoreRound, onNewGame }: Props) {
|
||||
export default function ScoreSheet({ game, onScoreRound, onNewGame }: Props) {
|
||||
const [confirmingNewGame, setConfirmingNewGame] = useState(false)
|
||||
|
||||
const totals = computeRunningTotals(game.rounds)
|
||||
const gameOver = isGameOver(totals)
|
||||
|
||||
const newGameButton = (
|
||||
<button className={styles.newGameBtn} onClick={() => setConfirmingNewGame(true)}>
|
||||
New Game
|
||||
</button>
|
||||
)
|
||||
|
||||
const confirmOverlay = (
|
||||
<div className={styles.confirmContent}>
|
||||
<p className={styles.confirmText}>End this game?</p>
|
||||
<div className={styles.confirmButtons}>
|
||||
<button className={styles.cancelBtn} onClick={() => setConfirmingNewGame(false)}>
|
||||
Cancel
|
||||
</button>
|
||||
<button className={styles.endGameBtn} onClick={onNewGame}>
|
||||
End Game
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<PageFrame title="Score Sheet">
|
||||
<button onClick={onScoreRound}>Score Round</button>
|
||||
<button onClick={onNewGame} style={{ fontSize: '0.75em' }}>New Game</button>
|
||||
<PageFrame
|
||||
title="Score Sheet"
|
||||
headerAction={newGameButton}
|
||||
overlay={confirmingNewGame ? confirmOverlay : undefined}
|
||||
footer={<PrimaryButton onClick={onScoreRound} disabled={gameOver}>Score Round</PrimaryButton>}
|
||||
>
|
||||
<div className={styles.tableWrapper}>
|
||||
<div
|
||||
className={styles.grid}
|
||||
style={{ '--player-count': game.players.length } as CSSProperties}
|
||||
>
|
||||
<div className={styles.labelCell} />
|
||||
{game.players.map((name, j) => (
|
||||
<div key={j} className={styles.headerCell}>{name}</div>
|
||||
))}
|
||||
|
||||
{game.rounds.map((roundScores, i) => (
|
||||
<Fragment key={i}>
|
||||
<div className={styles.labelCell}>Round {i + 1}</div>
|
||||
{roundScores.map((score, j) => (
|
||||
<div key={j} className={styles.scoreCell}>{score}</div>
|
||||
))}
|
||||
|
||||
{i > 0 && (
|
||||
<>
|
||||
<div className={`${styles.labelCell} ${styles.divider}`} />
|
||||
{totals[i].map((total, j) => (
|
||||
<div key={j} className={`${styles.totalCell} ${styles.divider}`}>{total}</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</PageFrame>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user