Add game cancellation feature with cascade deletion and host-only access control
Implemented DELETE `/games/{game_id}` endpoint allowing hosts to cancel games with cascade deletion of sheet states, chips, and memberships. Added `cancelGame` handler in App.jsx to delete game, refresh game list, reset state, and show confirmation snack. Added cancel button to GamePickerCard lobby section (pre-start) and started section (non-finished games) with bilingual confirmation dialog, loading state, and error handling.
This commit is contained in:
@@ -17,11 +17,13 @@ export default function GamePickerCard({
|
||||
winnerName = "",
|
||||
chipCount = 0,
|
||||
onStartGame,
|
||||
onCancelGame,
|
||||
}) {
|
||||
const { language, t } = useLanguage();
|
||||
const cur = games.find((x) => x.id === gameId);
|
||||
const [starting, setStarting] = useState(false);
|
||||
const [startError, setStartError] = useState("");
|
||||
const [cancelling, setCancelling] = useState(false);
|
||||
|
||||
const renderMemberName = (m) => {
|
||||
const base = ((m.display_name || "").trim() || (m.email || "").trim() || "—");
|
||||
@@ -67,6 +69,21 @@ export default function GamePickerCard({
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelGame = async () => {
|
||||
if (!onCancelGame || cancelling) return;
|
||||
const confirmed = window.confirm(language === "en" ? "Cancel and delete this game? All game data will be removed." : "Dieses Spiel wirklich abbrechen und löschen? Alle Spieldaten werden entfernt.");
|
||||
if (!confirmed) return;
|
||||
setCancelling(true);
|
||||
setStartError("");
|
||||
try {
|
||||
await onCancelGame();
|
||||
} catch (e) {
|
||||
setStartError(e?.message || (language === "en" ? "The game could not be cancelled." : "Das Spiel konnte nicht abgebrochen werden."));
|
||||
} finally {
|
||||
setCancelling(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<div style={styles.card}>
|
||||
@@ -169,6 +186,9 @@ export default function GamePickerCard({
|
||||
<div style={{ marginTop: 7, textAlign: "center", color: stylesTokens.textDim, fontSize: 11 }}>
|
||||
{members.length < 2 ? t("atLeastTwoPlayers") : t("chipsOnStart")}
|
||||
</div>
|
||||
<button onClick={handleCancelGame} style={{ ...styles.secondaryBtn, width: "100%", marginTop: 9, color: "#ffb3b3" }} disabled={cancelling}>
|
||||
{cancelling ? (language === "en" ? "Cancelling …" : "Wird abgebrochen …") : (language === "en" ? "✕ Cancel game" : "✕ Spiel abbrechen")}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ marginTop: 11, padding: "9px 10px", borderRadius: 11, background: "rgba(233,216,166,0.07)", color: stylesTokens.textDim, fontSize: 12, textAlign: "center" }}>
|
||||
@@ -179,11 +199,14 @@ export default function GamePickerCard({
|
||||
{startError && <div style={{ marginTop: 8, color: "#ffb3b3", fontSize: 12 }}>{startError}</div>}
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ margin: "0 12px 12px", padding: "10px 12px", borderRadius: 13, border: `1px solid ${stylesTokens.panelBorder}`, background: finished ? "rgba(233,216,166,0.09)" : "rgba(124,255,182,0.07)", color: stylesTokens.textDim, fontSize: 12 }}>
|
||||
{finished
|
||||
? `🏆 ${t("finished")}${winnerName ? ` · ${t("winner")}: ${winnerName}` : ""}`
|
||||
: `✓ ${t("started")} · ${chipCount} ${t("chipsCreated")}`}
|
||||
</div>
|
||||
<>
|
||||
<div style={{ margin: "0 12px 12px", padding: "10px 12px", borderRadius: 13, border: `1px solid ${stylesTokens.panelBorder}`, background: finished ? "rgba(233,216,166,0.09)" : "rgba(124,255,182,0.07)", color: stylesTokens.textDim, fontSize: 12 }}>
|
||||
{finished
|
||||
? `🏆 ${t("finished")}${winnerName ? ` · ${t("winner")}: ${winnerName}` : ""}`
|
||||
: `✓ ${t("started")} · ${chipCount} ${t("chipsCreated")}`}
|
||||
</div>
|
||||
{isHost && !finished && <button onClick={handleCancelGame} style={{ ...styles.secondaryBtn, margin: "0 12px 12px", width: "calc(100% - 24px)", color: "#ffb3b3" }} disabled={cancelling}>{cancelling ? (language === "en" ? "Cancelling …" : "Wird abgebrochen …") : (language === "en" ? "✕ Cancel game" : "✕ Spiel abbrechen")}</button>}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Spieler */}
|
||||
|
||||
Reference in New Issue
Block a user