From bf7ec15259c3cdc6e36e4cc3bbe64eea09f6fe0b Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:06:26 +0100 Subject: [PATCH] fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028) (cherry picked from commit 02f3e77eaf5d54ec9599ea2b097151502fa429f0) # Conflicts: # ui/CHANGELOG.md # ui/contexts/filter-transition-context.tsx # ui/hooks/use-url-filters.ts --- ui/CHANGELOG.md | 4 ++ ui/contexts/filter-transition-context.tsx | 9 ++++ ui/hooks/use-url-filters.ts | 57 +++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 6418e73304..a0f719a7f6 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -11,6 +11,10 @@ All notable changes to the **Prowler UI** are documented in this file. - Filter navigations not coordinating with Suspense boundaries due to missing startTransition in ProviderTypeSelector, AccountsSelector, and muted findings checkbox [(#10013)](https://github.com/prowler-cloud/prowler/pull/10013) - Scans page pagination not updating table data because ScansTableWithPolling kept stale state from initial mount [(#10013)](https://github.com/prowler-cloud/prowler/pull/10013) - Duplicate `filter[search]` parameter in findings and scans API calls [(#10013)](https://github.com/prowler-cloud/prowler/pull/10013) +<<<<<<< HEAD +======= +- All filters on `/findings` silently reverting on first click in production [(#10028)](https://github.com/prowler-cloud/prowler/pull/10028) +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) --- diff --git a/ui/contexts/filter-transition-context.tsx b/ui/contexts/filter-transition-context.tsx index cd529e0a3c..7d2075dc9b 100644 --- a/ui/contexts/filter-transition-context.tsx +++ b/ui/contexts/filter-transition-context.tsx @@ -39,6 +39,15 @@ interface FilterTransitionProviderProps { children: ReactNode; } +<<<<<<< HEAD +======= +/** + * Provides a shared pending state for filter changes. + * + * Filter navigation calls signalFilterChange() before router.push(). + * The pending state auto-resets when searchParams change. + */ +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) export const FilterTransitionProvider = ({ children, }: FilterTransitionProviderProps) => { diff --git a/ui/hooks/use-url-filters.ts b/ui/hooks/use-url-filters.ts index 911aa61558..daef0d28c6 100644 --- a/ui/hooks/use-url-filters.ts +++ b/ui/hooks/use-url-filters.ts @@ -5,6 +5,8 @@ import { useCallback, useTransition } from "react"; import { useFilterTransitionOptional } from "@/contexts"; +import { useFilterTransitionOptional } from "@/contexts"; + /** * Custom hook to handle URL filters and automatically reset * pagination when filters change. @@ -19,10 +21,23 @@ export const useUrlFilters = () => { const router = useRouter(); const searchParams = useSearchParams(); const pathname = usePathname(); +<<<<<<< HEAD // Use shared context if available, otherwise fall back to local transition const sharedTransition = useFilterTransitionOptional(); const [localIsPending, localStartTransition] = useTransition(); +======= + const filterTransition = useFilterTransitionOptional(); + + const navigate = (params: URLSearchParams) => { + const queryString = params.toString(); + if (queryString === searchParams.toString()) return; + + const targetUrl = queryString ? `${pathname}?${queryString}` : pathname; + filterTransition?.signalFilterChange(); + router.push(targetUrl, { scroll: false }); + }; +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) const isPending = sharedTransition?.isPending ?? localIsPending; const startTransition = @@ -43,6 +58,7 @@ export const useUrlFilters = () => { ? null : value; +<<<<<<< HEAD // If effective value is unchanged, do nothing (avoids redundant fetches) if (currentValue === nextValue) return; @@ -68,6 +84,11 @@ export const useUrlFilters = () => { (key: string) => { const params = new URLSearchParams(searchParams.toString()); const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`; +======= + // Always reset to first page when filters change. + // This also guarantees a query-string change on page 1 (no existing page param). + params.set("page", "1"); +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) params.delete(filterKey); @@ -83,7 +104,19 @@ export const useUrlFilters = () => { [router, searchParams, pathname, startTransition], ); +<<<<<<< HEAD const clearAllFilters = useCallback(() => { +======= + params.delete(filterKey); + + // Always reset to first page when filters change. + params.set("page", "1"); + + navigate(params); + }; + + const clearAllFilters = () => { +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) const params = new URLSearchParams(searchParams.toString()); Array.from(params.keys()).forEach((key) => { if (key.startsWith("filter[") || key === "sort") { @@ -103,13 +136,37 @@ export const useUrlFilters = () => { return Array.from(params.keys()).some( (key) => key.startsWith("filter[") || key === "sort", ); +<<<<<<< HEAD }, [searchParams]); +======= + }; + + /** + * Low-level navigation function for complex filter updates that need + * to modify multiple params atomically (e.g., setting provider_type + * while clearing provider_id). The modifier receives a mutable + * URLSearchParams; page is auto-reset if already present. + */ + const navigateWithParams = (modifier: (params: URLSearchParams) => void) => { + const params = new URLSearchParams(searchParams.toString()); + modifier(params); + + // Always reset to first page when filters change. + params.set("page", "1"); + + navigate(params); + }; +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) return { updateFilter, clearFilter, clearAllFilters, hasFilters, +<<<<<<< HEAD isPending, +======= + navigateWithParams, +>>>>>>> 02f3e77ea (fix(ui): re-integrate signalFilterChange into useUrlFilters and always reset page on filter change (#10028)) }; };