mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat: filters, search and sorting is working as expected
This commit is contained in:
@@ -26,11 +26,13 @@ export const getProvider = async ({
|
||||
|
||||
// Handle multiple filters
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, String(value));
|
||||
if (key !== "filter[search]") {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
console.log({ query });
|
||||
|
||||
try {
|
||||
const providers = await fetch(`${url.toString()}`, {
|
||||
const providers = await fetch(url.toString(), {
|
||||
headers: {
|
||||
"X-Tenant-ID": `${tenantId}`,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Spacer } from "@nextui-org/react";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProvider } from "@/actions";
|
||||
@@ -41,25 +40,24 @@ const SSRDataTable = async ({
|
||||
}: {
|
||||
searchParams: SearchParamsProps;
|
||||
}) => {
|
||||
const query = searchParams?.query || "";
|
||||
const page = searchParams?.page || "1";
|
||||
const sort = searchParams?.sort || "";
|
||||
const page = parseInt(searchParams.page?.toString() || "1", 10);
|
||||
const sort = searchParams.sort?.toString() || "";
|
||||
|
||||
// Extract all filter parameters
|
||||
const filters = Object.fromEntries(
|
||||
Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")),
|
||||
);
|
||||
|
||||
const providersData = await getProvider({ query, page, sort, filters });
|
||||
const [providers] = await Promise.all([providersData]);
|
||||
// Extract query from filters
|
||||
const query = (filters["filter[search]"] as string) || "";
|
||||
|
||||
if (providers?.errors) redirect("/providers");
|
||||
const providersData = await getProvider({ query, page, sort, filters });
|
||||
|
||||
return (
|
||||
<DataTableProvider
|
||||
columns={ColumnsProvider}
|
||||
data={providers?.data ?? []}
|
||||
metadata={providers?.meta}
|
||||
data={providersData.data}
|
||||
metadata={providersData.meta}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,7 +27,8 @@ export const DataTableColumnHeader = <TData, TValue>({
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const getToggleSortingHandler = () => {
|
||||
const currentSortParam = searchParams.get("sort");
|
||||
const currentParams = new URLSearchParams(searchParams.toString());
|
||||
const currentSortParam = currentParams.get("sort");
|
||||
let newSortParam = "";
|
||||
|
||||
if (currentSortParam === `${param}`) {
|
||||
@@ -41,13 +42,21 @@ export const DataTableColumnHeader = <TData, TValue>({
|
||||
newSortParam = `${param}`;
|
||||
}
|
||||
|
||||
// Construct the new URL with the sorting parameter
|
||||
const newUrl = newSortParam ? `${pathname}?sort=${newSortParam}` : pathname;
|
||||
// Update or remove the sort parameter
|
||||
if (newSortParam) {
|
||||
currentParams.set("sort", newSortParam);
|
||||
} else {
|
||||
currentParams.delete("sort");
|
||||
}
|
||||
|
||||
// Construct the new URL with all parameters
|
||||
const newUrl = `${pathname}?${currentParams.toString()}`;
|
||||
|
||||
router.push(newUrl, {
|
||||
scroll: false,
|
||||
});
|
||||
};
|
||||
|
||||
const renderSortIcon = () => {
|
||||
const currentSortParam = searchParams.get("sort");
|
||||
if (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button, Input } from "@nextui-org/react";
|
||||
import { XCircle } from "lucide-react"; // Import the clear icon
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
@@ -11,7 +12,10 @@ interface FilterColumnTableProps {
|
||||
export function FilterColumnTable({ filters }: FilterColumnTableProps) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [searchQuery, setSearchQuery] = useState(() => {
|
||||
// Initialize searchQuery with the current search filter value from URL
|
||||
return searchParams.get("filter[search]") || "";
|
||||
});
|
||||
const debouncedSearchQuery = useDebounce(searchQuery, 300);
|
||||
|
||||
const activeFilters = useMemo(() => {
|
||||
@@ -54,19 +58,29 @@ export function FilterColumnTable({ filters }: FilterColumnTableProps) {
|
||||
} else {
|
||||
params.delete("filter[search]");
|
||||
}
|
||||
console.log(params.toString(), "en el apply search");
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
router.replace(`?${params.toString()}`, { scroll: false });
|
||||
},
|
||||
[router, searchParams],
|
||||
);
|
||||
|
||||
const clearAllFilters = useCallback(() => {
|
||||
const params = new URLSearchParams();
|
||||
router.push(`?${params.toString()}`, { scroll: false });
|
||||
setSearchQuery(""); // Clear the search input
|
||||
}, [router]);
|
||||
|
||||
const clearIconSearch = () => {
|
||||
setSearchQuery("");
|
||||
applySearch("");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
applySearch(debouncedSearchQuery);
|
||||
}, [debouncedSearchQuery, applySearch]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row justify-between space-y-4 w-full items-center">
|
||||
<div className="w-full md:w-1/3">
|
||||
<div className="w-full md:w-1/3 flex space-x-2">
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
value={searchQuery}
|
||||
@@ -76,9 +90,16 @@ export function FilterColumnTable({ filters }: FilterColumnTableProps) {
|
||||
applySearch(searchQuery);
|
||||
}
|
||||
}}
|
||||
endContent={
|
||||
searchQuery && (
|
||||
<button onClick={clearIconSearch} className="focus:outline-none">
|
||||
<XCircle className="h-4 w-4 text-default-400" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex items-center space-x-2 flex-wrap">
|
||||
{filters.flatMap(({ key, values }) =>
|
||||
values.map((value) => (
|
||||
<Button
|
||||
@@ -86,11 +107,20 @@ export function FilterColumnTable({ filters }: FilterColumnTableProps) {
|
||||
onClick={() => applyFilter(key, value)}
|
||||
// eslint-disable-next-line security/detect-object-injection
|
||||
variant={activeFilters[key] === value ? "faded" : "light"}
|
||||
size="sm"
|
||||
>
|
||||
{value || "All"}
|
||||
</Button>
|
||||
)),
|
||||
)}
|
||||
<Button
|
||||
onClick={clearAllFilters}
|
||||
variant="flat"
|
||||
color="default"
|
||||
size="sm"
|
||||
>
|
||||
Clear Filters
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ export function useDebounce<T>(value: T, delay?: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setDebouncedValue(value), delay ?? 800);
|
||||
const timer = setTimeout(() => setDebouncedValue(value), delay ?? 900);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
|
||||
Reference in New Issue
Block a user