Add permanent account deletion

This commit is contained in:
2026-08-15 13:46:42 -04:00
parent 5f6987b84b
commit 1e7174fa60
3 changed files with 191 additions and 124 deletions
@@ -162,8 +162,11 @@ export function SettingsContent({
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [isLinking, setIsLinking] = useState(false);
const authentikEnabled = env.NEXT_PUBLIC_AUTHENTIK_ENABLED === true;
const { colorMode, updateAppearance, isUpdating: appearanceUpdating } =
useAppearance();
const {
colorMode,
updateAppearance,
isUpdating: appearanceUpdating,
} = useAppearance();
const utils = api.useUtils();
const { data: pdfSettings } = api.settings.getPdfSettings.useQuery();
const updatePdfSettingsMutation = api.settings.updatePdfSettings.useMutation({
@@ -221,8 +224,11 @@ export function SettingsContent({
};
// Queries
const { data: profile, refetch: refetchProfile, isFetched: profileFetched } =
api.settings.getProfile.useQuery();
const {
data: profile,
refetch: refetchProfile,
isFetched: profileFetched,
} = api.settings.getProfile.useQuery();
const isAdmin = profile?.role === "admin";
const { data: dataStats } = api.settings.getDataStats.useQuery();
@@ -285,10 +291,13 @@ export function SettingsContent({
},
});
const deleteDataMutation = api.settings.deleteAllData.useMutation({
onSuccess: () => {
toast.success("All data has been permanently deleted");
const deleteAccountMutation = api.settings.deleteAccount.useMutation({
onSuccess: async () => {
toast.success("Your account and data have been permanently deleted");
setDeleteConfirmText("");
await authClient.signOut().catch(() => undefined);
router.replace("/login");
router.refresh();
},
onError: (error: { message: string }) => {
toast.error(`Delete failed: ${error.message}`);
@@ -401,12 +410,12 @@ export function SettingsContent({
}
};
const handleDeleteAllData = () => {
if (deleteConfirmText !== "delete all my data") {
toast.error("Please type 'delete all my data' to confirm");
const handleDeleteAccount = () => {
if (deleteConfirmText !== "DELETE MY ACCOUNT") {
toast.error("Please type 'DELETE MY ACCOUNT' to confirm");
return;
}
deleteDataMutation.mutate({ confirmText: deleteConfirmText });
deleteAccountMutation.mutate({ confirmText: deleteConfirmText });
};
// Set initial name value once when profile loads
@@ -706,10 +715,7 @@ export function SettingsContent({
</SelectTrigger>
<SelectContent>
{colorModes.map((modeOption) => (
<SelectItem
key={modeOption.value}
value={modeOption.value}
>
<SelectItem key={modeOption.value} value={modeOption.value}>
{modeOption.label}
</SelectItem>
))}
@@ -1283,37 +1289,43 @@ export function SettingsContent({
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" className="w-full sm:w-auto">
Delete All Data
Delete Account
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete
your account and remove your data from our servers.
This action cannot be undone. It permanently deletes your
account, invoices, clients, businesses, expenses, time
entries, uploaded files, and sign-in data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<div className="my-4 space-y-2">
<Label htmlFor="confirm-delete">
Type <span className="font-bold">delete all my data</span>{" "}
to confirm
Type <span className="font-bold">DELETE MY ACCOUNT</span> to
confirm
</Label>
<Input
id="confirm-delete"
value={deleteConfirmText}
onChange={(e) => setDeleteConfirmText(e.target.value)}
placeholder="delete all my data"
placeholder="DELETE MY ACCOUNT"
/>
</div>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleDeleteAllData}
onClick={handleDeleteAccount}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
disabled={deleteConfirmText !== "delete all my data"}
disabled={
deleteConfirmText !== "DELETE MY ACCOUNT" ||
deleteAccountMutation.isPending
}
>
Delete Account
{deleteAccountMutation.isPending
? "Deleting…"
: "Delete Account"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>