From b7d324f1b0789d4f52c2fff364525bd8f91530fe Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 5 Aug 2024 13:09:44 +0200 Subject: [PATCH 1/3] feat: add modal and functionality for adding providers --- actions/providers.ts | 3 - app/(prowler)/providers/page.tsx | 21 +--- components/index.ts | 2 + components/providers/AddProvider.tsx | 2 +- components/providers/AddProviderModal.tsx | 96 +++++++++++++++++ components/ui/dialog/Dialog.tsx | 122 +++++++++++++++++++++ config/fonts.ts | 2 + config/site.ts | 63 ----------- package-lock.json | 126 ++++++++++++++++++++++ package.json | 1 + 10 files changed, 352 insertions(+), 86 deletions(-) create mode 100644 components/providers/AddProviderModal.tsx create mode 100644 components/ui/dialog/Dialog.tsx diff --git a/actions/providers.ts b/actions/providers.ts index e339883d24..30ac54ff5b 100644 --- a/actions/providers.ts +++ b/actions/providers.ts @@ -20,7 +20,6 @@ export const getProvider = async () => { return undefined; } }; - export const addProvider = async (formData: FormData) => { const keyServer = process.env.LOCAL_SERVER_URL; @@ -57,7 +56,6 @@ export const addProvider = async (formData: FormData) => { } revalidatePath("/providers"); }; - export const checkConnectionProvider = async (formData: FormData) => { const keyServer = process.env.LOCAL_SERVER_URL; @@ -82,7 +80,6 @@ export const checkConnectionProvider = async (formData: FormData) => { }; } }; - export const deleteProvider = async (formData: FormData) => { const keyServer = process.env.LOCAL_SERVER_URL; diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index a6627e1cca..c8bee988f3 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -3,38 +3,21 @@ import React, { Suspense } from "react"; import { getProvider } from "@/actions"; import { - AddProvider, + AddProviderModal, ColumnsProviders, DataTable, Header, - ModalWrap, SkeletonTableProvider, } from "@/components"; export default async function Providers() { - const onSave = async () => { - "use server"; - // event we want to pass down, ex. console.log("### hello"); - }; - return ( <>
- - -

Modal body content

- - } - actionButtonLabel="Save" - onAction={onSave} - openButtonLabel="Add Cloud Accounts" - /> +
}> diff --git a/components/index.ts b/components/index.ts index a31ebbb99f..c28ecc331c 100644 --- a/components/index.ts +++ b/components/index.ts @@ -1,6 +1,7 @@ export * from "./findings/ColumnsFindings"; export * from "./findings/SkeletonTableFindings"; export * from "./providers/AddProvider"; +export * from "./providers/AddProviderModal"; export * from "./providers/ButtonAddProvider"; export * from "./providers/ButtonCheckConnectionProvider"; export * from "./providers/ButtonDeleteProvider"; @@ -12,6 +13,7 @@ export * from "./providers/table/ColumnsProviders"; export * from "./providers/table/DataTable"; export * from "./providers/table/SkeletonTableProvider"; export * from "./ui/alert/Alert"; +export * from "./ui/dialog/Dialog"; export * from "./ui/header/Header"; export * from "./ui/modal"; export * from "./ui/sidebar"; diff --git a/components/providers/AddProvider.tsx b/components/providers/AddProvider.tsx index ba914f01fd..5417f0ffe4 100644 --- a/components/providers/AddProvider.tsx +++ b/components/providers/AddProvider.tsx @@ -23,7 +23,7 @@ export const AddProvider = () => { toast({ variant: "destructive", title: "Ups! Something went wrong", - description: errorMessage + description: errorMessage, }); }); } else { diff --git a/components/providers/AddProviderModal.tsx b/components/providers/AddProviderModal.tsx new file mode 100644 index 0000000000..761b60ba2a --- /dev/null +++ b/components/providers/AddProviderModal.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Button, Input } from "@nextui-org/react"; +import { useRef, useState } from "react"; + +import { addProvider } from "@/actions"; +import { + ButtonAddProvider, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + useToast, +} from "@/components"; + +export const AddProviderModal = () => { + const [open, setOpen] = useState(false); + + const ref = useRef(null); + const { toast } = useToast(); + + async function clientAction(formData: FormData) { + // reset the form + ref.current?.reset(); + // client-side validation + const data = await addProvider(formData); + if (data?.errors) { + data.errors.forEach((error: { detail: string }) => { + const errorMessage = `${error.detail}`; + // show error + toast({ + variant: "destructive", + title: "Ups! Something went wrong", + description: errorMessage, + }); + }); + } else { + toast({ + title: "Success!", + description: "The provider was added successfully.", + }); + } + } + return ( + + + + + + + + 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="flex flex-col gap-x-2" + > + + + + + +
+
+ ); +}; diff --git a/components/ui/dialog/Dialog.tsx b/components/ui/dialog/Dialog.tsx new file mode 100644 index 0000000000..a25a3caebc --- /dev/null +++ b/components/ui/dialog/Dialog.tsx @@ -0,0 +1,122 @@ +"use client"; + +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { X } from "lucide-react"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = "DialogHeader"; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = "DialogFooter"; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/config/fonts.ts b/config/fonts.ts index 16c934d5a6..9724605392 100644 --- a/config/fonts.ts +++ b/config/fonts.ts @@ -6,9 +6,11 @@ import { export const fontSans = FontSans({ subsets: ["latin"], variable: "--font-sans", + preload: false, }); export const fontMono = FontMono({ subsets: ["latin"], variable: "--font-mono", + preload: false, }); diff --git a/config/site.ts b/config/site.ts index 23fcc45378..d89839859f 100644 --- a/config/site.ts +++ b/config/site.ts @@ -4,67 +4,4 @@ export const siteConfig = { name: "Prowler", description: "The most comprehensive, free tool for AWS security. ProwlerPro is trusted by leading organizations to make cloud security effortless.", - navItems: [ - { - label: "Home", - href: "/", - }, - { - label: "Docs", - href: "/docs", - }, - { - label: "Pricing", - href: "/pricing", - }, - { - label: "Blog", - href: "/blog", - }, - { - label: "About", - href: "/about", - }, - ], - navMenuItems: [ - { - label: "Profile", - href: "/profile", - }, - { - label: "Dashboard", - href: "/dashboard", - }, - { - label: "Projects", - href: "/projects", - }, - { - label: "Team", - href: "/team", - }, - { - label: "Calendar", - href: "/calendar", - }, - { - label: "Settings", - href: "/settings", - }, - { - label: "Help & Feedback", - href: "/help-feedback", - }, - { - label: "Logout", - href: "/logout", - }, - ], - links: { - github: "https://github.com/nextui-org/nextui", - twitter: "https://twitter.com/getnextui", - docs: "https://nextui.org", - discord: "https://discord.gg/9b6yyZKmH4", - sponsor: "https://patreon.com/jrgarciadev", - }, }; diff --git a/package-lock.json b/package-lock.json index fa6ecc0a45..7b8757dbd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@nextui-org/react": "^2.4.2", "@nextui-org/system": "2.2.1", "@nextui-org/theme": "2.2.5", + "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-toast": "^1.2.1", "@react-aria/ssr": "3.9.4", @@ -2649,6 +2650,65 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz", + "integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", + "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.4", + "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/@radix-ui/react-dismissable-layer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", @@ -2675,6 +2735,44 @@ } } }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", + "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-icons": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", @@ -2683,6 +2781,23 @@ "react": "^16.x || ^17.x || ^18.x" } }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", @@ -4478,6 +4593,17 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", diff --git a/package.json b/package.json index d1837d47e3..7795424b1a 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "@nextui-org/react": "^2.4.2", "@nextui-org/system": "2.2.1", "@nextui-org/theme": "2.2.5", + "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-toast": "^1.2.1", "@react-aria/ssr": "3.9.4", From 14ff4282c0158933a3a72c406bd59d2ab668a35e Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 5 Aug 2024 16:00:02 +0200 Subject: [PATCH 2/3] feat: refactor the modal's content for providers --- components/index.ts | 1 + components/providers/AddProviderModal.tsx | 53 +++++++------- components/providers/CustomRadioProvider.tsx | 77 ++++++++++++++++++++ 3 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 components/providers/CustomRadioProvider.tsx diff --git a/components/index.ts b/components/index.ts index c28ecc331c..76c4ac2064 100644 --- a/components/index.ts +++ b/components/index.ts @@ -6,6 +6,7 @@ export * from "./providers/ButtonAddProvider"; export * from "./providers/ButtonCheckConnectionProvider"; export * from "./providers/ButtonDeleteProvider"; export * from "./providers/CheckConnectionProvider"; +export * from "./providers/CustomRadioProvider"; export * from "./providers/DateWithTime"; export * from "./providers/ProviderInfo"; export * from "./providers/ScanStatus"; diff --git a/components/providers/AddProviderModal.tsx b/components/providers/AddProviderModal.tsx index 761b60ba2a..878023ebb7 100644 --- a/components/providers/AddProviderModal.tsx +++ b/components/providers/AddProviderModal.tsx @@ -6,6 +6,7 @@ import { useRef, useState } from "react"; import { addProvider } from "@/actions"; import { ButtonAddProvider, + CustomRadioProvider, Dialog, DialogContent, DialogDescription, @@ -63,32 +64,34 @@ export const AddProviderModal = () => { ref={ref} action={clientAction} onSubmit={() => setOpen(false)} - className="flex flex-col gap-x-2" + className="grid sm:grid-cols-2 gap-6" > - - - - +
+ +
+
+ + +
+
+ +
diff --git a/components/providers/CustomRadioProvider.tsx b/components/providers/CustomRadioProvider.tsx new file mode 100644 index 0000000000..949cb86662 --- /dev/null +++ b/components/providers/CustomRadioProvider.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { UseRadioProps } from "@nextui-org/radio/dist/use-radio"; +import { cn, RadioGroup, useRadio, VisuallyHidden } from "@nextui-org/react"; +import React from "react"; + +import { AwsProvider, AzureProvider, GoogleCloudProvider } from "../icons"; + +interface CustomRadioProps extends UseRadioProps { + description?: string; + children?: React.ReactNode; +} + +export const CustomRadio: React.FC = (props) => { + const { + Component, + children, + description, + getBaseProps, + getWrapperProps, + getInputProps, + getLabelProps, + getLabelWrapperProps, + getControlProps, + } = useRadio(props); + + return ( + + + + + + + +
+ {children && {children}} + {description && ( + + {description} + + )} +
+
+ ); +}; + +export const CustomRadioProvider = () => { + return ( + + +
+ + GCP +
+
+ +
+ + GCP +
+
+ +
+ + Azure +
+
+
+ ); +}; From 0ae248926d65b7208b6bdc0270a9dc92bda867b1 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 7 Aug 2024 08:50:14 +0200 Subject: [PATCH 3/3] chore: fix provider name --- components/providers/CustomRadioProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/providers/CustomRadioProvider.tsx b/components/providers/CustomRadioProvider.tsx index 949cb86662..f40ac37056 100644 --- a/components/providers/CustomRadioProvider.tsx +++ b/components/providers/CustomRadioProvider.tsx @@ -57,7 +57,7 @@ export const CustomRadioProvider = () => {
- GCP + AWS