Merge pull request #2610 from scott-ray-wilson/group-tables-fixes/improvements

Fix: Group Tables/Pagination Fixes and Improvements
This commit is contained in:
Scott Wilson
2024-10-21 09:39:00 -07:00
committed by GitHub
18 changed files with 247 additions and 81 deletions
+2 -1
View File
@@ -165,7 +165,8 @@ export const registerGroupRouter = async (server: FastifyZodProvider) => {
querystring: z.object({ querystring: z.object({
offset: z.coerce.number().min(0).max(100).default(0).describe(GROUPS.LIST_USERS.offset), offset: z.coerce.number().min(0).max(100).default(0).describe(GROUPS.LIST_USERS.offset),
limit: z.coerce.number().min(1).max(100).default(10).describe(GROUPS.LIST_USERS.limit), limit: z.coerce.number().min(1).max(100).default(10).describe(GROUPS.LIST_USERS.limit),
username: z.string().optional().describe(GROUPS.LIST_USERS.username) username: z.string().trim().optional().describe(GROUPS.LIST_USERS.username),
search: z.string().trim().optional().describe(GROUPS.LIST_USERS.search)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -124,7 +124,9 @@ export const accessApprovalPolicyServiceFactory = ({
const verifyAllApprovers = [...approverUserIds]; const verifyAllApprovers = [...approverUserIds];
for (const groupId of groupApprovers) { for (const groupId of groupApprovers) {
usersPromises.push(groupDAL.findAllGroupPossibleMembers({ orgId: actorOrgId, groupId, offset: 0 })); usersPromises.push(
groupDAL.findAllGroupPossibleMembers({ orgId: actorOrgId, groupId, offset: 0 }).then((group) => group.members)
);
} }
const verifyGroupApprovers = (await Promise.all(usersPromises)) const verifyGroupApprovers = (await Promise.all(usersPromises))
.flat() .flat()
@@ -327,7 +329,11 @@ export const accessApprovalPolicyServiceFactory = ({
>[] = []; >[] = [];
for (const groupId of groupApprovers) { for (const groupId of groupApprovers) {
usersPromises.push(groupDAL.findAllGroupPossibleMembers({ orgId: actorOrgId, groupId, offset: 0 })); usersPromises.push(
groupDAL
.findAllGroupPossibleMembers({ orgId: actorOrgId, groupId, offset: 0 })
.then((group) => group.members)
);
} }
const verifyGroupApprovers = (await Promise.all(usersPromises)) const verifyGroupApprovers = (await Promise.all(usersPromises))
.flat() .flat()
@@ -147,10 +147,12 @@ export const accessApprovalRequestServiceFactory = ({
const groupUsers = ( const groupUsers = (
await Promise.all( await Promise.all(
approverGroupIds.map((groupApproverId) => approverGroupIds.map((groupApproverId) =>
groupDAL.findAllGroupPossibleMembers({ groupDAL
orgId: actorOrgId, .findAllGroupPossibleMembers({
groupId: groupApproverId orgId: actorOrgId,
}) groupId: groupApproverId
})
.then((group) => group.members)
) )
) )
).flat(); ).flat();
+27 -17
View File
@@ -65,16 +65,18 @@ export const groupDALFactory = (db: TDbClient) => {
groupId, groupId,
offset = 0, offset = 0,
limit, limit,
username username, // depreciated in favor of search
search
}: { }: {
orgId: string; orgId: string;
groupId: string; groupId: string;
offset?: number; offset?: number;
limit?: number; limit?: number;
username?: string; username?: string;
search?: string;
}) => { }) => {
try { try {
let query = db const query = db
.replicaNode()(TableName.OrgMembership) .replicaNode()(TableName.OrgMembership)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.OrgMembership}.orgId`, orgId)
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
@@ -92,31 +94,39 @@ export const groupDALFactory = (db: TDbClient) => {
db.ref("username").withSchema(TableName.Users), db.ref("username").withSchema(TableName.Users),
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId") db.ref("id").withSchema(TableName.Users).as("userId"),
db.raw(`count(*) OVER() as total_count`)
) )
.where({ isGhost: false }) .where({ isGhost: false })
.offset(offset); .offset(offset)
.orderBy("firstName", "asc");
if (limit) { if (limit) {
query = query.limit(limit); void query.limit(limit);
} }
if (username) { if (search) {
query = query.andWhere(`${TableName.Users}.username`, "ilike", `%${username}%`); void query.andWhereRaw(`CONCAT_WS(' ', "firstName", "lastName", "username") ilike '%${search}%'`);
} else if (username) {
void query.andWhere(`${TableName.Users}.username`, "ilike", `%${username}%`);
} }
const members = await query; const members = await query;
return members.map( return {
({ email, username: memberUsername, firstName, lastName, userId, groupId: memberGroupId }) => ({ members: members.map(
id: userId, ({ email, username: memberUsername, firstName, lastName, userId, groupId: memberGroupId }) => ({
email, id: userId,
username: memberUsername, email,
firstName, username: memberUsername,
lastName, firstName,
isPartOfGroup: !!memberGroupId lastName,
}) isPartOfGroup: !!memberGroupId
); })
),
// @ts-expect-error col select is raw and not strongly typed
totalCount: Number(members?.[0]?.total_count ?? 0)
};
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Find all org members" }); throw new DatabaseError({ error, name: "Find all org members" });
} }
@@ -221,7 +221,8 @@ export const groupServiceFactory = ({
actor, actor,
actorId, actorId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId,
search
}: TListGroupUsersDTO) => { }: TListGroupUsersDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" }); if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
@@ -244,17 +245,16 @@ export const groupServiceFactory = ({
message: `Failed to find group with ID ${id}` message: `Failed to find group with ID ${id}`
}); });
const users = await groupDAL.findAllGroupPossibleMembers({ const { members, totalCount } = await groupDAL.findAllGroupPossibleMembers({
orgId: group.orgId, orgId: group.orgId,
groupId: group.id, groupId: group.id,
offset, offset,
limit, limit,
username username,
search
}); });
const count = await orgDAL.countAllOrgMembers(group.orgId); return { users: members, totalCount };
return { users, totalCount: count };
}; };
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => { const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
@@ -38,6 +38,7 @@ export type TListGroupUsersDTO = {
offset: number; offset: number;
limit: number; limit: number;
username?: string; username?: string;
search?: string;
} & TGenericPermission; } & TGenericPermission;
export type TAddUserToGroupDTO = { export type TAddUserToGroupDTO = {
+6 -4
View File
@@ -834,10 +834,12 @@ export const scimServiceFactory = ({
}); });
} }
const users = await groupDAL.findAllGroupPossibleMembers({ const users = await groupDAL
orgId: group.orgId, .findAllGroupPossibleMembers({
groupId: group.id orgId: group.orgId,
}); groupId: group.id
})
.then((g) => g.members);
const orgMemberships = await orgDAL.findMembership({ const orgMemberships = await orgDAL.findMembership({
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId, [`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
+2 -1
View File
@@ -18,7 +18,8 @@ export const GROUPS = {
id: "The id of the group to list users for", id: "The id of the group to list users for",
offset: "The offset to start from. If you enter 10, it will start from the 10th user.", offset: "The offset to start from. If you enter 10, it will start from the 10th user.",
limit: "The number of users to return.", limit: "The number of users to return.",
username: "The username to search for." username: "The username to search for.",
search: "The text string that user email or name will be filtered by."
}, },
ADD_USER: { ADD_USER: {
id: "The id of the group to add the user to.", id: "The id of the group to add the user to.",
+8 -7
View File
@@ -10,13 +10,13 @@ export const groupKeys = {
slug, slug,
offset, offset,
limit, limit,
username search
}: { }: {
slug: string; slug: string;
offset: number; offset: number;
limit: number; limit: number;
username: string; search: string;
}) => [...groupKeys.forGroupUserMemberships(slug), { offset, limit, username }] as const }) => [...groupKeys.forGroupUserMemberships(slug), { offset, limit, search }] as const
}; };
type TUser = { type TUser = {
@@ -33,27 +33,28 @@ export const useListGroupUsers = ({
groupSlug, groupSlug,
offset = 0, offset = 0,
limit = 10, limit = 10,
username search
}: { }: {
id: string; id: string;
groupSlug: string; groupSlug: string;
offset: number; offset: number;
limit: number; limit: number;
username: string; search: string;
}) => { }) => {
return useQuery({ return useQuery({
queryKey: groupKeys.specificGroupUserMemberships({ queryKey: groupKeys.specificGroupUserMemberships({
slug: groupSlug, slug: groupSlug,
offset, offset,
limit, limit,
username search
}), }),
enabled: Boolean(groupSlug), enabled: Boolean(groupSlug),
keepPreviousData: true,
queryFn: async () => { queryFn: async () => {
const params = new URLSearchParams({ const params = new URLSearchParams({
offset: String(offset), offset: String(offset),
limit: String(limit), limit: String(limit),
username search
}); });
const { data } = await apiRequest.get<{ users: TUser[]; totalCount: number }>( const { data } = await apiRequest.get<{ users: TUser[]; totalCount: number }>(
+1
View File
@@ -3,6 +3,7 @@ export { useLeaveConfirm } from "./useLeaveConfirm";
export { usePagination } from "./usePagination"; export { usePagination } from "./usePagination";
export { usePersistentState } from "./usePersistentState"; export { usePersistentState } from "./usePersistentState";
export { usePopUp } from "./usePopUp"; export { usePopUp } from "./usePopUp";
export { useResetPageHelper } from "./useResetPageHelper";
export { useSyntaxHighlight } from "./useSyntaxHighlight"; export { useSyntaxHighlight } from "./useSyntaxHighlight";
export { useTimedReset } from "./useTimedReset"; export { useTimedReset } from "./useTimedReset";
export { useToggle } from "./useToggle"; export { useToggle } from "./useToggle";
+16
View File
@@ -0,0 +1,16 @@
import { Dispatch, SetStateAction, useEffect } from "react";
export const useResetPageHelper = ({
totalCount,
offset,
setPage
}: {
totalCount: number;
offset: number;
setPage: Dispatch<SetStateAction<number>>;
}) => {
useEffect(() => {
// reset page if no longer valid
if (totalCount <= offset) setPage(1);
}, [totalCount]);
};
@@ -21,6 +21,7 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { useDebounce, useResetPageHelper } from "@app/hooks";
import { useAddUserToGroup, useListGroupUsers, useRemoveUserFromGroup } from "@app/hooks/api"; import { useAddUserToGroup, useListGroupUsers, useRemoveUserFromGroup } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -33,18 +34,28 @@ export const OrgGroupMembersModal = ({ popUp, handlePopUpToggle }: Props) => {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10); const [perPage, setPerPage] = useState(10);
const [searchMemberFilter, setSearchMemberFilter] = useState(""); const [searchMemberFilter, setSearchMemberFilter] = useState("");
const [debouncedSearch] = useDebounce(searchMemberFilter);
const popUpData = popUp?.groupMembers?.data as { const popUpData = popUp?.groupMembers?.data as {
groupId: string; groupId: string;
slug: string; slug: string;
}; };
const offset = (page - 1) * perPage;
const { data, isLoading } = useListGroupUsers({ const { data, isLoading } = useListGroupUsers({
id: popUpData?.groupId, id: popUpData?.groupId,
groupSlug: popUpData?.slug, groupSlug: popUpData?.slug,
offset: (page - 1) * perPage, offset,
limit: perPage, limit: perPage,
username: searchMemberFilter search: debouncedSearch
});
const { totalCount = 0 } = data ?? {};
useResetPageHelper({
totalCount,
offset,
setPage
}); });
const { mutateAsync: assignMutateAsync } = useAddUserToGroup(); const { mutateAsync: assignMutateAsync } = useAddUserToGroup();
@@ -140,9 +151,9 @@ export const OrgGroupMembersModal = ({ popUp, handlePopUpToggle }: Props) => {
})} })}
</TBody> </TBody>
</Table> </Table>
{!isLoading && data?.totalCount !== undefined && ( {!isLoading && totalCount > 0 && (
<Pagination <Pagination
count={data.totalCount} count={totalCount}
page={page} page={page}
perPage={perPage} perPage={perPage}
onChangePage={(newPage) => setPage(newPage)} onChangePage={(newPage) => setPage(newPage)}
@@ -150,7 +161,10 @@ export const OrgGroupMembersModal = ({ popUp, handlePopUpToggle }: Props) => {
/> />
)} )}
{!isLoading && !data?.users?.length && ( {!isLoading && !data?.users?.length && (
<EmptyState title="No users found" icon={faUsers} /> <EmptyState
title={debouncedSearch ? "No users match search" : "No users found"}
icon={faUsers}
/>
)} )}
</TableContainer> </TableContainer>
</ModalContent> </ModalContent>
@@ -1,5 +1,11 @@
import { useState } from "react"; import { useMemo, useState } from "react";
import { faEllipsis, faMagnifyingGlass, faUsers } from "@fortawesome/free-solid-svg-icons"; import {
faArrowDown,
faArrowUp,
faEllipsis,
faMagnifyingGlass,
faUsers
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
@@ -11,6 +17,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
EmptyState, EmptyState,
IconButton,
Input, Input,
Select, Select,
SelectItem, SelectItem,
@@ -24,7 +31,9 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { useDebounce } from "@app/hooks";
import { useGetOrganizationGroups, useGetOrgRoles, useUpdateGroup } from "@app/hooks/api"; import { useGetOrganizationGroups, useGetOrgRoles, useUpdateGroup } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = { type Props = {
@@ -43,12 +52,21 @@ type Props = {
) => void; ) => void;
}; };
enum GroupsOrderBy {
Name = "name",
Slug = "slug",
Role = "role"
}
export const OrgGroupsTable = ({ handlePopUpOpen }: Props) => { export const OrgGroupsTable = ({ handlePopUpOpen }: Props) => {
const [searchGroupsFilter, setSearchGroupsFilter] = useState(""); const [searchGroupsFilter, setSearchGroupsFilter] = useState("");
const [debouncedSearch] = useDebounce(searchGroupsFilter.trim());
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { isLoading, data: groups } = useGetOrganizationGroups(orgId); const { isLoading, data: groups } = useGetOrganizationGroups(orgId);
const { mutateAsync: updateMutateAsync } = useUpdateGroup(); const { mutateAsync: updateMutateAsync } = useUpdateGroup();
const [orderBy, setOrderBy] = useState(GroupsOrderBy.Name);
const [orderDirection, setOrderDirection] = useState(OrderByDirection.ASC);
const { data: roles } = useGetOrgRoles(orgId); const { data: roles } = useGetOrgRoles(orgId);
@@ -72,6 +90,43 @@ export const OrgGroupsTable = ({ handlePopUpOpen }: Props) => {
} }
}; };
const filteredGroups = useMemo(() => {
const filtered = debouncedSearch
? groups?.filter(
({ name, slug }) =>
name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
slug.toLowerCase().includes(debouncedSearch.toLowerCase())
)
: groups;
const ordered = filtered?.sort((a, b) => {
switch (orderBy) {
case GroupsOrderBy.Role: {
const aValue = a.role === "custom" ? (a.customRole?.name as string) : a.role;
const bValue = b.role === "custom" ? (b.customRole?.name as string) : b.role;
return aValue.toLowerCase().localeCompare(bValue.toLowerCase());
}
default:
return a[orderBy].toLowerCase().localeCompare(b[orderBy].toLowerCase());
}
});
return orderDirection === OrderByDirection.ASC ? ordered : ordered?.reverse();
}, [debouncedSearch, groups, orderBy, orderDirection]);
const handleSort = (column: GroupsOrderBy) => {
if (column === orderBy) {
setOrderDirection((prev) =>
prev === OrderByDirection.ASC ? OrderByDirection.DESC : OrderByDirection.ASC
);
return;
}
setOrderBy(column);
setOrderDirection(OrderByDirection.ASC);
};
return ( return (
<div> <div>
<Input <Input
@@ -84,16 +139,70 @@ export const OrgGroupsTable = ({ handlePopUpOpen }: Props) => {
<Table> <Table>
<THead> <THead>
<Tr> <Tr>
<Th>Name</Th> <Th>
<Th>Slug</Th> <div className="flex items-center">
<Th>Role</Th> Name
<IconButton
variant="plain"
className={`ml-2 ${orderBy === GroupsOrderBy.Name ? "" : "opacity-30"}`}
ariaLabel="sort"
onClick={() => handleSort(GroupsOrderBy.Name)}
>
<FontAwesomeIcon
icon={
orderDirection === OrderByDirection.DESC && orderBy === GroupsOrderBy.Name
? faArrowUp
: faArrowDown
}
/>
</IconButton>
</div>
</Th>
<Th>
<div className="flex items-center">
Slug
<IconButton
variant="plain"
className={`ml-2 ${orderBy === GroupsOrderBy.Slug ? "" : "opacity-30"}`}
ariaLabel="sort"
onClick={() => handleSort(GroupsOrderBy.Slug)}
>
<FontAwesomeIcon
icon={
orderDirection === OrderByDirection.DESC && orderBy === GroupsOrderBy.Slug
? faArrowUp
: faArrowDown
}
/>
</IconButton>
</div>
</Th>
<Th>
<div className="flex items-center">
Role
<IconButton
variant="plain"
className={`ml-2 ${orderBy === GroupsOrderBy.Role ? "" : "opacity-30"}`}
ariaLabel="sort"
onClick={() => handleSort(GroupsOrderBy.Role)}
>
<FontAwesomeIcon
icon={
orderDirection === OrderByDirection.DESC && orderBy === GroupsOrderBy.Role
? faArrowUp
: faArrowDown
}
/>
</IconButton>
</div>
</Th>
<Th className="w-5" /> <Th className="w-5" />
</Tr> </Tr>
</THead> </THead>
<TBody> <TBody>
{isLoading && <TableSkeleton columns={4} innerKey="org-groups" />} {isLoading && <TableSkeleton columns={4} innerKey="org-groups" />}
{!isLoading && {!isLoading &&
groups?.map(({ id, name, slug, role, customRole }) => { filteredGroups?.map(({ id, name, slug, role, customRole }) => {
return ( return (
<Tr className="h-10" key={`org-group-${id}`}> <Tr className="h-10" key={`org-group-${id}`}>
<Td>{name}</Td> <Td>{name}</Td>
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { import {
faArrowDown, faArrowDown,
@@ -34,7 +33,7 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { usePagination } from "@app/hooks"; import { usePagination, useResetPageHelper } from "@app/hooks";
import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api"; import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types"; import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types";
@@ -87,10 +86,11 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
); );
const { totalCount = 0 } = data ?? {}; const { totalCount = 0 } = data ?? {};
useEffect(() => { useResetPageHelper({
// reset page if no longer valid totalCount,
if (totalCount <= offset) setPage(1); offset,
}, [totalCount]); setPage
});
const { data: roles } = useGetOrgRoles(organizationId); const { data: roles } = useGetOrgRoles(organizationId);
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { import {
faArrowDown, faArrowDown,
@@ -51,7 +50,7 @@ import {
useProjectPermission, useProjectPermission,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { usePagination, usePopUp } from "@app/hooks"; import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
import { useGetCmeksByProjectId, useUpdateCmek } from "@app/hooks/api/cmeks"; import { useGetCmeksByProjectId, useUpdateCmek } from "@app/hooks/api/cmeks";
import { CmekOrderBy, TCmek } from "@app/hooks/api/cmeks/types"; import { CmekOrderBy, TCmek } from "@app/hooks/api/cmeks/types";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
@@ -108,10 +107,11 @@ export const CmekTable = () => {
}); });
const { keys = [], totalCount = 0 } = data ?? {}; const { keys = [], totalCount = 0 } = data ?? {};
useEffect(() => { useResetPageHelper({
// reset page if no longer valid totalCount,
if (totalCount <= offset) setPage(1); offset,
}, [totalCount]); setPage
});
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"upsertKey", "upsertKey",
@@ -1,4 +1,3 @@
import { useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { import {
faArrowDown, faArrowDown,
@@ -44,7 +43,7 @@ import {
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { withProjectPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { usePagination } from "@app/hooks"; import { usePagination, useResetPageHelper } from "@app/hooks";
import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
import { IdentityMembership } from "@app/hooks/api/identities/types"; import { IdentityMembership } from "@app/hooks/api/identities/types";
@@ -99,10 +98,11 @@ export const IdentityTab = withProjectPermission(
const { totalCount = 0 } = data ?? {}; const { totalCount = 0 } = data ?? {};
useEffect(() => { useResetPageHelper({
// reset page if no longer valid totalCount,
if (totalCount <= offset) setPage(1); offset,
}, [totalCount]); setPage
});
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
@@ -16,7 +16,7 @@ import {
useProjectPermission, useProjectPermission,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { useDebounce, usePagination, usePopUp } from "@app/hooks"; import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
import { import {
useGetImportedSecretsSingleEnv, useGetImportedSecretsSingleEnv,
useGetSecretApprovalPolicyOfABoard, useGetSecretApprovalPolicyOfABoard,
@@ -164,10 +164,11 @@ const SecretMainPageContent = () => {
totalCount = 0 totalCount = 0
} = data ?? {}; } = data ?? {};
useEffect(() => { useResetPageHelper({
// reset page if no longer valid totalCount,
if (totalCount <= offset) setPage(1); offset,
}, [totalCount]); setPage
});
// fetch imported secrets to show user the overriden ones // fetch imported secrets to show user the overriden ones
const { data: importedSecrets } = useGetImportedSecretsSingleEnv({ const { data: importedSecrets } = useGetImportedSecretsSingleEnv({
@@ -55,7 +55,7 @@ import {
useProjectPermission, useProjectPermission,
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { useDebounce, usePagination, usePopUp } from "@app/hooks"; import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
import { import {
useCreateFolder, useCreateFolder,
useCreateSecretV3, useCreateSecretV3,
@@ -213,10 +213,11 @@ export const SecretOverviewPage = () => {
totalCount = 0 totalCount = 0
} = overview ?? {}; } = overview ?? {};
useEffect(() => { useResetPageHelper({
// reset page if no longer valid totalCount,
if (totalCount <= offset) setPage(1); offset,
}, [totalCount]); setPage
});
const { folderNames, getFolderByNameAndEnv, isFolderPresentInEnv } = useFolderOverview(folders); const { folderNames, getFolderByNameAndEnv, isFolderPresentInEnv } = useFolderOverview(folders);