From e67f4e5f29c7232bad563fb30bfd17962ad7dc8b Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 25 Sep 2024 06:56:34 +0200 Subject: [PATCH 01/24] feat: WIP --- components/filters/CustomSearchInput.tsx | 9 +- components/icons/Icons.tsx | 83 ++++++++++++++ .../table/data-table-column-header.tsx | 17 ++- .../table/data-table-filter-custom.tsx | 103 +++++++++++++++--- .../providers/table/data-table-provider.tsx | 32 +++++- components/ui/table/Table.tsx | 33 +++--- tailwind.config.js | 66 +++++++++++ 7 files changed, 298 insertions(+), 45 deletions(-) diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/CustomSearchInput.tsx index 98310f10b5..09fb786e39 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/CustomSearchInput.tsx @@ -1,6 +1,6 @@ import { Input } from "@nextui-org/react"; import debounce from "lodash.debounce"; -import { XCircle } from "lucide-react"; +import { SearchIcon, XCircle } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useState } from "react"; @@ -39,9 +39,9 @@ export const CustomSearchInput: React.FC = () => { } onChange={(e) => { const value = e.target.value; setSearchQuery(value); @@ -54,7 +54,8 @@ export const CustomSearchInput: React.FC = () => { ) } - size="sm" + radius="full" + size="lg" /> ); }; diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx index f665b49628..c6383a2af2 100644 --- a/components/icons/Icons.tsx +++ b/components/icons/Icons.tsx @@ -595,3 +595,86 @@ export const SuccessIcon: React.FC = ({ /> ); + +export const ArrowUpIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + ); +}; + +export const ArrowDownIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + ); +}; + +export const ChevronsLeftRightIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; diff --git a/components/providers/table/data-table-column-header.tsx b/components/providers/table/data-table-column-header.tsx index 3b460ae937..4df6af2d78 100644 --- a/components/providers/table/data-table-column-header.tsx +++ b/components/providers/table/data-table-column-header.tsx @@ -2,13 +2,14 @@ import { Button } from "@nextui-org/react"; import { Column } from "@tanstack/react-table"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { HTMLAttributes } from "react"; + import { ArrowDownIcon, ArrowUpIcon, ChevronsLeftRightIcon, -} from "lucide-react"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { HTMLAttributes } from "react"; +} from "@/components/icons"; interface DataTableColumnHeaderProps extends HTMLAttributes { @@ -64,12 +65,12 @@ export const DataTableColumnHeader = ({ currentSortParam === "" || (currentSortParam !== param && currentSortParam !== `-${param}`) ) { - return ; + return ; } return currentSortParam === `-${param}` ? ( - + ) : ( - + ); }; @@ -79,9 +80,7 @@ export const DataTableColumnHeader = ({ return ( - )), - )} - + <> + {/*
+
+
+ + } + placeholder="Search" + size="sm" + // value={filterValue} + // onValueChange={onSearchChange} + /> +
+
+ + + + + +
+ + All + Employee + Contractor + + + + All + Active + Inactive + Paused + Vacation + + + + All + Last 7 days + Last 30 days + Last 60 days + +
+
+
+
+
+
*/} + +
+ {filters.flatMap(({ key, values }) => + values.map((value) => ( + + )), + )} +
+ ); } diff --git a/components/providers/table/data-table-provider.tsx b/components/providers/table/data-table-provider.tsx index e300496913..6ea0133cb4 100644 --- a/components/providers/table/data-table-provider.tsx +++ b/components/providers/table/data-table-provider.tsx @@ -1,5 +1,6 @@ "use client"; - +import { Icon } from "@iconify/react"; +import { Button, Chip } from "@nextui-org/react"; import { ColumnDef, ColumnFiltersState, @@ -11,7 +12,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { Table, @@ -64,11 +65,36 @@ export function DataTableProvider({ { key: "connected", values: ["false", "true"] }, // Add more filter categories as needed ]; + const topBar = useMemo(() => { + return ( +
+
+

Providers

+ + 3 + +
+ +
+ ); + }, []); return ( <> + {topBar} + -
+ +
{table.getHeaderGroups().map((headerGroup) => ( diff --git a/components/ui/table/Table.tsx b/components/ui/table/Table.tsx index cdc1f5cfe6..91230d9f39 100644 --- a/components/ui/table/Table.tsx +++ b/components/ui/table/Table.tsx @@ -1,12 +1,11 @@ +import { cn } from "@nextui-org/react"; import * as React from "react"; -import { cn } from "@/lib/utils"; - const Table = React.forwardRef< HTMLTableElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
>(({ className, ...props }, ref) => ( - + tr]:first:rounded-lg [&>tr]:first:shadow-small", + className, + )} + {...props} + /> )); TableHeader.displayName = "TableHeader"; @@ -28,11 +34,7 @@ const TableBody = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableBody.displayName = "TableBody"; @@ -43,7 +45,7 @@ const TableFooter = React.forwardRef< tr]:last:border-b-0 dark:bg-slate-800/50", + "font-medium [&>tr]:last:border-b-0 dark:bg-slate-800/50", className, )} {...props} @@ -58,7 +60,7 @@ const TableRow = React.forwardRef< (({ className, ...props }, ref) => (
)); @@ -99,7 +104,7 @@ const TableCaption = React.forwardRef< >(({ className, ...props }, ref) => (
)); diff --git a/tailwind.config.js b/tailwind.config.js index 47d4df9ac4..18cf88a602 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -19,6 +19,31 @@ module.exports = { }, extend: { colors: { + prowler: { + blue: { + midnight: "#030921", + pale: "#f3fcff", + }, + grey: { + medium: "#353a4d", + light: "#868994", + }, + theme: { + green: "#6af400", + purple: "#5001d0", + coral: "#ff5356", + orange: "#f69000", + yellow: "#ffdf16", + }, + dark: { + title: "#E2E8F0", + text: "#94a3b8" /* primary default for dark mode */, + }, + light: { + title: "#1e293bff", + text: "#64748b" /* primary default for light mode */, + }, + }, system: { success: { DEFAULT: "#09BF3D", @@ -56,6 +81,47 @@ module.exports = { sans: ["var(--font-sans)"], mono: ["var(--font-geist-mono)"], }, + boxShadow: { + "box-light": + "-16px -16px 40px rgba(255, 255, 255, 0.8), 16px 4px 64px rgba(18, 61, 101, 0.3), inset -8px -6px 80px rgba(255, 255, 255, 0.18)", + "button-curved-default": + "-4px -2px 16px rgba(255, 255, 255, 0.7), 4px 2px 16px rgba(136, 165, 191, 0.38)", + "button-curved-pressed": + "inset -4px -4px 16px rgba(255, 255, 255, 0.8), inset 4px 4px 12px rgba(136, 165, 191, 0.4)", + "button-flat-nopressed": + "-4px -2px 16px #FFFFFF, 4px 2px 16px rgba(136, 165, 191, 0.48)", + "button-flat-pressed": + "inset -3px -3px 7px #FFFFFF, inset 3px 3px 7px rgba(136, 165, 191, 0.48)", + "box-down-light": + "inset -3px -3px 7px #FFFFFF, inset 2px 2px 5px rgba(136, 165, 191, 0.38)", + "box-up": + "-4px -2px 16px #FFFFFF, 4px 2px 16px rgba(136, 165, 191, 0.54)", + "box-dark": + "-4px -2px 16px rgba(195, 200, 205, 0.09), 4px 4px 18px rgba(0, 0, 0, 0.5)", + "box-dark-out": "inset 2px 2px 2px rgba(26, 32, 38, 0.4)", + "buttons-box-dark": + "-5px -6px 16px rgba(195, 200, 205, 0.04), 22px 22px 60px rgba(0, 0, 0, 0.5)", + "button-curved-default-dark": + "-4px -4px 16px rgba(195, 200, 205, 0.06), 4px 4px 18px rgba(0, 0, 0, 0.6)", + "button-curved-pressed-dark": + "-4px -2px 16px rgba(195, 200, 205, 0.07), 4px 4px 18px rgba(0, 0, 0, 0.44)", + "sky-light": + "-16px 20px 40px rgba(215, 215, 215, 0.3), -2px 2px 24px rgba(22, 28, 47, 0.3), -16px 28px 120px rgba(0, 0, 0, 0.1)", + "midnight-dark": + "-16px 20px 40px rgba(3, 9, 33, 0.3), -2px 2px 24px rgba(3, 9, 33, 0.6), -16px 28px 120px rgba(3, 9, 33, 0.1)", + switcher: + "0px -6px 24px #FFFFFF, 0px 7px 16px rgba(104, 132, 157, 0.5)", + up: "0.3rem 0.3rem 0.6rem #c8d0e7, -0.2rem -0.2rem 0.5rem #fff", + down: "inset 0.2rem 0.2rem 0.5rem #c8d0e7, inset -0.2rem -0.2rem 0.5rem #fff", + }, + animation: { + "fade-in": "fade-in 200ms ease-out 0s 1 normal forwards running", + "fade-out": "fade-out 200ms ease-in 0s 1 normal forwards running", + expand: "expand 400ms linear 0s 1 normal forwards running", + "slide-in": "slide-in 400ms linear 0s 1 normal forwards running", + "slide-out": "slide-out 400ms linear 0s 1 normal forwards running", + collapse: "collapse 400ms linear 0s 1 normal forwards running", + }, keyframes: { "accordion-down": { from: { height: "0" }, From 4b74a8a008760bbfff338362c3c7ba3cc62608d2 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:01:16 +0200 Subject: [PATCH 02/24] feat: add a header to use basic auth --- actions/providers.ts | 83 +++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/actions/providers.ts b/actions/providers.ts index d4b0dc4d29..22084527ed 100644 --- a/actions/providers.ts +++ b/actions/providers.ts @@ -6,6 +6,10 @@ import { redirect } from "next/navigation"; import { auth } from "@/auth.config"; import { parseStringify } from "@/lib"; +// Credentials for basic auth +const username = process.env.API_USERNAME; +const password = process.env.API_PASSWORD; + export const getProviders = async ({ page = 1, query = "", @@ -35,6 +39,7 @@ export const getProviders = async ({ const providers = await fetch(url.toString(), { headers: { "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), }, }); const data = await providers.json(); @@ -59,6 +64,7 @@ export const getProvider = async (formData: FormData) => { const providers = await fetch(url.toString(), { headers: { "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), }, }); const data = await providers.json(); @@ -86,6 +92,7 @@ export const updateProvider = async (formData: FormData) => { method: "PATCH", headers: { "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), "Content-Type": "application/vnd.api+json", }, body: JSON.stringify({ @@ -110,17 +117,24 @@ export const updateProvider = async (formData: FormData) => { }; export const addProvider = async (formData: FormData) => { + console.log(formData); + const session = await auth(); const keyServer = process.env.API_BASE_URL; + const tenantId = session?.user.tenantId; + const provider = formData.get("provider"); - const providerId = formData.get("id"); + const providerId = formData.get("providerId"); const alias = formData.get("alias"); + const url = new URL(`${keyServer}/providers`); + try { - const response = await fetch(`${keyServer}/providers`, { + const response = await fetch(url.toString(), { method: "POST", headers: { - "X-Tenant-ID": `${process.env.HEADER_TENANT_ID}`, + "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), "Content-Type": "application/vnd.api+json", }, body: JSON.stringify({ @@ -128,7 +142,7 @@ export const addProvider = async (formData: FormData) => { type: "Provider", attributes: { provider: provider, - provider_id: providerId, + uid: providerId, alias: alias, }, }, @@ -146,40 +160,20 @@ export const addProvider = async (formData: FormData) => { }; export const checkConnectionProvider = async (formData: FormData) => { + const session = await auth(); const keyServer = process.env.API_BASE_URL; + const tenantId = session?.user.tenantId; const providerId = formData.get("id"); - try { - const response = await fetch( - `${keyServer}/providers/${providerId}/connection`, - { - method: "POST", - headers: { - "X-Tenant-ID": `${process.env.HEADER_TENANT_ID}`, - }, - }, - ); - const data = await response.json(); - revalidatePath("/providers"); - return parseStringify(data); - } catch (error) { - return { - error: getErrorMessage(error), - }; - } -}; - -export const deleteProvider = async (formData: FormData) => { - const keyServer = process.env.API_BASE_URL; - - const providerId = formData.get("id"); + const url = new URL(`${keyServer}/providers/${providerId}/connection`); try { - const response = await fetch(`${keyServer}/providers/${providerId}`, { - method: "DELETE", + const response = await fetch(url.toString(), { + method: "POST", headers: { - "X-Tenant-ID": `${process.env.HEADER_TENANT_ID}`, + "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), }, }); const data = await response.json(); @@ -191,6 +185,33 @@ export const deleteProvider = async (formData: FormData) => { }; } }; + +export const deleteProvider = async (formData: FormData) => { + const session = await auth(); + const keyServer = process.env.API_BASE_URL; + const tenantId = session?.user.tenantId; + + const providerId = formData.get("id"); + const url = new URL(`${keyServer}/providers/${providerId}`); + + try { + const response = await fetch(url.toString(), { + method: "DELETE", + headers: { + "X-Tenant-ID": `${tenantId}`, + Authorization: "Basic " + btoa(`${username}:${password}`), + }, + }); + const data = await response.json(); + revalidatePath("/providers"); + return parseStringify(data); + } catch (error) { + return { + error: getErrorMessage(error), + }; + } +}; + export const getErrorMessage = (error: unknown): string => { let message: string; From c544a069a24a9a6c4c371bbe5a550f82ff7cd064 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:01:42 +0200 Subject: [PATCH 03/24] feat: create and integrate custom button component --- components/ui/custom/CustomButton.tsx | 84 +++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 components/ui/custom/CustomButton.tsx diff --git a/components/ui/custom/CustomButton.tsx b/components/ui/custom/CustomButton.tsx new file mode 100644 index 0000000000..1c322aaac3 --- /dev/null +++ b/components/ui/custom/CustomButton.tsx @@ -0,0 +1,84 @@ +import { Button } from "@nextui-org/react"; +import clsx from "clsx"; + +export const buttonClasses = { + base: "w-full md:w-fit px-4 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap", + primary: "bg-default-100 hover:bg-default-200 text-default-800", + secondary: "bg-prowler-grey-light dark:bg-prowler-grey-medium text-white", + action: "text-white bg-prowler-blue-smoky dark:bg-prowler-grey-medium", + dashed: + "border border-default border-dashed bg-transparent hover:bg-accent justify-center whitespace-nowrap font-medium shadow-sm hover:bg-accent hover:text-accent-foreground", + transparent: "border-0 border-transparent bg-transparent", + disabled: "pointer-events-none opacity-40", + hover: "hover:shadow-md", +}; + +interface ButtonProps { + variant?: + | "solid" + | "faded" + | "bordered" + | "light" + | "flat" + | "ghost" + | "dashed" + | "shadow"; + color?: + | "primary" + | "secondary" + | "action" + | "success" + | "warning" + | "danger" + | "transparent"; + onClick?: (event: React.MouseEvent) => void; + children: React.ReactNode; + startContent?: React.ReactNode; + endContent?: React.ReactNode; + size?: "sm" | "md" | "lg"; + radius?: "none" | "sm" | "md" | "lg" | "full"; + dashed?: boolean; + disabled?: boolean; + isLoading?: boolean; + isIconOnly?: boolean; +} + +export const CustomButton = ({ + variant = "solid", + color = "primary", + onClick, + children, + startContent, + endContent, + size = "md", + radius = "sm", + disabled = false, + isLoading = false, + isIconOnly, + ...props +}: ButtonProps) => ( + +); From a70fcf488d89fa1d7d49105d0f102cacc605d51c Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:02:13 +0200 Subject: [PATCH 04/24] chore: add new colors --- tailwind.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tailwind.config.js b/tailwind.config.js index 18cf88a602..e9dd78c401 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -23,11 +23,16 @@ module.exports = { blue: { midnight: "#030921", pale: "#f3fcff", + smoky: "#7b8390", }, grey: { medium: "#353a4d", light: "#868994", }, + green: { + DEFAULT: "#9FD655", + medium: "#09BF3D", + }, theme: { green: "#6af400", purple: "#5001d0", From 8db20eb2ba8dcc54c63e275186dac62daea44df9 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:02:52 +0200 Subject: [PATCH 05/24] chore: add new colors --- components/ui/custom/index.ts | 1 + types/components.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/ui/custom/index.ts b/components/ui/custom/index.ts index 0520404750..4c1021bc9e 100644 --- a/components/ui/custom/index.ts +++ b/components/ui/custom/index.ts @@ -1,5 +1,6 @@ export * from "./CustomAlertModal"; export * from "./CustomBox"; +export * from "./CustomButton"; export * from "./CustomButtonClientAction"; export * from "./CustomInput"; export * from "./CustomLoader"; diff --git a/types/components.ts b/types/components.ts index bd2502a44b..237d08d7e9 100644 --- a/types/components.ts +++ b/types/components.ts @@ -17,7 +17,7 @@ export interface ProviderProps { type: "providers"; attributes: { provider: "aws" | "azure" | "gcp"; - provider_id: string; + uid: string; alias: string; status: "completed" | "pending" | "cancelled"; resources: number; From ede597d02d37dd989fa99eca7fe2e7b8a54e05a0 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:03:13 +0200 Subject: [PATCH 06/24] chore: add new icons --- components/icons/Icons.tsx | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx index c6383a2af2..b590eb4661 100644 --- a/components/icons/Icons.tsx +++ b/components/icons/Icons.tsx @@ -678,3 +678,56 @@ export const ChevronsLeftRightIcon: React.FC = ({ ); }; + +export const PlusCircleIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + + + ); +}; + +export const CustomFilterIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + ); +}; From 1f75d70d4e86ce8a426c6b478f9d5439e04c5166 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 26 Sep 2024 16:05:17 +0200 Subject: [PATCH 07/24] fix: fix the providerId, the value coming from the API changed --- components/providers/table/columns-provider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/providers/table/columns-provider.tsx b/components/providers/table/columns-provider.tsx index 2b7ecbb5a5..f104fcec69 100644 --- a/components/providers/table/columns-provider.tsx +++ b/components/providers/table/columns-provider.tsx @@ -40,15 +40,15 @@ export const ColumnsProvider: ColumnDef[] = [ }, }, { - accessorKey: "id", + accessorKey: "uid", header: ({ column }) => ( ), cell: ({ row }) => { const { - attributes: { provider_id }, + attributes: { uid }, } = getProviderData(row); - return ; + return ; }, }, { From 840df1dab65e7441bd3e8bf64a68b30750d4e3ff Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Fri, 27 Sep 2024 07:47:17 +0200 Subject: [PATCH 08/24] feat: hide scrollbar when needed --- app/(prowler)/layout.tsx | 2 +- components/providers/SnippetIdProvider.tsx | 5 ++++- components/ui/sidebar/SidebarWrap.tsx | 4 ++-- styles/globals.css | 7 +++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/(prowler)/layout.tsx b/app/(prowler)/layout.tsx index b30817c0ba..ebf66c7975 100644 --- a/app/(prowler)/layout.tsx +++ b/app/(prowler)/layout.tsx @@ -46,7 +46,7 @@ export default function RootLayout({
-
+
{children}
diff --git a/components/providers/SnippetIdProvider.tsx b/components/providers/SnippetIdProvider.tsx index 775e72eaf7..4d4b901f2c 100644 --- a/components/providers/SnippetIdProvider.tsx +++ b/components/providers/SnippetIdProvider.tsx @@ -12,6 +12,7 @@ export const SnippetIdProvider: React.FC = ({ return ( = ({ >

- {providerId} + + {providerId} +

); diff --git a/components/ui/sidebar/SidebarWrap.tsx b/components/ui/sidebar/SidebarWrap.tsx index bb9287a826..2da8d70670 100644 --- a/components/ui/sidebar/SidebarWrap.tsx +++ b/components/ui/sidebar/SidebarWrap.tsx @@ -42,7 +42,7 @@ export const SidebarWrap = () => { return (
{ />{" "} - + Date: Fri, 27 Sep 2024 09:42:48 +0200 Subject: [PATCH 09/24] chore: edit and delete provider form have been refactored using custom buttons --- components/providers/forms/DeleteForm.tsx | 42 +++++++++++------------ components/providers/forms/EditForm.tsx | 41 +++++++++++----------- components/ui/custom/CustomButton.tsx | 39 +++++++++++++-------- tailwind.config.js | 1 + 4 files changed, 65 insertions(+), 58 deletions(-) diff --git a/components/providers/forms/DeleteForm.tsx b/components/providers/forms/DeleteForm.tsx index 2a9d79902c..639bee8c93 100644 --- a/components/providers/forms/DeleteForm.tsx +++ b/components/providers/forms/DeleteForm.tsx @@ -1,13 +1,13 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Button, CircularProgress } from "@nextui-org/react"; import React, { Dispatch, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; import { deleteProvider } from "@/actions"; import { useToast } from "@/components/ui"; +import { CustomButton } from "@/components/ui/custom"; import { Form } from "@/components/ui/form"; const formSchema = z.object({ @@ -46,6 +46,7 @@ export const DeleteForm = ({ description: "The provider was removed successfully.", }); } + setIsOpen(false); // Close the modal on success } return ( @@ -53,31 +54,28 @@ export const DeleteForm = ({
- - + Cancel + + + + {isLoading ? <>Loading : Delete} +
diff --git a/components/providers/forms/EditForm.tsx b/components/providers/forms/EditForm.tsx index 8479aeeabb..f61ef8d7c1 100644 --- a/components/providers/forms/EditForm.tsx +++ b/components/providers/forms/EditForm.tsx @@ -1,14 +1,13 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Button, CircularProgress } from "@nextui-org/react"; import React, { Dispatch, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import * as z from "zod"; import { updateProvider } from "@/actions"; import { useToast } from "@/components/ui"; -import { CustomInput } from "@/components/ui/custom"; +import { CustomButton, CustomInput } from "@/components/ui/custom"; import { Form } from "@/components/ui/form"; import { editProviderFormSchema } from "@/types"; @@ -32,6 +31,7 @@ export const EditForm = ({ }); const { toast } = useToast(); + const isLoading = form.formState.isSubmitting; const onSubmitClient = async (values: z.infer) => { @@ -86,29 +86,28 @@ export const EditForm = ({
- - - + Cancel + + + + {isLoading ? <>Loading : Save} +
diff --git a/components/ui/custom/CustomButton.tsx b/components/ui/custom/CustomButton.tsx index 1c322aaac3..c361561846 100644 --- a/components/ui/custom/CustomButton.tsx +++ b/components/ui/custom/CustomButton.tsx @@ -1,8 +1,8 @@ -import { Button } from "@nextui-org/react"; +import { Button, CircularProgress } from "@nextui-org/react"; import clsx from "clsx"; export const buttonClasses = { - base: "w-full md:w-fit px-4 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap", + base: "px-4 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap", primary: "bg-default-100 hover:bg-default-200 text-default-800", secondary: "bg-prowler-grey-light dark:bg-prowler-grey-medium text-white", action: "text-white bg-prowler-blue-smoky dark:bg-prowler-grey-medium", @@ -14,6 +14,8 @@ export const buttonClasses = { }; interface ButtonProps { + type: "button" | "submit" | "reset"; + className?: string; variant?: | "solid" | "faded" @@ -31,7 +33,7 @@ interface ButtonProps { | "warning" | "danger" | "transparent"; - onClick?: (event: React.MouseEvent) => void; + onPress?: (event: React.MouseEvent) => void; children: React.ReactNode; startContent?: React.ReactNode; endContent?: React.ReactNode; @@ -44,9 +46,11 @@ interface ButtonProps { } export const CustomButton = ({ + type = "button", + className, variant = "solid", color = "primary", - onClick, + onPress, children, startContent, endContent, @@ -58,23 +62,28 @@ export const CustomButton = ({ ...props }: ButtonProps) => ( + Reset + )}
); diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx index 7d11f620aa..1d2b8d4b1a 100644 --- a/components/icons/Icons.tsx +++ b/components/icons/Icons.tsx @@ -305,28 +305,31 @@ export const WifiPendingIcon: React.FC = ({ ); export const DeleteIcon: React.FC = ({ - size = 24, - width, + size, height, + width, ...props -}) => ( - -); +}) => { + return ( + + ); +}; export const CheckIcon: React.FC = ({ size = 24, From 8e1c8304d8bf0919f635f6736ef6e433290137c9 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Fri, 27 Sep 2024 10:42:16 +0200 Subject: [PATCH 12/24] feat: fix TS errors in customButton component --- components/ui/custom/CustomButton.tsx | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/components/ui/custom/CustomButton.tsx b/components/ui/custom/CustomButton.tsx index 4b1ce2b2a5..80671af547 100644 --- a/components/ui/custom/CustomButton.tsx +++ b/components/ui/custom/CustomButton.tsx @@ -1,13 +1,31 @@ import { Button, CircularProgress } from "@nextui-org/react"; +import type { PressEvent } from "@react-types/shared"; import clsx from "clsx"; +type NextUIVariants = + | "solid" + | "faded" + | "bordered" + | "light" + | "flat" + | "ghost" + | "shadow"; + +type NextUIColors = + | "primary" + | "secondary" + | "success" + | "warning" + | "danger" + | "default"; + export const buttonClasses = { base: "px-2 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap", primary: "bg-default-100 hover:bg-default-200 text-default-800", secondary: "bg-prowler-grey-light dark:bg-prowler-grey-medium text-white", action: "text-white bg-prowler-blue-smoky dark:bg-prowler-grey-medium", dashed: - "border border-default border-dashed bg-transparent hover:bg-accent justify-center whitespace-nowrap font-medium shadow-sm hover:bg-accent hover:text-accent-foreground", + "border border-default border-dashed bg-transparent justify-center whitespace-nowrap font-medium shadow-sm hover:border-solid hover:bg-default-100 active:bg-default-200 active:border-solid", transparent: "border-0 border-transparent bg-transparent", disabled: "pointer-events-none opacity-40", hover: "hover:shadow-md", @@ -33,7 +51,7 @@ interface ButtonProps { | "warning" | "danger" | "transparent"; - onPress?: (event: React.MouseEvent) => void; + onPress?: (e: PressEvent) => void; children: React.ReactNode; startContent?: React.ReactNode; endContent?: React.ReactNode; @@ -64,8 +82,8 @@ export const CustomButton = ({ - - - - - Add cloud account - - - You must manually deploy a new read-only IAM role for each account - you want to add. The following links will provide detailed - instructions how to do this: - - -
setOpen(false)} - className="grid sm:grid-cols-2 gap-6" - > -
- -
-
- - -
-
- -
-
-
- - ); -}; diff --git a/components/providers/ButtonAddProvider.tsx b/components/providers/ButtonAddProvider.tsx deleted file mode 100644 index 30f537ceae..0000000000 --- a/components/providers/ButtonAddProvider.tsx +++ /dev/null @@ -1,14 +0,0 @@ -"use client"; - -import { Button, CircularProgress } from "@nextui-org/react"; -import React from "react"; -import { useFormStatus } from "react-dom"; - -export const ButtonAddProvider = () => { - const { pending } = useFormStatus(); - return ( - - ); -}; diff --git a/components/providers/CustomRadioProvider.tsx b/components/providers/CustomRadioProvider.tsx index 05c9926b0a..68d2c59576 100644 --- a/components/providers/CustomRadioProvider.tsx +++ b/components/providers/CustomRadioProvider.tsx @@ -3,9 +3,15 @@ import { UseRadioProps } from "@nextui-org/radio/dist/use-radio"; import { cn, RadioGroup, useRadio, VisuallyHidden } from "@nextui-org/react"; import React from "react"; +import { Control, Controller } from "react-hook-form"; +import { z } from "zod"; + +import { addProviderFormSchema } from "@/types"; import { AWSProviderBadge, AzureProviderBadge } from "../icons/providers-badge"; import { GCPProviderBadge } from "../icons/providers-badge/GCPProviderBadge"; +import { KS8ProviderBadge } from "../icons/providers-badge/KS8ProviderBadge"; +import { FormMessage } from "../ui/form"; interface CustomRadioProps extends UseRadioProps { description?: string; @@ -52,27 +58,48 @@ export const CustomRadio: React.FC = (props) => { ); }; -export const CustomRadioProvider = () => { +interface CustomRadioProviderProps { + control: Control>; +} + +export const CustomRadioProvider: React.FC = ({ + control, +}) => { return ( - - -
- - AWS -
-
- -
- - GCP -
-
- -
- - Azure -
-
-
+ ( + <> + + +
+ + AWS +
+
+ +
+ + GCP +
+
+ +
+ + Azure +
+
+ +
+ + Kubernetes +
+
+
+ + + )} + /> ); }; diff --git a/components/providers/add-provider.tsx b/components/providers/add-provider.tsx new file mode 100644 index 0000000000..a665bb8309 --- /dev/null +++ b/components/providers/add-provider.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { Chip } from "@nextui-org/react"; +import { useState } from "react"; + +import { AddIcon } from "../icons"; +import { CustomAlertModal, CustomButton } from "../ui/custom"; +import { AddForm } from "./forms"; + +export const AddProvider = () => { + const [isAddOpen, setIsAddOpen] = useState(false); + + return ( + <> + + + + +
+
+

TBD

+ + 3 + +
+ setIsAddOpen(true)} + endContent={} + > + Add Account + +
+ + ); +}; diff --git a/components/providers/forms/AddForm.tsx b/components/providers/forms/AddForm.tsx new file mode 100644 index 0000000000..02ba53d67b --- /dev/null +++ b/components/providers/forms/AddForm.tsx @@ -0,0 +1,123 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { Dispatch, SetStateAction } from "react"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; + +import { addProvider } from "@/actions"; +import { SaveIcon } from "@/components/icons"; +import { useToast } from "@/components/ui"; +import { CustomButton, CustomInput } from "@/components/ui/custom"; +import { Form } from "@/components/ui/form"; +import { addProviderFormSchema } from "@/types"; + +import { CustomRadioProvider } from "../CustomRadioProvider"; + +export const AddForm = ({ + setIsOpen, +}: { + setIsOpen: Dispatch>; +}) => { + const formSchema = addProviderFormSchema; + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + providerType: "", + providerId: "", + providerAlias: "", + }, + }); + + const { toast } = useToast(); + + const isLoading = form.formState.isSubmitting; + + const onSubmitClient = async (values: z.infer) => { + console.log(values); + const formData = new FormData(); + + Object.entries(values).forEach( + ([key, value]) => value !== undefined && formData.append(key, value), + ); + + const data = await addProvider(formData); + + if (data?.errors && data.errors.length > 0) { + const error = data.errors[0]; + const errorMessage = `${error.detail}`; + // show error + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } else { + toast({ + title: "Success!", + description: "The provider was updated successfully.", + }); + setIsOpen(false); // Close the modal on success + } + }; + + return ( +
+ + + + + +
+ setIsOpen(false)} + disabled={isLoading} + > + Cancel + + + } + > + {isLoading ? <>Loading : Confirm} + +
+ + + ); +}; diff --git a/components/providers/forms/index.ts b/components/providers/forms/index.ts index af1cdc1b37..7bab77754f 100644 --- a/components/providers/forms/index.ts +++ b/components/providers/forms/index.ts @@ -1,2 +1,3 @@ +export * from "./AddForm"; export * from "./DeleteForm"; export * from "./EditForm"; diff --git a/components/providers/index.ts b/components/providers/index.ts index b92fe7eb83..0709c892b2 100644 --- a/components/providers/index.ts +++ b/components/providers/index.ts @@ -1,6 +1,4 @@ -export * from "./AddProvider"; -export * from "./AddProviderModal"; -export * from "./ButtonAddProvider"; +export * from "./add-provider"; export * from "./CheckConnectionProvider"; export * from "./CustomRadioProvider"; export * from "./DateWithTime"; diff --git a/components/providers/table/data-table-filter-custom.tsx b/components/providers/table/data-table-filter-custom.tsx index fea6df47ab..23388fd271 100644 --- a/components/providers/table/data-table-filter-custom.tsx +++ b/components/providers/table/data-table-filter-custom.tsx @@ -1,18 +1,21 @@ "use client"; -import { Button } from "@nextui-org/react"; -// import { -// Button, -// Input, -// Popover, -// PopoverContent, -// PopoverTrigger, -// RadioGroup, -// } from "@nextui-org/react"; -// import { Icon, Radio, SearchIcon } from "lucide-react"; +import { + Button, + Checkbox, + CheckboxGroup, + Input, + Popover, + PopoverContent, + PopoverTrigger, +} from "@nextui-org/react"; +import { SearchIcon } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import { useCallback, useMemo } from "react"; +import { CustomFilterIcon, PlusCircleIcon } from "@/components/icons"; +import { CustomButton } from "@/components/ui/custom"; + interface DataTableFilterCustomProps { filters: { key: string; values: string[] }[]; } @@ -53,7 +56,7 @@ export function DataTableFilterCustom({ filters }: DataTableFilterCustomProps) { return ( <> - {/*
+
-
- - All - Employee - Contractor - - - - All - Active - Inactive - Paused - Vacation - - - - All - Last 7 days - Last 30 days - Last 60 days - + Buenos Aires + Sydney + San Francisco + London + Tokyo +
-
*/} +
+ } + > + Show Filters + {filters.flatMap(({ key, values }) => values.map((value) => ( diff --git a/components/providers/table/data-table-provider.tsx b/components/providers/table/data-table-provider.tsx index 6ea0133cb4..7999009cde 100644 --- a/components/providers/table/data-table-provider.tsx +++ b/components/providers/table/data-table-provider.tsx @@ -1,6 +1,5 @@ "use client"; -import { Icon } from "@iconify/react"; -import { Button, Chip } from "@nextui-org/react"; + import { ColumnDef, ColumnFiltersState, @@ -12,7 +11,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { useMemo, useState } from "react"; +import { useState } from "react"; import { Table, @@ -65,33 +64,9 @@ export function DataTableProvider({ { key: "connected", values: ["false", "true"] }, // Add more filter categories as needed ]; - const topBar = useMemo(() => { - return ( -
-
-

Providers

- - 3 - -
- -
- ); - }, []); return ( <> - {topBar} -
diff --git a/types/components.ts b/types/components.ts index 3727d94f86..28ffbe7355 100644 --- a/types/components.ts +++ b/types/components.ts @@ -33,7 +33,7 @@ export interface ProviderProps { id: string; type: "providers"; attributes: { - provider: "aws" | "azure" | "gcp"; + provider: "aws" | "azure" | "gcp" | "kubernetes"; uid: string; alias: string; status: "completed" | "pending" | "cancelled"; diff --git a/types/formSchemas.ts b/types/formSchemas.ts index ee174f95d0..3726245f68 100644 --- a/types/formSchemas.ts +++ b/types/formSchemas.ts @@ -1,5 +1,11 @@ import { z } from "zod"; +export const addProviderFormSchema = z.object({ + providerType: z.string(), + providerAlias: z.string(), + providerId: z.string(), +}); + export const editProviderFormSchema = (currentAlias: string) => z.object({ alias: z From 1ea8addb04e6bd70dae82c38d7001cd4e0cf78a4 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Fri, 27 Sep 2024 15:53:16 +0200 Subject: [PATCH 16/24] chore: remove unused console log --- components/providers/forms/AddForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/components/providers/forms/AddForm.tsx b/components/providers/forms/AddForm.tsx index 02ba53d67b..be508dca0b 100644 --- a/components/providers/forms/AddForm.tsx +++ b/components/providers/forms/AddForm.tsx @@ -35,7 +35,6 @@ export const AddForm = ({ const isLoading = form.formState.isSubmitting; const onSubmitClient = async (values: z.infer) => { - console.log(values); const formData = new FormData(); Object.entries(values).forEach( From f496896884fb3a1912058b1d3c47fb3d0ecd9551 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Sat, 28 Sep 2024 05:29:04 +0200 Subject: [PATCH 17/24] feat: change render order by default, rename components, simplify structure --- actions/providers.ts | 2 +- components/findings/table/ColumnsFindings.tsx | 3 +-- components/findings/table/DataTableFindings.tsx | 2 +- components/providers/table/columns-provider.tsx | 2 +- components/providers/table/data-table-provider.tsx | 2 +- components/ui/index.ts | 3 --- components/ui/table/index.ts | 4 ++++ components/ui/table/{SeverityBadge.tsx => severity-badge.tsx} | 0 components/ui/table/{StatusBadge.tsx => status-badge.tsx} | 0 components/users/table/ColumnsUser.tsx | 2 +- components/users/table/DataTableUser.tsx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 components/ui/table/index.ts rename components/ui/table/{SeverityBadge.tsx => severity-badge.tsx} (100%) rename components/ui/table/{StatusBadge.tsx => status-badge.tsx} (100%) diff --git a/actions/providers.ts b/actions/providers.ts index 3dc232a39e..a35bbb1d98 100644 --- a/actions/providers.ts +++ b/actions/providers.ts @@ -22,7 +22,7 @@ export const getProviders = async ({ if (isNaN(Number(page)) || page < 1) redirect("/providers"); const keyServer = process.env.API_BASE_URL; - const url = new URL(`${keyServer}/providers`); + const url = new URL(`${keyServer}/providers?sort=-inserted_at`); if (page) url.searchParams.append("page[number]", page.toString()); if (query) url.searchParams.append("filter[search]", query); diff --git a/components/findings/table/ColumnsFindings.tsx b/components/findings/table/ColumnsFindings.tsx index 11d6d7b02c..ba9d128300 100644 --- a/components/findings/table/ColumnsFindings.tsx +++ b/components/findings/table/ColumnsFindings.tsx @@ -10,8 +10,7 @@ import { import { ColumnDef } from "@tanstack/react-table"; import { VerticalDotsIcon } from "@/components/icons"; -import { StatusBadge } from "@/components/ui"; -import { SeverityBadge } from "@/components/ui"; +import { SeverityBadge, StatusBadge } from "@/components/ui/table"; import { FindingProps } from "@/types"; const getFindingsAttributes = (row: { original: FindingProps }) => { diff --git a/components/findings/table/DataTableFindings.tsx b/components/findings/table/DataTableFindings.tsx index 1c1fa7da9c..91fa9077ea 100644 --- a/components/findings/table/DataTableFindings.tsx +++ b/components/findings/table/DataTableFindings.tsx @@ -19,7 +19,7 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui"; +} from "@/components/ui/table"; import { FindingProps } from "@/types"; interface DataTableFindingsProps { diff --git a/components/providers/table/columns-provider.tsx b/components/providers/table/columns-provider.tsx index f104fcec69..5f19489948 100644 --- a/components/providers/table/columns-provider.tsx +++ b/components/providers/table/columns-provider.tsx @@ -3,7 +3,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { add } from "date-fns"; -import { StatusBadge } from "@/components/ui"; +import { StatusBadge } from "@/components/ui/table"; import { ProviderProps } from "@/types"; import { DateWithTime } from "../DateWithTime"; diff --git a/components/providers/table/data-table-provider.tsx b/components/providers/table/data-table-provider.tsx index 7999009cde..9b59765ee8 100644 --- a/components/providers/table/data-table-provider.tsx +++ b/components/providers/table/data-table-provider.tsx @@ -20,7 +20,7 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui"; +} from "@/components/ui/table"; import { MetaDataProps } from "@/types"; import { DataTableFilterCustom } from "./data-table-filter-custom"; diff --git a/components/ui/index.ts b/components/ui/index.ts index 17f6f0e874..4f54c7d5b1 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -8,7 +8,4 @@ export * from "./header/Header"; export * from "./label/Label"; export * from "./select/Select"; export * from "./sidebar"; -export * from "./table/SeverityBadge"; -export * from "./table/StatusBadge"; -export * from "./table/Table"; export * from "./toast"; diff --git a/components/ui/table/index.ts b/components/ui/table/index.ts new file mode 100644 index 0000000000..36437ee01e --- /dev/null +++ b/components/ui/table/index.ts @@ -0,0 +1,4 @@ +// export * from "./data-table-toolbar"; +export * from "./severity-badge"; +export * from "./status-badge"; +export * from "./table"; diff --git a/components/ui/table/SeverityBadge.tsx b/components/ui/table/severity-badge.tsx similarity index 100% rename from components/ui/table/SeverityBadge.tsx rename to components/ui/table/severity-badge.tsx diff --git a/components/ui/table/StatusBadge.tsx b/components/ui/table/status-badge.tsx similarity index 100% rename from components/ui/table/StatusBadge.tsx rename to components/ui/table/status-badge.tsx diff --git a/components/users/table/ColumnsUser.tsx b/components/users/table/ColumnsUser.tsx index 70a05c189a..605120c707 100644 --- a/components/users/table/ColumnsUser.tsx +++ b/components/users/table/ColumnsUser.tsx @@ -3,7 +3,7 @@ import { ColumnDef } from "@tanstack/react-table"; import { DateWithTime } from "@/components/providers"; -import { StatusBadge } from "@/components/ui"; +import { StatusBadge } from "@/components/ui/table"; import { UserActions } from "@/components/users"; import { UserProps } from "@/types"; diff --git a/components/users/table/DataTableUser.tsx b/components/users/table/DataTableUser.tsx index b13093c2ab..68fee28e55 100644 --- a/components/users/table/DataTableUser.tsx +++ b/components/users/table/DataTableUser.tsx @@ -18,7 +18,7 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui"; +} from "@/components/ui/table"; import { MetaDataProps } from "@/types"; import { DataTablePagination } from "./DataTablePagination"; From ba1f8c9a3a7367a4d5311e7c91ffe9ca847e176e Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Sat, 28 Sep 2024 06:13:28 +0200 Subject: [PATCH 18/24] chore: handle lib folder dependencies with nextui --- lib/custom.ts | 21 ++++++++++ lib/index.ts | 1 + lib/utils.ts | 105 +------------------------------------------------- 3 files changed, 23 insertions(+), 104 deletions(-) create mode 100644 lib/custom.ts diff --git a/lib/custom.ts b/lib/custom.ts new file mode 100644 index 0000000000..075f6b95c8 --- /dev/null +++ b/lib/custom.ts @@ -0,0 +1,21 @@ +import { MetaDataProps } from "@/types"; + +export const parseStringify = (value: any) => JSON.parse(JSON.stringify(value)); + +export const convertFileToUrl = (file: File) => URL.createObjectURL(file); + +export const getPaginationInfo = (metadata: MetaDataProps) => { + const currentPage = metadata?.pagination.page ?? "1"; + const totalPages = metadata?.pagination.pages; + const totalEntries = metadata?.pagination.count; + + return { currentPage, totalPages, totalEntries }; +}; + +export function encryptKey(passkey: string) { + return btoa(passkey); +} + +export function decryptKey(passkey: string) { + return atob(passkey); +} diff --git a/lib/index.ts b/lib/index.ts index 3ed9e10f8f..8edc14574b 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,2 +1,3 @@ +export * from "./custom"; export * from "./seed"; export * from "./utils"; diff --git a/lib/utils.ts b/lib/utils.ts index c236eefe6a..365058cebd 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,109 +1,6 @@ import { type ClassValue, clsx } from "clsx"; -import { extendTailwindMerge } from "tailwind-merge"; - -const COMMON_UNITS = ["small", "medium", "large"]; -import { MetaDataProps } from "@/types"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -const twMerge = extendTailwindMerge({ - extend: { - theme: { - opacity: ["disabled"], - spacing: ["divider"], - borderWidth: COMMON_UNITS, - borderRadius: COMMON_UNITS, - }, - classGroups: { - shadow: [{ shadow: COMMON_UNITS }], - "font-size": [{ text: ["tiny", ...COMMON_UNITS] }], - "bg-image": ["bg-stripe-gradient"], - }, - }, -}); -export const parseStringify = (value: any) => JSON.parse(JSON.stringify(value)); - -export const convertFileToUrl = (file: File) => URL.createObjectURL(file); - -export const extractPaginationInfo = (metadata: MetaDataProps) => { - const currentPage = metadata?.pagination.page ?? "1"; - const totalPages = metadata?.pagination.pages; - const totalEntries = metadata?.pagination.count; - - return { currentPage, totalPages, totalEntries }; -}; - -// FORMAT DATE TIME -export const formatDateTime = ( - dateString: Date | string, - timeZone: string = Intl.DateTimeFormat().resolvedOptions().timeZone, -) => { - const dateTimeOptions: Intl.DateTimeFormatOptions = { - // weekday: "short", // abbreviated weekday name (e.g., 'Mon') - month: "short", // abbreviated month name (e.g., 'Oct') - day: "numeric", // numeric day of the month (e.g., '25') - year: "numeric", // numeric year (e.g., '2023') - hour: "numeric", // numeric hour (e.g., '8') - minute: "numeric", // numeric minute (e.g., '30') - hour12: true, // use 12-hour clock (true) or 24-hour clock (false), - timeZone: timeZone, // use the provided timezone - }; - - const dateDayOptions: Intl.DateTimeFormatOptions = { - weekday: "short", // abbreviated weekday name (e.g., 'Mon') - year: "numeric", // numeric year (e.g., '2023') - month: "2-digit", // abbreviated month name (e.g., 'Oct') - day: "2-digit", // numeric day of the month (e.g., '25') - timeZone: timeZone, // use the provided timezone - }; - - const dateOptions: Intl.DateTimeFormatOptions = { - month: "short", // abbreviated month name (e.g., 'Oct') - year: "numeric", // numeric year (e.g., '2023') - day: "numeric", // numeric day of the month (e.g., '25') - timeZone: timeZone, // use the provided timezone - }; - - const timeOptions: Intl.DateTimeFormatOptions = { - hour: "numeric", // numeric hour (e.g., '8') - minute: "numeric", // numeric minute (e.g., '30') - hour12: true, // use 12-hour clock (true) or 24-hour clock (false) - timeZone: timeZone, // use the provided timezone - }; - - const formattedDateTime: string = new Date(dateString).toLocaleString( - "en-US", - dateTimeOptions, - ); - - const formattedDateDay: string = new Date(dateString).toLocaleString( - "en-US", - dateDayOptions, - ); - - const formattedDate: string = new Date(dateString).toLocaleString( - "en-US", - dateOptions, - ); - - const formattedTime: string = new Date(dateString).toLocaleString( - "en-US", - timeOptions, - ); - - return { - dateTime: formattedDateTime, - dateDay: formattedDateDay, - dateOnly: formattedDate, - timeOnly: formattedTime, - }; -}; - -export function encryptKey(passkey: string) { - return btoa(passkey); -} - -export function decryptKey(passkey: string) { - return atob(passkey); -} From d9ec74b14948ec79da997e6d622c08adec1ce95c Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 06:21:56 +0200 Subject: [PATCH 19/24] chore: WIP --- .../providers/table/data-table-pagination.tsx | 5 +- .../ui/custom/custom-dropdown-filter.tsx | 112 ++++++++++++++++++ components/ui/custom/index.ts | 1 + .../users/table/DataTablePagination.tsx | 5 +- 4 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 components/ui/custom/custom-dropdown-filter.tsx diff --git a/components/providers/table/data-table-pagination.tsx b/components/providers/table/data-table-pagination.tsx index 0e195f0507..4c4278682f 100644 --- a/components/providers/table/data-table-pagination.tsx +++ b/components/providers/table/data-table-pagination.tsx @@ -9,7 +9,7 @@ import { import Link from "next/link"; import { usePathname, useSearchParams } from "next/navigation"; -import { extractPaginationInfo } from "@/lib"; +import { getPaginationInfo } from "@/lib"; import { MetaDataProps } from "@/types"; interface DataTablePaginationProps { @@ -22,8 +22,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) { const pathname = usePathname(); const searchParams = useSearchParams(); - const { currentPage, totalPages, totalEntries } = - extractPaginationInfo(metadata); + const { currentPage, totalPages, totalEntries } = getPaginationInfo(metadata); const createPageUrl = (pageNumber: number | string) => { const params = new URLSearchParams(searchParams); diff --git a/components/ui/custom/custom-dropdown-filter.tsx b/components/ui/custom/custom-dropdown-filter.tsx new file mode 100644 index 0000000000..e901bd2725 --- /dev/null +++ b/components/ui/custom/custom-dropdown-filter.tsx @@ -0,0 +1,112 @@ +"use client"; + +import { + Button, + Checkbox, + CheckboxGroup, + Popover, + PopoverContent, + PopoverTrigger, +} from "@nextui-org/react"; +import React, { useCallback, useState } from "react"; + +import { PlusCircleIcon } from "@/components/icons"; + +interface FilterOption { + key: string; + labelCheckboxGroup: string; + values: string[]; +} + +interface CustomDropdownFilterProps { + filter?: FilterOption; +} + +export const CustomDropdownFilter: React.FC = ({ + filter, +}) => { + // Early return if filter is undefined + if (!filter) { + return null; + } + + const [groupSelected, setGroupSelected] = useState(new Set()); + + const allFilterKeys = filter.values || []; + + const onSelectionChange = useCallback( + (keys: string[]) => { + const newSelection = new Set(keys); + + if ( + newSelection.size === allFilterKeys.length && + !newSelection.has("all") + ) { + setGroupSelected(new Set(["all", ...allFilterKeys])); + } else if (groupSelected.has("all")) { + newSelection.delete("all"); + const remainingValues = allFilterKeys.filter((key) => + newSelection.has(key), + ); + setGroupSelected(new Set(remainingValues)); + } else { + setGroupSelected(newSelection); + } + }, + [allFilterKeys, groupSelected], + ); + + const handleSelectAllClick = useCallback(() => { + if (groupSelected.has("all")) { + setGroupSelected(new Set()); + } else { + setGroupSelected(new Set(["all", ...allFilterKeys])); + } + }, [groupSelected, allFilterKeys]); + + return ( +
+ + + + + +
+ + + Select All + + {allFilterKeys.map((value) => ( + + {value} + + ))} + +
+
+
+

+ Selected:{" "} + {Array.from(groupSelected) + .filter((item) => item !== "all") + .join(", ")} +

+
+ ); +}; diff --git a/components/ui/custom/index.ts b/components/ui/custom/index.ts index 4c1021bc9e..dc543cb6ca 100644 --- a/components/ui/custom/index.ts +++ b/components/ui/custom/index.ts @@ -1,3 +1,4 @@ +export * from "./custom-dropdown-filter"; export * from "./CustomAlertModal"; export * from "./CustomBox"; export * from "./CustomButton"; diff --git a/components/users/table/DataTablePagination.tsx b/components/users/table/DataTablePagination.tsx index 0e195f0507..4c4278682f 100644 --- a/components/users/table/DataTablePagination.tsx +++ b/components/users/table/DataTablePagination.tsx @@ -9,7 +9,7 @@ import { import Link from "next/link"; import { usePathname, useSearchParams } from "next/navigation"; -import { extractPaginationInfo } from "@/lib"; +import { getPaginationInfo } from "@/lib"; import { MetaDataProps } from "@/types"; interface DataTablePaginationProps { @@ -22,8 +22,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) { const pathname = usePathname(); const searchParams = useSearchParams(); - const { currentPage, totalPages, totalEntries } = - extractPaginationInfo(metadata); + const { currentPage, totalPages, totalEntries } = getPaginationInfo(metadata); const createPageUrl = (pageNumber: number | string) => { const params = new URLSearchParams(searchParams); From db9faa2f4bf964226824344f20ef6f9b77caffa4 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 09:43:23 +0200 Subject: [PATCH 20/24] chore: WIP --- components/filters/FilterControls.tsx | 44 +++++++++++-------- components/filters/filters.ts | 13 ++++++ components/filters/index.ts | 1 + .../providers/table/data-table-provider.tsx | 13 ------ .../ui/custom/custom-dropdown-filter.tsx | 17 ++++--- 5 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 components/filters/filters.ts diff --git a/components/filters/FilterControls.tsx b/components/filters/FilterControls.tsx index fca3669405..b2aabdb337 100644 --- a/components/filters/FilterControls.tsx +++ b/components/filters/FilterControls.tsx @@ -4,6 +4,7 @@ import { useRouter, useSearchParams } from "next/navigation"; import React, { useCallback, useEffect, useState } from "react"; import { CrossIcon } from "../icons"; +import { DataTableFilterCustom } from "../providers/table"; import { CustomButton } from "../ui/custom"; import { CustomAccountSelection } from "./CustomAccountSelection"; import { CustomCheckboxMutedFindings } from "./CustomCheckboxMutedFindings"; @@ -19,6 +20,7 @@ interface FilterControlsProps { regions?: boolean; accounts?: boolean; mutedFindings?: boolean; + customFilters?: { key: string; values: string[] }[]; } export const FilterControls: React.FC = ({ @@ -28,6 +30,7 @@ export const FilterControls: React.FC = ({ regions = false, accounts = false, mutedFindings = false, + customFilters = [], }) => { const router = useRouter(); const searchParams = useSearchParams(); @@ -51,26 +54,29 @@ export const FilterControls: React.FC = ({ }, [router, searchParams]); return ( -
- {search && } - {providers && } - {date && } - {regions && } - {accounts && } - {mutedFindings && } +
+
+ {search && } + {providers && } + {date && } + {regions && } + {accounts && } + {mutedFindings && } - {showClearButton && ( - } - radius="sm" - > - Reset - - )} + {showClearButton && ( + } + radius="sm" + > + Reset + + )} +
+
); }; diff --git a/components/filters/filters.ts b/components/filters/filters.ts new file mode 100644 index 0000000000..22dddaf082 --- /dev/null +++ b/components/filters/filters.ts @@ -0,0 +1,13 @@ +export const filtersProviders = [ + { + key: "provider", + labelCheckboxGroup: "Provider", + values: ["aws", "gcp", "azure"], + }, + { + key: "connected", + labelCheckboxGroup: "Status provider", + values: ["false", "true"], + }, + // Add more filter categories as needed +]; diff --git a/components/filters/index.ts b/components/filters/index.ts index 7b437a7a63..f78eee1f34 100644 --- a/components/filters/index.ts +++ b/components/filters/index.ts @@ -5,3 +5,4 @@ export * from "../filters/CustomProviderInputs"; export * from "../filters/CustomRegionSelection"; export * from "../filters/CustomSelectProvider"; export * from "../filters/FilterControls"; +export * from "../filters/filters"; diff --git a/components/providers/table/data-table-provider.tsx b/components/providers/table/data-table-provider.tsx index 9b59765ee8..b2a0655779 100644 --- a/components/providers/table/data-table-provider.tsx +++ b/components/providers/table/data-table-provider.tsx @@ -23,7 +23,6 @@ import { } from "@/components/ui/table"; import { MetaDataProps } from "@/types"; -import { DataTableFilterCustom } from "./data-table-filter-custom"; import { DataTablePagination } from "./data-table-pagination"; interface DataTableProviderProps { @@ -55,20 +54,8 @@ export function DataTableProvider({ }, }); - // This will be used to have "custom" filters in the table and will be - // passed to the DataTableFilterCustom component. This needs to be dynamic - // based on the columns that are available in the table. - - const filters = [ - { key: "provider", values: ["aws", "gcp", "azure"] }, - { key: "connected", values: ["false", "true"] }, - // Add more filter categories as needed - ]; - return ( <> - -
diff --git a/components/ui/custom/custom-dropdown-filter.tsx b/components/ui/custom/custom-dropdown-filter.tsx index e901bd2725..972ed5b4b6 100644 --- a/components/ui/custom/custom-dropdown-filter.tsx +++ b/components/ui/custom/custom-dropdown-filter.tsx @@ -20,10 +20,12 @@ interface FilterOption { interface CustomDropdownFilterProps { filter?: FilterOption; + onFilterChange?: (key: string, values: string[]) => void; } export const CustomDropdownFilter: React.FC = ({ filter, + onFilterChange, }) => { // Early return if filter is undefined if (!filter) { @@ -52,8 +54,15 @@ export const CustomDropdownFilter: React.FC = ({ } else { setGroupSelected(newSelection); } + + if (onFilterChange && filter) { + const selectedValues = Array.from(newSelection).filter( + (key) => key !== "all", + ); + onFilterChange(filter.key, selectedValues); + } }, - [allFilterKeys, groupSelected], + [allFilterKeys, groupSelected, onFilterChange, filter], ); const handleSelectAllClick = useCallback(() => { @@ -85,11 +94,7 @@ export const CustomDropdownFilter: React.FC = ({ value={Array.from(groupSelected)} onValueChange={onSelectionChange} > - + Select All {allFilterKeys.map((value) => ( From f1469463194a6db7109e72d4aef6e43d6bf2e5f3 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 16:15:43 +0200 Subject: [PATCH 21/24] feat: big refactor for filters and tons of components --- app/(prowler)/providers/page.tsx | 5 +- ....tsx => custo-checkbox-muted-findings.tsx} | 0 ...ction.tsx => custom-account-selection.tsx} | 0 ...mDatePicker.tsx => custom-date-picker.tsx} | 2 - ...rInputs.tsx => custom-provider-inputs.tsx} | 0 ...ection.tsx => custom-region-selection.tsx} | 0 ...earchInput.tsx => custom-search-input.tsx} | 6 +- ...rovider.tsx => custom-select-provider.tsx} | 17 +- .../filters/{filters.ts => data-filters.ts} | 6 +- ...FilterControls.tsx => filter-controls.tsx} | 24 +- components/filters/index.ts | 16 +- components/icons/Icons.tsx | 17 +- components/providers/add-provider.tsx | 13 +- .../table/data-table-filter-custom.tsx | 149 +++---- .../ui/custom/custom-dropdown-filter.tsx | 104 +++-- .../ui/dialog/{Dialog.tsx => dialogg.tsx} | 10 +- components/ui/index.ts | 2 +- components/ui/table/index.ts | 1 - package-lock.json | 379 ++++++++++++++++++ package.json | 1 + types/filters.ts | 20 + types/index.ts | 1 + 22 files changed, 572 insertions(+), 201 deletions(-) rename components/filters/{CustomCheckboxMutedFindings.tsx => custo-checkbox-muted-findings.tsx} (100%) rename components/filters/{CustomAccountSelection.tsx => custom-account-selection.tsx} (100%) rename components/filters/{CustomDatePicker.tsx => custom-date-picker.tsx} (96%) rename components/filters/{CustomProviderInputs.tsx => custom-provider-inputs.tsx} (100%) rename components/filters/{CustomRegionSelection.tsx => custom-region-selection.tsx} (100%) rename components/filters/{CustomSearchInput.tsx => custom-search-input.tsx} (96%) rename components/filters/{CustomSelectProvider.tsx => custom-select-provider.tsx} (86%) rename components/filters/{filters.ts => data-filters.ts} (61%) rename components/filters/{FilterControls.tsx => filter-controls.tsx} (77%) rename components/ui/dialog/{Dialog.tsx => dialogg.tsx} (76%) create mode 100644 types/filters.ts diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index 7dcc5524aa..a10cd9a618 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -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({
- + @@ -33,7 +33,6 @@ export default async function Providers({ }> - {/* */} ); } diff --git a/components/filters/CustomCheckboxMutedFindings.tsx b/components/filters/custo-checkbox-muted-findings.tsx similarity index 100% rename from components/filters/CustomCheckboxMutedFindings.tsx rename to components/filters/custo-checkbox-muted-findings.tsx diff --git a/components/filters/CustomAccountSelection.tsx b/components/filters/custom-account-selection.tsx similarity index 100% rename from components/filters/CustomAccountSelection.tsx rename to components/filters/custom-account-selection.tsx diff --git a/components/filters/CustomDatePicker.tsx b/components/filters/custom-date-picker.tsx similarity index 96% rename from components/filters/CustomDatePicker.tsx rename to components/filters/custom-date-picker.tsx index 1fb89aa3e2..615acc5dd4 100644 --- a/components/filters/CustomDatePicker.tsx +++ b/components/filters/custom-date-picker.tsx @@ -59,7 +59,6 @@ export const CustomDatePicker = () => { return (
} CalendarTopContent={ { }} value={value} onChange={handleDateChange} - label="Scan date" size="sm" variant="flat" /> diff --git a/components/filters/CustomProviderInputs.tsx b/components/filters/custom-provider-inputs.tsx similarity index 100% rename from components/filters/CustomProviderInputs.tsx rename to components/filters/custom-provider-inputs.tsx diff --git a/components/filters/CustomRegionSelection.tsx b/components/filters/custom-region-selection.tsx similarity index 100% rename from components/filters/CustomRegionSelection.tsx rename to components/filters/custom-region-selection.tsx diff --git a/components/filters/CustomSearchInput.tsx b/components/filters/custom-search-input.tsx similarity index 96% rename from components/filters/CustomSearchInput.tsx rename to components/filters/custom-search-input.tsx index 09fb786e39..f958c9d6bd 100644 --- a/components/filters/CustomSearchInput.tsx +++ b/components/filters/custom-search-input.tsx @@ -37,7 +37,7 @@ export const CustomSearchInput: React.FC = () => { return ( { ) } - radius="full" - size="lg" + radius="sm" + size="sm" /> ); }; diff --git a/components/filters/CustomSelectProvider.tsx b/components/filters/custom-select-provider.tsx similarity index 86% rename from components/filters/CustomSelectProvider.tsx rename to components/filters/custom-select-provider.tsx index dd4254fdd3..ef65220660 100644 --- a/components/filters/CustomSelectProvider.tsx +++ b/components/filters/custom-select-provider.tsx @@ -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 ( - } - placeholder="Search" - size="sm" - // value={filterValue} - // onValueChange={onSearchChange} +
+ } + onPress={() => setShowFilters(!showFilters)} + > + {showFilters ? "Hide Filters" : "Show Filters"} + + +
+
+ {filters.map((filter) => ( + -
-
- - - - - -
- - Buenos Aires - Sydney - San Francisco - London - Tokyo - -
-
-
+ ))} +
+ + Selected
- -
- } - > - Show Filters - - {filters.flatMap(({ key, values }) => - values.map((value) => ( - - )), - )} -
- +
); -} +}; diff --git a/components/ui/custom/custom-dropdown-filter.tsx b/components/ui/custom/custom-dropdown-filter.tsx index 972ed5b4b6..f8d9d82b96 100644 --- a/components/ui/custom/custom-dropdown-filter.tsx +++ b/components/ui/custom/custom-dropdown-filter.tsx @@ -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 = ({ filter, onFilterChange, }) => { - // Early return if filter is undefined - if (!filter) { - return null; - } - + const searchParams = useSearchParams(); const [groupSelected, setGroupSelected] = useState(new Set()); - const allFilterKeys = filter.values || []; + const allFilterKeys = filter?.values || []; + + const getActiveFilter = useMemo(() => { + const currentFilters: Record = {}; + 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 = ({ setGroupSelected(new Set(["all", ...allFilterKeys])); } }, [groupSelected, allFilterKeys]); - return (
@@ -84,13 +99,46 @@ export const CustomDropdownFilter: React.FC = ({ } size="sm" > - {filter.key} + {filter?.labelCheckboxGroup} + {groupSelected.size > 0 && ( + <> + + + {groupSelected.size} + +
+ {groupSelected.size > 2 ? ( + + {groupSelected.size} selected + + ) : ( + Array.from(groupSelected) + .filter((value) => value !== "all") + .map((value) => ( + + {value} + + )) + )} +
+ + )}
@@ -106,12 +154,14 @@ export const CustomDropdownFilter: React.FC = ({
-

- Selected:{" "} - {Array.from(groupSelected) - .filter((item) => item !== "all") - .join(", ")} -

+ {groupSelected?.size > 0 && ( +

+ Selected:{" "} + {Array.from(groupSelected) + .filter((item) => item !== "all") + .join(", ")} +

+ )}
); }; diff --git a/components/ui/dialog/Dialog.tsx b/components/ui/dialog/dialogg.tsx similarity index 76% rename from components/ui/dialog/Dialog.tsx rename to components/ui/dialog/dialogg.tsx index a25a3caebc..93542df32f 100644 --- a/components/ui/dialog/Dialog.tsx +++ b/components/ui/dialog/dialogg.tsx @@ -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< {children} - - + + Close @@ -102,7 +102,7 @@ const DialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( )); diff --git a/components/ui/index.ts b/components/ui/index.ts index 4f54c7d5b1..dc72c678bd 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -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"; diff --git a/components/ui/table/index.ts b/components/ui/table/index.ts index 36437ee01e..27387081e2 100644 --- a/components/ui/table/index.ts +++ b/components/ui/table/index.ts @@ -1,4 +1,3 @@ -// export * from "./data-table-toolbar"; export * from "./severity-badge"; export * from "./status-badge"; export * from "./table"; diff --git a/package-lock.json b/package-lock.json index 11d2ea0433..a9c0840142 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 9a84242353..5e68426395 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/types/filters.ts b/types/filters.ts new file mode 100644 index 0000000000..944706f7aa --- /dev/null +++ b/types/filters.ts @@ -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[]; +} diff --git a/types/index.ts b/types/index.ts index 955dd0d6f8..c247bc2080 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,3 +1,4 @@ export * from "./authFormSchema"; export * from "./components"; +export * from "./filters"; export * from "./formSchemas"; From 904a4a61e9af79f2693f3cd64c4fe3cafca7ca4d Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 16:16:24 +0200 Subject: [PATCH 22/24] chore: rename component properly --- components/ui/dialog/{dialogg.tsx => dialog.tsx} | 0 components/ui/index.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename components/ui/dialog/{dialogg.tsx => dialog.tsx} (100%) diff --git a/components/ui/dialog/dialogg.tsx b/components/ui/dialog/dialog.tsx similarity index 100% rename from components/ui/dialog/dialogg.tsx rename to components/ui/dialog/dialog.tsx diff --git a/components/ui/index.ts b/components/ui/index.ts index dc72c678bd..07631d9636 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -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/dialogg"; +export * from "./dialog/dialog"; export * from "./dropdown/Dropdown"; export * from "./header/Header"; export * from "./label/Label"; From 39b7fca11f50d3dd0b229702e9f37dd861f5a18e Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 16:30:38 +0200 Subject: [PATCH 23/24] chore: rename component properly --- components/ui/table/index.ts | 2 +- components/ui/table/{Table.tsx => tablee.tsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename components/ui/table/{Table.tsx => tablee.tsx} (100%) diff --git a/components/ui/table/index.ts b/components/ui/table/index.ts index 27387081e2..f0fb22e450 100644 --- a/components/ui/table/index.ts +++ b/components/ui/table/index.ts @@ -1,3 +1,3 @@ export * from "./severity-badge"; export * from "./status-badge"; -export * from "./table"; +export * from "./tablee"; diff --git a/components/ui/table/Table.tsx b/components/ui/table/tablee.tsx similarity index 100% rename from components/ui/table/Table.tsx rename to components/ui/table/tablee.tsx From 5223cf3763014ef1c801ff61dcebe4f40e3021da Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 30 Sep 2024 16:31:15 +0200 Subject: [PATCH 24/24] chore: rename component properly --- components/ui/table/index.ts | 2 +- components/ui/table/{tablee.tsx => table.tsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename components/ui/table/{tablee.tsx => table.tsx} (100%) diff --git a/components/ui/table/index.ts b/components/ui/table/index.ts index f0fb22e450..27387081e2 100644 --- a/components/ui/table/index.ts +++ b/components/ui/table/index.ts @@ -1,3 +1,3 @@ export * from "./severity-badge"; export * from "./status-badge"; -export * from "./tablee"; +export * from "./table"; diff --git a/components/ui/table/tablee.tsx b/components/ui/table/table.tsx similarity index 100% rename from components/ui/table/tablee.tsx rename to components/ui/table/table.tsx