From 0ed8bec138d38f598271694632a23ae3d8c6a237 Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:21:31 -0500 Subject: [PATCH 1/8] Added dropdowns to user components --- .../src/main-page/users/ApprovedUserCard.tsx | 41 +++++++++++++++++++ frontend/src/main-page/users/Users.tsx | 10 ++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 7a8d04a..0b21d04 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -1,12 +1,24 @@ import UserPositionCard from "./UserPositionCard"; +import { Button, Menu } from "@chakra-ui/react"; +import { FaEllipsisVertical } from "react-icons/fa6"; +import { UserStatus } from "../../../../middle-layer/types/UserStatus"; interface ApprovedUserCardProps { name: string; + userId: string; email: string; position: string; } const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { + const handleClickChangePosition = () => { + // Open modal to confirm position change + } + + const handleClickDeleteUser = () => { + // Open modal to confirm user deletion + } + return (

{name}

@@ -15,6 +27,35 @@ const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => {
+
+ + + + + + + + + + + +
); }; diff --git a/frontend/src/main-page/users/Users.tsx b/frontend/src/main-page/users/Users.tsx index 60c4b42..f6d31d0 100644 --- a/frontend/src/main-page/users/Users.tsx +++ b/frontend/src/main-page/users/Users.tsx @@ -15,18 +15,13 @@ enum UsersTab { CurrentUsers, } - - const ITEMS_PER_PAGE = 8; - const Users = observer(() => { -const store = getAppStore(); - -const { user } = useAuthContext(); + const store = getAppStore(); + const { user } = useAuthContext(); - const [usersTabStatus, setUsersTabStatus] = useState( UsersTab.CurrentUsers ); @@ -99,6 +94,7 @@ const { user } = useAuthContext(); From c8ad54e60a3dea5f18bf4790c00f8a0d6d95364e Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:19:56 -0500 Subject: [PATCH 2/8] Fixed button padding and added icons --- .../src/main-page/users/ApprovedUserCard.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 0b21d04..343aca3 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -2,6 +2,8 @@ import UserPositionCard from "./UserPositionCard"; import { Button, Menu } from "@chakra-ui/react"; import { FaEllipsisVertical } from "react-icons/fa6"; import { UserStatus } from "../../../../middle-layer/types/UserStatus"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; interface ApprovedUserCardProps { name: string; @@ -13,12 +15,12 @@ interface ApprovedUserCardProps { const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { const handleClickChangePosition = () => { // Open modal to confirm position change - } + }; const handleClickDeleteUser = () => { // Open modal to confirm user deletion - } - + }; + return (

{name}

@@ -38,19 +40,20 @@ const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { From 407cbc1d1db4c01e635d95d53051394994c96c56 Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:35:56 -0500 Subject: [PATCH 3/8] Fixed trigger double border --- frontend/src/main-page/users/ApprovedUserCard.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 343aca3..8ca1124 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -32,7 +32,10 @@ const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => {
- From 9a6a583dd86e5f4ff6286a1a97d364a734123342 Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:44:26 -0500 Subject: [PATCH 4/8] Removed user name column from users page --- frontend/src/main-page/users/ApprovedUserCard.tsx | 6 ++---- frontend/src/main-page/users/PendingUserCard.tsx | 11 +++++------ frontend/src/main-page/users/Users.tsx | 6 ++---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 8ca1124..6651d7b 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -6,13 +6,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; interface ApprovedUserCardProps { - name: string; userId: string; email: string; position: string; } -const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { +const ApprovedUserCard = ({ userId, email, position }: ApprovedUserCardProps) => { const handleClickChangePosition = () => { // Open modal to confirm position change }; @@ -23,8 +22,7 @@ const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { return (
-

{name}

-

xxxxxxx

+

{userId}

{email}

diff --git a/frontend/src/main-page/users/PendingUserCard.tsx b/frontend/src/main-page/users/PendingUserCard.tsx index 8dd9c1a..bdeac23 100644 --- a/frontend/src/main-page/users/PendingUserCard.tsx +++ b/frontend/src/main-page/users/PendingUserCard.tsx @@ -9,7 +9,7 @@ import { toJS } from "mobx"; import { moveUserToActive } from "./UserActions"; interface PendingUserCardProps { - name: string; + userId: string; email: string; position: string; } @@ -65,23 +65,22 @@ const deleteUser = async (username: string) => { const PendingUserCard = ({ - name, + userId, email, position, }: PendingUserCardProps) => { return (
-

{name}

-

xxxxxxx

+

{userId}

{email}

- -
diff --git a/frontend/src/main-page/users/Users.tsx b/frontend/src/main-page/users/Users.tsx index f6d31d0..76d0ae8 100644 --- a/frontend/src/main-page/users/Users.tsx +++ b/frontend/src/main-page/users/Users.tsx @@ -85,7 +85,6 @@ const Users = observer(() => { {usersTabStatus === UsersTab.CurrentUsers ? ( <>
-

User Name

User ID

Email

Position

@@ -93,7 +92,6 @@ const Users = observer(() => { {currentPageUsers.map((user) => ( { ) : ( <>
-

User Name

User ID

Email

Position

@@ -111,7 +108,8 @@ const Users = observer(() => {
{currentPageUsers.map((user) => ( From cc4b6d19a66aefc89870d84e43161eac1a67afa8 Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:55:56 -0500 Subject: [PATCH 5/8] Fixed user cards not coloring based on position --- frontend/src/main-page/users/ApprovedUserCard.tsx | 2 +- frontend/src/main-page/users/PendingUserCard.tsx | 2 +- frontend/src/main-page/users/UserPositionCard.tsx | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 6651d7b..9295774 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -8,7 +8,7 @@ import { faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; interface ApprovedUserCardProps { userId: string; email: string; - position: string; + position: UserStatus; } const ApprovedUserCard = ({ userId, email, position }: ApprovedUserCardProps) => { diff --git a/frontend/src/main-page/users/PendingUserCard.tsx b/frontend/src/main-page/users/PendingUserCard.tsx index bdeac23..e0f5cee 100644 --- a/frontend/src/main-page/users/PendingUserCard.tsx +++ b/frontend/src/main-page/users/PendingUserCard.tsx @@ -11,7 +11,7 @@ import { moveUserToActive } from "./UserActions"; interface PendingUserCardProps { userId: string; email: string; - position: string; + position: UserStatus; } const approveInactiveUser = async (user: User) => { const store = getAppStore(); diff --git a/frontend/src/main-page/users/UserPositionCard.tsx b/frontend/src/main-page/users/UserPositionCard.tsx index 9cb5afc..701b5c6 100644 --- a/frontend/src/main-page/users/UserPositionCard.tsx +++ b/frontend/src/main-page/users/UserPositionCard.tsx @@ -2,19 +2,17 @@ import { useMemo } from "react"; import { UserStatus } from "../../../../middle-layer/types/UserStatus"; interface UserPositionCardProps { - position: string; + position: UserStatus; } const UserPositionCard = ({ position }: UserPositionCardProps) => { const cardStyles = useMemo(() => { - switch (position.toLowerCase()) { - case "Admin" as UserStatus: + switch (position) { + case UserStatus.Admin: return "bg-[#BCFFD8] border-[#119548] text-[#119548]"; - case "Employee" as UserStatus: + case UserStatus.Employee: return "bg-[#FFF8CA] border-[#F8CC16] text-[#8a710c]"; - case "deactive": - return "bg-[#FFB0B0] border-[#DF0404] text-[#DF0404]"; - case "Inactive" as UserStatus: + case UserStatus.Inactive: default: return "bg-[#D3D3D3] border-[#666666] text-[#666666]"; } From d304c93236774483ac2d463253fbebde90b190b0 Mon Sep 17 00:00:00 2001 From: Aaron Ashby <101434393+aaronashby@users.noreply.github.com> Date: Thu, 4 Dec 2025 00:47:14 -0500 Subject: [PATCH 6/8] Implemented confirmation modals and connected them to backend --- .../src/main-page/users/ApprovedUserCard.tsx | 120 +++++++++++++++++- .../src/main-page/users/PendingUserCard.tsx | 2 +- frontend/src/main-page/users/Users.tsx | 35 +++-- 3 files changed, 130 insertions(+), 27 deletions(-) diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 9295774..14ea5d4 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -4,6 +4,12 @@ import { FaEllipsisVertical } from "react-icons/fa6"; import { UserStatus } from "../../../../middle-layer/types/UserStatus"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; +import ActionConfirmation from "../../custom/ActionConfirmation"; +import { useState } from "react"; +import { api } from "../../api"; +import { User } from "../../../../middle-layer/types/User"; +import { toJS } from "mobx"; +import { getAppStore } from "../../external/bcanSatchel/store"; interface ApprovedUserCardProps { userId: string; @@ -11,17 +17,117 @@ interface ApprovedUserCardProps { position: UserStatus; } -const ApprovedUserCard = ({ userId, email, position }: ApprovedUserCardProps) => { - const handleClickChangePosition = () => { - // Open modal to confirm position change +const ApprovedUserCard = ({ + userId, + email, + position, +}: ApprovedUserCardProps) => { + const store = getAppStore(); + const [isChangeGroupModalOpen, setIsChangeGroupModalOpen] = useState(false); + const [isDeleteUserModalOpen, setIsDeleteUserModalOpen] = useState(false); + + const changeUserGroup = async () => { + console.log( + `Changing user ${userId} to ${ + position === UserStatus.Admin ? "employee" : "admin" + }...` + ); + + try { + const response = await api("/user/change-role", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + user: { + userId, + email, + position, + } as User, + groupName: + position === UserStatus.Admin + ? UserStatus.Employee + : UserStatus.Admin, + requestedBy: toJS(store.user) as User, + }), + }); + + if (response.ok) { + console.log( + `User ${userId} successfully changed to ${ + position === UserStatus.Admin ? "employee" : "admin" + }` + ); + alert( + `User ${userId} successfully changed to ${ + position === UserStatus.Admin ? "employee" : "admin" + }` + ); + setIsChangeGroupModalOpen(false); + } else { + const errorBody = await response.json(); + console.error("Error: ", errorBody) + } + } catch (error) { + console.error("Error changing user group: ", error); + } }; - const handleClickDeleteUser = () => { - // Open modal to confirm user deletion + const deleteUser = async () => { + try { + const response = await api("user/delete-user", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + user: { + userId, + email, + position, + } as User, + requestedBy: toJS(store.user) as User, + }), + }); + + if (response.ok) { + console.log(`User ${userId} has been deleted successfully`); + alert(`User ${userId} has been deleted successfully`); + } else { + alert("Failed to delete user"); + } + setIsDeleteUserModalOpen(false); + } catch (error) { + console.error("Error deleting user:", error); + alert("Error deleting user"); + } }; return (
+ setIsChangeGroupModalOpen(false)} + onConfirmDelete={changeUserGroup} + title={`Change User to ${ + position === UserStatus.Admin ? "Employee" : "Admin" + }`} + subtitle="Are you sure you want to change to" + boldSubtitle={position === UserStatus.Admin ? "employee" : "admin"} + warningMessage={`By changing to ${ + position === UserStatus.Admin ? "employee" : "admin" + }, they will ${ + position === UserStatus.Admin + ? "gain access to sensitive data." + : "lose access to admin pages." + }`} + /> + setIsDeleteUserModalOpen(false)} + onConfirmDelete={deleteUser} + title="Delete User" + subtitle="Are you sure you want to delete" + boldSubtitle={userId} + warningMessage="By deleting this user, they won't be available in the system anymore." + />

{userId}

{email}

@@ -42,7 +148,7 @@ const ApprovedUserCard = ({ userId, email, position }: ApprovedUserCardProps) =>