feat(ui): completed ui for dynamic secret

This commit is contained in:
Akhil Mohan
2024-03-21 20:23:44 +05:30
parent 177cd385cc
commit e201e80a06
16 changed files with 1713 additions and 18 deletions
@@ -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}
/>
</>
);
};