diff --git a/frontend/src/main-page/users/ApprovedUserCard.tsx b/frontend/src/main-page/users/ApprovedUserCard.tsx index 7a8d04a..aff3379 100644 --- a/frontend/src/main-page/users/ApprovedUserCard.tsx +++ b/frontend/src/main-page/users/ApprovedUserCard.tsx @@ -1,20 +1,179 @@ 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"; +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"; +import { setActiveUsers } from "../../external/bcanSatchel/actions"; interface ApprovedUserCardProps { - name: string; + userId: string; email: string; - position: string; + position: UserStatus; } -const ApprovedUserCard = ({ name, email, position }: ApprovedUserCardProps) => { +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" + }` + ); + const updatedUser = await response.json(); + setActiveUsers([...store.activeUsers.filter(u => u.userId !== userId), updatedUser as User]); + + setIsChangeGroupModalOpen(false); + } else { + const errorBody = await response.json(); + console.error("Error: ", errorBody) + } + } catch (error) { + console.error("Error changing user group: ", error); + } + }; + + 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`); + setActiveUsers(store.activeUsers.filter(u => u.userId !== userId)); + + } else { + const errorBody = await response.json(); + console.error("Error: ", errorBody) + alert("Failed to delete user"); + } + setIsDeleteUserModalOpen(false); + } catch (error) { + console.error("Error deleting user:", error); + alert("Error deleting user"); + } + }; + return (
-

{name}

-

xxxxxxx

+ 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}

+
+ + + + + + + + + + + +
); }; diff --git a/frontend/src/main-page/users/PendingUserCard.tsx b/frontend/src/main-page/users/PendingUserCard.tsx index 451ac71..5a4867a 100644 --- a/frontend/src/main-page/users/PendingUserCard.tsx +++ b/frontend/src/main-page/users/PendingUserCard.tsx @@ -13,14 +13,14 @@ import { useState } from "react"; const store = getAppStore(); interface PendingUserCardProps { - name: string; + userId: string; email: string; - position: string; + position: UserStatus; } const PendingUserCard = ({ - name, + userId, email, position, }: PendingUserCardProps) => { @@ -35,16 +35,16 @@ const PendingUserCard = ({ headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user: { - userId: name, - email: email, - position: position as UserStatus, + userId, + email, + position } as User, groupName: "Employee", requestedBy: toJS(store.user) as User, }), }); if (response.ok) { - alert(`User ${name} has been approved successfully`); + alert(`User ${userId} has been approved successfully`); const body = await response.json(); moveUserToActive(body as User) } else { @@ -66,9 +66,9 @@ const PendingUserCard = ({ headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user: { - userId: name, + userId, email: email, - position: position as UserStatus, + position, } as User, requestedBy: toJS(store.user) as User, }), @@ -90,8 +90,7 @@ const PendingUserCard = ({ return (
-

{name}

-

xxxxxxx

+

{userId}

{email}

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]"; } diff --git a/frontend/src/main-page/users/Users.tsx b/frontend/src/main-page/users/Users.tsx index dc7cdfe..7d94f4b 100644 --- a/frontend/src/main-page/users/Users.tsx +++ b/frontend/src/main-page/users/Users.tsx @@ -6,53 +6,54 @@ import { Pagination, ButtonGroup, IconButton } from "@chakra-ui/react"; import { HiChevronLeft, HiChevronRight } from "react-icons/hi"; import { observer } from "mobx-react-lite"; import { getAppStore } from "../../external/bcanSatchel/store"; +import { api } from "../../api"; +import { Navigate } from "react-router-dom"; +import { UserStatus } from "../../../../middle-layer/types/UserStatus"; +import { useAuthContext } from "../../context/auth/authContext"; // Represents a specific tab to show on the user page enum UsersTab { PendingUsers, CurrentUsers, } -import { api } from "../../api" const fetchActiveUsers = async (): Promise => { try { const response = await api("/user/active", { - method: 'GET' + method: "GET", }); - + if (!response.ok) { throw new Error(`HTTP Error, Status: ${response.status}`); } - + const activeUsers = await response.json(); return activeUsers as User[]; } catch (error) { console.error("Error fetching active users:", error); return []; // Return empty array on error } -} +}; const fetchInactiveUsers = async () => { try { - const response = await api("/user/inactive", {method : 'GET' }); + const response = await api("/user/inactive", { method: "GET" }); if (!response.ok) { throw new Error(`HTTP Error, Status: ${response.status}`); } const inactiveUsers = await response.json(); return inactiveUsers as User[]; - } - catch (error) { + } catch (error) { console.error("Error fetching active users:", error); } -} - +}; const ITEMS_PER_PAGE = 8; const Users = observer(() => { const store = getAppStore(); - - + const { user } = useAuthContext(); + useEffect(() => { const fetchUsers = async () => { const active = await fetchActiveUsers(); @@ -61,11 +62,12 @@ const Users = observer(() => { store.activeUsers = active; } if (inactive) { -store.inactiveUsers = inactive; } + store.inactiveUsers = inactive; + } }; fetchUsers(); - }, []); + const [usersTabStatus, setUsersTabStatus] = useState( UsersTab.CurrentUsers ); @@ -85,129 +87,134 @@ store.inactiveUsers = inactive; } : pageStartIndex + ITEMS_PER_PAGE; const currentPageUsers = filteredUsers.slice(pageStartIndex, pageEndIndex); - return ( -
-
-

- {usersTabStatus === UsersTab.CurrentUsers - ? "All Users" - : "Pending Users"} -

-

{numInactiveUsers} new users

-
-
-
- - + +
+
+ {usersTabStatus === UsersTab.CurrentUsers ? ( + <> +
+

User ID

+

Email

+

Position

+
+ {currentPageUsers.map((user) => ( + + ))} + + ) : ( + <> +
+

User ID

+

Email

+

Position

+
+
+ {currentPageUsers.map((user) => ( + + ))} + + )} +
+ { + setCurrentPage(e.page); }} > - Current Users - -
-
- {usersTabStatus === UsersTab.CurrentUsers ? ( - <> -
-

User Name

-

User ID

-

Email

-

Position

-
- {currentPageUsers.map((user) => ( - - ))} - - ) : ( - <> -
-

User Name

-

User ID

-

Email

-

Position

-
-
- {currentPageUsers.map((user) => ( - - ))} - - )} -
- { - setCurrentPage(e.page); - }} - > - - - - - - - - {({ pages }) => - pages.map((page, index) => - page.type === "page" ? ( - setCurrentPage(page.value)} - aria-label={`Go to page ${page.value}`} - > - {page.value} - - ) : ( - "..." + + + + + + + + {({ pages }) => + pages.map((page, index) => + page.type === "page" ? ( + setCurrentPage(page.value)} + aria-label={`Go to page ${page.value}`} + > + {page.value} + + ) : ( + "..." + ) ) - ) - } - - - - - - - - + } + + + + + + + + +
-
+ ) : ( + + ) + ) : ( + ); -}) +}); export default Users;