UI: единое подтверждение удаления через ConfirmDialog

- админка, записи: удаление партии раньше срабатывало сразу по клику на
  иконку и показывало «Удалено» даже при ошибке — теперь модалка
  подтверждения и честная обработка ошибок;
- ачивки и dev-удаление аккаунта: window.confirm заменён на ConfirmDialog
  (в приложении принят именно он);
- настройки группы: удаление участника — с подтверждением.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 17:40:53 +03:00
co-authored by Claude Fable 5
parent d2c0feafcc
commit 7b61443fb9
4 changed files with 96 additions and 34 deletions
@@ -1,7 +1,9 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { ApiError, api, unwrap } from "../../api/client";
import { qk } from "../../api/queryKeys";
import { ConfirmDialog } from "../../components/ConfirmDialog";
import { useToast } from "../../context/ToastContext";
/**
@@ -21,6 +23,7 @@ export function DevDeleteAccountButton({
}) {
const qc = useQueryClient();
const toast = useToast();
const [confirmOpen, setConfirmOpen] = useState(false);
const del = useMutation({
mutationFn: async () =>
unwrap(
@@ -32,29 +35,34 @@ export function DevDeleteAccountButton({
});
return (
<button
className="btn small"
// Яркая (сплошная) красная кнопка. Стиль инлайном, а не классом в общем CSS,
// чтобы в прод/тест-стили не попало ничего, связанного с удалением.
style={{ background: "var(--danger)", borderColor: "var(--danger)", color: "#fff" }}
disabled={del.isPending}
onClick={async () => {
if (
!window.confirm(
`Удалить аккаунт «${nickname}»? Партии сохранятся, но игрок будет ` +
`вычеркнут из них. Действие необратимо.`,
)
)
return;
try {
await del.mutateAsync();
toast.show("Аккаунт удалён");
} catch (e) {
toast.show(e instanceof ApiError ? e.message : "Не удалось удалить");
}
}}
>
Удалить
</button>
<>
<button
className="btn small"
// Яркая (сплошная) красная кнопка. Стиль инлайном, а не классом в общем CSS,
// чтобы в прод/тест-стили не попало ничего, связанного с удалением.
style={{ background: "var(--danger)", borderColor: "var(--danger)", color: "#fff" }}
disabled={del.isPending}
onClick={() => setConfirmOpen(true)}
>
Удалить
</button>
<ConfirmDialog
open={confirmOpen}
danger
title={`Удалить аккаунт «${nickname}»?`}
message="Партии сохранятся, но игрок будет вычеркнут из них. Действие необратимо."
confirmLabel="Удалить"
onCancel={() => setConfirmOpen(false)}
onConfirm={async () => {
setConfirmOpen(false);
try {
await del.mutateAsync();
toast.show("Аккаунт удалён");
} catch (e) {
toast.show(e instanceof ApiError ? e.message : "Не удалось удалить");
}
}}
/>
</>
);
}