mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
refactor(ui): improve layouts and styles (#9807)
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
"use server";
|
||||
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
|
||||
import { getLatestFindings } from "@/actions/findings/findings";
|
||||
import { LighthouseBanner } from "@/components/lighthouse/banner";
|
||||
import { LinkToFindings } from "@/components/overview";
|
||||
@@ -59,22 +57,19 @@ export async function FindingsViewSSR({ searchParams }: FindingsViewSSRProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col">
|
||||
<div className="flex w-full flex-col gap-6">
|
||||
<LighthouseBanner />
|
||||
<div className="relative flex w-full">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<h3 className="text-sm font-bold uppercase">
|
||||
<div className="relative w-full flex-col justify-between md:flex-row">
|
||||
<div className="flex w-full flex-col items-start gap-2 md:flex-row md:items-center">
|
||||
<h3 className="text-sm font-bold text-nowrap whitespace-nowrap uppercase">
|
||||
Latest new failing findings
|
||||
</h3>
|
||||
<p className="text-text-neutral-tertiary text-xs">
|
||||
<p className="text-text-neutral-tertiary text-xs whitespace-nowrap">
|
||||
Showing the latest 10 new failing findings by severity.
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute -top-6 right-0">
|
||||
<LinkToFindings />
|
||||
</div>
|
||||
</div>
|
||||
<Spacer y={4} />
|
||||
|
||||
<DataTable
|
||||
key={`dashboard-findings-${Date.now()}`}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import Link from "next/link";
|
||||
import React, { Suspense } from "react";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getInvitations } from "@/actions/invitations/invitation";
|
||||
import { getRoles } from "@/actions/roles";
|
||||
@@ -28,21 +27,22 @@ export default async function Invitations({
|
||||
<ContentLayout title="Invitations" icon="lucide:mail">
|
||||
<FilterControls search />
|
||||
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<DataTableFilterCustom filters={filterInvitations || []} />
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-end justify-between">
|
||||
<DataTableFilterCustom filters={filterInvitations || []} />
|
||||
|
||||
<Button asChild>
|
||||
<Link href="/invitations/new">
|
||||
Send Invitation
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<Link href="/invitations/new">
|
||||
Send Invitation
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableInvitation />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
<Spacer y={8} />
|
||||
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableInvitation />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</ContentLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProviders } from "@/actions/providers";
|
||||
@@ -26,20 +25,20 @@ export default async function Providers({
|
||||
|
||||
return (
|
||||
<ContentLayout title="Cloud Providers" icon="lucide:cloud-cog">
|
||||
<FilterControls search customFilters={filterProviders || []} />
|
||||
<Spacer y={8} />
|
||||
<ProvidersActions />
|
||||
<Spacer y={8} />
|
||||
<Suspense key={searchParamsKey} fallback={<ProvidersTableFallback />}>
|
||||
<ProvidersTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
<div className="flex flex-col gap-6">
|
||||
<FilterControls search customFilters={filterProviders || []} />
|
||||
<ProvidersActions />
|
||||
<Suspense key={searchParamsKey} fallback={<ProvidersTableFallback />}>
|
||||
<ProvidersTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
</ContentLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const ProvidersActions = () => {
|
||||
return (
|
||||
<div className="flex items-center gap-4 md:justify-end">
|
||||
<div className="flex flex-wrap gap-4 md:justify-end">
|
||||
<ManageGroupsButton />
|
||||
<MutedFindingsConfigButton />
|
||||
<AddProviderButton />
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProviders } from "@/actions/providers";
|
||||
@@ -63,36 +62,36 @@ export default async function Resources({
|
||||
return (
|
||||
<ContentLayout title="Resources" icon="lucide:warehouse">
|
||||
<FilterControls search date />
|
||||
<Spacer y={4} />
|
||||
<DataTableFilterCustom
|
||||
filters={[
|
||||
{
|
||||
key: "provider__in",
|
||||
labelCheckboxGroup: "Provider",
|
||||
values: providerIds,
|
||||
valueLabelMapping: providerDetails,
|
||||
},
|
||||
{
|
||||
key: "region__in",
|
||||
labelCheckboxGroup: "Region",
|
||||
values: uniqueRegions,
|
||||
},
|
||||
{
|
||||
key: "service__in",
|
||||
labelCheckboxGroup: "Service",
|
||||
values: uniqueServices,
|
||||
},
|
||||
{
|
||||
key: "groups__in",
|
||||
labelCheckboxGroup: "Group",
|
||||
values: uniqueGroups,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Spacer y={8} />
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableResources />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
<div className="flex flex-col gap-6">
|
||||
<DataTableFilterCustom
|
||||
filters={[
|
||||
{
|
||||
key: "provider__in",
|
||||
labelCheckboxGroup: "Provider",
|
||||
values: providerIds,
|
||||
valueLabelMapping: providerDetails,
|
||||
},
|
||||
{
|
||||
key: "region__in",
|
||||
labelCheckboxGroup: "Region",
|
||||
values: uniqueRegions,
|
||||
},
|
||||
{
|
||||
key: "service__in",
|
||||
labelCheckboxGroup: "Service",
|
||||
values: uniqueServices,
|
||||
},
|
||||
{
|
||||
key: "groups__in",
|
||||
labelCheckboxGroup: "Group",
|
||||
values: uniqueGroups,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableResources />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
</ContentLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
|
||||
@@ -6,8 +5,7 @@ import { getRoles } from "@/actions/roles";
|
||||
import { FilterControls } from "@/components/filters";
|
||||
import { filterRoles } from "@/components/filters/data-filters";
|
||||
import { AddIcon } from "@/components/icons";
|
||||
import { ColumnsRoles } from "@/components/roles/table";
|
||||
import { SkeletonTableRoles } from "@/components/roles/table";
|
||||
import { ColumnsRoles, SkeletonTableRoles } from "@/components/roles/table";
|
||||
import { Button } from "@/components/shadcn";
|
||||
import { ContentLayout } from "@/components/ui";
|
||||
import { DataTable, DataTableFilterCustom } from "@/components/ui/table";
|
||||
@@ -25,21 +23,21 @@ export default async function Roles({
|
||||
<ContentLayout title="Roles" icon="lucide:user-cog">
|
||||
<FilterControls search />
|
||||
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<DataTableFilterCustom filters={filterRoles || []} />
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-end justify-between">
|
||||
<DataTableFilterCustom filters={filterRoles || []} />
|
||||
<Button asChild>
|
||||
<Link href="/roles/new">
|
||||
Add Role
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button asChild>
|
||||
<Link href="/roles/new">
|
||||
Add Role
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableRoles />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
<Spacer y={8} />
|
||||
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableRoles />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</ContentLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProviders } from "@/actions/providers";
|
||||
@@ -87,33 +86,32 @@ export default async function Scans({
|
||||
<ContentLayout title="Scans" icon="lucide:timer">
|
||||
<AutoRefresh hasExecutingScan={hasExecutingScan} />
|
||||
<>
|
||||
{!hasManageScansPermission ? (
|
||||
<CustomBanner
|
||||
title={"Access Denied"}
|
||||
message={"You don't have permission to launch the scan."}
|
||||
<>
|
||||
{!hasManageScansPermission ? (
|
||||
<CustomBanner
|
||||
title={"Access Denied"}
|
||||
message={"You don't have permission to launch the scan."}
|
||||
/>
|
||||
) : thereIsNoProvidersConnected ? (
|
||||
<>
|
||||
<NoProvidersConnected />
|
||||
</>
|
||||
) : (
|
||||
<LaunchScanWorkflow providers={providerInfo} />
|
||||
)}
|
||||
</>
|
||||
<div className="flex flex-col gap-6">
|
||||
<ScansFilters
|
||||
providerUIDs={providerUIDs}
|
||||
providerDetails={providerDetails}
|
||||
/>
|
||||
) : thereIsNoProvidersConnected ? (
|
||||
<>
|
||||
<Spacer y={8} />
|
||||
<NoProvidersConnected />
|
||||
<Spacer y={8} />
|
||||
</>
|
||||
) : (
|
||||
<LaunchScanWorkflow providers={providerInfo} />
|
||||
)}
|
||||
|
||||
<ScansFilters
|
||||
providerUIDs={providerUIDs}
|
||||
providerDetails={providerDetails}
|
||||
/>
|
||||
<Spacer y={8} />
|
||||
<div className="flex items-center justify-end gap-4">
|
||||
<MutedFindingsConfigButton />
|
||||
<div className="flex items-center justify-end">
|
||||
<MutedFindingsConfigButton />
|
||||
</div>
|
||||
<Suspense fallback={<SkeletonTableScans />}>
|
||||
<SSRDataTableScans searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
<Spacer y={8} />
|
||||
<Suspense fallback={<SkeletonTableScans />}>
|
||||
<SSRDataTableScans searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</>
|
||||
</ContentLayout>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Spacer } from "@heroui/spacer";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getRoles } from "@/actions/roles/roles";
|
||||
import { getUsers } from "@/actions/users/users";
|
||||
import { FilterControls } from "@/components/filters";
|
||||
import { filterUsers } from "@/components/filters/data-filters";
|
||||
import { AddIcon } from "@/components/icons";
|
||||
import { Button } from "@/components/shadcn";
|
||||
import { ContentLayout } from "@/components/ui";
|
||||
import { DataTable, DataTableFilterCustom } from "@/components/ui/table";
|
||||
import { DataTable } from "@/components/ui/table";
|
||||
import { ColumnsUser, SkeletonTableUser } from "@/components/users/table";
|
||||
import { Role, SearchParamsProps, UserProps } from "@/types";
|
||||
|
||||
@@ -25,21 +23,20 @@ export default async function Users({
|
||||
<ContentLayout title="Users" icon="lucide:user">
|
||||
<FilterControls search />
|
||||
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<DataTableFilterCustom filters={filterUsers || []} />
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-end justify-end">
|
||||
<Button asChild>
|
||||
<Link href="/invitations/new">
|
||||
Invite User
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button asChild>
|
||||
<Link href="/invitations/new">
|
||||
Invite User
|
||||
<AddIcon size={20} />
|
||||
</Link>
|
||||
</Button>
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableUser />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</div>
|
||||
<Spacer y={8} />
|
||||
|
||||
<Suspense key={searchParamsKey} fallback={<SkeletonTableUser />}>
|
||||
<SSRDataTable searchParams={resolvedSearchParams} />
|
||||
</Suspense>
|
||||
</ContentLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,8 +73,10 @@ export const ComplianceHeader = ({
|
||||
{hasContent && (
|
||||
<div className="flex w-full items-start justify-between gap-6 sm:mb-8">
|
||||
<div className="flex flex-1 flex-col justify-end gap-4">
|
||||
{/* Showed in the details page */}
|
||||
{selectedScan && <ComplianceScanInfo scan={selectedScan} />}
|
||||
|
||||
{/* Showed in the compliance page */}
|
||||
{showProviders && <DataCompliance scans={scans} />}
|
||||
{!hideFilters && allFilters.length > 0 && (
|
||||
<DataTableFilterCustom filters={allFilters} />
|
||||
|
||||
@@ -27,6 +27,7 @@ export const ComplianceScanInfo = ({ scan }: ComplianceScanInfoProps) => {
|
||||
entityAlias={scan.providerInfo.alias}
|
||||
entityId={scan.providerInfo.uid}
|
||||
showCopyAction={false}
|
||||
maxWidth="w-[80px]"
|
||||
/>
|
||||
</div>
|
||||
<Divider orientation="vertical" className="h-8" />
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { X } from "lucide-react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { Badge } from "@/components/shadcn";
|
||||
import { useUrlFilters } from "@/hooks/use-url-filters";
|
||||
|
||||
export interface FilterBadgeConfig {
|
||||
/**
|
||||
* The filter key without the "filter[]" wrapper.
|
||||
* Example: "scan__in", "check_id__in", "provider__in"
|
||||
*/
|
||||
filterKey: string;
|
||||
|
||||
/**
|
||||
* Label to display before the value.
|
||||
* Example: "Scan", "Check ID", "Provider"
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Optional function to format a single value for display.
|
||||
* Useful for truncating UUIDs, etc.
|
||||
* Default: shows value as-is
|
||||
*/
|
||||
formatValue?: (value: string) => string;
|
||||
|
||||
/**
|
||||
* Optional function to format the display when multiple values are selected.
|
||||
* Default: "{count} {label}s filtered"
|
||||
*/
|
||||
formatMultiple?: (count: number, label: string) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default filter badge configurations for common use cases.
|
||||
* Add new filters here to automatically show them as badges.
|
||||
*/
|
||||
export const DEFAULT_FILTER_BADGES: FilterBadgeConfig[] = [
|
||||
{
|
||||
filterKey: "check_id__in",
|
||||
label: "Check ID",
|
||||
formatMultiple: (count) => `${count} Check IDs filtered`,
|
||||
},
|
||||
];
|
||||
|
||||
interface ActiveFilterBadgeProps {
|
||||
config: FilterBadgeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single filter badge component that reads from URL and displays if active.
|
||||
*/
|
||||
const ActiveFilterBadge = ({ config }: ActiveFilterBadgeProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
const { clearFilter } = useUrlFilters();
|
||||
|
||||
const {
|
||||
filterKey,
|
||||
label,
|
||||
formatValue = (v) => v,
|
||||
formatMultiple = (count, lbl) => `${count} ${lbl}s filtered`,
|
||||
} = config;
|
||||
|
||||
const fullKey = filterKey.startsWith("filter[")
|
||||
? filterKey
|
||||
: `filter[${filterKey}]`;
|
||||
|
||||
const filterValue = searchParams.get(fullKey);
|
||||
|
||||
if (!filterValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const values = filterValue.split(",");
|
||||
const displayText =
|
||||
values.length > 1
|
||||
? formatMultiple(values.length, label)
|
||||
: `${label}: ${formatValue(values[0])}`;
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="flex cursor-pointer items-center gap-1 px-3 py-1.5"
|
||||
onClick={() => clearFilter(filterKey)}
|
||||
>
|
||||
<span className="max-w-[200px] truncate text-sm">{displayText}</span>
|
||||
<X className="size-3.5 shrink-0" />
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
interface ActiveFilterBadgesProps {
|
||||
/**
|
||||
* Filter configurations to render as badges.
|
||||
* Defaults to DEFAULT_FILTER_BADGES if not provided.
|
||||
*/
|
||||
filters?: FilterBadgeConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders filter badges for all configured filters that are active in the URL.
|
||||
* Only shows badges for filters that have values in the URL params.
|
||||
*/
|
||||
export const ActiveFilterBadges = ({
|
||||
filters = DEFAULT_FILTER_BADGES,
|
||||
}: ActiveFilterBadgesProps) => {
|
||||
return (
|
||||
<>
|
||||
{filters.map((config) => (
|
||||
<ActiveFilterBadge key={config.filterKey} config={config} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -30,8 +30,8 @@ export const FilterControls = ({
|
||||
customFilters,
|
||||
}: FilterControlsProps) => {
|
||||
return (
|
||||
<div className="mb-4 flex flex-col">
|
||||
<div className="flex flex-col items-start gap-4 md:flex-row md:items-center">
|
||||
<div className="flex flex-col">
|
||||
<div className="mb-4 flex flex-col items-start gap-4 md:flex-row md:items-center">
|
||||
<div className="grid w-full flex-1 grid-cols-1 items-center gap-x-4 gap-y-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{search && <CustomSearchInput />}
|
||||
{providers && <CustomSelectProvider />}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from "./active-filter-badge";
|
||||
export * from "./clear-filters-button";
|
||||
export * from "./custom-account-selection";
|
||||
export * from "./custom-checkbox-muted-findings";
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/shadcn/tooltip";
|
||||
|
||||
export interface ChartLegendItem {
|
||||
label: string;
|
||||
color: string;
|
||||
@@ -18,36 +24,42 @@ export function ChartLegend({
|
||||
const isInteractive = !!onItemClick;
|
||||
|
||||
return (
|
||||
<div className="border-border-neutral-tertiary bg-bg-neutral-tertiary inline-flex items-center gap-2 rounded-full border">
|
||||
<div className="border-border-neutral-tertiary bg-bg-neutral-tertiary inline-flex max-w-full items-center overflow-hidden rounded-full border sm:gap-2">
|
||||
{items.map((item, index) => {
|
||||
const dataKey = item.dataKey ?? item.label.toLowerCase();
|
||||
const isSelected = selectedItem === dataKey;
|
||||
const isFaded = selectedItem !== null && !isSelected;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={`legend-${index}`}
|
||||
type="button"
|
||||
className={`flex items-center gap-2 px-4 py-3 transition-opacity duration-200 ${
|
||||
isInteractive
|
||||
? "cursor-pointer hover:opacity-80"
|
||||
: "cursor-default"
|
||||
} ${isFaded ? "opacity-30" : "opacity-100"}`}
|
||||
onClick={() => onItemClick?.(dataKey)}
|
||||
disabled={!isInteractive}
|
||||
>
|
||||
<div
|
||||
className={`h-3 w-3 rounded ${isSelected ? "ring-2 ring-offset-1" : ""}`}
|
||||
style={{
|
||||
backgroundColor: item.color,
|
||||
// @ts-expect-error ring-color is a valid Tailwind CSS variable
|
||||
"--tw-ring-color": item.color,
|
||||
}}
|
||||
/>
|
||||
<span className="text-text-neutral-secondary text-sm font-medium">
|
||||
{item.label}
|
||||
</span>
|
||||
</button>
|
||||
<Tooltip key={`legend-${index}`}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className={`flex min-w-0 items-center gap-1 px-2 py-3 transition-opacity duration-200 sm:gap-2 sm:px-4 ${
|
||||
isInteractive
|
||||
? "cursor-pointer hover:opacity-80"
|
||||
: "cursor-default"
|
||||
} ${isFaded ? "opacity-30" : "opacity-100"}`}
|
||||
onClick={() => onItemClick?.(dataKey)}
|
||||
disabled={!isInteractive}
|
||||
>
|
||||
<div
|
||||
className={`h-3 w-3 shrink-0 rounded ${isSelected ? "ring-2 ring-offset-1" : ""}`}
|
||||
style={{
|
||||
backgroundColor: item.color,
|
||||
// @ts-expect-error ring-color is a valid Tailwind CSS variable
|
||||
"--tw-ring-color": item.color,
|
||||
}}
|
||||
/>
|
||||
<span className="text-text-neutral-secondary max-w-[120px] truncate text-sm font-medium sm:max-w-[200px]">
|
||||
{item.label}
|
||||
</span>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{item.label}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { ComplianceScanInfo } from "@/components/compliance/compliance-header/compliance-scan-info";
|
||||
import { ActiveFilterBadges } from "@/components/filters/active-filter-badge";
|
||||
import { ClearFiltersButton } from "@/components/filters/clear-filters-button";
|
||||
import {
|
||||
MultiSelect,
|
||||
@@ -176,8 +175,7 @@ export const DataTableFilterCustom = ({
|
||||
);
|
||||
})}
|
||||
{!hideClearButton && (
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<ActiveFilterBadges />
|
||||
<div className="flex items-center justify-start">
|
||||
<ClearFiltersButton />
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user