feat: big refactor for filters and tons of components

This commit is contained in:
Pablo Lara
2024-09-30 16:15:43 +02:00
parent db9faa2f4b
commit f146946319
22 changed files with 572 additions and 201 deletions
+2 -3
View File
@@ -2,7 +2,7 @@ import { Spacer } from "@nextui-org/react";
import { Suspense } from "react";
import { getProviders } from "@/actions";
import { FilterControls } from "@/components/filters";
import { FilterControls, filtersProviders } from "@/components/filters";
import { AddProvider } from "@/components/providers";
import {
ColumnsProvider,
@@ -24,7 +24,7 @@ export default async function Providers({
<Header title="Providers" icon="fluent:cloud-sync-24-regular" />
<Spacer y={4} />
<FilterControls search providers />
<FilterControls search providers date customFilters={filtersProviders} />
<Spacer y={4} />
<AddProvider />
@@ -33,7 +33,6 @@ export default async function Providers({
<Suspense key={searchParamsKey} fallback={<SkeletonTableProvider />}>
<SSRDataTable searchParams={searchParams} />
</Suspense>
{/* <NewTable /> */}
</>
);
}
@@ -59,7 +59,6 @@ export const CustomDatePicker = () => {
return (
<div className="flex flex-col md:gap-2 w-full">
<DatePicker
CalendarBottomContent={<div className="min-w-[380px]"></div>}
CalendarTopContent={
<ButtonGroup
fullWidth
@@ -89,7 +88,6 @@ export const CustomDatePicker = () => {
}}
value={value}
onChange={handleDateChange}
label="Scan date"
size="sm"
variant="flat"
/>
@@ -37,7 +37,7 @@ export const CustomSearchInput: React.FC = () => {
return (
<Input
variant="bordered"
variant="flat"
placeholder="Search..."
labelPlacement="outside"
value={searchQuery}
@@ -54,8 +54,8 @@ export const CustomSearchInput: React.FC = () => {
</button>
)
}
radius="full"
size="lg"
radius="sm"
size="sm"
/>
);
};
@@ -9,7 +9,7 @@ import {
CustomProviderInputAzure,
CustomProviderInputGCP,
CustomProviderInputKubernetes,
} from "./CustomProviderInputs";
} from "./custom-provider-inputs";
const dataInputsProvider = [
{
@@ -43,9 +43,9 @@ export const CustomSelectProvider = () => {
(value: string) => {
const params = new URLSearchParams(searchParams.toString());
if (value) {
params.set("filter[provider]", value);
params.set("filter[provider__in]", value);
} else {
params.delete("filter[provider]");
params.delete("filter[provider__in]");
}
router.push(`?${params.toString()}`, { scroll: false });
},
@@ -53,21 +53,18 @@ export const CustomSelectProvider = () => {
);
useEffect(() => {
const providerFromUrl = searchParams.get("filter[provider]") || "";
const providerFromUrl = searchParams.get("filter[provider__in]") || "";
setSelectedProvider(providerFromUrl);
}, [searchParams]);
return (
<Select
items={dataInputsProvider}
label="Select a Provider"
// selectionMode="multiple"
// label="Select a Provider"
placeholder="Select a provider"
labelPlacement="inside"
labelPlacement="outside"
size="sm"
classNames={{
base: "w-full",
trigger: "h-12",
}}
onChange={(e) => {
const value = e.target.value;
setSelectedProvider(value);
@@ -1,8 +1,8 @@
export const filtersProviders = [
{
key: "provider",
labelCheckboxGroup: "Provider",
values: ["aws", "gcp", "azure"],
key: "provider__in",
labelCheckboxGroup: "Select a Provider",
values: ["aws", "gcp", "azure", "kubernetes"],
},
{
key: "connected",
@@ -3,25 +3,17 @@
import { useRouter, useSearchParams } from "next/navigation";
import React, { useCallback, useEffect, useState } from "react";
import { FilterControlsProps } from "@/types";
import { CrossIcon } from "../icons";
import { DataTableFilterCustom } from "../providers/table";
import { CustomButton } from "../ui/custom";
import { CustomAccountSelection } from "./CustomAccountSelection";
import { CustomCheckboxMutedFindings } from "./CustomCheckboxMutedFindings";
import { CustomDatePicker } from "./CustomDatePicker";
import { CustomRegionSelection } from "./CustomRegionSelection";
import { CustomSearchInput } from "./CustomSearchInput";
import { CustomSelectProvider } from "./CustomSelectProvider";
interface FilterControlsProps {
search?: boolean;
providers?: boolean;
date?: boolean;
regions?: boolean;
accounts?: boolean;
mutedFindings?: boolean;
customFilters?: { key: string; values: string[] }[];
}
import { CustomCheckboxMutedFindings } from "./custo-checkbox-muted-findings";
import { CustomAccountSelection } from "./custom-account-selection";
import { CustomDatePicker } from "./custom-date-picker";
import { CustomRegionSelection } from "./custom-region-selection";
import { CustomSearchInput } from "./custom-search-input";
import { CustomSelectProvider } from "./custom-select-provider";
export const FilterControls: React.FC<FilterControlsProps> = ({
search = false,
+8 -8
View File
@@ -1,8 +1,8 @@
export * from "../filters/CustomAccountSelection";
export * from "../filters/CustomCheckboxMutedFindings";
export * from "../filters/CustomDatePicker";
export * from "../filters/CustomProviderInputs";
export * from "../filters/CustomRegionSelection";
export * from "../filters/CustomSelectProvider";
export * from "../filters/FilterControls";
export * from "../filters/filters";
export * from "./custo-checkbox-muted-findings";
export * from "./custom-account-selection";
export * from "./custom-date-picker";
export * from "./custom-provider-inputs";
export * from "./custom-region-selection";
export * from "./custom-select-provider";
export * from "./data-filters";
export * from "./filter-controls";
+7 -10
View File
@@ -717,19 +717,16 @@ export const CustomFilterIcon: React.FC<IconSvgProps> = ({
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
height={size || height || 15}
viewBox="0 0 15 15"
width={size || width || 15}
aria-hidden="true"
height={size || height || 16}
width={size || width || 16}
viewBox="0 0 24 24"
{...props}
>
<path
d="M2.15 4a1.85 1.85 0 1 1 3.7 0 1.85 1.85 0 0 1-3.7 0ZM4 1.25a2.75 2.75 0 1 0 0 5.5 2.75 2.75 0 0 0 0-5.5ZM5.82 11 2.5 12.837V9.163L5.82 11ZM2.64 8.212a.7.7 0 0 0-1.039.612v4.352a.7.7 0 0 0 1.039.613l3.933-2.176a.7.7 0 0 0 0-1.225L2.64 8.212ZM8.3 9a.7.7 0 0 1 .7-.7h4a.7.7 0 0 1 .7.7v4a.7.7 0 0 1-.7.7H9a.7.7 0 0 1-.7-.7V9Zm.9.2v3.6h3.6V9.2H9.2Zm4.243-7.007a.45.45 0 0 0-.636-.636L11 3.364 9.193 1.557a.45.45 0 1 0-.636.636L10.364 4 8.557 5.807a.45.45 0 1 0 .636.636L11 4.636l1.807 1.807a.45.45 0 0 0 .636-.636L11.636 4l1.807-1.807Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
<g fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M9.5 14a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm5-10a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z" />
<path strokeLinecap="round" d="M15 16.959h7m-13-10H2m0 10h2m18-10h-2" />
</g>
</svg>
);
};
+1 -12
View File
@@ -1,6 +1,5 @@
"use client";
import { Chip } from "@nextui-org/react";
import { useState } from "react";
import { AddIcon } from "../icons";
@@ -23,17 +22,7 @@ export const AddProvider = () => {
<AddForm setIsOpen={setIsAddOpen} />
</CustomAlertModal>
<div className="w-full flex items-center justify-between">
<div className="flex w-fit items-center gap-2">
<h1 className="text-2xl font-[700] leading-[32px]">TBD</h1>
<Chip
className="hidden items-center text-default-500 sm:flex"
size="sm"
variant="flat"
>
3
</Chip>
</div>
<div className="w-full flex items-center justify-end">
<CustomButton
variant="solid"
color="action"
@@ -1,52 +1,34 @@
"use client";
import {
Button,
Checkbox,
CheckboxGroup,
Input,
Popover,
PopoverContent,
PopoverTrigger,
} from "@nextui-org/react";
import { SearchIcon } from "lucide-react";
import { Divider } from "@nextui-org/react";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useMemo } from "react";
import React, { useState } from "react";
import { useCallback } from "react";
import { CustomFilterIcon, PlusCircleIcon } from "@/components/icons";
import { CustomButton } from "@/components/ui/custom";
import { CustomFilterIcon } from "@/components/icons";
import { CustomButton, CustomDropdownFilter } from "@/components/ui/custom";
import { FilterOption } from "@/types";
interface DataTableFilterCustomProps {
filters: { key: string; values: string[] }[];
export interface DataTableFilterCustomProps {
filters: FilterOption[];
}
export function DataTableFilterCustom({ filters }: DataTableFilterCustomProps) {
export const DataTableFilterCustom = ({
filters,
}: DataTableFilterCustomProps) => {
const router = useRouter();
const searchParams = useSearchParams();
const [showFilters, setShowFilters] = useState(false);
const activeFilters = useMemo(() => {
const currentFilters: Record<string, string> = {};
Array.from(searchParams.entries()).forEach(([key, value]) => {
if (key.startsWith("filter[") && key.endsWith("]")) {
const filterKey = key.slice(7, -1);
if (filters.some((filter) => filter.key === filterKey)) {
// eslint-disable-next-line security/detect-object-injection
currentFilters[filterKey] = value;
}
}
});
return currentFilters;
}, [searchParams, filters]);
const applyFilter = useCallback(
(key: string, value: string) => {
const pushDropdownFilter = useCallback(
(key: string, values: string[]) => {
const params = new URLSearchParams(searchParams);
const filterKey = `filter[${key}]`;
if (params.get(filterKey) === value) {
if (values.length === 0) {
params.delete(filterKey);
} else {
params.set(filterKey, value);
params.set(filterKey, values.join(","));
}
router.push(`?${params.toString()}`);
@@ -55,74 +37,41 @@ export function DataTableFilterCustom({ filters }: DataTableFilterCustomProps) {
);
return (
<>
<div className="flex items-center gap-4 overflow-auto px-[6px] py-[4px]">
<div className="flex items-center gap-3">
<div className="flex items-center gap-4">
<Input
className="min-w-[200px]"
endContent={
<SearchIcon className="text-default-400" width={16} />
}
placeholder="Search"
size="sm"
// value={filterValue}
// onValueChange={onSearchChange}
<div className="flex flex-row items-center gap-4">
<CustomButton
variant="flat"
color={showFilters ? "action" : "primary"}
size="sm"
startContent={<CustomFilterIcon size={16} />}
onPress={() => setShowFilters(!showFilters)}
>
{showFilters ? "Hide Filters" : "Show Filters"}
</CustomButton>
<div
className={`transition-all duration-500 ease-in-out ${
showFilters
? "opacity-100 max-h-96 overflow-visible"
: "opacity-0 max-h-0 overflow-hidden"
}`}
>
<div className="flex flex-row gap-4">
{filters.map((filter) => (
<CustomDropdownFilter
key={filter.key}
filter={{
...filter,
labelCheckboxGroup: filter.labelCheckboxGroup,
}}
onFilterChange={pushDropdownFilter}
/>
</div>
<div>
<Popover placement="bottom">
<PopoverTrigger>
<Button
className="bg-default-100 text-default-800"
startContent={
<PlusCircleIcon className="text-default-400" width={16} />
}
size="sm"
>
Filter
</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="flex w-full flex-col gap-6 px-2 py-4">
<CheckboxGroup
label="Select cities"
defaultValue={["buenos-aires", "london"]}
>
<Checkbox value="buenos-aires">Buenos Aires</Checkbox>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="san-francisco">San Francisco</Checkbox>
<Checkbox value="london">London</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</CheckboxGroup>
</div>
</PopoverContent>
</Popover>
))}
<div className="flex flex-row items-center gap-2">
<Divider className="text-default-800 h-5" orientation="vertical" />
<span className="text-sm text-default-800">Selected</span>
</div>
</div>
</div>
<div className="flex flex-wrap items-center space-x-2">
<CustomButton
variant="dashed"
size="sm"
startContent={<CustomFilterIcon size={16} />}
>
Show Filters
</CustomButton>
{filters.flatMap(({ key, values }) =>
values.map((value) => (
<Button
key={`${key}-${value}`}
onPress={() => applyFilter(key, value)}
// eslint-disable-next-line security/detect-object-injection
variant={activeFilters[key] === value ? "faded" : "light"}
>
{value || "All"}
</Button>
)),
)}
</div>
</>
</div>
);
}
};
+77 -27
View File
@@ -1,40 +1,56 @@
"use client";
import {
Badge,
Button,
Checkbox,
CheckboxGroup,
Divider,
Popover,
PopoverContent,
PopoverTrigger,
} from "@nextui-org/react";
import React, { useCallback, useState } from "react";
import { useSearchParams } from "next/navigation";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { PlusCircleIcon } from "@/components/icons";
interface FilterOption {
key: string;
labelCheckboxGroup: string;
values: string[];
}
interface CustomDropdownFilterProps {
filter?: FilterOption;
onFilterChange?: (key: string, values: string[]) => void;
}
import { CustomDropdownFilterProps } from "@/types";
export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
filter,
onFilterChange,
}) => {
// Early return if filter is undefined
if (!filter) {
return null;
}
const searchParams = useSearchParams();
const [groupSelected, setGroupSelected] = useState(new Set<string>());
const allFilterKeys = filter.values || [];
const allFilterKeys = filter?.values || [];
const getActiveFilter = useMemo(() => {
const currentFilters: Record<string, string> = {};
Array.from(searchParams.entries()).forEach(([key, value]) => {
if (key.startsWith("filter[") && key.endsWith("]")) {
const filterKey = key.slice(7, -1);
if (filter && filter.key === filterKey) {
// eslint-disable-next-line security/detect-object-injection
currentFilters[filterKey] = value;
}
}
});
return currentFilters;
}, [searchParams, filter]);
useEffect(() => {
if (filter && getActiveFilter[filter.key]) {
const activeValues = getActiveFilter[filter.key].split(",");
const newSelection = new Set(activeValues);
if (newSelection.size === allFilterKeys.length) {
newSelection.add("all");
}
setGroupSelected(newSelection);
} else {
setGroupSelected(new Set());
}
}, [getActiveFilter, filter, allFilterKeys]);
const onSelectionChange = useCallback(
(keys: string[]) => {
@@ -72,7 +88,6 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
setGroupSelected(new Set(["all", ...allFilterKeys]));
}
}, [groupSelected, allFilterKeys]);
return (
<div className="flex w-full max-w-xs flex-col gap-2">
<Popover placement="bottom">
@@ -84,13 +99,46 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
}
size="sm"
>
{filter.key}
{filter?.labelCheckboxGroup}
{groupSelected.size > 0 && (
<>
<Divider orientation="vertical" className="mx-2 h-4" />
<Badge
variant="flat"
className="rounded-sm px-1 font-normal lg:hidden"
>
{groupSelected.size}
</Badge>
<div className="hidden space-x-1 lg:flex">
{groupSelected.size > 2 ? (
<Badge
variant="flat"
className="rounded-sm px-1 font-normal"
>
{groupSelected.size} selected
</Badge>
) : (
Array.from(groupSelected)
.filter((value) => value !== "all")
.map((value) => (
<Badge
variant="flat"
key={value}
className="rounded-sm px-1 font-normal"
>
{value}
</Badge>
))
)}
</div>
</>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="flex w-full flex-col gap-6 px-2 py-4">
<CheckboxGroup
label={filter.labelCheckboxGroup}
label={filter?.labelCheckboxGroup}
value={Array.from(groupSelected)}
onValueChange={onSelectionChange}
>
@@ -106,12 +154,14 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
</div>
</PopoverContent>
</Popover>
<p className="text-small text-default-500">
Selected:{" "}
{Array.from(groupSelected)
.filter((item) => item !== "all")
.join(", ")}
</p>
{groupSelected?.size > 0 && (
<p className="text-small text-default-500">
Selected:{" "}
{Array.from(groupSelected)
.filter((item) => item !== "all")
.join(", ")}
</p>
)}
</div>
);
};
@@ -1,7 +1,7 @@
"use client";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import { Cross2Icon } from "@radix-ui/react-icons";
import * as React from "react";
import { cn } from "@/lib/utils";
@@ -38,14 +38,14 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 data-[state=open]:text-slate-500 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800 dark:data-[state=open]:text-slate-400">
<X className="h-4 w-4" />
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
@@ -102,7 +102,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
+1 -1
View File
@@ -2,7 +2,7 @@ export * from "./action-card/ActionCard";
export * from "./alert/Alert";
export * from "./alert-dialog/AlertDialog";
export * from "./chart/Chart";
export * from "./dialog/Dialog";
export * from "./dialog/dialogg";
export * from "./dropdown/Dropdown";
export * from "./header/Header";
export * from "./label/Label";
-1
View File
@@ -1,4 +1,3 @@
// export * from "./data-table-toolbar";
export * from "./severity-badge";
export * from "./status-badge";
export * from "./table";
+379
View File
@@ -28,6 +28,7 @@
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"framer-motion": "~11.1.1",
"intl-messageformat": "^10.5.0",
@@ -6308,6 +6309,384 @@
"node": ">=6"
}
},
"node_modules/cmdk": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz",
"integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-dialog": "1.0.5",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/cmdk/node_modules/@radix-ui/primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-context": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-dialog": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-dismissable-layer": "1.0.5",
"@radix-ui/react-focus-guards": "1.0.1",
"@radix-ui/react-focus-scope": "1.0.4",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-portal": "1.0.4",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-slot": "1.0.2",
"@radix-ui/react-use-controllable-state": "1.0.1",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "2.5.5"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-escape-keydown": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
"integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
"integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-id": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-layout-effect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-portal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-presence": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-primitive": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-slot": "1.0.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-use-callback-ref": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.13.10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/cmdk/node_modules/react-remove-scroll": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
"integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.3",
"react-style-singleton": "^2.2.1",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.0",
"use-sidecar": "^1.1.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/code-block-writer": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
+1
View File
@@ -20,6 +20,7 @@
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"framer-motion": "~11.1.1",
"intl-messageformat": "^10.5.0",
+20
View File
@@ -0,0 +1,20 @@
export interface FilterOption {
key: string;
labelCheckboxGroup: string;
values: string[];
}
export interface CustomDropdownFilterProps {
filter: FilterOption;
onFilterChange: (key: string, values: string[]) => void;
}
export interface FilterControlsProps {
search?: boolean;
providers?: boolean;
date?: boolean;
regions?: boolean;
accounts?: boolean;
mutedFindings?: boolean;
customFilters?: FilterOption[];
}
+1
View File
@@ -1,3 +1,4 @@
export * from "./authFormSchema";
export * from "./components";
export * from "./filters";
export * from "./formSchemas";