mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-19 07:33:18 +00:00
feat(ui): completed ui for dynamic secret
This commit is contained in:
+259
@@ -0,0 +1,259 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faClose,
|
||||
faFingerprint,
|
||||
faInfoCircle,
|
||||
faPencilSquare
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
IconButton,
|
||||
Modal,
|
||||
ModalContent,
|
||||
Tag,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useDeleteDynamicSecret } from "@app/hooks/api";
|
||||
import {
|
||||
DynamicSecretProviders,
|
||||
DynamicSecretStatus,
|
||||
TDynamicSecret
|
||||
} from "@app/hooks/api/dynamicSecret/types";
|
||||
|
||||
import { SortDir } from "../../SecretMainPage.types";
|
||||
import { CreateDynamicSecretLease } from "./CreateDynamicSecretLease";
|
||||
import { DynamicSecretLease } from "./DynamicSecretLease";
|
||||
import { EditDynamicSecretForm } from "./EditDynamicSecretForm";
|
||||
|
||||
const formatProviderName = (type: DynamicSecretProviders) => {
|
||||
if (type === DynamicSecretProviders.SqlDatabase) return "SQL Database";
|
||||
return "";
|
||||
};
|
||||
|
||||
type Props = {
|
||||
dynamicSecrets: TDynamicSecret[];
|
||||
environment: string;
|
||||
workspaceId: string;
|
||||
secretPath?: string;
|
||||
sortDir: SortDir;
|
||||
};
|
||||
|
||||
export const DynamicSecretListView = ({
|
||||
dynamicSecrets = [],
|
||||
environment,
|
||||
workspaceId,
|
||||
secretPath = "/",
|
||||
sortDir = SortDir.ASC
|
||||
}: Props) => {
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
"dynamicSecretLeases",
|
||||
"createDynamicSecretLease",
|
||||
"updateDynamicSecret",
|
||||
"deleteDynamicSecret"
|
||||
] as const);
|
||||
|
||||
const { createNotification } = useNotificationContext();
|
||||
const deleteDynamicSecret = useDeleteDynamicSecret();
|
||||
|
||||
const handleDynamicSecretDelete = async () => {
|
||||
try {
|
||||
const { slug } = popUp.deleteDynamicSecret.data as TDynamicSecret;
|
||||
await deleteDynamicSecret.mutateAsync({
|
||||
environment,
|
||||
projectId: workspaceId,
|
||||
path: secretPath,
|
||||
slug
|
||||
});
|
||||
handlePopUpClose("deleteDynamicSecret");
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted secret"
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to delete secret"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{dynamicSecrets
|
||||
.sort((a, b) =>
|
||||
sortDir === SortDir.ASC
|
||||
? a.slug.toLowerCase().localeCompare(b.slug.toLowerCase())
|
||||
: b.slug.toLowerCase().localeCompare(a.slug.toLowerCase())
|
||||
)
|
||||
.map((secret) => {
|
||||
const isRevocking = secret.status === DynamicSecretStatus.Deleting;
|
||||
return (
|
||||
<Modal
|
||||
key={secret.id}
|
||||
isOpen={
|
||||
popUp.dynamicSecretLeases.isOpen && popUp.dynamicSecretLeases.data === secret.id
|
||||
}
|
||||
onOpenChange={(state) => handlePopUpToggle("dynamicSecretLeases", state)}
|
||||
>
|
||||
<div
|
||||
className="group flex cursor-pointer border-b border-mineshaft-600 hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(evt) => {
|
||||
if (evt.key === "Enter" && !isRevocking)
|
||||
handlePopUpOpen("dynamicSecretLeases", secret.id);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!isRevocking) {
|
||||
handlePopUpOpen("dynamicSecretLeases", secret.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex w-11 items-center px-5 py-3 text-yellow-700">
|
||||
<FontAwesomeIcon icon={faFingerprint} />
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-grow items-center px-4 py-3 uppercase"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{secret.slug}{" "}
|
||||
<Tag className="ml-4 py-0 px-2 text-xs normal-case">
|
||||
{formatProviderName(secret.type)}
|
||||
</Tag>
|
||||
{Boolean(secret.status) && (
|
||||
<Tooltip content={secret?.statusDetails || secret.status || ""}>
|
||||
<FontAwesomeIcon
|
||||
className="relative bottom-2 text-red-600"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center px-4 py-2">
|
||||
<Button
|
||||
size="xs"
|
||||
className="m-0 py-0.5 px-2 opacity-0 group-hover:opacity-100"
|
||||
isDisabled={isRevocking}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
handlePopUpOpen("createDynamicSecretLease", secret);
|
||||
}}
|
||||
>
|
||||
lease
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
renderTooltip
|
||||
allowedLabel="Edit"
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
ariaLabel="edit-dynamic-secret"
|
||||
variant="plain"
|
||||
size="sm"
|
||||
className="p-0 opacity-0 group-hover:opacity-100"
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
handlePopUpOpen("updateDynamicSecret", secret);
|
||||
}}
|
||||
isDisabled={!isAllowed || isRevocking}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPencilSquare} size="lg" />
|
||||
</IconButton>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
renderTooltip
|
||||
allowedLabel="Delete"
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
ariaLabel="delete-dynamic-secret"
|
||||
variant="plain"
|
||||
size="md"
|
||||
className="p-0 opacity-0 group-hover:opacity-100"
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
handlePopUpOpen("deleteDynamicSecret", secret);
|
||||
}}
|
||||
isDisabled={!isAllowed || isRevocking}
|
||||
>
|
||||
<FontAwesomeIcon icon={faClose} size="lg" />
|
||||
</IconButton>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
<ModalContent
|
||||
title="Dynamic secret leases"
|
||||
subTitle="Revoke or renew your secret leases"
|
||||
className="max-w-3xl"
|
||||
>
|
||||
<DynamicSecretLease
|
||||
onClickNewLease={() => handlePopUpOpen("createDynamicSecretLease", secret)}
|
||||
onClose={() => handlePopUpClose("dynamicSecretLeases")}
|
||||
projectId={workspaceId}
|
||||
key={secret.id}
|
||||
slug={secret.slug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
})}
|
||||
<Modal
|
||||
isOpen={popUp.createDynamicSecretLease.isOpen}
|
||||
onOpenChange={(state) => handlePopUpToggle("createDynamicSecretLease", state)}
|
||||
>
|
||||
<ModalContent title="Provision lease">
|
||||
<CreateDynamicSecretLease
|
||||
provider={
|
||||
(popUp.createDynamicSecretLease?.data as { type: DynamicSecretProviders })?.type
|
||||
}
|
||||
onClose={() => handlePopUpClose("createDynamicSecretLease")}
|
||||
projectId={workspaceId}
|
||||
slug={(popUp.createDynamicSecretLease?.data as { slug: string })?.slug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={popUp.updateDynamicSecret.isOpen}
|
||||
onOpenChange={(state) => handlePopUpToggle("updateDynamicSecret", state)}
|
||||
>
|
||||
<ModalContent title="Edit dynamic secret" className="max-w-3xl">
|
||||
<EditDynamicSecretForm
|
||||
onClose={() => handlePopUpClose("updateDynamicSecret")}
|
||||
projectId={workspaceId}
|
||||
slug={(popUp.updateDynamicSecret?.data as TDynamicSecret)?.slug}
|
||||
secretPath={secretPath}
|
||||
environment={environment}
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteDynamicSecret.isOpen}
|
||||
deleteKey={(popUp.deleteDynamicSecret?.data as TDynamicSecret)?.slug}
|
||||
title="Do you want to delete this dynamc secret?"
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteDynamicSecret", isOpen)}
|
||||
onDeleteApproved={handleDynamicSecretDelete}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user