From 998769ad0a43e9a5d14055a7283a8fd4b086a509 Mon Sep 17 00:00:00 2001 From: Razvan Titus Dimulescu Date: Thu, 12 Sep 2024 22:25:21 +0200 Subject: [PATCH] feat: delete account --- src/components/ProfileDeleteButton.tsx | 26 +++++ src/components/ProfileDeleteCard.tsx | 18 ++++ src/components/ProfileDeleteDialog.tsx | 144 +++++++++++++++++++++++++ src/components/ProfileSettings.tsx | 111 ++++++++++--------- src/services/settings/api.ts | 19 ++++ 5 files changed, 265 insertions(+), 53 deletions(-) create mode 100644 src/components/ProfileDeleteButton.tsx create mode 100644 src/components/ProfileDeleteCard.tsx create mode 100644 src/components/ProfileDeleteDialog.tsx diff --git a/src/components/ProfileDeleteButton.tsx b/src/components/ProfileDeleteButton.tsx new file mode 100644 index 0000000..7ee9af4 --- /dev/null +++ b/src/components/ProfileDeleteButton.tsx @@ -0,0 +1,26 @@ +"use client" + +import * as React from "react" +import { ProfileDeleteDialog } from "./ProfileDeleteDialog" +import { Button } from "./ui/button" + +export default function ProfileDeleteButton() { + const [showDeleteDialog, setShowDeleteDialog] = React.useState(false) + return ( + <> + setShowDeleteDialog(false)} + /> + + + ) +} diff --git a/src/components/ProfileDeleteCard.tsx b/src/components/ProfileDeleteCard.tsx new file mode 100644 index 0000000..fa6c8a9 --- /dev/null +++ b/src/components/ProfileDeleteCard.tsx @@ -0,0 +1,18 @@ +"use client" + +import ProfileDeleteButton from "./ProfileDeleteButton" +import { Card, CardContent, CardFooter, CardHeader } from "./ui/card" + +export function ProfileDeleteCard() { + return ( + + Delete account + + Once you delete your account, there is no going back. Please be certain. + + + + + + ) +} diff --git a/src/components/ProfileDeleteDialog.tsx b/src/components/ProfileDeleteDialog.tsx new file mode 100644 index 0000000..ae8c5a0 --- /dev/null +++ b/src/components/ProfileDeleteDialog.tsx @@ -0,0 +1,144 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer" +import { useMediaQuery } from "@/hooks/useMediaQuery" +import { deleteOwnAccount } from "@/services/settings/api" +import { TrashIcon } from "@radix-ui/react-icons" +import { useRouter } from "next/navigation" +import * as React from "react" +import { toast } from "sonner" +import { Icons } from "./icons" + +interface DeleteProfileDialogProps + extends React.ComponentPropsWithoutRef { + showTrigger?: boolean + onSuccess?: () => void +} + +export function ProfileDeleteDialog({ + showTrigger = true, + onSuccess, + ...props +}: DeleteProfileDialogProps) { + const router = useRouter() + const [isDeletePending, startDeleteTransition] = React.useTransition() + const isDesktop = useMediaQuery("(min-width: 640px)") + const DRAWER_TITLE = "Are you absolutely sure?" + const DRAWER_DESCRIPTION = + "This action cannot be undone. It will permanently delete your account from your Myntenance. This action will not affect your GitHub repository." + + function onDelete() { + startDeleteTransition(async () => { + try { + const res = await deleteOwnAccount() + router.push("/") + onSuccess?.() + toast.success(`Your account was successfully deleted!`) + } catch (error: unknown) { + const message = + error instanceof Error + ? error.message + : "An error occurred while deleting your account." + toast.error(message) + } + }) + } + + if (isDesktop) { + return ( + + {showTrigger ? ( + + + + ) : null} + + + {DRAWER_TITLE} + {DRAWER_DESCRIPTION} + + + + + + + + + + ) + } + + return ( + + {showTrigger ? ( + + + + ) : null} + + + {DRAWER_TITLE} + {DRAWER_DESCRIPTION} + + + + + + + + + + ) +} diff --git a/src/components/ProfileSettings.tsx b/src/components/ProfileSettings.tsx index 092e61a..bed1d91 100644 --- a/src/components/ProfileSettings.tsx +++ b/src/components/ProfileSettings.tsx @@ -5,6 +5,7 @@ import { getOwnSettings, updateOwnSettings } from "@/services/settings/api" import { useForm } from "@tanstack/react-form" import { use, useTransition } from "react" import { toast } from "sonner" +import { ProfileDeleteCard } from "./ProfileDeleteCard" import { Button } from "./ui/button" import { Card, CardContent, CardFooter, CardHeader } from "./ui/card" import { Checkbox } from "./ui/checkbox" @@ -48,58 +49,62 @@ export function ProfileSettings({ settingsPromise, profilePromise }: Props) { }) return ( -
{ - e.preventDefault() - form.handleSubmit() - }} - > - - Public Profile - - - {(field) => ( -
- - field.handleChange(e.target.value)} - /> -
- )} -
- - {(field) => ( -
- - field.handleChange(checked === true) - } - /> - -
- )} -
-
- - [state.canSubmit, state.isSubmitting]} - > - {([canSubmit, isSubmitting]) => { - const isSubmitPending = isUpdatePending || isSubmitting - return ( - - ) - }} - - -
-
+ <> +
{ + e.preventDefault() + form.handleSubmit() + }} + > + + Public Profile + + + {(field) => ( +
+ + field.handleChange(e.target.value)} + /> +
+ )} +
+ + {(field) => ( +
+ + field.handleChange(checked === true) + } + /> + +
+ )} +
+
+ + [state.canSubmit, state.isSubmitting]} + > + {([canSubmit, isSubmitting]) => { + const isSubmitPending = isUpdatePending || isSubmitting + return ( + + ) + }} + + +
+
+ + + ) } diff --git a/src/services/settings/api.ts b/src/services/settings/api.ts index c7dc593..ae61b92 100644 --- a/src/services/settings/api.ts +++ b/src/services/settings/api.ts @@ -2,6 +2,7 @@ import { createClient } from "@/lib/supabase/server" import { ProfileUpdate, SettingsUpdate } from "@/lib/supabase/types" import { getCurrentUserId } from "@/lib/supabase/utils" +import { revalidatePath } from "next/cache" export async function getOwnSettings() { const supabase = createClient() @@ -38,3 +39,21 @@ export async function updateOwnSettings( return { error: profileError } } + +// TODO Set right parameters for the delete +export async function deleteOwnAccount() { + const supabase = createClient() + const userId = await getCurrentUserId(supabase) + const result = await supabase + .from("user_profiles") + .delete() + .eq("id", userId!) + .select() + .single() + .throwOnError() + + await supabase.auth.signOut() + revalidatePath("/") + + return result +}