diff --git a/client/src/components/AdministratorNavigationPanel.tsx b/client/src/components/AdministratorNavigationPanel.tsx index 08c5511..e6dfd87 100644 --- a/client/src/components/AdministratorNavigationPanel.tsx +++ b/client/src/components/AdministratorNavigationPanel.tsx @@ -19,6 +19,7 @@ import { ChatBubbleOvalLeftIcon, ChevronLeftIcon, ArrowRightStartOnRectangleIcon, + UsersIcon, } from "../icons"; import EcoconnectFullLogo from "./EcoconnectFullLogo"; import { retrieveUserInformation } from "../security/users"; @@ -192,7 +193,12 @@ export default function AdministratorNavigationPanel() {

Users

} + onClickRef="users-management" + /> + } onClickRef="#" /> diff --git a/client/src/icons.tsx b/client/src/icons.tsx index 47dd10c..f40819b 100644 --- a/client/src/icons.tsx +++ b/client/src/icons.tsx @@ -443,4 +443,80 @@ export const TrashIcon = () => { /> ); -}; \ No newline at end of file +}; + +export const UsersIcon = () => { + return ( + + + + ); +}; + +export const ClipboardDocumentIcon = () => { + return ( + + + + ); +}; + +export const ArchiveBoxIcon = () => { + return ( + + + + ); +}; + +export const LifebuoyIcon = () => { + return ( + + + + ); +}; diff --git a/client/src/pages/UsersManagement.tsx b/client/src/pages/UsersManagement.tsx new file mode 100644 index 0000000..144e44e --- /dev/null +++ b/client/src/pages/UsersManagement.tsx @@ -0,0 +1,185 @@ +import { + getKeyValue, + Table, + TableBody, + TableCell, + TableColumn, + TableHeader, + TableRow, + Button, + Tooltip, +} from "@nextui-org/react"; +import { useEffect, useState } from "react"; +import instance from "../security/http"; +import config from "../config"; +import { popErrorToast, popToast } from "../utilities"; +import { ArchiveBoxIcon, ClipboardDocumentIcon, LifebuoyIcon } from "../icons"; + +export default function UsersManagement() { + const [userInformationlist, setUserInformationList] = useState([]); + const columns = [ + { + key: "firstName", + label: "FISRT NAME", + }, + { + key: "lastName", + label: "LAST NAME", + }, + { + key: "email", + label: "EMAIL ADDRESS", + }, + { + key: "phoneNumber", + label: "TELEPHONE", + }, + { + key: "accountType", + label: "ACCOUNT TYPE", + }, + { + key: "isArchived", + label: "STATUS", + }, + { + key: "actions", + label: "ACTIONS", + }, + ]; + + const populateUserInformationList = () => { + instance + .get(`${config.serverAddress}/users/all`) + .then((response) => { + setUserInformationList(response.data); + console.log(userInformationlist); + }) + .catch((error) => { + popErrorToast(error); + }); + }; + + useEffect(() => { + populateUserInformationList(); + }, []); + + const handleCopyID = (userId: string, firstName: string) => { + navigator.clipboard.writeText(userId); + popToast(firstName + "'s User ID has been copied!", 1); + }; + + const handleArchiveToggle = (userId: string, isArchived: boolean) => { + instance + .put( + `${config.serverAddress}/users/${ + isArchived ? "unarchive" : "archive" + }/${userId}` + ) + .then(() => { + window.location.reload(); + }) + .catch((error) => { + popErrorToast(error); + }); + }; + + return ( +
+ {userInformationlist && ( +
+

Users Onboard

+ + + {(column) => ( + {column.label} + )} + + + {(userEntry: any) => ( + + {(columnKey) => ( + + {columnKey === "accountType" ? ( + (() => { + const accountType = getKeyValue(userEntry, columnKey); + switch (accountType) { + case 0: + return "User"; + case 1: + return "Karang Guni"; + case 2: + return "Admin"; + default: + return ""; + } + })() + ) : columnKey === "actions" ? ( +
+ + + + + + +
+ ) : columnKey == "isArchived" ? ( + getKeyValue(userEntry, columnKey) ? ( + "Archived" + ) : ( + "Active" + ) + ) : ( +

+ {getKeyValue(userEntry, columnKey)} +

+ )} +
+ )} +
+ )} +
+
+
+ )} +
+ ); +} diff --git a/server/routes/users.js b/server/routes/users.js index edeb1df..349aa17 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -79,6 +79,7 @@ router.get("/all", async (req, res) => { let list = await User.findAll({ where: condition, order: [["createdAt", "DESC"]], + attributes: { exclude: ["password", "profilePicture"] }, }); res.json(list); }); @@ -97,6 +98,8 @@ router.get("/individual/:id", validateToken, async (req, res) => { message: `ERR_ACC_IS_ARCHIVED`, }); } else { + user.password = undefined; + user.profilePicture = undefined; res.json(user); } }); @@ -223,6 +226,30 @@ router.put("/archive/:id", validateToken, async (req, res) => { } }); +router.put("/unarchive/:id", validateToken, async (req, res) => { + let id = req.params.id; + let user = await User.findByPk(id); + + if (!user) { + res.sendStatus(404); + return; + } + + try { + await User.update( + { isArchived: false }, + { + where: { id: id }, + } + ); + res.json({ + message: "User archived successfully.", + }); + } catch (err) { + res.status(400).json({ errors: err.errors }); + } +}); + router.get("/profile-image/:id", async (req, res) => { let id = req.params.id; let user = await User.findByPk(id);