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) => {
)}
-
-
-
-
- |
-
- Accounts
- handleSort(PamAccountOrderBy.Name)}
- >
-
-
-
- |
- |
-
-
-
- {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)}
+ />
+ ))}
+ >
+ )}
+
+
- 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)}