From 733138f2bee5499004028e705dc479e0666b1393 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 26 Sep 2023 13:23:53 +0100 Subject: [PATCH] Added more options to the User List --- src/constants/userPermissions.ts | 7 ++ src/hooks/useUsers.tsx | 8 ++- src/pages/(admin)/Lists/UserList.tsx | 103 ++++++++++++++++++++++++--- src/pages/(admin)/Lists/index.tsx | 5 +- src/pages/admin.tsx | 2 +- src/pages/api/users/update.ts | 15 +++- 6 files changed, 122 insertions(+), 18 deletions(-) diff --git a/src/constants/userPermissions.ts b/src/constants/userPermissions.ts index 5ba49074..70d9f2cc 100644 --- a/src/constants/userPermissions.ts +++ b/src/constants/userPermissions.ts @@ -15,4 +15,11 @@ export const PERMISSIONS = { owner: ["developer", "owner"], developer: ["developer"], }, + updateUser: { + student: ["teacher", "admin", "developer", "owner"], + teacher: ["admin", "developer", "owner"], + admin: ["owner", "developer"], + owner: ["developer", "owner"], + developer: ["developer"], + }, }; diff --git a/src/hooks/useUsers.tsx b/src/hooks/useUsers.tsx index 231aa0f2..1564f309 100644 --- a/src/hooks/useUsers.tsx +++ b/src/hooks/useUsers.tsx @@ -7,13 +7,15 @@ export default function useUsers() { const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - useEffect(() => { + const getData = () => { setIsLoading(true); axios .get("/api/users/list") .then((response) => setUsers(response.data)) .finally(() => setIsLoading(false)); - }, []); + }; - return {users, isLoading, isError}; + useEffect(getData, []); + + return {users, isLoading, isError, reload: getData}; } diff --git a/src/pages/(admin)/Lists/UserList.tsx b/src/pages/(admin)/Lists/UserList.tsx index 25497231..cff69b2f 100644 --- a/src/pages/(admin)/Lists/UserList.tsx +++ b/src/pages/(admin)/Lists/UserList.tsx @@ -1,15 +1,46 @@ +import Button from "@/components/Low/Button"; +import {PERMISSIONS} from "@/constants/userPermissions"; import useUsers from "@/hooks/useUsers"; import {Type, User} from "@/interfaces/user"; +import {Popover, Transition} from "@headlessui/react"; import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import axios from "axios"; import clsx from "clsx"; import {capitalize} from "lodash"; -import {useState} from "react"; +import {Fragment} from "react"; import {BsCheck, BsPerson, BsTrash} from "react-icons/bs"; +import {toast} from "react-toastify"; const columnHelper = createColumnHelper(); -export default function UserList() { - const {users} = useUsers(); +export default function UserList({user}: {user: User}) { + const {users, reload} = useUsers(); + + const updateAccountType = (user: User, type: Type) => { + if (!confirm(`Are you sure you want to update ${user.name}'s account from ${capitalize(user.type)} to ${capitalize(type)}?`)) return; + + axios + .post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {...user, type}) + .then(() => { + toast.success("User type updated successfully!"); + reload(); + }) + .catch(() => { + toast.error("Something went wrong!", {toastId: "update-error"}); + }); + }; + + const verifyAccount = (user: User) => { + axios + .post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {...user, isVerified: true}) + .then(() => { + toast.success("User verified successfully!"); + reload(); + }) + .catch(() => { + toast.error("Something went wrong!", {toastId: "update-error"}); + }); + }; const defaultColumns = [ columnHelper.accessor("name", { @@ -46,12 +77,66 @@ export default function UserList() { cell: ({row}: {row: {original: User}}) => { return (
-
- -
-
- -
+ {PERMISSIONS.updateUser[row.original.type].includes(user.type) && ( + + +
+ +
+
+ + +
+ + + + +
+
+
+
+ )} + {PERMISSIONS.deleteUser[row.original.type].includes(user.type) && ( +
+ +
+ )} + {!row.original.isVerified && PERMISSIONS.updateUser[row.original.type].includes(user.type) && ( +
verifyAccount(row.original)}> + +
+ )}
); }, diff --git a/src/pages/(admin)/Lists/index.tsx b/src/pages/(admin)/Lists/index.tsx index 677ab1a4..0c34ac21 100644 --- a/src/pages/(admin)/Lists/index.tsx +++ b/src/pages/(admin)/Lists/index.tsx @@ -1,9 +1,10 @@ +import {User} from "@/interfaces/user"; import {Tab} from "@headlessui/react"; import clsx from "clsx"; import ExamList from "./ExamList"; import UserList from "./UserList"; -export default function Lists() { +export default function Lists({user}: {user: User}) { return ( @@ -32,7 +33,7 @@ export default function Lists() { - + diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx index 5dde71b3..ad514133 100644 --- a/src/pages/admin.tsx +++ b/src/pages/admin.tsx @@ -63,7 +63,7 @@ export default function Admin() {
- +
)} diff --git a/src/pages/api/users/update.ts b/src/pages/api/users/update.ts index c2f6e207..65bf3aa9 100644 --- a/src/pages/api/users/update.ts +++ b/src/pages/api/users/update.ts @@ -21,9 +21,15 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { return; } - const userRef = doc(db, "users", req.session.user.id); + const userRef = doc(db, "users", req.query.id ? (req.query.id as string) : req.session.user.id); const updatedUser = req.body as User & {password?: string; newPassword?: string}; + if (!!req.query.id) { + await setDoc(userRef, updatedUser, {merge: true}); + res.status(200).json({ok: true}); + return; + } + if (updatedUser.profilePicture && updatedUser.profilePicture !== req.session.user.profilePicture) { const profilePictureFiletype = updatedUser.profilePicture.split(";")[0].split("/")[1]; const profilePictureRef = ref(storage, `profile_pictures/${req.session.user.id}.${profilePictureFiletype}`); @@ -62,8 +68,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const docUser = await getDoc(doc(db, "users", req.session.user.id)); const user = docUser.data() as User; - req.session.user = {...user, id: req.session.user.id}; - await req.session.save(); + + if (!req.query.id) { + req.session.user = {...user, id: req.session.user.id}; + await req.session.save(); + } res.status(200).json({user}); }