improvement: secret tags table pagination

This commit is contained in:
Scott Wilson
2024-11-28 14:29:41 -08:00
parent 4293665130
commit dab8f0b261
3 changed files with 130 additions and 53 deletions
@@ -57,7 +57,7 @@ export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
const filteredGroupMemberships = useMemo( const filteredGroupMemberships = useMemo(
() => () =>
groupMemberships groupMemberships
?.filter((group) => group.name.toLowerCase().includes(search.trim().toLowerCase())) .filter((group) => group.name.toLowerCase().includes(search.trim().toLowerCase()))
.sort((a, b) => { .sort((a, b) => {
const [membershipOne, membershipTwo] = const [membershipOne, membershipTwo] =
orderDirection === OrderByDirection.ASC ? [a, b] : [b, a]; orderDirection === OrderByDirection.ASC ? [a, b] : [b, a];
@@ -72,13 +72,14 @@ export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
offset, offset,
setPage setPage
}); });
return ( return (
<div> <div>
<Input <Input
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />} leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
placeholder="Search projects..." placeholder="Search groups..."
/> />
<TableContainer className="mt-4"> <TableContainer className="mt-4">
<Table> <Table>
@@ -19,7 +19,6 @@ import { SecretTagsTable } from "./SecretTagsTable";
type DeleteModalData = { name: string; id: string }; type DeleteModalData = { name: string; id: string };
export const SecretTagsSection = (): JSX.Element => { export const SecretTagsSection = (): JSX.Element => {
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([ const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
"CreateSecretTag", "CreateSecretTag",
"deleteTagConfirmation" "deleteTagConfirmation"
@@ -65,7 +64,7 @@ export const SecretTagsSection = (): JSX.Element => {
}} }}
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Create tag Create Tag
</Button> </Button>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
@@ -1,10 +1,20 @@
import { faTags, faTrashCan } from "@fortawesome/free-solid-svg-icons"; import { useMemo } from "react";
import {
faArrowDown,
faArrowUp,
faMagnifyingGlass,
faSearch,
faTag,
faTrashCan
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
EmptyState, EmptyState,
IconButton, IconButton,
Input,
Pagination,
Table, Table,
TableContainer, TableContainer,
TableSkeleton, TableSkeleton,
@@ -15,7 +25,9 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { usePagination, useResetPageHelper } from "@app/hooks";
import { useGetWsTags } from "@app/hooks/api"; import { useGetWsTags } 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 = {
@@ -31,59 +43,124 @@ type Props = {
) => void; ) => void;
}; };
enum TagsOrderBy {
Slug = "slug"
}
export const SecretTagsTable = ({ handlePopUpOpen }: Props) => { export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useGetWsTags(currentWorkspace?.id ?? ""); const { data: tags = [], isLoading } = useGetWsTags(currentWorkspace?.id ?? "");
const {
search,
setSearch,
setPage,
page,
perPage,
setPerPage,
offset,
orderDirection,
toggleOrderDirection
} = usePagination(TagsOrderBy.Slug, { initPerPage: 10 });
const filteredTags = useMemo(
() =>
tags
.filter((tag) => tag.slug.toLowerCase().includes(search.trim().toLowerCase()))
.sort((a, b) => {
const [tagOne, tagTwo] = orderDirection === OrderByDirection.ASC ? [a, b] : [b, a];
return tagOne.slug.toLowerCase().localeCompare(tagTwo.slug.toLowerCase());
}),
[tags, orderDirection, search]
);
useResetPageHelper({
totalCount: filteredTags.length,
offset,
setPage
});
return ( return (
<TableContainer className="mt-4"> <div>
<Table> <Input
<THead> value={search}
<Tr> onChange={(e) => setSearch(e.target.value)}
<Th>Slug</Th> leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
<Th aria-label="button" /> placeholder="Search tags..."
</Tr> />
</THead> <TableContainer className="mt-4">
<TBody> <Table>
{isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />} <THead>
{!isLoading &&
data &&
data.map(({ id, slug }) => (
<Tr key={id}>
<Td>{slug}</Td>
<Td className="flex items-center justify-end">
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.Tags}
>
{(isAllowed) => (
<IconButton
onClick={() =>
handlePopUpOpen("deleteTagConfirmation", {
name: slug,
id
})
}
colorSchema="danger"
ariaLabel="update"
isDisabled={!isAllowed}
>
<FontAwesomeIcon icon={faTrashCan} />
</IconButton>
)}
</ProjectPermissionCan>
</Td>
</Tr>
))}
{!isLoading && data && data?.length === 0 && (
<Tr> <Tr>
<Td colSpan={3}> <Th className="w-full">
<EmptyState title="No secret tags found" icon={faTags} /> <div className="flex items-center">
</Td> Slug
<IconButton
variant="plain"
className="ml-2"
ariaLabel="sort"
onClick={toggleOrderDirection}
>
<FontAwesomeIcon
icon={orderDirection === OrderByDirection.DESC ? faArrowUp : faArrowDown}
/>
</IconButton>
</div>
</Th>
<Th aria-label="button" />
</Tr> </Tr>
)} </THead>
</TBody> <TBody>
</Table> {isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />}
</TableContainer> {!isLoading &&
filteredTags.slice(offset, perPage * page).map(({ id, slug }) => (
<Tr key={id}>
<Td>{slug}</Td>
<Td className="flex items-center justify-end">
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.Tags}
>
{(isAllowed) => (
<IconButton
onClick={() =>
handlePopUpOpen("deleteTagConfirmation", {
name: slug,
id
})
}
size="xs"
colorSchema="danger"
ariaLabel="update"
variant="plain"
isDisabled={!isAllowed}
>
<FontAwesomeIcon icon={faTrashCan} />
</IconButton>
)}
</ProjectPermissionCan>
</Td>
</Tr>
))}
</TBody>
</Table>
{Boolean(filteredTags.length) && (
<Pagination
count={filteredTags.length}
page={page}
perPage={perPage}
onChangePage={setPage}
onChangePerPage={setPerPage}
/>
)}
{!isLoading && !filteredTags?.length && (
<EmptyState
title={tags.length ? "No tags match search..." : "No tags found for project"}
icon={tags.length ? faSearch : faTag}
/>
)}
</TableContainer>
</div>
); );
}; };