diff --git a/actions/providers.ts b/actions/providers.ts index 22084527ed..3dc232a39e 100644 --- a/actions/providers.ts +++ b/actions/providers.ts @@ -117,15 +117,14 @@ 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 providerType = formData.get("providerType"); const providerId = formData.get("providerId"); - const alias = formData.get("alias"); + const providerAlias = formData.get("providerAlias"); const url = new URL(`${keyServer}/providers`); @@ -141,9 +140,9 @@ export const addProvider = async (formData: FormData) => { data: { type: "Provider", attributes: { - provider: provider, + provider: providerType, uid: providerId, - alias: alias, + alias: providerAlias, }, }, }), diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx index 6a79a8a872..7dcc5524aa 100644 --- a/app/(prowler)/providers/page.tsx +++ b/app/(prowler)/providers/page.tsx @@ -3,7 +3,7 @@ import { Suspense } from "react"; import { getProviders } from "@/actions"; import { FilterControls } from "@/components/filters"; -import { AddProviderModal } from "@/components/providers"; +import { AddProvider } from "@/components/providers"; import { ColumnsProvider, DataTableProvider, @@ -22,18 +22,18 @@ export default async function Providers({ return ( <>
+ -
-
- -
- - }> - - -
+ + + + + }> + + + {/* */} ); } diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx index 1d2b8d4b1a..0e593a01ed 100644 --- a/components/icons/Icons.tsx +++ b/components/icons/Icons.tsx @@ -757,3 +757,29 @@ export const SaveIcon: React.FC = ({ ); }; + +export const AddIcon: React.FC = ({ + size, + height, + width, + ...props +}) => { + return ( + + ); +}; diff --git a/components/providers/AddProvider.tsx b/components/providers/AddProvider.tsx deleted file mode 100644 index 22cbfb2943..0000000000 --- a/components/providers/AddProvider.tsx +++ /dev/null @@ -1,63 +0,0 @@ -"use client"; - -import { useRef } from "react"; - -import { addProvider } from "@/actions"; - -import { useToast } from "../ui/toast"; -import { ButtonAddProvider } from "./ButtonAddProvider"; - -export const AddProvider = () => { - 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: "Oops! Something went wrong", - description: errorMessage, - }); - }); - } else { - toast({ - title: "Success!", - description: "The provider was added successfully.", - }); - } - } - - return ( -
- - - - - - ); -}; diff --git a/components/providers/AddProviderModal.tsx b/components/providers/AddProviderModal.tsx deleted file mode 100644 index c81602198b..0000000000 --- a/components/providers/AddProviderModal.tsx +++ /dev/null @@ -1,102 +0,0 @@ -"use client"; - -import { Button, Input } from "@nextui-org/react"; -import { useRef, useState } from "react"; - -import { addProvider } from "@/actions"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, - useToast, -} from "@/components/ui"; - -import { ButtonAddProvider } from "./ButtonAddProvider"; -import { CustomRadioProvider } from "./CustomRadioProvider"; - -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: "Oops! 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="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