refactor: Update Race To step UI and logic

- Renames 'Offen' to 'Endlos' and moves it to a separate line.
- Extends quick-pick buttons to include 1-9.
- Removes 'Custom' button and makes numeric input always visible.
- Updates placeholder text for the custom input.
- Closes #10
This commit is contained in:
Frank Schwenk
2025-06-20 10:23:14 +02:00
parent 1c77661dbc
commit 14fd711858
4 changed files with 152 additions and 11 deletions

2
.gitea
View File

@@ -1 +1 @@
@https://gitea.schwenk.online/froxxxy/bscscore/issues/9
@https://gitea.schwenk.online/froxxxy/bscscore/issues/10

View File

@@ -2,7 +2,7 @@ import { h } from 'preact';
import { useState, useEffect, useCallback } from 'preact/hooks';
import GameList from './GameList.jsx';
import GameDetail from './GameDetail.jsx';
import { Player1Step, Player2Step, Player3Step, GameTypeStep } from './NewGame.jsx';
import { Player1Step, Player2Step, Player3Step, GameTypeStep, RaceToStep } from './NewGame.jsx';
import Modal from './Modal.jsx';
import ValidationModal from './ValidationModal.jsx';
import GameCompletionModal from './GameCompletionModal.jsx';
@@ -156,10 +156,9 @@ const App = () => {
setNewGameStep('raceTo');
};
const handleRaceToNext = (raceTo) => {
setNewGameData(data => ({ ...data, raceTo }));
// Finalize game creation here
// For now, just go back to game list
setScreen('game-list');
const finalData = { ...newGameData, raceTo };
const newId = handleCreateGame(finalData);
showGameDetail(newId);
setNewGameStep(null);
setNewGameData({ player1: '', player2: '', player3: '', gameType: '', raceTo: '' });
};
@@ -221,10 +220,11 @@ const App = () => {
/>
)}
{newGameStep === 'raceTo' && (
<div style={{ padding: 40, textAlign: 'center' }}>
<h2>Race To Step (TODO)</h2>
<button onClick={() => handleRaceToNext(5)}>5</button>
</div>
<RaceToStep
onNext={handleRaceToNext}
onCancel={() => setNewGameStep('gameType')}
initialValue={newGameData.raceTo}
/>
)}
</div>
</div>

View File

@@ -483,4 +483,86 @@ const GameTypeStep = ({ onNext, onCancel, initialValue = '' }) => {
);
};
export { Player1Step, Player2Step, Player3Step, GameTypeStep };
/**
* Race To selection step for multi-step game creation wizard.
* @param {object} props
* @param {Function} props.onNext
* @param {Function} props.onCancel
* @param {string|number} [props.initialValue]
* @returns {import('preact').VNode}
*/
const RaceToStep = ({ onNext, onCancel, initialValue = '' }) => {
const [currentValue, setCurrentValue] = useState(initialValue);
const quickPicks = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const handleQuickPick = (value) => {
onNext(value);
};
const handleInputChange = (e) => {
setCurrentValue(e.target.value);
};
const handleCustomSubmit = (e) => {
e.preventDefault();
onNext(parseInt(currentValue, 10) || 0);
};
return (
<form className={styles['new-game-form']} onSubmit={handleCustomSubmit} aria-label="Race To auswählen">
<div className={styles['screen-title']}>Neues Spiel Schritt 5/5</div>
<div className={styles['progress-indicator']} style={{ marginBottom: 24 }}>
<span className={styles['progress-dot']} />
<span className={styles['progress-dot']} />
<span className={styles['progress-dot']} />
<span className={styles['progress-dot']} />
<span className={styles['progress-dot'] + ' ' + styles['active']} />
</div>
<div className={styles['endlos-container']}>
<button
type="button"
className={`${styles['race-to-btn']} ${styles['endlos-btn']} ${initialValue === 0 ? styles.selected : ''}`}
onClick={() => handleQuickPick(0)}
>
Endlos
</button>
</div>
<div className={styles['race-to-selection']}>
{quickPicks.map(value => (
<button
key={value}
type="button"
className={`${styles['race-to-btn']} ${parseInt(initialValue, 10) === value ? styles.selected : ''}`}
onClick={() => handleQuickPick(value)}
>
{value}
</button>
))}
</div>
<div className={styles['custom-race-to']}>
<input
type="number"
pattern="[0-9]*"
value={currentValue}
onInput={handleInputChange}
className={styles['name-input']}
placeholder="manuelle Eingabe"
/>
<button type="submit" className={styles['arrow-btn']} aria-label="Bestätigen">&#10003;</button>
</div>
<div className={styles['arrow-nav']} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 48 }}>
<button
type="button"
className={styles['arrow-btn']}
aria-label="Zurück"
onClick={onCancel}
>
&#8592;
</button>
<div style={{ width: 80 }} />
</div>
</form>
);
};
export { Player1Step, Player2Step, Player3Step, GameTypeStep, RaceToStep };

View File

@@ -215,4 +215,63 @@
.game-type-btn.selected {
background: #4a4a4a;
border-color: #777;
}
.race-to-selection {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
gap: 12px;
width: 100%;
margin: 16px 0;
}
.race-to-btn {
background: #2a2a2a;
border: 2px solid #333;
color: #fff;
font-size: 1.3rem;
font-weight: 600;
padding: 16px 8px;
border-radius: 8px;
cursor: pointer;
text-align: center;
transition: background 0.2s, border-color 0.2s;
min-height: 70px;
}
.race-to-btn:hover {
background: #333;
border-color: #555;
}
.race-to-btn.selected {
background: #4a4a4a;
border-color: #777;
}
.custom-race-to {
display: flex;
gap: 12px;
margin-top: 24px;
align-items: center;
}
.custom-race-to input {
flex-grow: 1;
}
.custom-race-to .arrow-btn {
width: 60px;
height: 60px;
font-size: 32px;
flex-shrink: 0;
}
.endlos-container {
width: 100%;
margin-bottom: 12px;
}
.endlos-btn {
width: 100%;
}