fix: kolo štěstí točí až od dvou možností
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 25s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 1m50s
CI / Build and push Docker image (push) Successful in 46s
CI / Notify (push) Successful in 2s

This commit is contained in:
2026-09-23 16:18:11 +02:00
parent 0a59f97c5c
commit bd52ae071e
@@ -45,6 +45,8 @@ const WHEEL_SIZE = 360;
const RADIUS = WHEEL_SIZE / 2 - 6;
const SEGMENT_COLORS = 6;
const MAX_LABELED_SEGMENTS = 40;
/** Točit má smysl až od dvou možností — jediná zbylá se vybere rovnou. */
const MIN_SPIN_ITEMS = 2;
/** Kryptograficky náhodný index v rozsahu 0..n-1 (bez modulo zkreslení to u malých n nevadí). */
function randomIndex(n: number): number {
@@ -198,9 +200,17 @@ export default function SpinWheelModal({ isOpen, onClose, menus, onPick }: Reado
setRotation(r => r % 360);
};
// Zbyla jediná možnost — není co losovat, vybere se rovnou (lze pokračovat dál / převzít)
const onlyOne = wheelItems.length === 1;
useEffect(() => {
if (spinning || !onlyOne) return;
if (phase === 'place' && place?.id !== places[0].id) setPlace(places[0]);
else if (phase === 'dish' && dish?.name !== dishes[0].name) setDish(dishes[0]);
}, [onlyOne, phase, places, dishes, spinning, place?.id, dish?.name]);
const spin = () => {
const n = wheelItems.length;
if (n === 0 || spinning) return;
if (n < MIN_SPIN_ITEMS || spinning) return;
const index = randomIndex(n);
const angle = 360 / n;
const jitter = (Math.random() - 0.5) * angle * 0.6;
@@ -337,17 +347,21 @@ export default function SpinWheelModal({ isOpen, onClose, menus, onPick }: Reado
)}
<div className={`spin-result${result ? ' visible' : ''}`} aria-live="polite">
{result && (phase === 'place'
? <>Padlo: <strong>{place!.label}</strong> <small>({place!.kind === 'menu' ? 'dnešní menu' : 'rozvoz'})</small></>
: <>Padlo: <strong>{dish!.name}</strong>{dish!.price && <small> ({dish!.price})</small>}</>)}
? <>{onlyOne ? 'Zbývá jediná možnost' : 'Padlo'}: <strong>{place!.label}</strong> <small>({place!.kind === 'menu' ? 'dnešní menu' : 'rozvoz'})</small></>
: <>{onlyOne ? 'Zbývá jediné jídlo' : 'Padlo'}: <strong>{dish!.name}</strong>{dish!.price && <small> ({dish!.price})</small>}</>)}
</div>
<div className="spin-actions">
<Button onClick={spin} disabled={spinning || wheelItems.length === 0} className="spin-button">
{spinning ? 'Točí se…' : result ? 'Točit znovu' : 'Roztočit!'}
<Button onClick={spin} disabled={spinning || wheelItems.length < MIN_SPIN_ITEMS} className="spin-button"
title={wheelItems.length < MIN_SPIN_ITEMS ? 'Na kole musí být aspoň 2 možnosti' : undefined}>
{spinning ? 'Točí se…' : result && !onlyOne ? 'Točit znovu' : 'Roztočit!'}
</Button>
{result && !spinning && wheelItems.length > 1 && (
<Button variant="outline-primary" onClick={removeResultAndRespin}>Vyřadit a točit znovu</Button>
)}
</div>
{onlyOne && (
<small className="text-muted">Na kole musí být aspoň 2 možnosti — vrať nějaké vyřazené, nebo pokračuj s touto.</small>
)}
</div>
<div className="spin-options">
<div className="spin-options-header">