From 949b53a3c0b666420df2f7dc1ae6f85e9885c431 Mon Sep 17 00:00:00 2001 From: dvala041 <86671843+dvala041@users.noreply.github.com> Date: Sat, 8 Jun 2024 20:13:07 -0400 Subject: [PATCH] Implement profile account deletion (#230) * Created Modal in ProfileForm.tsx, added profile deletion from firebase functionality to deleteUser endpoint in views.tsx and created a BigRed Variety for buttons in Button.tsx * Revise Changes * include error notifs * remove comments * Minor fixes * Comment fixes * Remove unused imports --------- Co-authored-by: Brandon Lerit Co-authored-by: Akinfolami Akin-Alamu Co-authored-by: Jason Zheng --- backend/src/users/views.ts | 2 + .../src/components/organisms/ProfileForm.tsx | 139 +++++++++++++++++- frontend/src/pages/profile.tsx | 2 +- 3 files changed, 138 insertions(+), 5 deletions(-) diff --git a/backend/src/users/views.ts b/backend/src/users/views.ts index 282e15e0..22fc9daa 100644 --- a/backend/src/users/views.ts +++ b/backend/src/users/views.ts @@ -13,6 +13,7 @@ import { const userRouter = Router(); import * as firebase from "firebase-admin"; import { attempt, socketNotify } from "../utils/helpers"; +import admin from "firebase-admin"; let useAuth: RequestHandler; let useAdminAuth: RequestHandler; @@ -85,6 +86,7 @@ userRouter.post( userRouter.delete("/:userid", useAuth, async (req: Request, res: Response) => { // #swagger.tags = ['Users'] + await admin.auth().deleteUser(req.params.userid); attempt(res, 200, () => userController.deleteUser(req.params.userid)); socketNotify("/users"); }); diff --git a/frontend/src/components/organisms/ProfileForm.tsx b/frontend/src/components/organisms/ProfileForm.tsx index eb4f3df2..4b00de4d 100644 --- a/frontend/src/components/organisms/ProfileForm.tsx +++ b/frontend/src/components/organisms/ProfileForm.tsx @@ -4,10 +4,11 @@ import TextField from "../atoms/TextField"; import Checkbox from "../atoms/Checkbox"; import { useForm, SubmitHandler } from "react-hook-form"; import Snackbar from "../atoms/Snackbar"; +import { useRouter } from "next/router"; import { api } from "@/utils/api"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { updatePassword } from "firebase/auth"; -import { User } from "firebase/auth"; +import Modal from "@/components/molecules/Modal"; +import { useAuth } from "@/utils/AuthContext"; // - ndavid import { Controller } from "react-hook-form"; type FormValues = { @@ -19,6 +20,10 @@ type FormValues = { emailNotifications: boolean; }; +type DeleteAccountFormValues = { + confirmation: string; +}; + type formData = { id: string; email: string; @@ -39,8 +44,114 @@ interface ProfileFormProps { userDetails: formData; } +interface ModalBodyProps { + userDetails: formData; + handleClose: () => void; + setErrorNotificationOpen: React.Dispatch>; +} + +/** Confirmation modal to delete an account */ +const ModalBody = ({ + userDetails, + handleClose, + setErrorNotificationOpen, +}: ModalBodyProps) => { + const { + register, + handleSubmit, + watch, + formState: { errors }, + } = useForm(); + + // deleteUserProfile handles deleting user from prisma and firebase + const { + mutateAsync: deleteUserProfile, + error: deleteUserProfileError, + isPending, + } = useMutation({ + mutationFn: async () => { + const { data } = await api.delete(`/users/${userDetails.id}`); + return data; + }, + retry: false, + onSuccess: () => {}, + }); + + // Variables for signing out user + const { error, signOutUser } = useAuth(); + const queryClient = useQueryClient(); + const router = useRouter(); + + // Signs out user and redirects them to login page + const handleSignOut = async () => { + try { + await signOutUser(); + queryClient.clear(); // Clear cache for react query + router.replace("/login"); + } catch (error) { + console.log(error); + } + }; + + /** + * When delete form is submitted, we will delete user, end their session, + * and redirect them to login page + */ + const handleDeleteAccount = async () => { + try { + await deleteUserProfile(); + await handleSignOut(); + } catch (error) { + console.log(error); + setErrorNotificationOpen(true); + } + }; + + return ( +
+
Delete Account
+
+
+ Are you sure you want to delete your account? This change is{" "} + permanent and there is{" "} + no ability to restore your account once it is deleted. +
+
+ +
+ + value === `${userDetails.email}` || + "Confirmation message is incorrect.", + }, + })} + /> +
+
+ +
+
+ +
+
+ +
+ ); +}; + const ProfileForm = ({ userDetails }: ProfileFormProps) => { + const router = useRouter(); const queryClient = useQueryClient(); + const { userid } = router.query; /** State variables for the notification popups for profile update */ const [successNotificationOpen, setSuccessNotificationOpen] = useState(false); @@ -55,6 +166,9 @@ const ProfileForm = ({ userDetails }: ProfileFormProps) => { return "Something went wrong. Please try again."; } }; + const [open, setOpen] = useState(false); + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); /** React hook form */ const { @@ -118,6 +232,18 @@ const ProfileForm = ({ userDetails }: ProfileFormProps) => { return ( <> + + } + /> + {/* Profile update error snackbar */} { /> )} /> -
-
+
+
+
+ +