diff --git a/ui/actions/mute-rules/mute-rules.ts b/ui/actions/mute-rules/mute-rules.ts
index ea0fd77695..3ec972b0cf 100644
--- a/ui/actions/mute-rules/mute-rules.ts
+++ b/ui/actions/mute-rules/mute-rules.ts
@@ -252,16 +252,15 @@ export const updateMuteRule = async (
});
if (!response.ok) {
+ let errorMessage = `Failed to update mute rule: ${response.statusText}`;
try {
const errorData = await response.json();
- throw new Error(
- errorData?.errors?.[0]?.detail ||
- errorData?.message ||
- `Failed to update mute rule: ${response.statusText}`,
- );
+ errorMessage =
+ errorData?.errors?.[0]?.detail || errorData?.message || errorMessage;
} catch {
- throw new Error(`Failed to update mute rule: ${response.statusText}`);
+ // JSON parsing failed, use default error message
}
+ throw new Error(errorMessage);
}
revalidatePath("/mutelist");
@@ -306,16 +305,15 @@ export const toggleMuteRule = async (
});
if (!response.ok) {
+ let errorMessage = `Failed to toggle mute rule: ${response.statusText}`;
try {
const errorData = await response.json();
- throw new Error(
- errorData?.errors?.[0]?.detail ||
- errorData?.message ||
- `Failed to toggle mute rule: ${response.statusText}`,
- );
+ errorMessage =
+ errorData?.errors?.[0]?.detail || errorData?.message || errorMessage;
} catch {
- throw new Error(`Failed to toggle mute rule: ${response.statusText}`);
+ // JSON parsing failed, use default error message
}
+ throw new Error(errorMessage);
}
revalidatePath("/mutelist");
diff --git a/ui/app/(prowler)/mutelist/_components/simple/mute-rules-table.tsx b/ui/app/(prowler)/mutelist/_components/simple/mute-rules-table.tsx
index 82ca215b33..519afc3590 100644
--- a/ui/app/(prowler)/mutelist/_components/simple/mute-rules-table.tsx
+++ b/ui/app/(prowler)/mutelist/_components/simple/mute-rules-table.tsx
@@ -3,13 +3,23 @@ import { Info } from "lucide-react";
import { getMuteRules } from "@/actions/mute-rules";
import { Card, Skeleton } from "@/components/shadcn";
import { DataTable } from "@/components/ui/table";
+import { SearchParamsProps } from "@/types/components";
import { muteRulesColumns } from "./mute-rules-columns";
-export async function MuteRulesTable() {
+interface MuteRulesTableProps {
+ searchParams: SearchParamsProps;
+}
+
+export async function MuteRulesTable({ searchParams }: MuteRulesTableProps) {
+ const page = parseInt(searchParams.page?.toString() || "1", 10);
+ const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10);
+ const sort = searchParams.sort?.toString() || "-inserted_at";
+
const muteRulesData = await getMuteRules({
- pageSize: 50,
- sort: "-inserted_at",
+ page,
+ pageSize,
+ sort,
});
const muteRules = muteRulesData?.data || [];
@@ -55,7 +65,15 @@ export async function MuteRulesTable() {
Toggle rules on/off to enable or disable muting.
-
+
);
}
diff --git a/ui/app/(prowler)/mutelist/page.tsx b/ui/app/(prowler)/mutelist/page.tsx
index bf8501f5a8..97fd2733c2 100644
--- a/ui/app/(prowler)/mutelist/page.tsx
+++ b/ui/app/(prowler)/mutelist/page.tsx
@@ -1,17 +1,25 @@
import { Suspense } from "react";
import { ContentLayout } from "@/components/ui";
+import { SearchParamsProps } from "@/types/components";
import { MuteRulesTable, MuteRulesTableSkeleton } from "./_components/simple";
import { MutelistTabs } from "./mutelist-tabs";
-export default function MutelistPage() {
+export default async function MutelistPage({
+ searchParams,
+}: {
+ searchParams: Promise;
+}) {
+ const resolvedSearchParams = await searchParams;
+ const searchParamsKey = JSON.stringify(resolvedSearchParams);
+
return (
}>
-
+ }>
+
}
/>
diff --git a/ui/components/compliance/compliance-accordion/client-accordion-content.tsx b/ui/components/compliance/compliance-accordion/client-accordion-content.tsx
index ab54c022e8..965df073a1 100644
--- a/ui/components/compliance/compliance-accordion/client-accordion-content.tsx
+++ b/ui/components/compliance/compliance-accordion/client-accordion-content.tsx
@@ -159,9 +159,11 @@ export const ClientAccordionContent = ({
Findings
index !== 0 && index !== 6,
+ (col) =>
+ col.id !== "select" &&
+ !("accessorKey" in col && col.accessorKey === "updated_at"),
)}
data={expandedFindings || []}
metadata={findings?.meta}
diff --git a/ui/components/filters/custom-checkbox-muted-findings.tsx b/ui/components/filters/custom-checkbox-muted-findings.tsx
index d21bda2877..9af7722957 100644
--- a/ui/components/filters/custom-checkbox-muted-findings.tsx
+++ b/ui/components/filters/custom-checkbox-muted-findings.tsx
@@ -4,6 +4,12 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Checkbox } from "@/components/shadcn";
+// Constants for muted filter URL values
+const MUTED_FILTER_VALUES = {
+ EXCLUDE: "false",
+ INCLUDE: "include",
+} as const;
+
export const CustomCheckboxMutedFindings = () => {
const router = useRouter();
const pathname = usePathname();
@@ -16,7 +22,7 @@ export const CustomCheckboxMutedFindings = () => {
// URL states:
// - filter[muted]=false → Exclude muted (checkbox UNCHECKED)
// - filter[muted]=include → Include muted (checkbox CHECKED)
- const includeMuted = mutedFilterValue === "include";
+ const includeMuted = mutedFilterValue === MUTED_FILTER_VALUES.INCLUDE;
const handleMutedChange = (checked: boolean | "indeterminate") => {
const isChecked = checked === true;
@@ -24,10 +30,10 @@ export const CustomCheckboxMutedFindings = () => {
if (isChecked) {
// Include muted: set special value (API will ignore invalid value and show all)
- params.set("filter[muted]", "include");
+ params.set("filter[muted]", MUTED_FILTER_VALUES.INCLUDE);
} else {
// Exclude muted: apply filter to show only non-muted
- params.set("filter[muted]", "false");
+ params.set("filter[muted]", MUTED_FILTER_VALUES.EXCLUDE);
}
// Reset to page 1 when changing filter
diff --git a/ui/components/findings/floating-mute-button.tsx b/ui/components/findings/floating-mute-button.tsx
index f99a512d28..5b76d65fcc 100644
--- a/ui/components/findings/floating-mute-button.tsx
+++ b/ui/components/findings/floating-mute-button.tsx
@@ -4,14 +4,12 @@ import { VolumeX } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/shadcn";
-import { FindingProps } from "@/types";
import { MuteFindingsModal } from "./mute-findings-modal";
interface FloatingMuteButtonProps {
selectedCount: number;
selectedFindingIds: string[];
- selectedFindings: FindingProps[];
onComplete?: () => void;
}
diff --git a/ui/components/findings/table/findings-table-with-selection.tsx b/ui/components/findings/table/findings-table-with-selection.tsx
index b819d081d6..3c9d732937 100644
--- a/ui/components/findings/table/findings-table-with-selection.tsx
+++ b/ui/components/findings/table/findings-table-with-selection.tsx
@@ -28,19 +28,22 @@ export function FindingsTableWithSelection({
setRowSelection({});
}, [metadata?.pagination?.page]);
+ // Ensure data is always an array for safe operations
+ const safeData = data ?? [];
+
// Get selected finding IDs and data (only non-muted findings can be selected)
const selectedFindingIds = Object.keys(rowSelection)
.filter((key) => rowSelection[key])
- .map((idx) => data[parseInt(idx)]?.id)
+ .map((idx) => safeData[parseInt(idx)]?.id)
.filter(Boolean);
const selectedFindings = Object.keys(rowSelection)
.filter((key) => rowSelection[key])
- .map((idx) => data[parseInt(idx)])
+ .map((idx) => safeData[parseInt(idx)])
.filter(Boolean);
// Count of selectable rows (non-muted findings only)
- const selectableRowCount = data.filter((f) => !f.attributes.muted).length;
+ const selectableRowCount = safeData.filter((f) => !f.attributes.muted).length;
// Function to determine if a row can be selected (muted findings cannot be selected)
const getRowCanSelect = (row: Row): boolean => {
@@ -75,7 +78,7 @@ export function FindingsTableWithSelection({
>
)}