mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #3174 from Infisical/feat/addFolderDescription
Add descriptions to secret folders
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "@app/db/schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasProjectDescription = await knex.schema.hasColumn(TableName.SecretFolder, "description");
|
||||
|
||||
if (!hasProjectDescription) {
|
||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||
t.string("description");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasProjectDescription = await knex.schema.hasColumn(TableName.SecretFolder, "description");
|
||||
|
||||
if (hasProjectDescription) {
|
||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||
t.dropColumn("description");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@ export const SecretFoldersSchema = z.object({
|
||||
updatedAt: z.date(),
|
||||
envId: z.string().uuid(),
|
||||
parentId: z.string().uuid().nullable().optional(),
|
||||
isReserved: z.boolean().default(false).nullable().optional()
|
||||
isReserved: z.boolean().default(false).nullable().optional(),
|
||||
description: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
||||
|
||||
@@ -1142,6 +1142,7 @@ interface CreateFolderEvent {
|
||||
folderId: string;
|
||||
folderName: string;
|
||||
folderPath: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -638,7 +638,8 @@ export const FOLDERS = {
|
||||
environment: "The slug of the environment to create the folder in.",
|
||||
name: "The name of the folder to create.",
|
||||
path: "The path of the folder to create.",
|
||||
directory: "The directory of the folder to create. (Deprecated in favor of path)"
|
||||
directory: "The directory of the folder to create. (Deprecated in favor of path)",
|
||||
description: "An optional description label for the folder."
|
||||
},
|
||||
UPDATE: {
|
||||
folderId: "The ID of the folder to update.",
|
||||
@@ -647,7 +648,8 @@ export const FOLDERS = {
|
||||
path: "The path of the folder to update.",
|
||||
directory: "The new directory of the folder to update. (Deprecated in favor of path)",
|
||||
projectSlug: "The slug of the project where the folder is located.",
|
||||
workspaceId: "The ID of the project where the folder is located."
|
||||
workspaceId: "The ID of the project where the folder is located.",
|
||||
description: "An optional description label for the folder."
|
||||
},
|
||||
DELETE: {
|
||||
folderIdOrName: "The ID or name of the folder to delete.",
|
||||
|
||||
@@ -47,7 +47,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.default("/")
|
||||
.transform(prefixWithSlash)
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.CREATE.directory)
|
||||
.describe(FOLDERS.CREATE.directory),
|
||||
description: z.string().optional().nullable().describe(FOLDERS.CREATE.description)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -65,7 +66,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
projectId: req.body.workspaceId,
|
||||
path
|
||||
path,
|
||||
description: req.body.description
|
||||
});
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
@@ -76,7 +78,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
environment: req.body.environment,
|
||||
folderId: folder.id,
|
||||
folderName: folder.name,
|
||||
folderPath: path
|
||||
folderPath: path,
|
||||
...(req.body.description ? { description: req.body.description } : {})
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -125,7 +128,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.default("/")
|
||||
.transform(prefixWithSlash)
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.UPDATE.directory)
|
||||
.describe(FOLDERS.UPDATE.directory),
|
||||
description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -196,7 +200,8 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.default("/")
|
||||
.transform(prefixWithSlash)
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.UPDATE.path)
|
||||
.describe(FOLDERS.UPDATE.path),
|
||||
description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description)
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
|
||||
@@ -50,7 +50,8 @@ export const secretFolderServiceFactory = ({
|
||||
actorOrgId,
|
||||
name,
|
||||
environment,
|
||||
path: secretPath
|
||||
path: secretPath,
|
||||
description
|
||||
}: TCreateFolderDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
@@ -121,7 +122,10 @@ export const secretFolderServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const doc = await folderDAL.create({ name, envId: env.id, version: 1, parentId: parentFolderId }, tx);
|
||||
const doc = await folderDAL.create(
|
||||
{ name, envId: env.id, version: 1, parentId: parentFolderId, description },
|
||||
tx
|
||||
);
|
||||
await folderVersionDAL.create(
|
||||
{
|
||||
name: doc.name,
|
||||
@@ -170,7 +174,7 @@ export const secretFolderServiceFactory = ({
|
||||
const result = await folderDAL.transaction(async (tx) =>
|
||||
Promise.all(
|
||||
folders.map(async (newFolder) => {
|
||||
const { environment, path: secretPath, id, name } = newFolder;
|
||||
const { environment, path: secretPath, id, name, description } = newFolder;
|
||||
|
||||
const parentFolder = await folderDAL.findBySecretPath(project.id, environment, secretPath);
|
||||
if (!parentFolder) {
|
||||
@@ -217,7 +221,7 @@ export const secretFolderServiceFactory = ({
|
||||
|
||||
const [doc] = await folderDAL.update(
|
||||
{ envId: env.id, id: folder.id, parentId: parentFolder.id },
|
||||
{ name },
|
||||
{ name, description },
|
||||
tx
|
||||
);
|
||||
await folderVersionDAL.create(
|
||||
@@ -259,7 +263,8 @@ export const secretFolderServiceFactory = ({
|
||||
name,
|
||||
environment,
|
||||
path: secretPath,
|
||||
id
|
||||
id,
|
||||
description
|
||||
}: TUpdateFolderDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
@@ -312,7 +317,7 @@ export const secretFolderServiceFactory = ({
|
||||
const newFolder = await folderDAL.transaction(async (tx) => {
|
||||
const [doc] = await folderDAL.update(
|
||||
{ envId: env.id, id: folder.id, parentId: parentFolder.id, isReserved: false },
|
||||
{ name },
|
||||
{ name, description },
|
||||
tx
|
||||
);
|
||||
await folderVersionDAL.create(
|
||||
|
||||
@@ -9,6 +9,7 @@ export type TCreateFolderDTO = {
|
||||
environment: string;
|
||||
path: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TUpdateFolderDTO = {
|
||||
@@ -16,6 +17,7 @@ export type TUpdateFolderDTO = {
|
||||
path: string;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TUpdateManyFoldersDTO = {
|
||||
@@ -25,6 +27,7 @@ export type TUpdateManyFoldersDTO = {
|
||||
path: string;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
}[];
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
|
||||
@@ -148,12 +148,13 @@ export const useUpdateFolder = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<object, object, TUpdateFolderDTO>({
|
||||
mutationFn: async ({ path = "/", folderId, name, environment, projectId }) => {
|
||||
mutationFn: async ({ path = "/", folderId, name, environment, projectId, description }) => {
|
||||
const { data } = await apiRequest.patch(`/api/v1/folders/${folderId}`, {
|
||||
name,
|
||||
environment,
|
||||
workspaceId: projectId,
|
||||
path
|
||||
path,
|
||||
description
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum ReservedFolders {
|
||||
export type TSecretFolder = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type TGetProjectFoldersDTO = {
|
||||
@@ -24,6 +25,7 @@ export type TCreateFolderDTO = {
|
||||
environment: string;
|
||||
name: string;
|
||||
path?: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type TUpdateFolderDTO = {
|
||||
@@ -32,6 +34,7 @@ export type TUpdateFolderDTO = {
|
||||
name: string;
|
||||
folderId: string;
|
||||
path?: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type TDeleteFolderDTO = {
|
||||
@@ -49,5 +52,6 @@ export type TUpdateFolderBatchDTO = {
|
||||
environment: string;
|
||||
id: string;
|
||||
path?: string;
|
||||
description?: string | null;
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -2,13 +2,22 @@ import { useCallback, useMemo } from "react";
|
||||
|
||||
import { DashboardProjectSecretsOverview } from "@app/hooks/api/dashboard/types";
|
||||
|
||||
type FolderNameAndDescription = {
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export const useFolderOverview = (folders: DashboardProjectSecretsOverview["folders"]) => {
|
||||
const folderNames = useMemo(() => {
|
||||
const names = new Set<string>();
|
||||
const folderNamesAndDescriptions = useMemo(() => {
|
||||
const namesAndDescriptions = new Map<string, FolderNameAndDescription>();
|
||||
|
||||
folders?.forEach((folder) => {
|
||||
names.add(folder.name);
|
||||
if (!namesAndDescriptions.has(folder.name)) {
|
||||
namesAndDescriptions.set(folder.name, { name: folder.name, description: folder.description });
|
||||
}
|
||||
});
|
||||
return [...names];
|
||||
|
||||
return Array.from(namesAndDescriptions.values());
|
||||
}, [folders]);
|
||||
|
||||
const isFolderPresentInEnv = useCallback(
|
||||
@@ -31,7 +40,7 @@ export const useFolderOverview = (folders: DashboardProjectSecretsOverview["fold
|
||||
[folders]
|
||||
);
|
||||
|
||||
return { folderNames, isFolderPresentInEnv, getFolderByNameAndEnv };
|
||||
return { folderNamesAndDescriptions, isFolderPresentInEnv, getFolderByNameAndEnv };
|
||||
};
|
||||
|
||||
export const useDynamicSecretOverview = (
|
||||
|
||||
@@ -228,7 +228,7 @@ export const OverviewPage = () => {
|
||||
setPage
|
||||
});
|
||||
|
||||
const { folderNames, getFolderByNameAndEnv, isFolderPresentInEnv } = useFolderOverview(folders);
|
||||
const { folderNamesAndDescriptions, getFolderByNameAndEnv, isFolderPresentInEnv } = useFolderOverview(folders);
|
||||
|
||||
const { dynamicSecretNames, isDynamicSecretPresentInEnv } =
|
||||
useDynamicSecretOverview(dynamicSecrets);
|
||||
@@ -251,14 +251,15 @@ export const OverviewPage = () => {
|
||||
"updateFolder"
|
||||
] as const);
|
||||
|
||||
const handleFolderCreate = async (folderName: string) => {
|
||||
const handleFolderCreate = async (folderName: string, description: string | null) => {
|
||||
const promises = userAvailableEnvs.map((env) => {
|
||||
const environment = env.slug;
|
||||
return createFolder({
|
||||
name: folderName,
|
||||
path: secretPath,
|
||||
environment,
|
||||
projectId: workspaceId
|
||||
projectId: workspaceId,
|
||||
description
|
||||
});
|
||||
});
|
||||
|
||||
@@ -279,7 +280,7 @@ export const OverviewPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleFolderUpdate = async (newFolderName: string) => {
|
||||
const handleFolderUpdate = async (newFolderName: string, description: string | null) => {
|
||||
const { name: oldFolderName } = popUp.updateFolder.data as TSecretFolder;
|
||||
|
||||
const updatedFolders: TUpdateFolderBatchDTO["folders"] = [];
|
||||
@@ -296,7 +297,8 @@ export const OverviewPage = () => {
|
||||
environment: env.slug,
|
||||
name: newFolderName,
|
||||
id: folder.id,
|
||||
path: secretPath
|
||||
path: secretPath,
|
||||
description
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1027,7 +1029,7 @@ export const OverviewPage = () => {
|
||||
)}
|
||||
{!isOverviewLoading && visibleEnvs.length > 0 && (
|
||||
<>
|
||||
{folderNames.map((folderName, index) => (
|
||||
{folderNamesAndDescriptions.map(({name: folderName, description}, index) => (
|
||||
<SecretOverviewFolderRow
|
||||
folderName={folderName}
|
||||
isFolderPresentInEnv={isFolderPresentInEnv}
|
||||
@@ -1039,7 +1041,7 @@ export const OverviewPage = () => {
|
||||
key={`overview-${folderName}-${index + 1}`}
|
||||
onClick={handleFolderClick}
|
||||
onToggleFolderEdit={(name: string) =>
|
||||
handlePopUpOpen("updateFolder", { name })
|
||||
handlePopUpOpen("updateFolder", { name, description })
|
||||
}
|
||||
/>
|
||||
))}
|
||||
@@ -1159,7 +1161,9 @@ export const OverviewPage = () => {
|
||||
<FolderForm
|
||||
isEdit
|
||||
defaultFolderName={(popUp.updateFolder?.data as Pick<TSecretFolder, "name">)?.name}
|
||||
defaultDescription={(popUp.updateFolder?.data as Pick<TSecretFolder, "description">)?.description}
|
||||
onUpdateFolder={handleFolderUpdate}
|
||||
showDescriptionOverwriteWarning
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
@@ -128,13 +128,14 @@ export const ActionBar = ({
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const handleFolderCreate = async (folderName: string) => {
|
||||
const handleFolderCreate = async (folderName: string, description: string | null) => {
|
||||
try {
|
||||
await createFolder({
|
||||
name: folderName,
|
||||
path: secretPath,
|
||||
environment,
|
||||
projectId: workspaceId
|
||||
projectId: workspaceId,
|
||||
description
|
||||
});
|
||||
handlePopUpClose("addFolder");
|
||||
createNotification({
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { useRef } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Button, FormControl, Input, ModalClose } from "@app/components/v2";
|
||||
import { TextArea } from "@app/components/v2/TextArea/TextArea";
|
||||
|
||||
type Props = {
|
||||
onCreateFolder?: (folderName: string) => Promise<void>;
|
||||
onUpdateFolder?: (folderName: string) => Promise<void>;
|
||||
onCreateFolder?: (folderName: string, description: string | null) => Promise<void>;
|
||||
onUpdateFolder?: (folderName: string, description: string | null) => Promise<void>;
|
||||
isEdit?: boolean;
|
||||
defaultFolderName?: string;
|
||||
defaultDescription?: string;
|
||||
showDescriptionOverwriteWarning?: boolean;
|
||||
};
|
||||
|
||||
const descriptionOverwriteWarningMessage = "Warning: Any changes made here will overwrite any custom edits in individual environment folders."
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
@@ -18,15 +24,20 @@ const formSchema = z.object({
|
||||
.regex(
|
||||
/^[a-zA-Z0-9-_]+$/,
|
||||
"Folder name can only contain letters, numbers, dashes, and underscores"
|
||||
)
|
||||
),
|
||||
description: z
|
||||
.string()
|
||||
.optional()
|
||||
});
|
||||
type TFormData = z.infer<typeof formSchema>;
|
||||
|
||||
export const FolderForm = ({
|
||||
isEdit,
|
||||
defaultFolderName,
|
||||
defaultDescription,
|
||||
onCreateFolder,
|
||||
onUpdateFolder
|
||||
onUpdateFolder,
|
||||
showDescriptionOverwriteWarning = false
|
||||
}: Props): JSX.Element => {
|
||||
const {
|
||||
control,
|
||||
@@ -36,15 +47,32 @@ export const FolderForm = ({
|
||||
} = useForm<TFormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: defaultFolderName
|
||||
name: defaultFolderName,
|
||||
description: defaultDescription || ""
|
||||
}
|
||||
});
|
||||
|
||||
const onSubmit = async ({ name }: TFormData) => {
|
||||
const descriptionRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleInput = () => {
|
||||
const textarea = descriptionRef.current;
|
||||
if (textarea) {
|
||||
const lines = textarea.value.split("\n");
|
||||
const maxDescriptionLines = 10;
|
||||
|
||||
if (lines.length > maxDescriptionLines) {
|
||||
textarea.value = lines.slice(0, maxDescriptionLines).join("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async ({ name, description }: TFormData) => {
|
||||
const descriptionShaped = description && description.trim() !== "" ? description : null;
|
||||
|
||||
if (isEdit) {
|
||||
await onUpdateFolder?.(name);
|
||||
await onUpdateFolder?.(name, descriptionShaped);
|
||||
} else {
|
||||
await onCreateFolder?.(name);
|
||||
await onCreateFolder?.(name, descriptionShaped);
|
||||
}
|
||||
reset();
|
||||
};
|
||||
@@ -61,6 +89,31 @@ export const FolderForm = ({
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Folder Description"
|
||||
isError={Boolean(error)}
|
||||
tooltipText={showDescriptionOverwriteWarning ? descriptionOverwriteWarningMessage : undefined}
|
||||
isOptional
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
>
|
||||
<TextArea
|
||||
placeholder="Folder description"
|
||||
{...field}
|
||||
rows={3}
|
||||
ref={descriptionRef}
|
||||
onInput={handleInput}
|
||||
className="thin-scrollbar w-full !resize-none bg-mineshaft-900"
|
||||
maxLength={255}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button className="mr-4" type="submit" isDisabled={isSubmitting} isLoading={isSubmitting}>
|
||||
{isEdit ? "Save" : "Create"}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import { faClose, faFolder, faPencilSquare } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faClose, faFolder, faPencilSquare, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useDeleteFolder, useUpdateFolder } from "@app/hooks/api";
|
||||
import { TSecretFolder } from "@app/hooks/api/secretFolders/types";
|
||||
import { Tooltip } from "@app/components/v2/Tooltip/Tooltip";
|
||||
|
||||
import { FolderForm } from "../ActionBar/FolderForm";
|
||||
|
||||
@@ -42,7 +43,7 @@ export const FolderListView = ({
|
||||
const { mutateAsync: updateFolder } = useUpdateFolder();
|
||||
const { mutateAsync: deleteFolder } = useDeleteFolder();
|
||||
|
||||
const handleFolderUpdate = async (newFolderName: string) => {
|
||||
const handleFolderUpdate = async (newFolderName: string, newFolderDescription: string | null) => {
|
||||
try {
|
||||
const { id: folderId } = popUp.updateFolder.data as TSecretFolder;
|
||||
await updateFolder({
|
||||
@@ -50,7 +51,8 @@ export const FolderListView = ({
|
||||
name: newFolderName,
|
||||
path: secretPath,
|
||||
environment,
|
||||
projectId: workspaceId
|
||||
projectId: workspaceId,
|
||||
description: newFolderDescription
|
||||
});
|
||||
handlePopUpClose("updateFolder");
|
||||
createNotification({
|
||||
@@ -98,7 +100,7 @@ export const FolderListView = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{folders.map(({ name, id }) => (
|
||||
{folders.map(({ name, id, description }) => (
|
||||
<div
|
||||
key={id}
|
||||
className="group flex cursor-pointer border-b border-mineshaft-600 hover:bg-mineshaft-700"
|
||||
@@ -116,6 +118,16 @@ export const FolderListView = ({
|
||||
onClick={() => handleFolderClick(name)}
|
||||
>
|
||||
{name}
|
||||
{
|
||||
description &&
|
||||
<Tooltip
|
||||
position="right"
|
||||
className="flex items-center space-x-4 max-w-lg py-4 whitespace-pre-wrap"
|
||||
content={description}
|
||||
>
|
||||
<FontAwesomeIcon icon={faInfoCircle} className="text-mineshaft-400 ml-1" />
|
||||
</Tooltip>
|
||||
}
|
||||
</div>
|
||||
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
|
||||
<ProjectPermissionCan
|
||||
@@ -130,7 +142,7 @@ export const FolderListView = ({
|
||||
variant="plain"
|
||||
size="sm"
|
||||
className="p-0 opacity-0 group-hover:opacity-100"
|
||||
onClick={() => handlePopUpOpen("updateFolder", { id, name })}
|
||||
onClick={() => handlePopUpOpen("updateFolder", { id, name, description })}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPencilSquare} size="lg" />
|
||||
@@ -167,6 +179,7 @@ export const FolderListView = ({
|
||||
<FolderForm
|
||||
isEdit
|
||||
defaultFolderName={(popUp.updateFolder?.data as TSecretFolder)?.name}
|
||||
defaultDescription={(popUp.updateFolder?.data as TSecretFolder)?.description}
|
||||
onUpdateFolder={handleFolderUpdate}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
Reference in New Issue
Block a user