diff --git a/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningFindingRow.tsx b/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningFindingRow.tsx
index 7f47bf6c1..f2aea5a5f 100644
--- a/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningFindingRow.tsx
+++ b/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningFindingRow.tsx
@@ -9,6 +9,7 @@ import { ProjectPermissionCan } from "@app/components/permissions";
import {
Badge,
Button,
+ Checkbox,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
@@ -34,11 +35,18 @@ import {
} from "@app/hooks/api/secretScanningV2";
type Props = {
+ isSelected: boolean;
+ onToggleSelect: (e: boolean) => void;
finding: TSecretScanningFinding;
onUpdate: (finding: TSecretScanningFinding) => void;
};
-export const SecretScanningFindingRow = ({ finding, onUpdate }: Props) => {
+export const SecretScanningFindingRow = ({
+ isSelected,
+ onToggleSelect,
+ finding,
+ onUpdate
+}: Props) => {
const {
resourceName,
id,
@@ -84,6 +92,16 @@ export const SecretScanningFindingRow = ({ finding, onUpdate }: Props) => {
)}
key={`resource-${id}`}
>
+
+ onToggleSelect(!isSelected)}
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
+ />
+ |
![]() {
status: initStatus ? [initStatus] : []
});
+ const [selectedRows, setSelectedRows] = useState ([]);
+
const {
search,
setSearch,
@@ -276,6 +285,20 @@ export const SecretScanningFindingsTable = ({ findings }: Props) => {
+ |
+
+ setSelectedRows((sr) =>
+ sr.length === filteredFindings.length ? [] : filteredFindings.map((f) => f.id)
+ )
+ }
+ onClick={(e) => {
+ e.stopPropagation();
+ }}
+ />
+ |
Platform |
@@ -335,9 +358,15 @@ export const SecretScanningFindingsTable = ({ findings }: Props) => {
{filteredFindings.slice(offset, perPage * page).map((finding) => (
+ v
+ ? setSelectedRows((sr) => [...sr, finding.id])
+ : setSelectedRows((sr) => sr.filter((r) => r !== finding.id))
+ }
key={finding.id}
finding={finding}
- onUpdate={() => handlePopUpOpen("updateFinding", finding)}
+ onUpdate={() => handlePopUpOpen("updateFinding", [finding])}
/>
))}
@@ -365,8 +394,37 @@ export const SecretScanningFindingsTable = ({ findings }: Props) => {
handlePopUpToggle("updateFinding", isOpen)}
- finding={popUp.updateFinding.data}
+ findings={popUp.updateFinding.data}
/>
+ {selectedRows.length > 0 && (
+
+
+ {selectedRows.length} finding{selectedRows.length === 1 ? "" : "s"} selected
+
+
+
+
+ {(isAllowed) => (
+
+ )}
+
+
+
+ )}
);
};
diff --git a/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningUpdateFindingModal.tsx b/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningUpdateFindingModal.tsx
index 3651ae3c2..c4e04df91 100644
--- a/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningUpdateFindingModal.tsx
+++ b/frontend/src/pages/secret-scanning/SecretScanningFindingsPage/components/SecretScanningUpdateFindingModal.tsx
@@ -21,52 +21,60 @@ import {
} from "@app/hooks/api/secretScanningV2";
type Props = {
- finding?: TSecretScanningFinding;
+ findings?: TSecretScanningFinding[];
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
};
const FormSchema = z.object({
remarks: z.string().max(256, "Cannot exceed 256 characters").optional(),
- status: z.nativeEnum(SecretScanningFindingStatus)
+ status: z.nativeEnum(SecretScanningFindingStatus).optional()
});
type FormType = z.infer;
type ContentProps = {
- finding: TSecretScanningFinding;
+ findings: TSecretScanningFinding[];
onComplete: () => void;
};
-const Content = ({ finding, onComplete }: ContentProps) => {
+const Content = ({ findings, onComplete }: ContentProps) => {
const updateFinding = useUpdateSecretScanningFinding();
+ const single = findings.length === 1;
+
const { handleSubmit, control } = useForm({
resolver: zodResolver(FormSchema),
defaultValues: {
- status: finding.status,
- remarks: finding.remarks ?? ""
+ status: single ? findings[0].status : undefined,
+ remarks: single ? findings[0].remarks : undefined
}
});
const onSubmit = async (data: FormType) => {
try {
- await updateFinding.mutateAsync({
- ...data,
- findingId: finding.id,
- projectId: finding.projectId
- });
+ for await (const finding of findings) {
+ // If a status wasn't set, don't update
+ if (data.status) {
+ await updateFinding.mutateAsync({
+ ...data,
+ status: data.status,
+ findingId: finding.id,
+ projectId: finding.projectId
+ });
+ }
+ }
createNotification({
type: "success",
- text: "Finding status successfully updated"
+ text: `Finding status${single ? "" : "es"} successfully updated`
});
onComplete();
} catch {
createNotification({
type: "error",
- text: "Failed to update Finding status"
+ text: `Failed to update finding status${single ? "" : "es"}`
});
}
};
@@ -81,12 +89,15 @@ const Content = ({ finding, onComplete }: ContentProps) => {
| |