diff --git a/src/App.tsx b/src/App.tsx index cae4216..7d8f681 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -37,12 +37,15 @@ export default function App() { setPage('scoresheet') } - function handleSubmitScores() { + function handleSubmitScores(scores: number[]) { + const updated: GameState = { ...game!, rounds: [...game!.rounds, scores] } + saveGame(updated) + setGame(updated) setPage('scoresheet') } if (page === 'title') return if (page === 'setup') return if (page === 'scoresheet') return setPage('enterscores')} onNewGame={handleNewGame} /> - if (page === 'enterscores') return + if (page === 'enterscores') return } diff --git a/src/game/scoring.ts b/src/game/scoring.ts index 4bba1c9..fc02fdf 100644 --- a/src/game/scoring.ts +++ b/src/game/scoring.ts @@ -1,3 +1,11 @@ +export function isIndividualScoreValid(n: number): boolean { + return n % 5 === 0 && n <= 110 +} + +export function isRoundScoreValid(scores: number[]): boolean { + return scores.every(isIndividualScoreValid) && scores.reduce((a, b) => a + b, 0) === -110 +} + // Returns totals[i][j] = cumulative score for player j after round i. export function computeRunningTotals(rounds: number[][]): number[][] { const totals: number[][] = [] diff --git a/src/pages/EnterScores.module.css b/src/pages/EnterScores.module.css new file mode 100644 index 0000000..fac417d --- /dev/null +++ b/src/pages/EnterScores.module.css @@ -0,0 +1,40 @@ +.row { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.name { + flex: 1; + font-size: 1.1rem; + font-weight: 500; +} + +.scoreInput { + width: 6rem; + padding: 0.85rem 0.5rem; + font-size: 1.1rem; + font-family: inherit; + text-align: center; + border: 1.5px solid var(--color-parchment-border); + border-radius: 0.75rem; + background: #fdfaf4; +} + +.scoreInputError { + border-color: #c0392b; + background: #fff5f5; +} + +.sumLine { + margin: 0.5rem 0 0; + text-align: center; + font-size: 0.95rem; + color: var(--color-parchment-border); +} + +.sumValid { + color: #4a9e6a; + font-weight: 600; +} diff --git a/src/pages/EnterScores.tsx b/src/pages/EnterScores.tsx index b797a62..f5fe15f 100644 --- a/src/pages/EnterScores.tsx +++ b/src/pages/EnterScores.tsx @@ -1,13 +1,52 @@ +import { useState } from 'react' import PageFrame from '../components/PageFrame' +import PrimaryButton from '../components/PrimaryButton' +import { isIndividualScoreValid, isRoundScoreValid } from '../game/scoring' +import { type GameState } from '../types' +import styles from './EnterScores.module.css' interface Props { - onSubmit: () => void + game: GameState + onSubmit: (scores: number[]) => void } -export default function EnterScores({ onSubmit }: Props) { +export default function EnterScores({ game, onSubmit }: Props) { + const [values, setValues] = useState(() => game.players.map(() => '')) + + function updateValue(i: number, v: string) { + setValues(vs => vs.map((val, idx) => (idx === i ? v : val))) + } + + const parsed = values.map(v => parseInt(v, 10)) + const fieldError = values.map((v, i) => v !== '' && (isNaN(parsed[i]) || !isIndividualScoreValid(parsed[i]))) + const allFilled = parsed.every(n => !isNaN(n)) + const sum = parsed.reduce((acc, n) => acc + (isNaN(n) ? 0 : n), 0) + const canSubmit = allFilled && isRoundScoreValid(parsed) + return ( - - Submit + onSubmit(parsed)} disabled={!canSubmit}> + Submit + + } + > + {game.players.map((name, i) => ( + + {name} + updateValue(i, e.target.value)} + /> + + ))} + + Sum: {sum} / −110 + ) }
+ Sum: {sum} / −110 +