mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 23:27:35 +00:00
Added secret reminders to sidebar (and formatting)
This commit is contained in:
+297
-239
@@ -5,6 +5,7 @@ import {
|
|||||||
faCheckCircle,
|
faCheckCircle,
|
||||||
faCircle,
|
faCircle,
|
||||||
faCircleDot,
|
faCircleDot,
|
||||||
|
faClock,
|
||||||
faPlus,
|
faPlus,
|
||||||
faTag
|
faTag
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
@@ -33,9 +34,11 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
|
||||||
|
import { useToggle } from "@app/hooks";
|
||||||
import { useGetSecretVersion } from "@app/hooks/api";
|
import { useGetSecretVersion } from "@app/hooks/api";
|
||||||
import { DecryptedSecret, UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
import { DecryptedSecret, UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
||||||
|
|
||||||
|
import { CreateReminderForm } from "./CreateReminderForm";
|
||||||
import { formSchema, SecretActionType, TFormSchema } from "./SecretListView.utils";
|
import { formSchema, SecretActionType, TFormSchema } from "./SecretListView.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -147,262 +150,317 @@ export const SecretDetailSidebar = ({
|
|||||||
await onSaveSecret(secret, { ...secret, ...data }, () => reset());
|
await onSaveSecret(secret, { ...secret, ...data }, () => reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [createReminderFormOpen, setCreateReminderFormOpen] = useToggle(false);
|
||||||
|
|
||||||
|
const secretReminderRepeatDays = watch("reminderRepeatDays");
|
||||||
|
const secretReminderNote = watch("reminderNote");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<>
|
||||||
onOpenChange={(state) => {
|
<CreateReminderForm
|
||||||
if (isOpen && isDirty) {
|
repeatDays={secretReminderRepeatDays}
|
||||||
if (
|
note={secretReminderNote}
|
||||||
// eslint-disable-next-line no-alert
|
isOpen={createReminderFormOpen}
|
||||||
window.confirm("You have edited the secret. Are you sure you want to reset the change?")
|
onOpenChange={(_, data) => {
|
||||||
) {
|
setCreateReminderFormOpen.toggle();
|
||||||
onToggle(false);
|
|
||||||
reset();
|
if (data) {
|
||||||
} else return;
|
setValue("reminderRepeatDays", data.days, { shouldDirty: true });
|
||||||
}
|
setValue("reminderNote", data.note, { shouldDirty: true });
|
||||||
onToggle(state);
|
}
|
||||||
}}
|
}}
|
||||||
isOpen={isOpen}
|
/>
|
||||||
>
|
<Drawer
|
||||||
<DrawerContent title="Secret">
|
onOpenChange={(state) => {
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)} className="h-full">
|
if (isOpen && isDirty) {
|
||||||
<div className="flex h-full flex-col">
|
if (
|
||||||
<FormControl label="Key">
|
// eslint-disable-next-line no-alert
|
||||||
<Input isDisabled {...register("key")} />
|
window.confirm(
|
||||||
</FormControl>
|
"You have edited the secret. Are you sure you want to reset the change?"
|
||||||
<ProjectPermissionCan
|
)
|
||||||
I={ProjectPermissionActions.Edit}
|
) {
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
onToggle(false);
|
||||||
>
|
reset();
|
||||||
{(isAllowed) => (
|
} else return;
|
||||||
<Controller
|
}
|
||||||
name="value"
|
onToggle(state);
|
||||||
key="secret-value"
|
}}
|
||||||
control={control}
|
isOpen={isOpen}
|
||||||
render={({ field }) => (
|
>
|
||||||
<FormControl label="Value">
|
<DrawerContent title="Secret">
|
||||||
<SecretInput
|
<form onSubmit={handleSubmit(handleFormSubmit)} className="h-full">
|
||||||
isReadOnly={isReadOnly}
|
<div className="flex h-full flex-col">
|
||||||
key="secret-value"
|
<FormControl label="Key">
|
||||||
isDisabled={isOverridden || !isAllowed}
|
<Input isDisabled {...register("key")} />
|
||||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
</FormControl>
|
||||||
{...field}
|
|
||||||
autoFocus={false}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<div className="mb-2 border-b border-mineshaft-600 pb-4">
|
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Switch
|
<Controller
|
||||||
isDisabled={!isAllowed}
|
name="value"
|
||||||
id="personal-override"
|
key="secret-value"
|
||||||
onCheckedChange={handleOverrideClick}
|
control={control}
|
||||||
isChecked={isOverridden}
|
render={({ field }) => (
|
||||||
>
|
<FormControl label="Value">
|
||||||
Override with a personal value
|
<SecretInput
|
||||||
</Switch>
|
isReadOnly={isReadOnly}
|
||||||
|
key="secret-value"
|
||||||
|
isDisabled={isOverridden || !isAllowed}
|
||||||
|
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
||||||
|
{...field}
|
||||||
|
autoFocus={false}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
</div>
|
<div className="mb-2 border-b border-mineshaft-600 pb-4">
|
||||||
{isOverridden && (
|
|
||||||
<Controller
|
|
||||||
name="valueOverride"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormControl label="Value Override">
|
|
||||||
<SecretInput
|
|
||||||
isReadOnly={isReadOnly}
|
|
||||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<FormControl label="Tags" className="">
|
|
||||||
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
|
|
||||||
{fields.map(({ tagColor, id: formId, name, id }) => (
|
|
||||||
<Tag
|
|
||||||
className="flex w-min items-center space-x-2"
|
|
||||||
key={formId}
|
|
||||||
onClose={() => {
|
|
||||||
if (cannotEditSecret) {
|
|
||||||
createNotification({ type: "error", text: "Access denied" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tag = tags?.find(({ id: tagId }) => id === tagId);
|
|
||||||
if (tag) handleTagSelect(tag);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="h-3 w-3 rounded-full"
|
|
||||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
|
||||||
/>
|
|
||||||
<div className="text-sm">{name}</div>
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
<DropdownMenu>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<IconButton
|
|
||||||
ariaLabel="add"
|
|
||||||
variant="outline_bg"
|
|
||||||
size="xs"
|
|
||||||
className="rounded-md"
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faPlus} />
|
|
||||||
</IconButton>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<DropdownMenuContent align="end" className="z-[100]">
|
|
||||||
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
|
||||||
{tags.map((tag) => {
|
|
||||||
const { id: tagId, name, color } = tag;
|
|
||||||
|
|
||||||
const isSelected = selectedTagsGroupById?.[tagId];
|
|
||||||
return (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleTagSelect(tag)}
|
|
||||||
key={tagId}
|
|
||||||
icon={isSelected && <FontAwesomeIcon icon={faCheckCircle} />}
|
|
||||||
iconPos="right"
|
|
||||||
>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div
|
|
||||||
className="mr-2 h-2 w-2 rounded-full"
|
|
||||||
style={{ background: color || "#bec2c8" }}
|
|
||||||
/>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Create}
|
|
||||||
a={ProjectPermissionSub.Tags}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Button
|
|
||||||
size="xs"
|
|
||||||
className="w-full"
|
|
||||||
colorSchema="primary"
|
|
||||||
variant="outline_bg"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faTag} />}
|
|
||||||
onClick={onCreateTag}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
>
|
|
||||||
Create a tag
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl label="Comments & Notes">
|
|
||||||
<TextArea
|
|
||||||
className="border border-mineshaft-600 text-sm"
|
|
||||||
{...register("comment")}
|
|
||||||
readOnly={isReadOnly}
|
|
||||||
rows={5}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<div className="my-2 mb-6 border-b border-mineshaft-600 pb-4">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="skipMultilineEncoding"
|
|
||||||
render={({ field: { value, onChange, onBlur } }) => (
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Edit}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<Switch
|
|
||||||
id="skipmultiencoding-option"
|
|
||||||
onCheckedChange={(isChecked) => onChange(!isChecked)}
|
|
||||||
isChecked={!value}
|
|
||||||
onBlur={onBlur}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
className="items-center"
|
|
||||||
>
|
|
||||||
Enable multi line encoding
|
|
||||||
<Tooltip
|
|
||||||
content="Infisical encodes multiline secrets by escaping newlines and wrapping in quotes. To disable, enable this option"
|
|
||||||
className="z-[100]"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" />
|
|
||||||
</Tooltip>
|
|
||||||
</Switch>
|
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="dark mb-4 flex-grow text-sm text-bunker-300">
|
|
||||||
<div className="mb-2">Version History</div>
|
|
||||||
<div className="flex h-48 flex-col space-y-2 overflow-y-auto overflow-x-hidden rounded-md border border-mineshaft-600 bg-bunker-800 p-2 dark:[color-scheme:dark]">
|
|
||||||
{secretVersion?.map(({ createdAt, value, id }, i) => (
|
|
||||||
<div key={id} className="flex flex-col space-y-1">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div>
|
|
||||||
<FontAwesomeIcon icon={i === 0 ? faCircleDot : faCircle} size="sm" />
|
|
||||||
</div>
|
|
||||||
<div>{format(new Date(createdAt), "Pp")}</div>
|
|
||||||
</div>
|
|
||||||
<div className="ml-1.5 flex items-center space-x-2 border-l border-bunker-300 pl-4">
|
|
||||||
<div className="self-start rounded-sm bg-primary-500/30 px-1">Value:</div>
|
|
||||||
<div className="break-all font-mono">{value}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col space-y-4">
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Edit}
|
I={ProjectPermissionActions.Edit}
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<Button
|
<Switch
|
||||||
isFullWidth
|
isDisabled={!isAllowed}
|
||||||
type="submit"
|
id="personal-override"
|
||||||
isDisabled={isSubmitting || !isDirty || !isAllowed}
|
onCheckedChange={handleOverrideClick}
|
||||||
isLoading={isSubmitting}
|
isChecked={isOverridden}
|
||||||
>
|
>
|
||||||
Save Changes
|
Override with a personal value
|
||||||
</Button>
|
</Switch>
|
||||||
)}
|
|
||||||
</ProjectPermissionCan>
|
|
||||||
<ProjectPermissionCan
|
|
||||||
I={ProjectPermissionActions.Delete}
|
|
||||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
|
||||||
>
|
|
||||||
{(isAllowed) => (
|
|
||||||
<Button colorSchema="danger" isDisabled={!isAllowed} onClick={onDeleteSecret}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
|
{isOverridden && (
|
||||||
|
<Controller
|
||||||
|
name="valueOverride"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormControl label="Value Override">
|
||||||
|
<SecretInput
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FormControl label="Tags" className="">
|
||||||
|
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
|
||||||
|
{fields.map(({ tagColor, id: formId, name, id }) => (
|
||||||
|
<Tag
|
||||||
|
className="flex w-min items-center space-x-2"
|
||||||
|
key={formId}
|
||||||
|
onClose={() => {
|
||||||
|
if (cannotEditSecret) {
|
||||||
|
createNotification({ type: "error", text: "Access denied" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tag = tags?.find(({ id: tagId }) => id === tagId);
|
||||||
|
if (tag) handleTagSelect(tag);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="h-3 w-3 rounded-full"
|
||||||
|
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
||||||
|
/>
|
||||||
|
<div className="text-sm">{name}</div>
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
<DropdownMenu>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="add"
|
||||||
|
variant="outline_bg"
|
||||||
|
size="xs"
|
||||||
|
className="rounded-md"
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPlus} />
|
||||||
|
</IconButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<DropdownMenuContent align="end" className="z-[100]">
|
||||||
|
<DropdownMenuLabel>Apply tags to this secrets</DropdownMenuLabel>
|
||||||
|
{tags.map((tag) => {
|
||||||
|
const { id: tagId, name, color } = tag;
|
||||||
|
|
||||||
|
const isSelected = selectedTagsGroupById?.[tagId];
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleTagSelect(tag)}
|
||||||
|
key={tagId}
|
||||||
|
icon={isSelected && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||||
|
iconPos="right"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div
|
||||||
|
className="mr-2 h-2 w-2 rounded-full"
|
||||||
|
style={{ background: color || "#bec2c8" }}
|
||||||
|
/>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Create}
|
||||||
|
a={ProjectPermissionSub.Tags}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuItem asChild>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
className="w-full"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faTag} />}
|
||||||
|
onClick={onCreateTag}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
Create a tag
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="Reminder">
|
||||||
|
{secretReminderRepeatDays && secretReminderRepeatDays > 0 ? (
|
||||||
|
<div className="mt-2 ml-1 flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<FontAwesomeIcon className="text-primary-500" icon={faClock} />
|
||||||
|
<span className="text-sm text-bunker-300">
|
||||||
|
Reminder every {secretReminderRepeatDays}{" "}
|
||||||
|
{secretReminderRepeatDays > 1 ? "days" : "day"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
className="px-2 py-1"
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => setCreateReminderFormOpen.on()}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-2 ml-1 flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
className="px-2 py-1"
|
||||||
|
variant="outline_bg"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faClock} />}
|
||||||
|
onClick={() => setCreateReminderFormOpen.on()}
|
||||||
|
>
|
||||||
|
Create Reminder
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="Comments & Notes">
|
||||||
|
<TextArea
|
||||||
|
className="border border-mineshaft-600 text-sm"
|
||||||
|
{...register("comment")}
|
||||||
|
readOnly={isReadOnly}
|
||||||
|
rows={5}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<div className="my-2 mb-6 border-b border-mineshaft-600 pb-4">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="skipMultilineEncoding"
|
||||||
|
render={({ field: { value, onChange, onBlur } }) => (
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Switch
|
||||||
|
id="skipmultiencoding-option"
|
||||||
|
onCheckedChange={(isChecked) => onChange(!isChecked)}
|
||||||
|
isChecked={!value}
|
||||||
|
onBlur={onBlur}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
className="items-center"
|
||||||
|
>
|
||||||
|
Enable multi line encoding
|
||||||
|
<Tooltip
|
||||||
|
content="Infisical encodes multiline secrets by escaping newlines and wrapping in quotes. To disable, enable this option"
|
||||||
|
className="z-[100]"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="dark mb-4 flex-grow text-sm text-bunker-300">
|
||||||
|
<div className="mb-2">Version History</div>
|
||||||
|
<div className="flex h-48 flex-col space-y-2 overflow-y-auto overflow-x-hidden rounded-md border border-mineshaft-600 bg-bunker-800 p-2 dark:[color-scheme:dark]">
|
||||||
|
{secretVersion?.map(({ createdAt, value, id }, i) => (
|
||||||
|
<div key={id} className="flex flex-col space-y-1">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div>
|
||||||
|
<FontAwesomeIcon icon={i === 0 ? faCircleDot : faCircle} size="sm" />
|
||||||
|
</div>
|
||||||
|
<div>{format(new Date(createdAt), "Pp")}</div>
|
||||||
|
</div>
|
||||||
|
<div className="ml-1.5 flex items-center space-x-2 border-l border-bunker-300 pl-4">
|
||||||
|
<div className="self-start rounded-sm bg-primary-500/30 px-1">Value:</div>
|
||||||
|
<div className="break-all font-mono">{value}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
isFullWidth
|
||||||
|
type="submit"
|
||||||
|
isDisabled={isSubmitting || !isDirty || !isAllowed}
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Delete}
|
||||||
|
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button colorSchema="danger" isDisabled={!isAllowed} onClick={onDeleteSecret}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
</DrawerContent>
|
||||||
</DrawerContent>
|
</Drawer>
|
||||||
</Drawer>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user