Files
ForbiddenStarsApp/frontend/src/pages/admin/DevDeleteAccountButton.tsx
T
NotBigGhostandClaude Opus 5 3587fc8af2 Тосты: убрать лишние, остальные показывать как уведомления сверху
Многие тосты повторяли то, что пользователь и так видит сразу после действия:
новую фракцию или аватар в профиле, переход на страницу партии, исчезнувшую
карточку приглашения, закрывшийся редактор. Такой шум приучает не читать
всплывашки, в том числе ошибки. Убраны 19 таких тостов в профиле, группах,
партиях и админке.

Остались ошибки, предупреждения (лимит фото, повторы фракций, конфликт правок,
«вы не в группе») и сообщения о действиях без видимого следа: приглашение
отправлено, дополнения сохранены, вступление в группу, пароль задан. Сохранение
текстового поля, где заметна только погасшая кнопка, тоже осталось.

По просьбе владельца оставшиеся тосты показываются не нижней «таблеткой», а
всплывашкой сверху, как входящие уведомления. ToastProvider держит одну стопку
на всё приложение: show — обычное сообщение, error — ошибка с красной полосой
(держится 5 с), notify — уведомление с переходом по клику. Одинаковое сообщение
не копится, а заменяет прежнее; клик по карточке закрывает её.
NotificationToaster больше не рисует свою стопку, а отдаёт новые уведомления в
общую, поэтому они не наезжают на сообщения. Все вызовы с ApiError и отказы
(лимит фото, партия изменилась на другом устройстве) переведены на toast.error.
Нижний класс .toast удалён.

#41

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
2026-09-13 15:45:34 +03:00

68 lines
2.6 KiB
TypeScript

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";
/**
* DEV-ТОЛЬКО кнопка жёсткого удаления аккаунта.
*
* Эндпоинт `DELETE /api/admin/dev/users/{id}` существует только в dev-сборке бэкенда
* (backend/app/routers/dev_admin.py, исключён из прод/тест-образа). Этот модуль
* рендерится лишь под `import.meta.env.DEV` в AdminAccountsPage, поэтому в прод-сборке
* он не используется и вырезается тришейкингом — в прод/тест удаление недоступно.
*/
export function DevDeleteAccountButton({
userId,
nickname,
}: {
userId: number;
nickname: string;
}) {
const qc = useQueryClient();
const toast = useToast();
const [confirmOpen, setConfirmOpen] = useState(false);
const del = useMutation({
mutationFn: async () =>
unwrap(
await api.DELETE("/api/admin/dev/users/{user_id}", {
params: { path: { user_id: userId } },
}),
),
onSuccess: () => qc.invalidateQueries({ queryKey: qk.adminUsers }),
});
return (
<>
<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();
} catch (e) {
toast.error(e instanceof ApiError ? e.message : "Не удалось удалить");
}
}}
/>
</>
);
}