mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 12:31:54 +00:00
fix: error about page number persistence when filters change (#7655)
This commit is contained in:
committed by
Pepe Fagoaga
parent
dc3d5149e9
commit
635b3f1978
@@ -8,12 +8,14 @@ import {
|
||||
} from "@internationalized/date";
|
||||
import { Button, ButtonGroup, DatePicker } from "@nextui-org/react";
|
||||
import { useLocale } from "@react-aria/i18n";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
|
||||
export const CustomDatePicker = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { updateFilter } = useUrlFilters();
|
||||
|
||||
const [value, setValue] = React.useState(() => {
|
||||
const dateParam = searchParams.get("filter[inserted_at]");
|
||||
@@ -28,15 +30,13 @@ export const CustomDatePicker = () => {
|
||||
|
||||
const applyDateFilter = useCallback(
|
||||
(date: any) => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (date) {
|
||||
params.set("filter[inserted_at]", date.toString());
|
||||
updateFilter("inserted_at", date.toString());
|
||||
} else {
|
||||
params.delete("filter[inserted_at]");
|
||||
updateFilter("inserted_at", null);
|
||||
}
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
},
|
||||
[router, searchParams],
|
||||
[updateFilter],
|
||||
);
|
||||
|
||||
const initialRender = useRef(true);
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
import { Input } from "@nextui-org/react";
|
||||
import debounce from "lodash.debounce";
|
||||
import { SearchIcon, XCircle } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export const CustomSearchInput: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
|
||||
export const CustomSearchInput: React.FC = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const { updateFilter } = useUrlFilters();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const applySearch = useCallback(
|
||||
(query: string) => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (query) {
|
||||
params.set("filter[search]", query);
|
||||
updateFilter("search", query);
|
||||
} else {
|
||||
params.delete("filter[search]");
|
||||
updateFilter("search", null);
|
||||
}
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
},
|
||||
[router, searchParams],
|
||||
[updateFilter],
|
||||
);
|
||||
|
||||
const debouncedChangeHandler = useCallback(
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
import { FilterControlsProps } from "@/types";
|
||||
|
||||
import { CrossIcon } from "../icons";
|
||||
@@ -24,8 +25,8 @@ export const FilterControls: React.FC<FilterControlsProps> = ({
|
||||
mutedFindings = false,
|
||||
customFilters,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { clearAllFilters } = useUrlFilters();
|
||||
const [showClearButton, setShowClearButton] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,16 +36,6 @@ export const FilterControls: React.FC<FilterControlsProps> = ({
|
||||
setShowClearButton(hasFilters);
|
||||
}, [searchParams]);
|
||||
|
||||
const clearAllFilters = useCallback(() => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
Array.from(params.keys()).forEach((key) => {
|
||||
if (key.startsWith("filter[") || key === "sort") {
|
||||
params.delete(key);
|
||||
}
|
||||
});
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
}, [router, searchParams]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-1 items-center gap-x-4 gap-y-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
|
||||
@@ -11,10 +11,11 @@ import {
|
||||
ScrollShadow,
|
||||
} from "@nextui-org/react";
|
||||
import { XCircle } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { PlusCircleIcon } from "@/components/icons";
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
import { CustomDropdownFilterProps } from "@/types";
|
||||
|
||||
const filterSelectedClass =
|
||||
@@ -24,8 +25,8 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
filter,
|
||||
onFilterChange,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { clearFilter } = useUrlFilters();
|
||||
const [groupSelected, setGroupSelected] = useState(new Set<string>());
|
||||
const [pendingClearFilter, setPendingClearFilter] = useState<string | null>(
|
||||
null,
|
||||
@@ -114,13 +115,11 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
|
||||
// Execute the update in the router after the render
|
||||
useEffect(() => {
|
||||
if (pendingClearFilter) {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
params.delete(`filter[${pendingClearFilter}]`);
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
if (pendingClearFilter && filter) {
|
||||
clearFilter(pendingClearFilter);
|
||||
setPendingClearFilter(null); // Reset the state
|
||||
}
|
||||
}, [pendingClearFilter, searchParams, router]);
|
||||
}, [pendingClearFilter, clearFilter, filter]);
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full flex-col gap-2">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import React, { useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { CustomFilterIcon } from "@/components/icons";
|
||||
import { CustomButton, CustomDropdownFilter } from "@/components/ui/custom";
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
import { FilterOption } from "@/types";
|
||||
|
||||
export interface DataTableFilterCustomProps {
|
||||
@@ -17,24 +17,14 @@ export const DataTableFilterCustom = ({
|
||||
filters,
|
||||
defaultOpen = false,
|
||||
}: DataTableFilterCustomProps) => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { updateFilter } = useUrlFilters();
|
||||
const [showFilters, setShowFilters] = useState(defaultOpen);
|
||||
|
||||
const pushDropdownFilter = useCallback(
|
||||
(key: string, values: string[]) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
const filterKey = `filter[${key}]`;
|
||||
|
||||
if (values.length === 0) {
|
||||
params.delete(filterKey);
|
||||
} else {
|
||||
params.set(filterKey, values.join(","));
|
||||
}
|
||||
|
||||
router.push(`?${params.toString()}`);
|
||||
updateFilter(key, values.length > 0 ? values : null);
|
||||
},
|
||||
[router, searchParams],
|
||||
[updateFilter],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* Custom hook to handle URL filters and automatically reset
|
||||
* pagination when filters change.
|
||||
*/
|
||||
export const useUrlFilters = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
const updateFilter = useCallback(
|
||||
(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");
|
||||
|
||||
const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`;
|
||||
|
||||
if (value === null || (Array.isArray(value) && value.length === 0)) {
|
||||
params.delete(filterKey);
|
||||
} else if (Array.isArray(value)) {
|
||||
params.set(filterKey, value.join(","));
|
||||
} else {
|
||||
params.set(filterKey, value);
|
||||
}
|
||||
|
||||
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||
},
|
||||
[router, searchParams, pathname],
|
||||
);
|
||||
|
||||
const clearFilter = useCallback(
|
||||
(key: string) => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`;
|
||||
|
||||
params.delete(filterKey);
|
||||
params.set("page", "1");
|
||||
|
||||
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||
},
|
||||
[router, searchParams, pathname],
|
||||
);
|
||||
|
||||
const clearAllFilters = useCallback(() => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
Array.from(params.keys()).forEach((key) => {
|
||||
if (key.startsWith("filter[") || key === "sort") {
|
||||
params.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
params.delete("page");
|
||||
|
||||
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||
}, [router, searchParams, pathname]);
|
||||
|
||||
return {
|
||||
updateFilter,
|
||||
clearFilter,
|
||||
clearAllFilters,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user