toggleSelectedEntry(EntryType.SECRET, key)}
secretPath={secretPath}
isImportedSecretPresentInEnv={isImportedSecretPresentInEnv}
onSecretCreate={handleSecretCreate}
diff --git a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx
index 597541fe7..c60aff912 100644
--- a/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx
+++ b/frontend/src/views/SecretOverviewPage/components/SecretOverviewFolderRow/SecretOverviewFolderRow.tsx
@@ -2,20 +2,23 @@ import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
-import { Td, Tr } from "@app/components/v2";
+import { Checkbox, Td, Tr } from "@app/components/v2";
type Props = {
folderName: string;
environments: { name: string; slug: string }[];
isFolderPresentInEnv: (name: string, env: string) => boolean;
onClick: (path: string) => void;
+ isSelected: boolean;
+ onToggleFolderSelect: (folderName: string) => void;
};
export const SecretOverviewFolderRow = ({
folderName,
environments = [],
isFolderPresentInEnv,
-
+ isSelected,
+ onToggleFolderSelect,
onClick
}: Props) => {
return (
@@ -23,7 +26,21 @@ export const SecretOverviewFolderRow = ({
-
+ {
+ onToggleFolderSelect(folderName);
+ }}
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
+ className={twMerge("hidden group-hover:flex", isSelected && "flex")}
+ />
+
{folderName}
diff --git a/frontend/src/views/SecretOverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx b/frontend/src/views/SecretOverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx
index 076ff22c8..f1476c167 100644
--- a/frontend/src/views/SecretOverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx
+++ b/frontend/src/views/SecretOverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx
@@ -11,7 +11,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
-import { Button, TableContainer, Td, Tooltip, Tr } from "@app/components/v2";
+import { Button, Checkbox, TableContainer, Td, Tooltip, Tr } from "@app/components/v2";
import { useToggle } from "@app/hooks";
import { DecryptedSecret } from "@app/hooks/api/secrets/types";
@@ -23,6 +23,8 @@ type Props = {
secretPath: string;
environments: { name: string; slug: string }[];
expandableColWidth: number;
+ isSelected: boolean;
+ onToggleSecretSelect: (key: string) => void;
getSecretByKey: (slug: string, key: string) => DecryptedSecret | undefined;
onSecretCreate: (env: string, key: string, value: string) => Promise;
onSecretUpdate: (env: string, key: string, value: string, secretId?: string) => Promise;
@@ -39,7 +41,9 @@ export const SecretOverviewTableRow = ({
onSecretCreate,
onSecretDelete,
isImportedSecretPresentInEnv,
- expandableColWidth
+ expandableColWidth,
+ onToggleSecretSelect,
+ isSelected
}: Props) => {
const [isFormExpanded, setIsFormExpanded] = useToggle();
const totalCols = environments.length + 1; // secret key row
@@ -56,7 +60,21 @@ export const SecretOverviewTableRow = ({
-
+ {
+ onToggleSecretSelect(secretKey);
+ }}
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
+ className={twMerge("hidden group-hover:flex", isSelected && "flex")}
+ />
+
{secretKey}
diff --git a/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx b/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx
new file mode 100644
index 000000000..6b95ffccf
--- /dev/null
+++ b/frontend/src/views/SecretOverviewPage/components/SelectionPanel/SelectionPanel.tsx
@@ -0,0 +1,78 @@
+import { subject } from "@casl/ability";
+import { faMinusSquare, faTrash } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { twMerge } from "tailwind-merge";
+
+import { Button, DeleteActionModal, IconButton, Tooltip } from "@app/components/v2";
+import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
+import { usePopUp } from "@app/hooks";
+
+import { useSelectedEntries, useSelectedEntryActions } from "../../SecretOverviewPage.store";
+
+type Props = {
+ secretPath: string;
+};
+
+export const SelectionPanel = ({ secretPath }: Props) => {
+ const { permission } = useProjectPermission();
+
+ const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
+ "bulkDeleteEntries"
+ ] as const);
+
+ const selectedEntries = useSelectedEntries();
+ const { reset: resetSelectedEntries } = useSelectedEntryActions();
+ const selectedCount =
+ Object.keys(selectedEntries.folder).length + Object.keys(selectedEntries.secret).length;
+
+ const isMultiSelectActive = selectedCount > 0;
+
+ // TODO: REVISIT RBAC
+ const shouldShowDelete = permission.can(
+ ProjectPermissionActions.Delete,
+ subject(ProjectPermissionSub.Secrets, { environment: "", secretPath })
+ );
+
+ const handleBulkDelete = async () => {
+ handlePopUpClose("bulkDeleteEntries");
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {selectedCount} Selected
+ {shouldShowDelete && (
+ }
+ className="ml-4"
+ onClick={() => handlePopUpOpen("bulkDeleteEntries")}
+ size="xs"
+ >
+ Delete
+
+ )}
+
+
+ handlePopUpToggle("bulkDeleteEntries", isOpen)}
+ onDeleteApproved={handleBulkDelete}
+ />
+ >
+ );
+};
|