From b65689a616922755a7e7e2509a45610657239b4d Mon Sep 17 00:00:00 2001 From: x032205 Date: Wed, 10 Dec 2025 14:54:17 -0500 Subject: [PATCH] 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)}