diff --git a/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectsTable.tsx b/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectsTable.tsx index 86d6185f5..195f74111 100644 --- a/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectsTable.tsx +++ b/frontend/src/views/Org/IdentityPage/components/IdentityProjectsSection/IdentityProjectsTable.tsx @@ -68,7 +68,7 @@ export const IdentityProjectsTable = ({ identityId, handlePopUpOpen }: Props) => return membershipOne.project.name .toLowerCase() .localeCompare(membershipTwo.project.name.toLowerCase()); - }) ?? [], + }), [projectMemberships, orderDirection, search] ); diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx index eda562055..9f4a8bc03 100644 --- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx +++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx @@ -71,7 +71,7 @@ export const UserProjectsTable = ({ membershipId, handlePopUpOpen }: Props) => { return membershipOne.project.name .toLowerCase() .localeCompare(membershipTwo.project.name.toLowerCase()); - }) ?? [], + }), [projectMemberships, orderDirection, search] ); diff --git a/frontend/src/views/Project/MembersPage/components/MembersTab/components/MembersTable.tsx b/frontend/src/views/Project/MembersPage/components/MembersTab/components/MembersTable.tsx index 591d2663b..d81d4005c 100644 --- a/frontend/src/views/Project/MembersPage/components/MembersTab/components/MembersTable.tsx +++ b/frontend/src/views/Project/MembersPage/components/MembersTab/components/MembersTable.tsx @@ -1,9 +1,12 @@ -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import { useRouter } from "next/router"; import { + faArrowDown, + faArrowUp, faClock, faEllipsisV, faMagnifyingGlass, + faSearch, faTrash, faUsers } from "@fortawesome/free-solid-svg-icons"; @@ -18,6 +21,7 @@ import { HoverCardTrigger, IconButton, Input, + Pagination, Table, TableContainer, TableSkeleton, @@ -35,7 +39,9 @@ import { useUser, useWorkspace } from "@app/context"; +import { usePagination, useResetPageHelper } from "@app/hooks"; import { useGetWorkspaceUsers } from "@app/hooks/api"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -54,9 +60,12 @@ type Props = { ) => void; }; -export const MembersTable = ({ handlePopUpOpen }: Props) => { - const [searchMemberFilter, setSearchMemberFilter] = useState(""); +enum MembersOrderBy { + Name = "firstName", + Email = "email" +} +export const MembersTable = ({ handlePopUpOpen }: Props) => { const { currentWorkspace } = useWorkspace(); const { user } = useUser(); const router = useRouter(); @@ -64,26 +73,77 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => { const userId = user?.id || ""; const workspaceId = currentWorkspace?.id || ""; - const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId); + const { + search, + setSearch, + setPage, + page, + perPage, + setPerPage, + offset, + orderDirection, + orderBy, + setOrderBy, + setOrderDirection, + toggleOrderDirection + } = usePagination(MembersOrderBy.Name); - const filterdUsers = useMemo( + const { data: members = [], isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId); + + const filteredUsers = useMemo( () => - members?.filter( - ({ user: u, inviteEmail }) => - u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) || - u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) || - u?.username?.toLowerCase().includes(searchMemberFilter.toLowerCase()) || - u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) || - inviteEmail?.includes(searchMemberFilter.toLowerCase()) - ), - [members, searchMemberFilter] + members + ?.filter( + ({ user: u, inviteEmail }) => + u?.firstName?.toLowerCase().includes(search.toLowerCase()) || + u?.lastName?.toLowerCase().includes(search.toLowerCase()) || + u?.username?.toLowerCase().includes(search.toLowerCase()) || + u?.email?.toLowerCase().includes(search.toLowerCase()) || + inviteEmail?.toLowerCase().includes(search.toLowerCase()) + ) + .sort((a, b) => { + const [memberOne, memberTwo] = orderDirection === OrderByDirection.ASC ? [a, b] : [b, a]; + + let valueOne: string; + let valueTwo: string; + + switch (orderBy) { + case MembersOrderBy.Email: + valueOne = memberOne.user.email || memberOne.inviteEmail; + valueTwo = memberTwo.user.email || memberTwo.inviteEmail; + break; + case MembersOrderBy.Name: + default: + valueOne = memberOne.user.firstName; + valueTwo = memberTwo.user.firstName; + } + + return valueOne.toLowerCase().localeCompare(valueTwo.toLowerCase()); + }), + [members, search, orderDirection, orderBy] ); + useResetPageHelper({ + totalCount: filteredUsers.length, + offset, + setPage + }); + + const handleSort = (column: MembersOrderBy) => { + if (column === orderBy) { + toggleOrderDirection(); + return; + } + + setOrderBy(column); + setOrderDirection(OrderByDirection.ASC); + }; + return (
setSearchMemberFilter(e.target.value)} + value={search} + onChange={(e) => setSearch(e.target.value)} leftIcon={} placeholder="Search members..." /> @@ -91,8 +151,44 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => { - - + + @@ -100,7 +196,7 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => { {isMembersLoading && } {!isMembersLoading && - filterdUsers?.map((projectMember) => { + filteredUsers.slice(offset, perPage * page).map((projectMember) => { const { user: u, inviteEmail, id: membershipId, roles } = projectMember; const name = u.firstName || u.lastName ? `${u.firstName} ${u.lastName || ""}` : "-"; const email = u?.email || inviteEmail; @@ -239,8 +335,22 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => { })}
NameUsername +
+ Name + handleSort(MembersOrderBy.Name)} + > + + +
+
+
+ Email + handleSort(MembersOrderBy.Email)} + > + + +
+
Role
- {!isMembersLoading && filterdUsers?.length === 0 && ( - + {Boolean(filteredUsers.length) && ( + + )} + {!isMembersLoading && !filteredUsers?.length && ( + )}