feat: complete wizard navigation for all steps

- Adds forward navigation arrows to the 'Game Type' and 'Race To' steps in the new game wizard.
- Unifies navigation logic across all five steps.
- Users can now review their selections before proceeding.

Closes #11
This commit is contained in:
Frank Schwenk
2025-06-20 10:44:27 +02:00
parent 14fd711858
commit b466dd2a0a

View File

@@ -438,14 +438,22 @@ const Player3Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
* @returns {import('preact').VNode} * @returns {import('preact').VNode}
*/ */
const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => { const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => {
const [gameType, setGameType] = useState(initialValue);
const gameTypes = ['8-Ball', '9-Ball', '10-Ball', '14/1 endlos']; const gameTypes = ['8-Ball', '9-Ball', '10-Ball', '14/1 endlos'];
const handleSelect = (gameType) => { const handleSelect = (selectedType) => {
onNext(gameType); setGameType(selectedType);
};
const handleSubmit = (e) => {
e.preventDefault();
if (gameType) {
onNext(gameType);
}
}; };
return ( return (
<div className={styles['new-game-form']} aria-label="Spielart auswählen"> <form className={styles['new-game-form']} onSubmit={handleSubmit} aria-label="Spielart auswählen">
<div className={styles['screen-title']}>Neues Spiel Schritt 4/5</div> <div className={styles['screen-title']}>Neues Spiel Schritt 4/5</div>
<div className={styles['progress-indicator']} style={{ marginBottom: 24 }}> <div className={styles['progress-indicator']} style={{ marginBottom: 24 }}>
<span className={styles['progress-dot']} /> <span className={styles['progress-dot']} />
@@ -459,7 +467,7 @@ const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => {
<button <button
key={type} key={type}
type="button" type="button"
className={`${styles['game-type-btn']} ${initialValue === type ? styles.selected : ''}`} className={`${styles['game-type-btn']} ${gameType === type ? styles.selected : ''}`}
onClick={() => handleSelect(type)} onClick={() => handleSelect(type)}
> >
{type} {type}
@@ -474,12 +482,32 @@ const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => {
onClick={onCancel} onClick={onCancel}
style={{ fontSize: 48, width: 80, height: 80, borderRadius: '50%', background: '#222', color: '#fff', border: 'none', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', cursor: 'pointer' }} style={{ fontSize: 48, width: 80, height: 80, borderRadius: '50%', background: '#222', color: '#fff', border: 'none', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', cursor: 'pointer' }}
> >
{/* Unicode left arrow */}
&#8592; &#8592;
</button> </button>
{/* No "weiter" arrow, selection proceeds automatically */} <button
<div style={{ width: 80 }} /> {/* Placeholder to balance the flex container */} type="submit"
className={styles['arrow-btn']}
aria-label="Weiter"
disabled={!gameType}
style={{
fontSize: 48,
width: 80,
height: 80,
borderRadius: '50%',
background: '#222',
color: '#fff',
border: 'none',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
cursor: 'pointer',
opacity: !gameType ? 0.5 : 1,
}}
>
{/* Unicode right arrow */}
&#8594;
</button>
</div> </div>
</div> </form>
); );
}; };
@@ -492,24 +520,24 @@ const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => {
* @returns {import('preact').VNode} * @returns {import('preact').VNode}
*/ */
const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => { const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
const [currentValue, setCurrentValue] = useState(initialValue); const [raceTo, setRaceTo] = useState(initialValue);
const quickPicks = [1, 2, 3, 4, 5, 6, 7, 8, 9]; const quickPicks = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const handleQuickPick = (value) => { const handleQuickPick = (value) => {
onNext(value); setRaceTo(value);
}; };
const handleInputChange = (e) => { const handleInputChange = (e) => {
setCurrentValue(e.target.value); setRaceTo(e.target.value);
}; };
const handleCustomSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
onNext(parseInt(currentValue, 10) || 0); onNext(parseInt(raceTo, 10) || 0);
}; };
return ( return (
<form className={styles['new-game-form']} onSubmit={handleCustomSubmit} aria-label="Race To auswählen"> <form className={styles['new-game-form']} onSubmit={handleSubmit} aria-label="Race To auswählen">
<div className={styles['screen-title']}>Neues Spiel Schritt 5/5</div> <div className={styles['screen-title']}>Neues Spiel Schritt 5/5</div>
<div className={styles['progress-indicator']} style={{ marginBottom: 24 }}> <div className={styles['progress-indicator']} style={{ marginBottom: 24 }}>
<span className={styles['progress-dot']} /> <span className={styles['progress-dot']} />
@@ -521,7 +549,7 @@ const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
<div className={styles['endlos-container']}> <div className={styles['endlos-container']}>
<button <button
type="button" type="button"
className={`${styles['race-to-btn']} ${styles['endlos-btn']} ${initialValue === 0 ? styles.selected : ''}`} className={`${styles['race-to-btn']} ${styles['endlos-btn']} ${raceTo === 0 ? styles.selected : ''}`}
onClick={() => handleQuickPick(0)} onClick={() => handleQuickPick(0)}
> >
Endlos Endlos
@@ -532,7 +560,7 @@ const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
<button <button
key={value} key={value}
type="button" type="button"
className={`${styles['race-to-btn']} ${parseInt(initialValue, 10) === value ? styles.selected : ''}`} className={`${styles['race-to-btn']} ${parseInt(raceTo, 10) === value ? styles.selected : ''}`}
onClick={() => handleQuickPick(value)} onClick={() => handleQuickPick(value)}
> >
{value} {value}
@@ -543,12 +571,11 @@ const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
<input <input
type="number" type="number"
pattern="[0-9]*" pattern="[0-9]*"
value={currentValue} value={raceTo}
onInput={handleInputChange} onInput={handleInputChange}
className={styles['name-input']} className={styles['name-input']}
placeholder="manuelle Eingabe" placeholder="manuelle Eingabe"
/> />
<button type="submit" className={styles['arrow-btn']} aria-label="Bestätigen">&#10003;</button>
</div> </div>
<div className={styles['arrow-nav']} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 48 }}> <div className={styles['arrow-nav']} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 48 }}>
<button <button
@@ -556,10 +583,20 @@ const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
className={styles['arrow-btn']} className={styles['arrow-btn']}
aria-label="Zurück" aria-label="Zurück"
onClick={onCancel} onClick={onCancel}
style={{ fontSize: 48, width: 80, height: 80, borderRadius: '50%', background: '#222', color: '#fff', border: 'none', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', cursor: 'pointer' }}
> >
{/* Unicode left arrow */}
&#8592; &#8592;
</button> </button>
<div style={{ width: 80 }} /> <button
type="submit"
className={styles['arrow-btn']}
aria-label="Fertigstellen"
style={{ fontSize: 48, width: 80, height: 80, borderRadius: '50%', background: '#222', color: '#fff', border: 'none', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', cursor: 'pointer' }}
>
{/* Unicode checkmark */}
&#10003;
</button>
</div> </div>
</form> </form>
); );