diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 1b37bc2e61..8e21c711fc 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the **Prowler UI** are documented in this file. ## [v1.8.0] (Prowler v5.8.0) – Not released +### 🐞 Fixes + +- Fix sync between filter buttons and URL when filters change. [(#7928)](https://github.com/prowler-cloud/prowler/pull/7928) + ### 🚀 Added - New profile page with details about the user and their roles. [(#7780)](https://github.com/prowler-cloud/prowler/pull/7780) diff --git a/ui/components/ui/custom/custom-dropdown-filter.tsx b/ui/components/ui/custom/custom-dropdown-filter.tsx index 074c1f301a..6a9a4dafa9 100644 --- a/ui/components/ui/custom/custom-dropdown-filter.tsx +++ b/ui/components/ui/custom/custom-dropdown-filter.tsx @@ -14,10 +14,9 @@ import { ChevronDown, X } from "lucide-react"; import { useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { EntityInfoShort } from "@/components/ui/entities"; import { CustomDropdownFilterProps } from "@/types"; -import { EntityInfoShort } from "../entities"; - export const CustomDropdownFilter = ({ filter, onFilterChange, @@ -38,18 +37,86 @@ export const CustomDropdownFilter = ({ return filterParam ? filterParam.split(",") : []; }, [searchParams, filter?.key]); +<<<<<<< HEAD // Sync URL state with component state useEffect(() => { if (activeFilterValue.length > 0) { const newSelection = new Set(activeFilterValue); if (newSelection.size === filterValues.length) { +======= + // Helper function to handle URL filter values sync + const syncWithActiveFilters = useCallback(() => { + const newSelection = new Set(activeFilterValue); + if ( + newSelection.size === filterValues.length && + filter?.showSelectAll !== false + ) { + newSelection.add("all"); + } + setGroupSelected(newSelection); + }, [activeFilterValue, filterValues, filter?.showSelectAll]); + + const resetComponentState = useCallback(() => { + setGroupSelected(new Set()); + hasUserInteracted.current = false; + }, []); + + const applyDefaultValues = useCallback(() => { + if (filter?.defaultToSelectAll && filterValues.length > 0) { + const newSelection = new Set(filterValues); + if (filter?.showSelectAll !== false) { + newSelection.add("all"); + } + setGroupSelected(newSelection); + } else if (filter?.defaultValues && filter.defaultValues.length > 0) { + const validDefaultValues = filter.defaultValues.filter((value) => + filterValues.includes(value), + ); + const newSelection = new Set(validDefaultValues); + + // Add "all" if all items are selected and showSelectAll is not false + if ( + validDefaultValues.length === filterValues.length && + filter?.showSelectAll !== false + ) { +>>>>>>> c74360ab6 (fix: clear filters sync (#7928)) newSelection.add("all"); } setGroupSelected(newSelection); } else { setGroupSelected(new Set()); } +<<<<<<< HEAD }, [activeFilterValue, filterValues.length]); +======= + }, [ + filterValues, + filter?.defaultToSelectAll, + filter?.defaultValues, + filter?.showSelectAll, + ]); +>>>>>>> c74360ab6 (fix: clear filters sync (#7928)) + + useEffect(() => { + const hasActiveFilters = activeFilterValue.length > 0; + const userHasInteracted = hasUserInteracted.current; + + if (hasActiveFilters) { + // URL has filter values - sync component state with URL + syncWithActiveFilters(); + } else if (userHasInteracted) { + // URL has no filters but user had interacted - reset component state + resetComponentState(); + } else { + // URL has no filters and user hasn't interacted - apply defaults + applyDefaultValues(); + } + }, [ + activeFilterValue, + syncWithActiveFilters, + resetComponentState, + applyDefaultValues, + ]); const updateSelection = useCallback( (newValues: string[]) => { diff --git a/ui/hooks/use-url-filters.ts b/ui/hooks/use-url-filters.ts index c29de37327..83bd225791 100644 --- a/ui/hooks/use-url-filters.ts +++ b/ui/hooks/use-url-filters.ts @@ -16,8 +16,10 @@ export const useUrlFilters = () => { (key: string, value: string | string[] | null) => { const params = new URLSearchParams(searchParams.toString()); - // Always reset page to 1 when a filter is applied - params.set("page", "1"); + // Only reset page to 1 if page parameter already exists + if (params.has("page")) { + params.set("page", "1"); + } const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`; @@ -40,7 +42,11 @@ export const useUrlFilters = () => { const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`; params.delete(filterKey); - params.set("page", "1"); + + // Only reset page to 1 if page parameter already exists + if (params.has("page")) { + params.set("page", "1"); + } router.push(`${pathname}?${params.toString()}`, { scroll: false }); },