aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/settings/DeleteAccount.tsx
blob: 6ebafff95c39958e8f4bd690c09358332ca8716a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { ActionButton } from "@/components/ui/action-button";
import ActionConfirmingDialog from "@/components/ui/action-confirming-dialog";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { toast } from "@/components/ui/use-toast";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Eye, EyeOff, Trash2 } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";

import {
  useDeleteAccount,
  useWhoAmI,
} from "@karakeep/shared-react/hooks/users";

import { Button } from "../ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";

const createDeleteAccountSchema = (isLocalUser: boolean) =>
  z.object({
    password: isLocalUser
      ? z.string().min(1, "Password is required")
      : z.string().optional(),
  });

export function DeleteAccount() {
  const router = useRouter();
  const [showPassword, setShowPassword] = useState(false);
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const { data: user } = useWhoAmI();

  const isLocalUser = user?.localUser ?? false;
  const deleteAccountSchema = createDeleteAccountSchema(isLocalUser);

  const form = useForm<z.infer<typeof deleteAccountSchema>>({
    resolver: zodResolver(deleteAccountSchema),
    defaultValues: {
      password: "",
    },
  });

  const deleteAccountMutation = useDeleteAccount({
    onSuccess: () => {
      toast({
        description: "Your account has been successfully deleted.",
      });
      // Redirect to home page after successful deletion
      router.push("/");
      setIsDialogOpen(false);
    },
    onError: (error) => {
      if (error.data?.code === "UNAUTHORIZED") {
        toast({
          description: "Invalid password. Please try again.",
          variant: "destructive",
        });
      } else {
        toast({
          description: "Failed to delete account. Please try again.",
          variant: "destructive",
        });
      }
    },
  });

  const onSubmit = (values: z.infer<typeof deleteAccountSchema>) => {
    deleteAccountMutation.mutate({ password: values.password });
  };

  return (
    <Card className="border-destructive/20">
      <CardHeader>
        <CardTitle className="flex items-center gap-2 text-xl text-destructive">
          <AlertTriangle className="h-5 w-5" />
          Danger Zone
        </CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="space-y-2">
          <h3 className="text-lg font-medium">Delete Account</h3>
          <p className="text-sm text-muted-foreground">
            Permanently delete your account and all associated data. This action
            cannot be undone.
          </p>
        </div>

        <ActionConfirmingDialog
          open={isDialogOpen}
          setOpen={setIsDialogOpen}
          title="Delete Account"
          description={
            <div className="space-y-4">
              <div className="flex items-start gap-3 rounded-lg border border-destructive/20 bg-destructive/5 p-4">
                <AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-destructive" />
                <div className="space-y-2">
                  <p className="font-medium text-destructive">
                    This action is irreversible
                  </p>
                  <p className="text-sm text-muted-foreground">
                    All your bookmarks, lists, tags, highlights, and other data
                    will be permanently deleted. This cannot be undone.
                  </p>
                </div>
              </div>

              <Form {...form}>
                <form
                  onSubmit={form.handleSubmit(onSubmit)}
                  className="space-y-4"
                >
                  {isLocalUser && (
                    <FormField
                      control={form.control}
                      name="password"
                      render={({ field }) => (
                        <FormItem>
                          <FormLabel>
                            Enter your password to confirm deletion
                          </FormLabel>
                          <div className="relative">
                            <FormControl>
                              <Input
                                type={showPassword ? "text" : "password"}
                                placeholder="Enter your password"
                                className="pr-10"
                                {...field}
                              />
                            </FormControl>
                            <Button
                              type="button"
                              variant="ghost"
                              size="sm"
                              className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
                              onClick={() => setShowPassword(!showPassword)}
                            >
                              {showPassword ? (
                                <EyeOff className="h-4 w-4" />
                              ) : (
                                <Eye className="h-4 w-4" />
                              )}
                            </Button>
                          </div>
                          <FormMessage />
                        </FormItem>
                      )}
                    />
                  )}
                </form>
              </Form>
            </div>
          }
          actionButton={() => (
            <ActionButton
              variant="destructive"
              loading={deleteAccountMutation.isPending}
              onClick={form.handleSubmit(onSubmit)}
            >
              <Trash2 className="mr-2 h-4 w-4" />
              Delete Account
            </ActionButton>
          )}
        >
          <Button variant="destructive" className="w-full">
            <Trash2 className="mr-2 h-4 w-4" />
            Delete Account
          </Button>
        </ActionConfirmingDialog>
      </CardContent>
    </Card>
  );
}