From dab8f0b2610f4d4cacf4691d62c40a7bd3eac676 Mon Sep 17 00:00:00 2001
From: Scott Wilson <scottraywilson@gmail.com>
Date: Thu, 28 Nov 2024 14:29:41 -0800
Subject: [PATCH] improvement: secret tags table pagination

---
 .../UserProjectsSection/UserGroupsTable.tsx   |   5 +-
 .../SecretTagsSection/SecretTagsSection.tsx   |   3 +-
 .../SecretTagsSection/SecretTagsTable.tsx     | 175 +++++++++++++-----
 3 files changed, 130 insertions(+), 53 deletions(-)

diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
index 999ffe794..af136d7ff 100644
--- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
+++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
@@ -57,7 +57,7 @@ export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
   const filteredGroupMemberships = useMemo(
     () =>
       groupMemberships
-        ?.filter((group) => group.name.toLowerCase().includes(search.trim().toLowerCase()))
+        .filter((group) => group.name.toLowerCase().includes(search.trim().toLowerCase()))
         .sort((a, b) => {
           const [membershipOne, membershipTwo] =
             orderDirection === OrderByDirection.ASC ? [a, b] : [b, a];
@@ -72,13 +72,14 @@ export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
     offset,
     setPage
   });
+
   return (
     <div>
       <Input
         value={search}
         onChange={(e) => setSearch(e.target.value)}
         leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
-        placeholder="Search projects..."
+        placeholder="Search groups..."
       />
       <TableContainer className="mt-4">
         <Table>
diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx
index d1ba06835..26be8eb17 100644
--- a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx
+++ b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx
@@ -19,7 +19,6 @@ import { SecretTagsTable } from "./SecretTagsTable";
 type DeleteModalData = { name: string; id: string };
 
 export const SecretTagsSection = (): JSX.Element => {
-  
   const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
     "CreateSecretTag",
     "deleteTagConfirmation"
@@ -65,7 +64,7 @@ export const SecretTagsSection = (): JSX.Element => {
               }}
               isDisabled={!isAllowed}
             >
-              Create tag
+              Create Tag
             </Button>
           )}
         </ProjectPermissionCan>
diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsTable.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsTable.tsx
index cc68b0700..b6793ea1d 100644
--- a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsTable.tsx
+++ b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsTable.tsx
@@ -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 { ProjectPermissionCan } from "@app/components/permissions";
 import {
   EmptyState,
   IconButton,
+  Input,
+  Pagination,
   Table,
   TableContainer,
   TableSkeleton,
@@ -15,7 +25,9 @@ import {
   Tr
 } from "@app/components/v2";
 import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
+import { usePagination, useResetPageHelper } from "@app/hooks";
 import { useGetWsTags } from "@app/hooks/api";
+import { OrderByDirection } from "@app/hooks/api/generic/types";
 import { UsePopUpState } from "@app/hooks/usePopUp";
 
 type Props = {
@@ -31,59 +43,124 @@ type Props = {
   ) => void;
 };
 
+enum TagsOrderBy {
+  Slug = "slug"
+}
+
 export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
   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 (
-    <TableContainer className="mt-4">
-      <Table>
-        <THead>
-          <Tr>
-            <Th>Slug</Th>
-            <Th aria-label="button" />
-          </Tr>
-        </THead>
-        <TBody>
-          {isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />}
-          {!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 && (
+    <div>
+      <Input
+        value={search}
+        onChange={(e) => setSearch(e.target.value)}
+        leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
+        placeholder="Search tags..."
+      />
+      <TableContainer className="mt-4">
+        <Table>
+          <THead>
             <Tr>
-              <Td colSpan={3}>
-                <EmptyState title="No secret tags found" icon={faTags} />
-              </Td>
+              <Th className="w-full">
+                <div className="flex items-center">
+                  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>
-          )}
-        </TBody>
-      </Table>
-    </TableContainer>
+          </THead>
+          <TBody>
+            {isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />}
+            {!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>
   );
 };