improve access account styling, add folder path badge to flat view,

increase log gap
This commit is contained in:
x032205
2025-10-03 13:04:36 -04:00
parent db6e30a0bf
commit 178a5494f2
4 changed files with 61 additions and 36 deletions
@@ -1,10 +1,11 @@
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { faCopy } from "@fortawesome/free-regular-svg-icons";
import { faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons"; import { faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import ms from "ms"; import ms from "ms";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, FormLabel, Input, Modal, ModalClose, ModalContent } from "@app/components/v2"; import { FormLabel, IconButton, Input, Modal, ModalContent } from "@app/components/v2";
import { PamResourceType, TPamAccount } from "@app/hooks/api/pam"; import { PamResourceType, TPamAccount } from "@app/hooks/api/pam";
type Props = { type Props = {
@@ -28,21 +29,10 @@ export const PamAccessAccountModal = ({ isOpen, onOpenChange, account }: Props)
if (!account) return null; if (!account) return null;
const copyCommand = () => {
navigator.clipboard.writeText(command);
createNotification({
text: "Command copied to clipboard",
type: "info"
});
onOpenChange(false);
};
return ( return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}> <Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent <ModalContent
className="max-w-2xl" className="max-w-2xl pb-2"
title="Access Account" title="Access Account"
subTitle={`Access ${account.name} using a CLI command.`} subTitle={`Access ${account.name} using a CLI command.`}
> >
@@ -57,7 +47,27 @@ export const PamAccessAccountModal = ({ isOpen, onOpenChange, account }: Props)
isError={!isDurationValid} isError={!isDurationValid}
/> />
<FormLabel label="CLI Command" className="mt-4" /> <FormLabel label="CLI Command" className="mt-4" />
<Input value={command} isDisabled /> <div className="flex gap-2">
<Input value={command} isDisabled className="opacity-50" />
<IconButton
ariaLabel="copy"
variant="outline_bg"
colorSchema="secondary"
onClick={() => {
navigator.clipboard.writeText(command);
createNotification({
text: "Command copied to clipboard",
type: "info"
});
onOpenChange(false);
}}
className="w-10"
>
<FontAwesomeIcon icon={faCopy} />
</IconButton>
</div>
<a <a
href="https://infisical.com/docs/cli/overview" href="https://infisical.com/docs/cli/overview"
target="_blank" target="_blank"
@@ -67,22 +77,6 @@ export const PamAccessAccountModal = ({ isOpen, onOpenChange, account }: Props)
<span>Install the Infisical CLI</span> <span>Install the Infisical CLI</span>
<FontAwesomeIcon icon={faUpRightFromSquare} className="size-3" /> <FontAwesomeIcon icon={faUpRightFromSquare} className="size-3" />
</a> </a>
<div className="mt-6 flex items-center">
<Button
isDisabled={!isDurationValid}
className="mr-4"
size="sm"
colorSchema="secondary"
onClick={copyCommand}
>
Copy Command
</Button>
<ModalClose asChild>
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</ModalClose>
</div>
</ModalContent> </ModalContent>
</Modal> </Modal>
); );
@@ -5,6 +5,7 @@ import {
faCopy, faCopy,
faEdit, faEdit,
faEllipsisV, faEllipsisV,
faFolder,
faRightToBracket, faRightToBracket,
faTrash faTrash
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
@@ -37,9 +38,19 @@ type Props = {
onUpdate: (resource: TPamAccount) => void; onUpdate: (resource: TPamAccount) => void;
onDelete: (resource: TPamAccount) => void; onDelete: (resource: TPamAccount) => void;
search: string; search: string;
isFlatView: boolean;
accountPath?: string;
}; };
export const PamAccountRow = ({ account, search, onAccess, onUpdate, onDelete }: Props) => { export const PamAccountRow = ({
account,
search,
onAccess,
onUpdate,
onDelete,
isFlatView,
accountPath
}: Props) => {
const { id, name } = account; const { id, name } = account;
const { image, name: resourceTypeName } = PAM_RESOURCE_TYPE_MAP[account.resource.resourceType]; const { image, name: resourceTypeName } = PAM_RESOURCE_TYPE_MAP[account.resource.resourceType];
@@ -81,6 +92,14 @@ export const PamAccountRow = ({ account, search, onAccess, onUpdate, onDelete }:
<HighlightText text={account.resource.name} highlight={search} /> <HighlightText text={account.resource.name} highlight={search} />
</span> </span>
</Badge> </Badge>
{isFlatView && accountPath && (
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-bunker-300/20 text-bunker-300">
<FontAwesomeIcon icon={faFolder} />
<span>
<HighlightText text={accountPath} highlight={search} />
</span>
</Badge>
)}
</div> </div>
</div> </div>
</Td> </Td>
@@ -114,10 +114,11 @@ export const PamAccountsTable = ({ accounts, folders, projectId }: Props) => {
setOrderBy setOrderBy
} = usePagination<OrderBy>(OrderBy.Name, { initPerPage: 20, initSearch }); } = usePagination<OrderBy>(OrderBy.Name, { initPerPage: 20, initSearch });
const { foldersByParentId, pathMap } = useMemo(() => { const { foldersByParentId, pathMap, folderPaths } = useMemo(() => {
const foldersById: Record<string, TPamFolder> = {}; const foldersById: Record<string, TPamFolder> = {};
const tempFoldersByParentId: Record<string, TPamFolder[]> = { null: [] }; const tempFoldersByParentId: Record<string, TPamFolder[]> = { null: [] };
const tempPathMap: Record<string, string> = { "/": "null" }; const tempPathMap: Record<string, string> = { "/": "null" };
const tempFolderPaths: Record<string, string> = {};
folders.forEach((folder) => { folders.forEach((folder) => {
foldersById[folder.id] = folder; foldersById[folder.id] = folder;
@@ -131,13 +132,18 @@ export const PamAccountsTable = ({ accounts, folders, projectId }: Props) => {
(tempFoldersByParentId[parentId || "null"] || []).forEach((folder) => { (tempFoldersByParentId[parentId || "null"] || []).forEach((folder) => {
const newPath = `${currentPath}${folder.name}/`; const newPath = `${currentPath}${folder.name}/`;
tempPathMap[newPath] = folder.id; tempPathMap[newPath] = folder.id;
tempFolderPaths[folder.id] = newPath;
buildPaths(folder.id, newPath); buildPaths(folder.id, newPath);
}); });
}; };
buildPaths(null, "/"); buildPaths(null, "/");
return { foldersByParentId: tempFoldersByParentId, pathMap: tempPathMap }; return {
foldersByParentId: tempFoldersByParentId,
pathMap: tempPathMap,
folderPaths: tempFolderPaths
};
}, [folders]); }, [folders]);
const effectiveFolderIdForFiltering = useMemo(() => { const effectiveFolderIdForFiltering = useMemo(() => {
@@ -181,11 +187,13 @@ export const PamAccountsTable = ({ accounts, folders, projectId }: Props) => {
} }
const searchValue = search.trim().toLowerCase(); const searchValue = search.trim().toLowerCase();
const path = (account.folderId && folderPaths[account.folderId]) || "";
return ( return (
name.toLowerCase().includes(searchValue) || name.toLowerCase().includes(searchValue) ||
resourceName.toLowerCase().includes(searchValue) || resourceName.toLowerCase().includes(searchValue) ||
(description || "").toLowerCase().includes(searchValue) (description || "").toLowerCase().includes(searchValue) ||
path.toLowerCase().includes(searchValue)
); );
}) })
.sort((a, b) => { .sort((a, b) => {
@@ -197,7 +205,7 @@ export const PamAccountsTable = ({ accounts, folders, projectId }: Props) => {
return accOne.name.toLowerCase().localeCompare(accTwo.name.toLowerCase()); return accOne.name.toLowerCase().localeCompare(accTwo.name.toLowerCase());
} }
}), }),
[accountsToProcess, orderDirection, search, orderBy, filters] [accountsToProcess, orderDirection, search, orderBy, filters, folderPaths]
); );
useResetPageHelper({ useResetPageHelper({
@@ -434,6 +442,10 @@ export const PamAccountsTable = ({ accounts, folders, projectId }: Props) => {
key={account.id} key={account.id}
account={account} account={account}
search={search} search={search}
isFlatView={accountView === AccountView.Flat}
accountPath={
account.folderId ? folderPaths[account.folderId]?.slice(0, -1) : undefined
}
onAccess={(e) => { onAccess={(e) => {
handlePopUpOpen("accessAccount", e); handlePopUpOpen("accessAccount", e);
}} }}
@@ -13,7 +13,7 @@ export const PamSessionLogsSection = ({ session }: Props) => {
<div className="flex items-center border-b border-mineshaft-400 pb-4"> <div className="flex items-center border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Session Logs</h3> <h3 className="text-lg font-semibold text-mineshaft-100">Session Logs</h3>
</div> </div>
<div className="flex grow flex-col gap-4 overflow-y-auto text-xs"> <div className="flex grow flex-col gap-8 overflow-y-auto text-xs">
{session.commandLogs.length > 0 ? ( {session.commandLogs.length > 0 ? (
session.commandLogs.map((log) => ( session.commandLogs.map((log) => (
<div key={log.timestamp} className="flex flex-col"> <div key={log.timestamp} className="flex flex-col">