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({ > )}