From e461787c78b20129d3c0e9744f6216969b1d0b50 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Wed, 8 May 2024 23:24:33 +0800 Subject: [PATCH] feat: added support for renaming folders in the overview page --- .../SecretOverviewPage/SecretOverviewPage.tsx | 81 ++++++++++++++++++- .../SecretOverviewFolderRow.tsx | 18 ++++- 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx index fab7ebecf..e7517b37e 100644 --- a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx +++ b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx @@ -47,6 +47,7 @@ import { ProjectPermissionActions, ProjectPermissionSub, useOrganization, + useProjectPermission, useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; @@ -59,8 +60,10 @@ import { useGetImportedSecretsAllEnvs, useGetProjectSecretsAllEnv, useGetUserWsKey, + useUpdateFolder, useUpdateSecretV3 } from "@app/hooks/api"; +import { TSecretFolder } from "@app/hooks/api/types"; import { ProjectVersion } from "@app/hooks/api/workspace/types"; import { FolderForm } from "../SecretMainPage/components/ActionBar/FolderForm"; @@ -87,6 +90,7 @@ export const SecretOverviewPage = () => { const parentTableRef = useRef(null); const [expandableTableWidth, setExpandableTableWidth] = useState(0); const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); + const { permission } = useProjectPermission(); useEffect(() => { if (parentTableRef.current) { @@ -201,11 +205,13 @@ export const SecretOverviewPage = () => { const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3(); const { mutateAsync: createFolder } = useCreateFolder(); + const { mutateAsync: updateFolder } = useUpdateFolder(); const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([ "addSecretsInAllEnvs", "addFolder", - "misc" + "misc", + "updateFolder" ] as const); const handleFolderCreate = async (folderName: string) => { @@ -236,6 +242,64 @@ export const SecretOverviewPage = () => { } }; + const handleFolderUpdate = async (newFolderName: string) => { + const { name: oldFolderName } = popUp.updateFolder.data as TSecretFolder; + let processedUpdates = 0; + let totalFolderMatchCount = 0; + + await Promise.allSettled( + userAvailableEnvs.map(async (env) => { + if ( + permission.cannot( + ProjectPermissionActions.Edit, + subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath }) + ) + ) { + return; + } + + const folder = getFolderByNameAndEnv(oldFolderName, env.slug); + if (folder) { + totalFolderMatchCount += 1; + + await updateFolder({ + folderId: folder.id, + name: newFolderName, + path: secretPath, + environment: env.slug, + projectId: workspaceId + }); + + processedUpdates += 1; + } + }) + ); + + if (totalFolderMatchCount === 0) { + createNotification({ + type: "info", + text: "You don't have access to rename selected folder" + }); + } else if (processedUpdates === totalFolderMatchCount) { + createNotification({ + type: "success", + text: "Successfully renamed folder across environments" + }); + } else if (processedUpdates > 0) { + createNotification({ + type: "warning", + text: "Partially renamed folder across environments" + }); + } else { + createNotification({ + type: "error", + text: "Failed to rename folder across environments" + }); + } + + handlePopUpClose("updateFolder"); + }; + const handleSecretCreate = async (env: string, key: string, value: string) => { try { // create folder if not existing @@ -726,6 +790,9 @@ export const SecretOverviewPage = () => { environments={visibleEnvs} key={`overview-${folderName}-${index + 1}`} onClick={handleFolderClick} + onToggleFolderEdit={(name: string) => + handlePopUpOpen("updateFolder", { name }) + } /> ))} {!isTableLoading && @@ -800,6 +867,18 @@ export const SecretOverviewPage = () => { + handlePopUpToggle("updateFolder", isOpen)} + > + + )?.name} + onUpdateFolder={handleFolderUpdate} + /> + + ); }; diff --git a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx index c60aff912..2625d14c6 100644 --- a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx +++ b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx @@ -1,8 +1,8 @@ -import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faFolder, faPencil, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { twMerge } from "tailwind-merge"; -import { Checkbox, Td, Tr } from "@app/components/v2"; +import { Checkbox, IconButton, Td, Tr } from "@app/components/v2"; type Props = { folderName: string; @@ -11,6 +11,7 @@ type Props = { onClick: (path: string) => void; isSelected: boolean; onToggleFolderSelect: (folderName: string) => void; + onToggleFolderEdit: (name: string) => void; }; export const SecretOverviewFolderRow = ({ @@ -19,6 +20,7 @@ export const SecretOverviewFolderRow = ({ isFolderPresentInEnv, isSelected, onToggleFolderSelect, + onToggleFolderEdit, onClick }: Props) => { return ( @@ -43,6 +45,18 @@ export const SecretOverviewFolderRow = ({ />
{folderName}
+ { + onToggleFolderEdit(folderName); + e.stopPropagation(); + }} + > + + {environments.map(({ slug }, i) => {