Skip to content

Commit

Permalink
Implement profile account deletion (#230)
Browse files Browse the repository at this point in the history
* 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 <bsl77@cornell.edu>
Co-authored-by: Akinfolami Akin-Alamu <aoa9@cornell.edu>
Co-authored-by: Jason Zheng <jasonz4200@gmail.com>
  • Loading branch information
4 people authored Jun 9, 2024
1 parent 7d72abe commit 949b53a
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 5 deletions.
2 changes: 2 additions & 0 deletions backend/src/users/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
});
Expand Down
139 changes: 135 additions & 4 deletions frontend/src/components/organisms/ProfileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -19,6 +20,10 @@ type FormValues = {
emailNotifications: boolean;
};

type DeleteAccountFormValues = {
confirmation: string;
};

type formData = {
id: string;
email: string;
Expand All @@ -39,8 +44,114 @@ interface ProfileFormProps {
userDetails: formData;
}

interface ModalBodyProps {
userDetails: formData;
handleClose: () => void;
setErrorNotificationOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

/** Confirmation modal to delete an account */
const ModalBody = ({
userDetails,
handleClose,
setErrorNotificationOpen,
}: ModalBodyProps) => {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<DeleteAccountFormValues>();

// 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 (
<div className="space-y-4">
<div className="font-bold text-2xl text-center">Delete Account</div>
<div className="mb-12">
<div>
Are you sure you want to delete your account? This change is{" "}
<b>permanent</b> and there is{" "}
<b>no ability to restore your account once it is deleted</b>.
</div>
</div>

<form onSubmit={handleSubmit(handleDeleteAccount)}>
<TextField
error={errors.confirmation?.message}
label={`Type "${userDetails.email}" to confirm`}
{...register("confirmation", {
required: { value: true, message: "Required" },
validate: {
matchConfirmation: (value) =>
value === `${userDetails.email}` ||
"Confirmation message is incorrect.",
},
})}
/>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 mt-10">
<div className="order-1 sm:order-2">
<Button loading={isPending} variety="mainError" type="submit">
Delete
</Button>
</div>
<div className="order-2 sm:order-1">
<Button variety="secondary" onClick={handleClose}>
Cancel
</Button>
</div>
</div>
</form>
</div>
);
};

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);
Expand All @@ -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 {
Expand Down Expand Up @@ -118,6 +232,18 @@ const ProfileForm = ({ userDetails }: ProfileFormProps) => {

return (
<>
<Modal
open={open}
handleClose={handleClose}
children={
<ModalBody
userDetails={userDetails}
handleClose={handleClose}
setErrorNotificationOpen={setErrorNotificationOpen}
/>
}
/>

{/* Profile update error snackbar */}
<Snackbar
variety="error"
Expand Down Expand Up @@ -194,10 +320,15 @@ const ProfileForm = ({ userDetails }: ProfileFormProps) => {
/>
)}
/>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="order-1 sm:order-2">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div className="order-1 sm:order-3">
<Button type="submit">Save changes</Button>
</div>
<div className="order-2 sm:order-2">
<Button variety="error" type="button" onClick={handleOpen}>
Delete account
</Button>
</div>
<div className="order-2 sm:order-1">
<Button
type="button"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const Profile = () => {
and should absolutely be refactored into something sane ASAP */}
<h3 className="mt-0 mb-2 font-normal">
You are {data?.role === "ADMIN" ? "an " : "a "}
<span className="font-bold">{formatRoleOrStatus(data.role)}</span>
<span className="font-bold">{formatRoleOrStatus(data?.role)}</span>
</h3>
{data?.role === "ADMIN" && (
<div>
Expand Down

0 comments on commit 949b53a

Please sign in to comment.