tablet error screen can recover from a stale bar_id; admin drink reorder gets a non-drag fallback
- App.tsx: the error screen (shown when api.config(barId) fails, e.g. an admin deleted/renamed the bar while this tablet was offline) only offered 'Neu laden', which re-reads the same stale localStorage bar_id and fails again — a permanent stuck loop with no in-app fix. Added a 'Bar wechseln' button reusing the existing changeBar() logic, which now also clears the error state. - Admin.tsx BarDrinkEditor: the drag-and-drop reorder is a mouse-oriented API that doesn't fire on touch-only input and has no keyboard equivalent. Added ▲/▼ buttons alongside the drag handle so reordering works regardless of input method.
This commit is contained in:
parent
59f6041a16
commit
058c77524a
3 changed files with 30 additions and 1 deletions
|
|
@ -322,6 +322,19 @@ function BarDrinkEditor({
|
||||||
>
|
>
|
||||||
<span class="grip" aria-hidden="true">⋮⋮</span>
|
<span class="grip" aria-hidden="true">⋮⋮</span>
|
||||||
<span class="dnd-name">{idx + 1}. {d.name}</span>
|
<span class="dnd-name">{idx + 1}. {d.name}</span>
|
||||||
|
{/* Drag-and-drop above doesn't fire on touch-only input (no
|
||||||
|
mouse) and has no keyboard equivalent — these buttons are
|
||||||
|
the fallback that works regardless of input method. */}
|
||||||
|
<button onClick={() => reorder(idx, idx - 1)} disabled={idx === 0} aria-label="nach oben">
|
||||||
|
▲
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => reorder(idx, idx + 1)}
|
||||||
|
disabled={idx === selected.length - 1}
|
||||||
|
aria-label="nach unten"
|
||||||
|
>
|
||||||
|
▼
|
||||||
|
</button>
|
||||||
<button onClick={() => remove(idx)}>×</button>
|
<button onClick={() => remove(idx)}>×</button>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -295,4 +295,5 @@ button.danger { background: #5a2a2a; border-color: #7a3a3a; color: #fff; }
|
||||||
.dnd-item.drop-target { border-color: #6aa; background: var(--surface-2); }
|
.dnd-item.drop-target { border-color: #6aa; background: var(--surface-2); }
|
||||||
.dnd-item .grip { opacity: 0.4; cursor: grab; user-select: none; }
|
.dnd-item .grip { opacity: 0.4; cursor: grab; user-select: none; }
|
||||||
.dnd-item .dnd-name { flex: 1; }
|
.dnd-item .dnd-name { flex: 1; }
|
||||||
|
.dnd-item button { padding: 2px 8px; font-size: 13px; }
|
||||||
.dnd-list li.muted { padding: 6px 8px; }
|
.dnd-list li.muted { padding: 6px 8px; }
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,24 @@ export function App() {
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
setBarId(null);
|
setBarId(null);
|
||||||
setConfig(null);
|
setConfig(null);
|
||||||
|
setError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) return <div class="bar-picker"><h1>Fehler</h1><p>{error}</p><button onClick={() => location.reload()}>Neu laden</button></div>;
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div class="bar-picker">
|
||||||
|
<h1>Fehler</h1>
|
||||||
|
<p>{error}</p>
|
||||||
|
<button onClick={() => location.reload()}>Neu laden</button>
|
||||||
|
{/* A stuck-forever loop is otherwise possible here: the stored bar_id
|
||||||
|
can be permanently invalid (e.g. an admin deleted/renamed the bar
|
||||||
|
while this tablet was offline), and "Neu laden" alone re-reads
|
||||||
|
that same stale id and fails again. Route back through the same
|
||||||
|
changeBar() the working UI uses, which clears it. */}
|
||||||
|
<button onClick={changeBar}>Bar wechseln</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (barId == null) return <BarPicker bars={bars} onPick={pick} />;
|
if (barId == null) return <BarPicker bars={bars} onPick={pick} />;
|
||||||
if (!config) return <div class="bar-picker"><p>Lade…</p></div>;
|
if (!config) return <div class="bar-picker"><p>Lade…</p></div>;
|
||||||
return <Sale config={config} onChangeBar={changeBar} />;
|
return <Sale config={config} onChangeBar={changeBar} />;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue