Files
openmonetis/src/features/settings/components/delete-account-form.tsx
Felipe Coutinho 94bf93194f chore: ajustes de componentes, estilos, dependências e métricas do dashboard
- dashboard: melhorias em métricas, filtros de transações e overview de período
- transactions: colunas, tabela e página com novos campos e ajustes de exibição
- ui: card, table, navigation-menu, navbar, month-picker, logo-picker, theme-toggler
- calculator: ajustes de display, keypad e estado
- calendar: melhorias de grid e day-cell
- insights: atualização de constantes
- settings: pequenos ajustes
- pnpm-lock: atualização de dependências
- pdf.worker: atualização do worker

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 22:08:53 +00:00

232 lines
6.3 KiB
TypeScript

"use client";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
import { toast } from "sonner";
import {
deleteAccountAction,
resetAccountAction,
} from "@/features/settings/actions";
import { Button } from "@/shared/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/shared/components/ui/dialog";
import { Input } from "@/shared/components/ui/input";
import { Label } from "@/shared/components/ui/label";
import { authClient } from "@/shared/lib/auth/client";
const RESET_CONFIRMATION = "ZERAR";
const DELETE_CONFIRMATION = "DELETAR";
type DangerAction = "reset" | "delete";
export function DeleteAccountForm() {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [dangerAction, setDangerAction] = useState<DangerAction | null>(null);
const [confirmation, setConfirmation] = useState("");
const handleAction = () => {
if (!dangerAction) return;
const currentAction = dangerAction;
startTransition(async () => {
const result =
currentAction === "reset"
? await resetAccountAction({
confirmation: confirmation as typeof RESET_CONFIRMATION,
})
: await deleteAccountAction({
confirmation: confirmation as typeof DELETE_CONFIRMATION,
});
if (result.success) {
toast.success(result.message);
if (currentAction === "delete") {
await authClient.signOut();
router.push("/");
return;
}
setConfirmation("");
setDangerAction(null);
router.refresh();
} else {
toast.error(result.error);
}
});
};
const handleOpenModal = (action: DangerAction) => {
setConfirmation("");
setDangerAction(action);
};
const handleCloseModal = () => {
if (isPending) return;
setConfirmation("");
setDangerAction(null);
};
const confirmationWord =
dangerAction === "reset" ? RESET_CONFIRMATION : DELETE_CONFIRMATION;
const isResetAction = dangerAction === "reset";
return (
<>
<div className="flex flex-col space-y-6">
<div className="rounded-lg border p-4">
<div className="space-y-4">
<div>
<h3 className="font-semibold">Zerar conta</h3>
<p className="text-sm text-muted-foreground">
Apaga todos os dados do OpenMonetis e deixa sua conta no estado
inicial, mantendo seu login e credenciais de acesso.
</p>
</div>
<ul className="list-disc list-inside text-sm text-muted-foreground space-y-1">
<li>Lançamentos, faturas, antecipações e pré-lançamentos</li>
<li>Contas, cartões, orçamentos e anotações</li>
<li>Pessoas próprios e compartilhamentos recebidos</li>
<li>
Preferências do app, insights salvos e tokens do Companion
</li>
<li>
Categorias padrão e pessoa admin serão recriadas automaticamente
</li>
</ul>
<div className="flex justify-end">
<Button
variant="outline"
onClick={() => handleOpenModal("reset")}
disabled={isPending}
className="w-fit border-destructive/30 text-destructive hover:bg-destructive/10 hover:text-destructive"
>
Zerar conta
</Button>
</div>
</div>
</div>
<div className="rounded-lg border border-destructive/30 p-4">
<div className="space-y-4">
<div>
<h3 className="font-semibold text-destructive">Deletar conta</h3>
<p className="text-sm text-muted-foreground">
Remove seu usuário e todos os dados associados de forma
permanente.
</p>
</div>
<ul className="list-disc list-inside text-sm text-destructive space-y-1">
<li>Lançamentos, orçamentos e anotações</li>
<li>Faturas, antecipações e pré-lançamentos</li>
<li>Contas, cartões e categorias</li>
<li>Pessoas, credenciais e configurações</li>
<li className="font-medium">
Resumindo, sua conta irá de arrasta pra cima!
</li>
</ul>
<div className="flex justify-end">
<Button
variant="destructive"
onClick={() => handleOpenModal("delete")}
disabled={isPending}
className="w-fit"
>
Deletar conta
</Button>
</div>
</div>
</div>
</div>
<Dialog
open={dangerAction !== null}
onOpenChange={(isOpen) => {
if (!isOpen) {
handleCloseModal();
}
}}
>
<DialogContent
className="max-w-md"
onEscapeKeyDown={(e) => {
if (isPending) e.preventDefault();
}}
onPointerDownOutside={(e) => {
if (isPending) e.preventDefault();
}}
>
<DialogHeader>
<DialogTitle>
{isResetAction ? "Zerar sua conta?" : "Você tem certeza?"}
</DialogTitle>
<DialogDescription>
{isResetAction
? "Essa ação não pode ser desfeita. Todos os dados do app serão apagados e sua conta voltará ao estado inicial, mas seu login continuará existindo."
: "Essa ação não pode ser desfeita. Isso irá deletar permanentemente sua conta e remover seus dados de nossos servidores."}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="confirmation">
Para confirmar, digite <strong>{confirmationWord}</strong> no
campo abaixo.
</Label>
<Input
id="confirmation"
value={confirmation}
onChange={(e) => setConfirmation(e.target.value)}
disabled={isPending}
placeholder={confirmationWord}
autoComplete="off"
/>
</div>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={handleCloseModal}
disabled={isPending}
>
Cancelar
</Button>
<Button
type="button"
variant={isResetAction ? "outline" : "destructive"}
onClick={handleAction}
disabled={isPending || confirmation !== confirmationWord}
className={
isResetAction
? "border-destructive/30 text-destructive hover:bg-destructive/10 hover:text-destructive"
: undefined
}
>
{isPending
? isResetAction
? "Zerando..."
: "Deletando..."
: isResetAction
? "Zerar conta"
: "Deletar"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}