diff --git a/frontend/src/hooks/api/secretFolders/queries.tsx b/frontend/src/hooks/api/secretFolders/queries.tsx
index bcda2b0a4..71c63f3eb 100644
--- a/frontend/src/hooks/api/secretFolders/queries.tsx
+++ b/frontend/src/hooks/api/secretFolders/queries.tsx
@@ -94,7 +94,21 @@ export const useGetFoldersByEnv = ({
[(folders || []).map((folder) => folder.data)]
);
- return { folders, folderNames, isFolderPresentInEnv };
+ const getFolderByNameAndEnv = useCallback(
+ (name: string, env: string) => {
+ const selectedEnvIndex = environments.indexOf(env);
+ if (selectedEnvIndex !== -1) {
+ return folders?.[selectedEnvIndex]?.data?.find(
+ ({ name: folderName }) => folderName === name
+ );
+ }
+
+ return undefined;
+ },
+ [(folders || []).map((folder) => folder.data)]
+ );
+
+ return { folders, folderNames, isFolderPresentInEnv, getFolderByNameAndEnv };
};
export const useCreateFolder = () => {
diff --git a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx
index c6d97361a..cd00af883 100644
--- a/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx
+++ b/frontend/src/views/SecretOverviewPage/SecretOverviewPage.tsx
@@ -134,7 +134,8 @@ export const SecretOverviewPage = () => {
secretPath,
decryptFileKey: latestFileKey!
});
- const { folders, folderNames, isFolderPresentInEnv } = useGetFoldersByEnv({
+
+ const { folders, folderNames, isFolderPresentInEnv, getFolderByNameAndEnv } = useGetFoldersByEnv({
projectId: workspaceId,
path: secretPath,
environments: userAvailableEnvs.map(({ slug }) => slug)
@@ -548,7 +549,11 @@ export const SecretOverviewPage = () => {
-
+
diff --git a/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx b/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx
index 6b95ffccf..f68e804bf 100644
--- a/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx
+++ b/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx
@@ -3,17 +3,27 @@ import { faMinusSquare, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
+import { createNotification } from "@app/components/notifications";
import { Button, DeleteActionModal, IconButton, Tooltip } from "@app/components/v2";
-import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
+import {
+ ProjectPermissionActions,
+ ProjectPermissionSub,
+ useProjectPermission,
+ useWorkspace
+} from "@app/context";
import { usePopUp } from "@app/hooks";
+import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
+import { DecryptedSecret, TDeleteSecretBatchDTO, TSecretFolder } from "@app/hooks/api/types";
import { useSelectedEntries, useSelectedEntryActions } from "../../SecretOverviewPage.store";
type Props = {
secretPath: string;
+ getSecretByKey: (slug: string, key: string) => DecryptedSecret | undefined;
+ getFolderByNameAndEnv: (name: string, env: string) => TSecretFolder | undefined;
};
-export const SelectionPanel = ({ secretPath }: Props) => {
+export const SelectionPanel = ({ getFolderByNameAndEnv, getSecretByKey, secretPath }: Props) => {
const { permission } = useProjectPermission();
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
@@ -25,6 +35,12 @@ export const SelectionPanel = ({ secretPath }: Props) => {
const selectedCount =
Object.keys(selectedEntries.folder).length + Object.keys(selectedEntries.secret).length;
+ const { currentWorkspace } = useWorkspace();
+ const workspaceId = currentWorkspace?.id || "";
+ const userAvailableEnvs = currentWorkspace?.environments || [];
+ const { mutateAsync: deleteBatchSecretV3 } = useDeleteSecretBatch();
+ const { mutateAsync: deleteFolder } = useDeleteFolder();
+
const isMultiSelectActive = selectedCount > 0;
// TODO: REVISIT RBAC
@@ -34,7 +50,63 @@ export const SelectionPanel = ({ secretPath }: Props) => {
);
const handleBulkDelete = async () => {
- handlePopUpClose("bulkDeleteEntries");
+ const promises = userAvailableEnvs.map(async (env) => {
+ await Promise.all(
+ Object.keys(selectedEntries.folder).map(async (folderName) => {
+ const folder = getFolderByNameAndEnv(folderName, env.slug);
+ if (folder) {
+ await deleteFolder({
+ folderId: folder?.id,
+ path: secretPath,
+ environment: env.slug,
+ projectId: workspaceId
+ });
+ }
+ })
+ );
+
+ const secretsToDelete = Object.keys(selectedEntries.secret).reduce(
+ (accum: TDeleteSecretBatchDTO["secrets"], secretName) => {
+ const entry = getSecretByKey(env.slug, secretName);
+ if (entry) {
+ return [
+ ...accum,
+ {
+ secretName: entry.key,
+ type: "shared" as "shared"
+ }
+ ];
+ }
+ return accum;
+ },
+ []
+ );
+
+ if (secretsToDelete.length > 0) {
+ await deleteBatchSecretV3({
+ secretPath,
+ workspaceId,
+ environment: env.slug,
+ secrets: secretsToDelete
+ });
+ }
+ });
+
+ const results = await Promise.allSettled(promises);
+ const areEntriesDeleted = results.some((result) => result.status === "fulfilled");
+ if (areEntriesDeleted) {
+ handlePopUpClose("bulkDeleteEntries");
+ resetSelectedEntries();
+ createNotification({
+ type: "success",
+ text: "Successfully deleted selected secrets and folders"
+ });
+ } else {
+ createNotification({
+ type: "error",
+ text: "Failed to delete selected secrets and folders"
+ });
+ }
};
return (