From b65689a616922755a7e7e2509a45610657239b4d Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 10 Dec 2025 14:54:17 -0500 Subject: [PATCH 1/4] cards for flat account view --- .../components/v2/Pagination/Pagination.tsx | 6 +- .../components/AccountViewToggle.tsx | 25 ++- .../components/FolderBreadCrumbs.tsx | 4 +- .../components/PamAccountCard.tsx | 46 +++++ .../components/PamAccountRow.tsx | 14 +- .../components/PamAccountsTable.tsx | 189 ++++++++++-------- 6 files changed, 177 insertions(+), 107 deletions(-) create mode 100644 frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx diff --git a/frontend/src/components/v2/Pagination/Pagination.tsx b/frontend/src/components/v2/Pagination/Pagination.tsx index db33f6260..7b79e1ca5 100644 --- a/frontend/src/components/v2/Pagination/Pagination.tsx +++ b/frontend/src/components/v2/Pagination/Pagination.tsx @@ -49,7 +49,7 @@ export const Pagination = ({ return (
@@ -94,7 +94,7 @@ export const Pagination = ({ onClick={() => onChangePage(1)} isDisabled={!canGoFirst} > - + onChangePage(upperLimit)} isDisabled={!canGoLast} > - +
diff --git a/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx b/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx index 51e568551..671517914 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx @@ -1,4 +1,7 @@ -import { Button } from "@app/components/v2"; +import { faBorderAll, faList } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { IconButton } from "@app/components/v2"; import { PamAccountView } from "@app/hooks/api/pam"; type Props = { @@ -8,31 +11,33 @@ type Props = { export const AccountViewToggle = ({ value, onChange }: Props) => { return ( -
- - + +
); }; diff --git a/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx b/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx index f32e3f682..44a92054b 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx @@ -27,7 +27,7 @@ export const FolderBreadCrumbs = ({ path = "/" }: Props) => { return (
onFolderCrumbClick(0)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { @@ -48,7 +48,7 @@ export const FolderBreadCrumbs = ({ path = "/" }: Props) => { key={`path-${index + 1}`} className={`breadcrumb relative z-20 ${ index + 1 === arr.length ? "cursor-default" : "cursor-pointer" - } border-solid border-mineshaft-600 py-1 pr-2 pl-5 text-sm text-mineshaft-200`} + } border-mineshaft-600 text-mineshaft-200 border-solid py-1 pl-5 pr-2 text-sm`} onClick={() => onFolderCrumbClick(index + 1)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx new file mode 100644 index 000000000..5e34d59b6 --- /dev/null +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx @@ -0,0 +1,46 @@ +import { PAM_RESOURCE_TYPE_MAP, TPamAccount } from "@app/hooks/api/pam"; +import { LogInIcon } from "lucide-react"; + +type Props = { + account: TPamAccount; + onAccess: (resource: TPamAccount) => void; + accountPath?: string; +}; + +export const PamAccountCard = ({ account, onAccess, accountPath }: Props) => { + const { name, description } = account; + + const { image, name: resourceTypeName } = PAM_RESOURCE_TYPE_MAP[account.resource.resourceType]; + + return ( + + ); +}; diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountRow.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountRow.tsx index a2c668b27..c8196cf32 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountRow.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountRow.tsx @@ -10,7 +10,7 @@ import { } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { formatDistance } from "date-fns"; -import { FolderIcon, PackageOpenIcon } from "lucide-react"; +import { PackageOpenIcon } from "lucide-react"; import { twMerge } from "tailwind-merge"; import { createNotification } from "@app/components/notifications"; @@ -39,8 +39,6 @@ type Props = { onUpdate: (resource: TPamAccount) => void; onDelete: (resource: TPamAccount) => void; search: string; - isFlatView: boolean; - accountPath?: string; isAccessLoading?: boolean; }; @@ -50,8 +48,6 @@ export const PamAccountRow = ({ onAccess, onUpdate, onDelete, - isFlatView, - accountPath, isAccessLoading }: Props) => { const { id, name } = account; @@ -95,14 +91,6 @@ export const PamAccountRow = ({ - {isFlatView && accountPath && ( - - - - - - - )} {"lastRotatedAt" in account && account.lastRotatedAt && ( { setOrderDirection, setOrderBy } = usePagination(PamAccountOrderBy.Name, { - initPerPage: getUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, 20), + initPerPage: getUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, 16), initSearch }); @@ -231,10 +232,25 @@ export const PamAccountsTable = ({ projectId }: Props) => { const resources = resourcesData?.resources || []; + function accessAccount(account: TPamAccount) { + // For AWS IAM, directly open console without modal + if (account.resource.resourceType === PamResourceType.AwsIam) { + let fullAccountPath = account?.name; + const folderPath = account.folderId ? folderPaths[account.folderId] : undefined; + if (folderPath) { + const path = folderPath.replace(/^\/+|\/+$/g, ""); + fullAccountPath = `${path}/${account?.name}`; + } + + accessAwsIam(account, fullAccountPath); + } else { + handlePopUpOpen("accessAccount", account); + } + } + return ( -
- {accountView === PamAccountView.Nested && } -
+
+
{(isAllowed) => isAllowed && ( @@ -244,6 +260,12 @@ export const PamAccountsTable = ({ projectId }: Props) => { setPage(1); setFilter({ resourceIds: [] }); setAccountView(e); + + // Reset perPage to appropriate default for the view + const newPerPage = e === PamAccountView.Flat ? 16 : 10; + setPerPage(newPerPage); + setUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, newPerPage); + navigate({ search: (prev) => ({ ...prev, @@ -277,14 +299,14 @@ export const PamAccountsTable = ({ projectId }: Props) => { variant="plain" size="sm" className={twMerge( - "flex h-10 min-w-10 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 transition-all hover:border-primary/60 hover:bg-primary/10", + "border-mineshaft-600 bg-mineshaft-800 hover:border-primary/60 hover:bg-primary/10 flex h-10 min-w-10 items-center justify-center overflow-hidden border p-0 transition-all", isTableFiltered && "border-primary/50 text-primary" )} > - + Resource {resources.length ? ( resources.map((resource) => { @@ -359,7 +381,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { @@ -392,32 +414,55 @@ export const PamAccountsTable = ({ projectId }: Props) => { )}
- - - - - - - - - {isLoading && } - {!isLoading && ( - <> - {accountView !== PamAccountView.Flat && - foldersToRender.map((folder) => ( + {accountView === PamAccountView.Nested && } + {accountView === PamAccountView.Flat ? ( + <> +
+ {filteredAccounts.map((account) => ( + accessAccount(e)} + /> + ))} +
+ setPage(newPage)} + onChangePerPage={handlePerPageChange} + perPageList={[8, 12, 16, 20, 40]} + /> + + ) : ( + +
-
- Accounts - handleSort(PamAccountOrderBy.Name)} - > - - -
-
-
+ + + + + + + {isLoading && } + {!isLoading && ( + <> + {foldersToRender.map((folder) => ( { onDelete={(e) => handlePopUpOpen("deleteFolder", e)} /> ))} - {filteredAccounts.map((account) => ( - { - // For AWS IAM, directly open console without modal - if (e.resource.resourceType === PamResourceType.AwsIam) { - let fullAccountPath = e?.name; - const folderPath = e.folderId ? folderPaths[e.folderId] : undefined; - if (folderPath) { - const path = folderPath.replace(/^\/+|\/+$/g, ""); - fullAccountPath = `${path}/${e?.name}`; - } + {filteredAccounts.map((account) => ( + accessAccount(e)} + onUpdate={(e) => handlePopUpOpen("updateAccount", e)} + onDelete={(e) => handlePopUpOpen("deleteAccount", e)} + /> + ))} + + )} + +
+
+ Accounts + handleSort(PamAccountOrderBy.Name)} + > + + +
+
+
- accessAwsIam(e, fullAccountPath); - } else { - handlePopUpOpen("accessAccount", e); - } - }} - onUpdate={(e) => handlePopUpOpen("updateAccount", e)} - onDelete={(e) => handlePopUpOpen("deleteAccount", e)} - /> - ))} - - )} - - - {Boolean(totalCount) && !isLoading && ( - setPage(newPage)} - onChangePerPage={handlePerPageChange} - /> - )} - {!isLoading && isContentEmpty && ( - - )} -
+ {Boolean(totalCount) && !isLoading && ( + setPage(newPage)} + onChangePerPage={handlePerPageChange} + /> + )} + {!isLoading && isContentEmpty && ( + + )} + + )} handlePopUpToggle("deleteFolder", isOpen)} From 94629124d818ff038b3cb1b35143f6237d74a76e Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 10 Dec 2025 15:02:40 -0500 Subject: [PATCH 2/4] lint --- .../src/components/v2/Pagination/Pagination.tsx | 6 +++--- .../components/AccountViewToggle.tsx | 6 +++--- .../components/FolderBreadCrumbs.tsx | 4 ++-- .../PamAccountsPage/components/PamAccountCard.tsx | 13 +++++++------ .../PamAccountsPage/components/PamAccountsTable.tsx | 8 ++++---- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/v2/Pagination/Pagination.tsx b/frontend/src/components/v2/Pagination/Pagination.tsx index 7b79e1ca5..db33f6260 100644 --- a/frontend/src/components/v2/Pagination/Pagination.tsx +++ b/frontend/src/components/v2/Pagination/Pagination.tsx @@ -49,7 +49,7 @@ export const Pagination = ({ return (
@@ -94,7 +94,7 @@ export const Pagination = ({ onClick={() => onChangePage(1)} isDisabled={!canGoFirst} > - + onChangePage(upperLimit)} isDisabled={!canGoLast} > - +
diff --git a/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx b/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx index 671517914..4448845cf 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/AccountViewToggle.tsx @@ -11,7 +11,7 @@ type Props = { export const AccountViewToggle = ({ value, onChange }: Props) => { return ( -
+
{ @@ -21,7 +21,7 @@ export const AccountViewToggle = ({ value, onChange }: Props) => { size="xs" className={`${ value === PamAccountView.Flat ? "bg-mineshaft-500" : "bg-transparent" - } hover:bg-mineshaft-600 min-w-[2.4rem] rounded border-none`} + } min-w-[2.4rem] rounded border-none hover:bg-mineshaft-600`} > @@ -34,7 +34,7 @@ export const AccountViewToggle = ({ value, onChange }: Props) => { size="xs" className={`${ value === PamAccountView.Nested ? "bg-mineshaft-500" : "bg-transparent" - } hover:bg-mineshaft-600 min-w-[2.4rem] rounded border-none`} + } min-w-[2.4rem] rounded border-none hover:bg-mineshaft-600`} > diff --git a/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx b/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx index 44a92054b..f32e3f682 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/FolderBreadCrumbs.tsx @@ -27,7 +27,7 @@ export const FolderBreadCrumbs = ({ path = "/" }: Props) => { return (
onFolderCrumbClick(0)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { @@ -48,7 +48,7 @@ export const FolderBreadCrumbs = ({ path = "/" }: Props) => { key={`path-${index + 1}`} className={`breadcrumb relative z-20 ${ index + 1 === arr.length ? "cursor-default" : "cursor-pointer" - } border-mineshaft-600 text-mineshaft-200 border-solid py-1 pl-5 pr-2 text-sm`} + } border-solid border-mineshaft-600 py-1 pr-2 pl-5 text-sm text-mineshaft-200`} onClick={() => onFolderCrumbClick(index + 1)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx index 5e34d59b6..235607e9b 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx @@ -1,6 +1,7 @@ -import { PAM_RESOURCE_TYPE_MAP, TPamAccount } from "@app/hooks/api/pam"; import { LogInIcon } from "lucide-react"; +import { PAM_RESOURCE_TYPE_MAP, TPamAccount } from "@app/hooks/api/pam"; + type Props = { account: TPamAccount; onAccess: (resource: TPamAccount) => void; @@ -17,10 +18,10 @@ export const PamAccountCard = ({ account, onAccess, accountPath }: Props) => { onClick={() => onAccess(account)} type="button" key={account.id} - className="border-mineshaft-600 bg-mineshaft-800 hover:bg-mineshaft-700 flex cursor-pointer flex-col overflow-clip rounded-sm border p-4 text-start transition-transform duration-100 hover:scale-[103%]" + className="flex cursor-pointer flex-col overflow-clip rounded-sm border border-mineshaft-600 bg-mineshaft-800 p-4 text-start transition-transform duration-100 hover:scale-[103%] hover:bg-mineshaft-700" >
-
+
{resourceTypeName} {
-

{name}

- +

{name}

+

{

-

{description || "No description"}

+

{description || "No description"}

); }; diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx index 3150717c6..153680775 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx @@ -299,14 +299,14 @@ export const PamAccountsTable = ({ projectId }: Props) => { variant="plain" size="sm" className={twMerge( - "border-mineshaft-600 bg-mineshaft-800 hover:border-primary/60 hover:bg-primary/10 flex h-10 min-w-10 items-center justify-center overflow-hidden border p-0 transition-all", + "flex h-10 min-w-10 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 transition-all hover:border-primary/60 hover:bg-primary/10", isTableFiltered && "border-primary/50 text-primary" )} > - + Resource {resources.length ? ( resources.map((resource) => { @@ -381,7 +381,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { @@ -428,7 +428,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { ))}
Date: Wed, 10 Dec 2025 20:19:36 -0500 Subject: [PATCH 3/4] tweaks --- .../components/PamAccountCard.tsx | 38 ++++++++++--------- .../components/PamAccountsTable.tsx | 6 +-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx index 235607e9b..032483a63 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountCard.tsx @@ -1,6 +1,6 @@ -import { LogInIcon } from "lucide-react"; - +import { Badge, UnstableButton } from "@app/components/v3"; import { PAM_RESOURCE_TYPE_MAP, TPamAccount } from "@app/hooks/api/pam"; +import { LogInIcon, PackageOpenIcon } from "lucide-react"; type Props = { account: TPamAccount; @@ -9,39 +9,43 @@ type Props = { }; export const PamAccountCard = ({ account, onAccess, accountPath }: Props) => { - const { name, description } = account; + const { name, description, resource } = account; const { image, name: resourceTypeName } = PAM_RESOURCE_TYPE_MAP[account.resource.resourceType]; return ( ); }; diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx index 153680775..7c44a4fa9 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx @@ -262,7 +262,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { setAccountView(e); // Reset perPage to appropriate default for the view - const newPerPage = e === PamAccountView.Flat ? 16 : 10; + const newPerPage = e === PamAccountView.Flat ? 12 : 10; setPerPage(newPerPage); setUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, newPerPage); @@ -417,7 +417,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { {accountView === PamAccountView.Nested && } {accountView === PamAccountView.Flat ? ( <> -
+
{filteredAccounts.map((account) => ( { perPage={perPage} onChangePage={(newPage) => setPage(newPage)} onChangePerPage={handlePerPageChange} - perPageList={[8, 12, 16, 20, 40]} + perPageList={[9, 12, 15, 18, 27]} /> ) : ( From ca010af8286b8797d7021e17d626dfe2ff3f0e93 Mon Sep 17 00:00:00 2001 From: x032205 Date: Thu, 11 Dec 2025 17:52:10 -0500 Subject: [PATCH 4/4] address feedback --- .../components/PamAccountsTable.tsx | 51 ++++++++++--------- .../components/PamAddAccountModal.tsx | 1 + 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx index 7c44a4fa9..ea6e9e5f6 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountsTable.tsx @@ -129,7 +129,7 @@ export const PamAccountsTable = ({ projectId }: Props) => { setOrderDirection, setOrderBy } = usePagination(PamAccountOrderBy.Name, { - initPerPage: getUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, 16), + initPerPage: getUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, 18), initSearch }); @@ -260,12 +260,6 @@ export const PamAccountsTable = ({ projectId }: Props) => { setPage(1); setFilter({ resourceIds: [] }); setAccountView(e); - - // Reset perPage to appropriate default for the view - const newPerPage = e === PamAccountView.Flat ? 12 : 10; - setPerPage(newPerPage); - setUserTablePreference("pamAccountsTable", PreferenceKey.PerPage, newPerPage); - navigate({ search: (prev) => ({ ...prev, @@ -427,15 +421,24 @@ export const PamAccountsTable = ({ projectId }: Props) => { /> ))}
- setPage(newPage)} - onChangePerPage={handlePerPageChange} - perPageList={[9, 12, 15, 18, 27]} - /> + {!isLoading && isContentEmpty && ( + + )} + {Boolean(totalCount) && !isLoading && !isContentEmpty && ( + setPage(newPage)} + onChangePerPage={handlePerPageChange} + perPageList={[9, 18, 48, 99]} + /> + )} ) : ( @@ -487,20 +490,20 @@ export const PamAccountsTable = ({ projectId }: Props) => { )} - - {Boolean(totalCount) && !isLoading && ( + {!isLoading && isContentEmpty && ( + + )} + {Boolean(totalCount) && !isLoading && !isContentEmpty && ( setPage(newPage)} onChangePerPage={handlePerPageChange} - /> - )} - {!isLoading && isContentEmpty && ( - )} diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAddAccountModal.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAddAccountModal.tsx index 6074e572a..00ef0e1b7 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAddAccountModal.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAddAccountModal.tsx @@ -48,6 +48,7 @@ export const PamAddAccountModal = ({ onComplete={(account) => { if (onComplete) onComplete(account); onOpenChange(false); + setSelectedResource(null); }} onBack={() => setSelectedResource(null)} resourceId={selectedResource.id}