From ce9b66ef148ca7e50b32e511fb05732025bed421 Mon Sep 17 00:00:00 2001 From: Scott Wilson Date: Wed, 11 Sep 2024 12:40:27 -0700 Subject: [PATCH] address feedback suggestions --- backend/src/lib/api-docs/constants.ts | 8 +-- .../server/routes/v2/identity-org-router.ts | 15 ++---- .../routes/v2/identity-project-router.ts | 15 ++---- .../identity-project/identity-project-dal.ts | 19 +++---- .../identity-project-service.ts | 10 ++-- .../identity-project-types.ts | 4 +- .../src/services/identity/identity-org-dal.ts | 25 +++++----- .../src/services/identity/identity-service.ts | 10 ++-- .../src/services/identity/identity-types.ts | 4 +- frontend/src/hooks/api/generic/types.ts | 4 ++ .../src/hooks/api/organization/queries.tsx | 16 +++--- frontend/src/hooks/api/organization/types.ts | 12 +++-- frontend/src/hooks/api/workspace/queries.tsx | 16 +++--- frontend/src/hooks/api/workspace/types.ts | 12 +++-- .../IdentitySection/IdentityTable.tsx | 50 ++++++++++++------- .../components/IdentityTab/IdentityTab.tsx | 39 +++++++++------ 16 files changed, 147 insertions(+), 112 deletions(-) create mode 100644 frontend/src/hooks/api/generic/types.ts diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 52ae765c6..003b29811 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -367,8 +367,8 @@ export const ORGANIZATIONS = { offset: "The offset to start from. If you enter 10, it will start from the 10th identity membership.", limit: "The number of identity memberships to return.", orderBy: "The column to order identity memberships by.", - direction: "The direction identity memberships will be sorted in.", - textFilter: "The text string that identity membership names will be filtered by." + orderDirection: "The direction identity memberships will be sorted in.", + search: "The text string that identity membership names will be filtered by." }, GET_PROJECTS: { organizationId: "The ID of the organization to get projects from." @@ -479,8 +479,8 @@ export const PROJECT_IDENTITIES = { offset: "The offset to start from. If you enter 10, it will start from the 10th identity membership.", limit: "The number of identity memberships to return.", orderBy: "The column to order identity memberships by.", - direction: "The direction identity memberships will be sorted in.", - textFilter: "The text string that identity membership names will be filtered by." + orderDirection: "The direction identity memberships will be sorted in.", + search: "The text string that identity membership names will be filtered by." }, GET_IDENTITY_MEMBERSHIP_BY_ID: { identityId: "The ID of the identity to get the membership for.", diff --git a/backend/src/server/routes/v2/identity-org-router.ts b/backend/src/server/routes/v2/identity-org-router.ts index 8779757cd..edc270171 100644 --- a/backend/src/server/routes/v2/identity-org-router.ts +++ b/backend/src/server/routes/v2/identity-org-router.ts @@ -40,17 +40,12 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => { .default(OrgIdentityOrderBy.Name) .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderBy) .optional(), - direction: z + orderDirection: z .nativeEnum(OrderByDirection) .default(OrderByDirection.ASC) - .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.direction) + .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderDirection) .optional(), - textFilter: z - .string() - .trim() - .default("") - .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.textFilter) - .optional() + search: z.string().trim().describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.search).optional() }), response: { 200: z.object({ @@ -80,8 +75,8 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => { limit: req.query.limit, offset: req.query.offset, orderBy: req.query.orderBy, - direction: req.query.direction, - textFilter: req.query.textFilter + orderDirection: req.query.orderDirection, + search: req.query.search }); return { identityMemberships, totalCount }; diff --git a/backend/src/server/routes/v2/identity-project-router.ts b/backend/src/server/routes/v2/identity-project-router.ts index 58b165339..adb070ad9 100644 --- a/backend/src/server/routes/v2/identity-project-router.ts +++ b/backend/src/server/routes/v2/identity-project-router.ts @@ -235,17 +235,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) .default(ProjectIdentityOrderBy.Name) .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderBy) .optional(), - direction: z + orderDirection: z .nativeEnum(OrderByDirection) .default(OrderByDirection.ASC) - .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.direction) + .describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderDirection) .optional(), - textFilter: z - .string() - .trim() - .default("") - .describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.textFilter) - .optional() + search: z.string().trim().describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.search).optional() }), response: { 200: z.object({ @@ -287,8 +282,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) limit: req.query.limit, offset: req.query.offset, orderBy: req.query.orderBy, - direction: req.query.direction, - textFilter: req.query.textFilter + orderDirection: req.query.orderDirection, + search: req.query.search }); return { identityMemberships, totalCount }; } diff --git a/backend/src/services/identity-project/identity-project-dal.ts b/backend/src/services/identity-project/identity-project-dal.ts index c5327b7eb..63955fc73 100644 --- a/backend/src/services/identity-project/identity-project-dal.ts +++ b/backend/src/services/identity-project/identity-project-dal.ts @@ -112,7 +112,7 @@ export const identityProjectDALFactory = (db: TDbClient) => { projectId: string, filter: { identityId?: string } & Pick< TListProjectIdentityDTO, - "limit" | "offset" | "textFilter" | "orderBy" | "direction" + "limit" | "offset" | "search" | "orderBy" | "orderDirection" > = {}, tx?: Knex ) => { @@ -126,8 +126,8 @@ export const identityProjectDALFactory = (db: TDbClient) => { void qb.where("identityId", filter.identityId); } - if (filter.textFilter) { - void qb.whereILike(`${TableName.Identity}.name`, `%${filter.textFilter}%`); + if (filter.search) { + void qb.whereILike(`${TableName.Identity}.name`, `%${filter.search}%`); } }) .join( @@ -173,7 +173,7 @@ export const identityProjectDALFactory = (db: TDbClient) => { if (filter.orderBy) { switch (filter.orderBy) { case "name": - void query.orderBy(`${TableName.Identity}.${filter.orderBy}`, filter.direction); + void query.orderBy(`${TableName.Identity}.${filter.orderBy}`, filter.orderDirection); break; default: // do nothing @@ -238,7 +238,7 @@ export const identityProjectDALFactory = (db: TDbClient) => { const getCountByProjectId = async ( projectId: string, - filter: { identityId?: string } & Pick = {}, + filter: { identityId?: string } & Pick = {}, tx?: Knex ) => { try { @@ -251,12 +251,13 @@ export const identityProjectDALFactory = (db: TDbClient) => { void qb.where("identityId", filter.identityId); } - if (filter.textFilter) { - void qb.whereILike(`${TableName.Identity}.name`, `%${filter.textFilter}%`); + if (filter.search) { + void qb.whereILike(`${TableName.Identity}.name`, `%${filter.search}%`); } - }); + }) + .count(); - return identities.length; + return Number(identities[0].count); } catch (error) { throw new DatabaseError({ error, name: "GetCountByProjectId" }); } diff --git a/backend/src/services/identity-project/identity-project-service.ts b/backend/src/services/identity-project/identity-project-service.ts index 927790d12..4e48bf805 100644 --- a/backend/src/services/identity-project/identity-project-service.ts +++ b/backend/src/services/identity-project/identity-project-service.ts @@ -272,8 +272,8 @@ export const identityProjectServiceFactory = ({ limit, offset, orderBy, - direction, - textFilter + orderDirection, + search }: TListProjectIdentityDTO) => { const { permission } = await permissionService.getProjectPermission( actor, @@ -288,11 +288,11 @@ export const identityProjectServiceFactory = ({ limit, offset, orderBy, - direction, - textFilter + orderDirection, + search }); - const totalCount = await identityProjectDAL.getCountByProjectId(projectId, { textFilter }); + const totalCount = await identityProjectDAL.getCountByProjectId(projectId, { search }); return { identityMemberships, totalCount }; }; diff --git a/backend/src/services/identity-project/identity-project-types.ts b/backend/src/services/identity-project/identity-project-types.ts index 0c834fc0e..607fd4823 100644 --- a/backend/src/services/identity-project/identity-project-types.ts +++ b/backend/src/services/identity-project/identity-project-types.ts @@ -44,8 +44,8 @@ export type TListProjectIdentityDTO = { limit?: number; offset?: number; orderBy?: ProjectIdentityOrderBy; - direction?: OrderByDirection; - textFilter?: string; + orderDirection?: OrderByDirection; + search?: string; } & TProjectPermission; export type TGetProjectIdentityByIdentityIdDTO = { diff --git a/backend/src/services/identity/identity-org-dal.ts b/backend/src/services/identity/identity-org-dal.ts index c3a54736c..f309d3e29 100644 --- a/backend/src/services/identity/identity-org-dal.ts +++ b/backend/src/services/identity/identity-org-dal.ts @@ -34,11 +34,11 @@ export const identityOrgDALFactory = (db: TDbClient) => { limit, offset = 0, orderBy, - direction = OrderByDirection.ASC, - textFilter, + orderDirection = OrderByDirection.ASC, + search, ...filter }: Partial & - Pick, + Pick, tx?: Knex ) => { try { @@ -65,18 +65,18 @@ export const identityOrgDALFactory = (db: TDbClient) => { if (orderBy) { switch (orderBy) { case "name": - void query.orderBy(`${TableName.Identity}.${orderBy}`, direction); + void query.orderBy(`${TableName.Identity}.${orderBy}`, orderDirection); break; case "role": - void query.orderBy(`${TableName.IdentityOrgMembership}.${orderBy}`, direction); + void query.orderBy(`${TableName.IdentityOrgMembership}.${orderBy}`, orderDirection); break; default: // do nothing } } - if (textFilter?.length) { - void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`); + if (search?.length) { + void query.whereILike(`${TableName.Identity}.name`, `%${search}%`); } const docs = await query; @@ -117,21 +117,22 @@ export const identityOrgDALFactory = (db: TDbClient) => { }; const countAllOrgIdentities = async ( - { textFilter, ...filter }: Partial & Pick, + { search, ...filter }: Partial & Pick, tx?: Knex ) => { try { const query = (tx || db.replicaNode())(TableName.IdentityOrgMembership) .where(filter) - .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`); + .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) + .count(); - if (textFilter?.length) { - void query.whereILike(`${TableName.Identity}.name`, `%${textFilter}%`); + if (search?.length) { + void query.whereILike(`${TableName.Identity}.name`, `%${search}%`); } const identities = await query; - return identities.length; + return Number(identities[0].count); } catch (error) { throw new DatabaseError({ error, name: "countAllOrgIdentities" }); } diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index f31c5cebe..0416d1851 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -204,8 +204,8 @@ export const identityServiceFactory = ({ limit, offset, orderBy, - direction, - textFilter + orderDirection, + search }: TListOrgIdentitiesByOrgIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); @@ -215,13 +215,13 @@ export const identityServiceFactory = ({ limit, offset, orderBy, - direction, - textFilter + orderDirection, + search }); const totalCount = await identityOrgMembershipDAL.countAllOrgIdentities({ [`${TableName.IdentityOrgMembership}.orgId` as "orgId"]: orgId, - textFilter + search }); return { identityMemberships, totalCount }; diff --git a/backend/src/services/identity/identity-types.ts b/backend/src/services/identity/identity-types.ts index 9de5d8f35..8a5a81ba5 100644 --- a/backend/src/services/identity/identity-types.ts +++ b/backend/src/services/identity/identity-types.ts @@ -34,8 +34,8 @@ export type TListOrgIdentitiesByOrgIdDTO = { limit?: number; offset?: number; orderBy?: OrgIdentityOrderBy; - direction?: OrderByDirection; - textFilter?: string; + orderDirection?: OrderByDirection; + search?: string; } & TOrgPermission; export enum OrgIdentityOrderBy { diff --git a/frontend/src/hooks/api/generic/types.ts b/frontend/src/hooks/api/generic/types.ts new file mode 100644 index 000000000..1f09c5c0a --- /dev/null +++ b/frontend/src/hooks/api/generic/types.ts @@ -0,0 +1,4 @@ +export enum OrderByDirection { + ASC = "asc", + DESC = "desc" +} diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index 53e44092b..670f39146 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -1,6 +1,7 @@ import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { TGroupOrgMembership } from "../groups/types"; import { @@ -8,6 +9,7 @@ import { Invoice, License, Organization, + OrgIdentityOrderBy, OrgPlanTable, PlanBillingInfo, PmtMethod, @@ -372,9 +374,9 @@ export const useGetIdentityMembershipOrgs = ( organizationId, offset = 0, limit = 100, - orderBy = "name", - direction = "asc", - textFilter = "" + orderBy = OrgIdentityOrderBy.Name, + orderDirection = OrderByDirection.ASC, + search = "" }: TListOrgIdentitiesDTO, options?: Omit< UseQueryOptions< @@ -390,8 +392,8 @@ export const useGetIdentityMembershipOrgs = ( offset: String(offset), limit: String(limit), orderBy: String(orderBy), - direction: String(direction), - textFilter: String(textFilter) + orderDirection: String(orderDirection), + search: String(search) }); return useQuery({ queryKey: organizationKeys.getOrgIdentityMembershipsWithParams({ @@ -399,8 +401,8 @@ export const useGetIdentityMembershipOrgs = ( offset, limit, orderBy, - direction, - textFilter + orderDirection, + search }), queryFn: async () => { const { data } = await apiRequest.get( diff --git a/frontend/src/hooks/api/organization/types.ts b/frontend/src/hooks/api/organization/types.ts index 56dcb22e7..684a53af4 100644 --- a/frontend/src/hooks/api/organization/types.ts +++ b/frontend/src/hooks/api/organization/types.ts @@ -1,3 +1,4 @@ +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { IdentityMembershipOrg } from "@app/hooks/api/identities/types"; export type Organization = { @@ -109,12 +110,17 @@ export type TListOrgIdentitiesDTO = { organizationId: string; offset?: number; limit?: number; - orderBy?: string; - direction?: string; - textFilter?: string; + orderBy?: OrgIdentityOrderBy; + orderDirection?: OrderByDirection; + search?: string; }; export type TOrgIdentitiesList = { identityMemberships: IdentityMembershipOrg[]; totalCount: number; }; + +export enum OrgIdentityOrderBy { + Name = "name", + Role = "role" +} diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index bccb4b23d..00e814c50 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -1,6 +1,7 @@ import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { CaStatus } from "../ca/enums"; import { TCertificateAuthority } from "../ca/types"; @@ -23,6 +24,7 @@ import { DeleteEnvironmentDTO, DeleteWorkspaceDTO, NameWorkspaceSecretsDTO, + ProjectIdentityOrderBy, RenameWorkspaceDTO, TGetUpgradeProjectStatusDTO, TListProjectIdentitiesDTO, @@ -538,9 +540,9 @@ export const useGetWorkspaceIdentityMemberships = ( workspaceId, offset = 0, limit = 100, - orderBy = "name", - direction = "asc", - textFilter = "" + orderBy = ProjectIdentityOrderBy.Name, + orderDirection = OrderByDirection.ASC, + search = "" }: TListProjectIdentitiesDTO, options?: Omit< UseQueryOptions< @@ -558,16 +560,16 @@ export const useGetWorkspaceIdentityMemberships = ( offset, limit, orderBy, - direction, - textFilter + orderDirection, + search }), queryFn: async () => { const params = new URLSearchParams({ offset: String(offset), limit: String(limit), orderBy: String(orderBy), - direction: String(direction), - textFilter: String(textFilter) + orderDirection: String(orderDirection), + search: String(search) }); const { data } = await apiRequest.get( diff --git a/frontend/src/hooks/api/workspace/types.ts b/frontend/src/hooks/api/workspace/types.ts index 42f532221..3af4d1e16 100644 --- a/frontend/src/hooks/api/workspace/types.ts +++ b/frontend/src/hooks/api/workspace/types.ts @@ -1,3 +1,5 @@ +import { OrderByDirection } from "@app/hooks/api/generic/types"; + import { TProjectRole } from "../roles/types"; export enum ProjectVersion { @@ -146,7 +148,11 @@ export type TListProjectIdentitiesDTO = { workspaceId: string; offset?: number; limit?: number; - orderBy?: string; - direction?: string; - textFilter?: string; + orderBy?: ProjectIdentityOrderBy; + orderDirection?: OrderByDirection; + search?: string; }; + +export enum ProjectIdentityOrderBy { + Name = "name" +} diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx index 0bdbefdcd..8b55fab23 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx @@ -36,6 +36,8 @@ import { import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { useDebounce } from "@app/hooks"; import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; +import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { @@ -55,10 +57,10 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { const { currentOrg } = useOrganization(); const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(INIT_PER_PAGE); - const [direction, setDirection] = useState("asc"); - const [orderBy, setOrderBy] = useState("name"); - const [textFilter, setTextFilter] = useState(""); - const debouncedTextFilter = useDebounce(textFilter); + const [orderDirection, setOrderDirection] = useState(OrderByDirection.ASC); + const [orderBy, setOrderBy] = useState(OrgIdentityOrderBy.Name); + const [search, setSearch] = useState(""); + const debouncedSearch = useDebounce(search); const organizationId = currentOrg?.id || ""; @@ -70,9 +72,9 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { organizationId, offset, limit: perPage, - direction, + orderDirection, orderBy, - textFilter: debouncedTextFilter + search: debouncedSearch }, { keepPreviousData: true } ); @@ -84,14 +86,16 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { const { data: roles } = useGetOrgRoles(organizationId); - const handleSort = (column: string) => { + const handleSort = (column: OrgIdentityOrderBy) => { if (column === orderBy) { - setDirection((prev) => (prev === "asc" ? "desc" : "asc")); + setOrderDirection((prev) => + prev === OrderByDirection.ASC ? OrderByDirection.DESC : OrderByDirection.ASC + ); return; } setOrderBy(column); - setDirection("asc"); + setOrderDirection(OrderByDirection.ASC); }; const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => { @@ -122,8 +126,8 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
setTextFilter(e.target.value)} + value={search} + onChange={(e) => setSearch(e.target.value)} leftIcon={} placeholder="Search identities by name..." /> @@ -136,12 +140,17 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { Name handleSort("name")} + onClick={() => handleSort(OrgIdentityOrderBy.Name)} >
@@ -151,12 +160,17 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { Role handleSort("role")} + onClick={() => handleSort(OrgIdentityOrderBy.Role)} > @@ -275,7 +289,7 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { {!isLoading && data && data?.identityMemberships.length === 0 && ( 0 + debouncedSearch.trim().length > 0 ? "No identities match search filter" : "No identities have been created in this organization" } diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx index 17c3849fb..500a1926a 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/IdentityTab.tsx @@ -46,8 +46,10 @@ import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@a import { withProjectPermission } from "@app/hoc"; import { useDebounce } from "@app/hooks"; import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; +import { OrderByDirection } from "@app/hooks/api/generic/types"; import { IdentityMembership } from "@app/hooks/api/identities/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; +import { ProjectIdentityOrderBy } from "@app/hooks/api/workspace/types"; import { usePopUp } from "@app/hooks/usePopUp"; import { IdentityModal } from "./components/IdentityModal"; @@ -67,10 +69,10 @@ export const IdentityTab = withProjectPermission( const [page, setPage] = useState(1); const [perPage, setPerPage] = useState(INIT_PER_PAGE); - const [direction, setDirection] = useState("asc"); - const [orderBy, setOrderBy] = useState("name"); - const [textFilter, setTextFilter] = useState(""); - const debouncedTextFilter = useDebounce(textFilter); + const [orderDirection, setOrderDirection] = useState(OrderByDirection.ASC); + const [orderBy, setOrderBy] = useState(ProjectIdentityOrderBy.Name); + const [search, setSearch] = useState(""); + const debouncedSearch = useDebounce(search); const workspaceId = currentWorkspace?.id ?? ""; @@ -80,9 +82,9 @@ export const IdentityTab = withProjectPermission( workspaceId: currentWorkspace?.id || "", offset, limit: perPage, - direction, + orderDirection, orderBy, - textFilter: debouncedTextFilter + search: debouncedSearch }, { keepPreviousData: true } ); @@ -125,14 +127,16 @@ export const IdentityTab = withProjectPermission( if (data && data.totalCount < offset) setPage(1); }, [data?.totalCount]); - const handleSort = (column: string) => { + const handleSort = (column: ProjectIdentityOrderBy) => { if (column === orderBy) { - setDirection((prev) => (prev === "asc" ? "desc" : "asc")); + setOrderDirection((prev) => + prev === OrderByDirection.ASC ? OrderByDirection.DESC : OrderByDirection.ASC + ); return; } setOrderBy(column); - setDirection("asc"); + setOrderDirection(OrderByDirection.ASC); }; return ( @@ -176,8 +180,8 @@ export const IdentityTab = withProjectPermission( setTextFilter(e.target.value)} + value={search} + onChange={(e) => setSearch(e.target.value)} leftIcon={} placeholder="Search identities by name..." /> @@ -190,13 +194,18 @@ export const IdentityTab = withProjectPermission( Name handleSort("name")} + onClick={() => handleSort(ProjectIdentityOrderBy.Name)} > @@ -372,7 +381,7 @@ export const IdentityTab = withProjectPermission( {!isLoading && data && data?.identityMemberships.length === 0 && ( 0 + debouncedSearch.trim().length > 0 ? "No identities match search filter" : "No identities have been added to this project" }