diff --git a/frontend/src/hooks/usePagination.tsx b/frontend/src/hooks/usePagination.tsx index 3d3002b4c..e4c9fddbb 100644 --- a/frontend/src/hooks/usePagination.tsx +++ b/frontend/src/hooks/usePagination.tsx @@ -3,9 +3,16 @@ import { useState } from "react"; import { OrderByDirection } from "@app/hooks/api/generic/types"; import { useDebounce } from "@app/hooks/useDebounce"; -export const usePagination = (initialOrderBy: T) => { +export const usePagination = ( + initialOrderBy: T, + { + initPerPage = 100 + }: { + initPerPage?: number; + } = {} +) => { const [page, setPage] = useState(1); - const [perPage, setPerPage] = useState(100); + const [perPage, setPerPage] = useState(initPerPage); const [orderDirection, setOrderDirection] = useState(OrderByDirection.ASC); const [orderBy, setOrderBy] = useState(initialOrderBy); const [search, setSearch] = useState(""); @@ -26,6 +33,10 @@ export const usePagination = (initialOrderBy: T) => { search, setSearch, orderBy, - setOrderBy + setOrderBy, + toggleOrderDirection: () => + setOrderDirection((prev) => + prev === OrderByDirection.DESC ? OrderByDirection.ASC : OrderByDirection.DESC + ) }; }; diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx index fff2007ef..f519312c5 100644 --- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx +++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectRow.tsx @@ -58,7 +58,7 @@ export const UserProjectRow = ({ }); }} > - {project.name} + {project.name} {`${formatRoleName(roles[0].role, roles[0].customRoleName)}${ roles.length > 1 ? ` (+${roles.length - 1})` : "" }`} diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx index 88e1a2925..17b4b0ba6 100644 --- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx +++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserProjectsTable.tsx @@ -1,7 +1,18 @@ -import { faFolder } from "@fortawesome/free-solid-svg-icons"; +import { useMemo } from "react"; +import { + faArrowDown, + faArrowUp, + faFolder, + faMagnifyingGlass, + faSearch +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { EmptyState, + IconButton, + Input, + Pagination, Table, TableContainer, TableSkeleton, @@ -11,7 +22,9 @@ import { Tr } from "@app/components/v2"; import { useOrganization } from "@app/context"; +import { usePagination, useResetPageHelper } from "@app/hooks"; import { useGetOrgMembershipProjectMemberships } from "@app/hooks/api"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; import { UserProjectRow } from "./UserProjectRow"; @@ -21,42 +34,117 @@ type Props = { handlePopUpOpen: (popUpName: keyof UsePopUpState<["removeUserFromProject"]>, data?: {}) => void; }; +enum UserProjectsOrderBy { + Name = "Name" +} + export const UserProjectsTable = ({ membershipId, handlePopUpOpen }: Props) => { const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; + const { + search, + setSearch, + setPage, + page, + perPage, + setPerPage, + offset, + limit, + orderDirection, + toggleOrderDirection + } = usePagination(UserProjectsOrderBy.Name, { initPerPage: 10 }); - const { data: projectMemberships, isLoading } = useGetOrgMembershipProjectMemberships( + const { data: projectMemberships = [], isLoading } = useGetOrgMembershipProjectMemberships( orgId, membershipId ); + const filteredProjects = useMemo( + () => + projectMemberships + ?.filter((membership) => + membership.project.name.toLowerCase().includes(search.trim().toLowerCase()) + ) + .sort((a, b) => { + const [membershipOne, membershipTwo] = + orderDirection === OrderByDirection.ASC ? [a, b] : [b, a]; + + return membershipOne.project.name + .toLowerCase() + .localeCompare(membershipTwo.project.name.toLowerCase()); + }) ?? [], + [projectMemberships, orderDirection, search] + ); + + useResetPageHelper({ + totalCount: filteredProjects.length, + offset, + setPage + }); + return ( - - - - - - - - - - {isLoading && } - {!isLoading && - projectMemberships?.map((membership) => { - return ( - - ); - })} - -
NameRole -
- {!isLoading && !projectMemberships?.length && ( - - )} -
+
+ setSearch(e.target.value)} + leftIcon={} + placeholder="Search projects..." + /> + + + + + + + + + + {isLoading && } + {!isLoading && + filteredProjects?.slice(offset, limit * page)?.map((membership) => { + return ( + + ); + })} + +
+
+ Name + + + +
+
Role +
+ + {!isLoading && !filteredProjects?.length && ( + + )} +
+
); };