diff --git a/ui/actions/providers/providers.ts b/ui/actions/providers/providers.ts index 6e907dcd056..b11841de40a 100644 --- a/ui/actions/providers/providers.ts +++ b/ui/actions/providers/providers.ts @@ -253,6 +253,102 @@ export const addCredentialsProvider = async (formData: FormData) => { } }; +export const updateCredentialsProvider = async ( + credentialsId: string, + formData: FormData, +) => { + const session = await auth(); + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/providers/secrets/${credentialsId}`); + + const secretName = formData.get("secretName"); + const providerType = formData.get("providerType"); + + const isRole = formData.get("role_arn") !== null; + + let secret = {}; + + if (providerType === "aws") { + if (isRole) { + // Role-based configuration for AWS + secret = { + role_arn: formData.get("role_arn"), + aws_access_key_id: formData.get("aws_access_key_id") || undefined, + aws_secret_access_key: + formData.get("aws_secret_access_key") || undefined, + aws_session_token: formData.get("aws_session_token") || undefined, + session_duration: + parseInt(formData.get("session_duration") as string, 10) || 3600, + external_id: formData.get("external_id") || undefined, + role_session_name: formData.get("role_session_name") || undefined, + }; + } else { + // Static credentials configuration for AWS + secret = { + aws_access_key_id: formData.get("aws_access_key_id"), + aws_secret_access_key: formData.get("aws_secret_access_key"), + aws_session_token: formData.get("aws_session_token") || undefined, + }; + } + } else if (providerType === "azure") { + // Static credentials configuration for Azure + secret = { + client_id: formData.get("client_id"), + client_secret: formData.get("client_secret"), + tenant_id: formData.get("tenant_id"), + }; + } else if (providerType === "gcp") { + // Static credentials configuration for GCP + secret = { + client_id: formData.get("client_id"), + client_secret: formData.get("client_secret"), + refresh_token: formData.get("refresh_token"), + }; + } else if (providerType === "kubernetes") { + // Static credentials configuration for Kubernetes + secret = { + kubeconfig_content: formData.get("kubeconfig_content"), + }; + } + + const bodyData = { + data: { + type: "provider-secrets", + id: credentialsId, + attributes: { + name: secretName, + secret, + }, + }, + }; + + try { + const response = await fetch(url.toString(), { + method: "PATCH", + headers: { + "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, + }, + body: JSON.stringify(bodyData), + }); + + if (!response.ok) { + throw new Error(`Failed to update credentials: ${response.statusText}`); + } + + const data = await response.json(); + revalidatePath("/providers"); + return parseStringify(data); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + return { + error: getErrorMessage(error), + }; + } +}; + export const checkConnectionProvider = async (formData: FormData) => { const session = await auth(); const keyServer = process.env.API_BASE_URL; @@ -270,7 +366,7 @@ export const checkConnectionProvider = async (formData: FormData) => { }, }); const data = await response.json(); - await wait(1000); + await wait(2000); revalidatePath("/providers"); return parseStringify(data); } catch (error) { diff --git a/ui/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx b/ui/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx index 6a525990fac..fce06d1a882 100644 --- a/ui/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx +++ b/ui/app/(prowler)/providers/(set-up-provider)/add-credentials/page.tsx @@ -1,35 +1,30 @@ -import { redirect } from "next/navigation"; import React from "react"; import { ViaCredentialsForm, ViaRoleForm, } from "@/components/providers/workflow/forms"; +import { SelectViaAWS } from "@/components/providers/workflow/forms/select-via-aws/select-via-aws"; interface Props { searchParams: { type: string; id: string; via?: string }; } export default function AddCredentialsPage({ searchParams }: Props) { - if ( - !searchParams.type || - !searchParams.id || - (searchParams.type === "aws" && !searchParams.via) - ) { - redirect("/providers/connect-account"); - } - - const useCredentialsForm = - (searchParams.type === "aws" && searchParams.via === "credentials") || - (searchParams.type !== "aws" && !searchParams.via); - - const useRoleForm = - searchParams.type === "aws" && searchParams.via === "role"; - return ( <> - {useCredentialsForm && } - {useRoleForm && } + {searchParams.type === "aws" && !searchParams.via && ( + + )} + + {((searchParams.type === "aws" && searchParams.via === "credentials") || + searchParams.type !== "aws") && ( + + )} + + {searchParams.type === "aws" && searchParams.via === "role" && ( + + )} ); } diff --git a/ui/app/(prowler)/providers/(set-up-provider)/test-connection/page.tsx b/ui/app/(prowler)/providers/(set-up-provider)/test-connection/page.tsx index 13cac50f6d9..b3e3e29c448 100644 --- a/ui/app/(prowler)/providers/(set-up-provider)/test-connection/page.tsx +++ b/ui/app/(prowler)/providers/(set-up-provider)/test-connection/page.tsx @@ -5,7 +5,7 @@ import { getProvider } from "@/actions/providers"; import { TestConnectionForm } from "@/components/providers/workflow/forms"; interface Props { - searchParams: { type: string; id: string }; + searchParams: { type: string; id: string; updated: string }; } export default async function TestConnectionPage({ searchParams }: Props) { @@ -25,7 +25,7 @@ export default async function TestConnectionPage({ searchParams }: Props) { async function SSRTestConnection({ searchParams, }: { - searchParams: { type: string; id: string }; + searchParams: { type: string; id: string; updated: string }; }) { const formData = new FormData(); formData.append("id", searchParams.id); diff --git a/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx b/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx new file mode 100644 index 00000000000..c3476099ab5 --- /dev/null +++ b/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +import { + UpdateViaCredentialsForm, + UpdateViaRoleForm, +} from "@/components/providers/workflow/forms"; +import { SelectViaAWS } from "@/components/providers/workflow/forms/select-via-aws/select-via-aws"; + +interface Props { + searchParams: { type: string; id: string; via?: string }; +} + +export default function UpdateCredentialsPage({ searchParams }: Props) { + return ( + <> + {searchParams.type === "aws" && !searchParams.via && ( + + )} + + {((searchParams.type === "aws" && searchParams.via === "credentials") || + searchParams.type !== "aws") && ( + + )} + + {searchParams.type === "aws" && searchParams.via === "role" && ( + + )} + + ); +} diff --git a/ui/components/providers/table/data-table-row-actions.tsx b/ui/components/providers/table/data-table-row-actions.tsx index b388d3ed6bc..7cc95cada5f 100644 --- a/ui/components/providers/table/data-table-row-actions.tsx +++ b/ui/components/providers/table/data-table-row-actions.tsx @@ -17,6 +17,7 @@ import { Row } from "@tanstack/react-table"; import clsx from "clsx"; import { useState } from "react"; +import { checkConnectionProvider } from "@/actions/providers/providers"; import { VerticalDotsIcon } from "@/components/icons"; import { CustomAlertModal } from "@/components/ui/custom"; @@ -37,6 +38,15 @@ export function DataTableRowActions({ const providerId = (row.original as { id: string }).id; const providerType = (row.original as any).attributes?.provider; const providerAlias = (row.original as any).attributes?.alias; + const providerSecretId = + (row.original as any).relationships?.secret?.data?.id || null; + + const handleTestConnection = async () => { + const formData = new FormData(); + formData.append("providerId", providerId); + await checkConnectionProvider(formData); + }; + return ( <> ({ > } + > + Update Credentials + + } + onClick={handleTestConnection} > Test Connection diff --git a/ui/components/providers/workflow/forms/connect-account-form.tsx b/ui/components/providers/workflow/forms/connect-account-form.tsx index 800bfd52109..5c0fbf71acb 100644 --- a/ui/components/providers/workflow/forms/connect-account-form.tsx +++ b/ui/components/providers/workflow/forms/connect-account-form.tsx @@ -19,7 +19,6 @@ import { Form } from "@/components/ui/form"; import { addProvider } from "../../../../actions/providers/providers"; import { addProviderFormSchema, ApiError } from "../../../../types"; import { RadioGroupProvider } from "../../radio-group-provider"; -import { RadioGroupAWSViaCredentialsForm } from "./radio-group-aws-via-credentials-form"; export type FormValues = z.infer; @@ -36,7 +35,6 @@ export const ConnectAccountForm = () => { providerType: undefined, providerUid: "", providerAlias: "", - awsCredentialsType: "", }, }); @@ -51,55 +49,60 @@ export const ConnectAccountForm = () => { ([key, value]) => value !== undefined && formData.append(key, value), ); - const data = await addProvider(formData); - - if (data?.errors && data.errors.length > 0) { - // Handle server-side validation errors - data.errors.forEach((error: ApiError) => { - const errorMessage = error.detail; - const pointer = error.source?.pointer; - - switch (pointer) { - case "/data/attributes/provider": - form.setError("providerType", { - type: "server", - message: errorMessage, - }); - break; - case "/data/attributes/uid": - case "/data/attributes/__all__": - form.setError("providerUid", { - type: "server", - message: errorMessage, - }); - break; - case "/data/attributes/alias": - form.setError("providerAlias", { - type: "server", - message: errorMessage, - }); - break; - default: - toast({ - variant: "destructive", - title: "Oops! Something went wrong", - description: errorMessage, - }); - } + try { + const data = await addProvider(formData); + + if (data?.errors && data.errors.length > 0) { + // Handle server-side validation errors + data.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + const pointer = error.source?.pointer; + + switch (pointer) { + case "/data/attributes/provider": + form.setError("providerType", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/uid": + case "/data/attributes/__all__": + form.setError("providerUid", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/alias": + form.setError("providerAlias", { + type: "server", + message: errorMessage, + }); + break; + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + return; + } else { + // Go to the next step after successful submission + const { + id, + attributes: { provider: providerType }, + } = data.data; + + router.push(`/providers/add-credentials?type=${providerType}&id=${id}`); + } + } catch (error: any) { + console.error("Error during submission:", error); + toast({ + variant: "destructive", + title: "Submission Error", + description: error.message || "Something went wrong. Please try again.", }); - return; - } else { - // Navigate to the next step after successful submission - const { - id, - attributes: { provider: providerType }, - } = data.data; - const credentialsParam = values.awsCredentialsType - ? `&via=${values.awsCredentialsType}` - : ""; - router.push( - `/providers/add-credentials?type=${providerType}&id=${id}${credentialsParam}`, - ); } }; @@ -158,13 +161,6 @@ export const ConnectAccountForm = () => { isRequired={false} isInvalid={!!form.formState.errors.providerAlias} /> - {providerType === "aws" && ( - - )} )} {/* Navigation buttons */} diff --git a/ui/components/providers/workflow/forms/index.ts b/ui/components/providers/workflow/forms/index.ts index be1bb3277d0..d7f0d07134b 100644 --- a/ui/components/providers/workflow/forms/index.ts +++ b/ui/components/providers/workflow/forms/index.ts @@ -2,5 +2,7 @@ export * from "./connect-account-form"; export * from "./launch-scan-form"; export * from "./radio-group-aws-via-credentials-form"; export * from "./test-connection-form"; +export * from "./update-via-credentials-form"; +export * from "./update-via-role-form"; export * from "./via-credentials-form"; export * from "./via-role-form"; diff --git a/ui/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx b/ui/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx index 7a870c79ff7..bb53c5a5b95 100644 --- a/ui/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx +++ b/ui/components/providers/workflow/forms/radio-group-aws-via-credentials-form.tsx @@ -7,18 +7,18 @@ import { Control, Controller } from "react-hook-form"; import { CustomRadio } from "@/components/ui/custom"; import { FormMessage } from "@/components/ui/form"; -import { FormValues } from "./connect-account-form"; - type RadioGroupAWSViaCredentialsFormProps = { - control: Control; + control: Control; isInvalid: boolean; errorMessage?: string; + onChange?: (value: string) => void; }; export const RadioGroupAWSViaCredentialsForm = ({ control, isInvalid, errorMessage, + onChange, }: RadioGroupAWSViaCredentialsFormProps) => { return ( { + field.onChange(value); + if (onChange) { + onChange(value); + } + }} >
Using IAM Role diff --git a/ui/components/providers/workflow/forms/select-via-aws/index.ts b/ui/components/providers/workflow/forms/select-via-aws/index.ts new file mode 100644 index 00000000000..8dd9822258b --- /dev/null +++ b/ui/components/providers/workflow/forms/select-via-aws/index.ts @@ -0,0 +1 @@ +export * from "./select-via-aws"; diff --git a/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx b/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx new file mode 100644 index 00000000000..59197bb40ef --- /dev/null +++ b/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useForm } from "react-hook-form"; + +import { Form } from "@/components/ui/form"; + +import { RadioGroupAWSViaCredentialsForm } from "../radio-group-aws-via-credentials-form"; + +interface SelectViaAWSProps { + initialVia?: string; +} + +export const SelectViaAWS = ({ initialVia }: SelectViaAWSProps) => { + const router = useRouter(); + const form = useForm({ + defaultValues: { + awsCredentialsType: initialVia || "", + }, + }); + + const handleSelectionChange = (value: string) => { + const url = new URL(window.location.href); + url.searchParams.set("via", value); + router.push(url.toString()); + }; + + return ( +
+ + + ); +}; diff --git a/ui/components/providers/workflow/forms/test-connection-form.tsx b/ui/components/providers/workflow/forms/test-connection-form.tsx index 6f21f146549..41e5ebed668 100644 --- a/ui/components/providers/workflow/forms/test-connection-form.tsx +++ b/ui/components/providers/workflow/forms/test-connection-form.tsx @@ -29,7 +29,7 @@ export const TestConnectionForm = ({ searchParams, providerData, }: { - searchParams: { type: string; id: string }; + searchParams: { type: string; id: string; updated: string }; providerData: { data: { id: string; @@ -75,6 +75,7 @@ export const TestConnectionForm = ({ }); const isLoading = form.formState.isSubmitting; + const isUpdated = searchParams?.updated === "true"; const onSubmitClient = async (values: FormValues) => { const formData = new FormData(); @@ -124,9 +125,19 @@ export const TestConnectionForm = ({ message: data.error, }); } else { - router.push( - `/providers/launch-scan?type=${providerType}&id=${providerId}`, - ); + const urlParams = new URLSearchParams(window.location.search); + const isUpdated = urlParams.get("updated") === "true"; + + if (!isUpdated) { + router.push( + `/providers/launch-scan?type=${providerType}&id=${providerId}`, + ); + } else { + setConnectionStatus({ + connected: true, + error: null, + }); + } } } catch (error) { form.setError("providerId", { @@ -254,7 +265,7 @@ export const TestConnectionForm = ({ ) : connectionStatus?.error ? ( router.back() : onResetCredentials} type="button" ariaLabel={"Save"} className="w-1/2" @@ -268,12 +279,21 @@ export const TestConnectionForm = ({ {isResettingCredentials ? ( <>Loading ) : ( - Reset credentials + + {isUpdated ? "Update credentials" : "Reset credentials"} + )} ) : ( router.push("/providers") + : undefined + } ariaLabel={"Save"} className="w-1/2" variant="solid" @@ -282,7 +302,15 @@ export const TestConnectionForm = ({ isLoading={isLoading} endContent={!isLoading && } > - {isLoading ? <>Loading : Launch} + {isLoading ? ( + <>Loading + ) : ( + + {isUpdated && connectionStatus?.connected + ? "Go to providers" + : "Launch"} + + )} )}
diff --git a/ui/components/providers/workflow/forms/update-via-credentials-form.tsx b/ui/components/providers/workflow/forms/update-via-credentials-form.tsx new file mode 100644 index 00000000000..c3279655913 --- /dev/null +++ b/ui/components/providers/workflow/forms/update-via-credentials-form.tsx @@ -0,0 +1,243 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Control, useForm } from "react-hook-form"; +import * as z from "zod"; + +import { updateCredentialsProvider } from "@/actions/providers/providers"; +import { useToast } from "@/components/ui"; +import { CustomButton } from "@/components/ui/custom"; +import { getProviderLogo } from "@/components/ui/entities"; +import { getProviderName } from "@/components/ui/entities"; +import { ProviderType } from "@/components/ui/entities"; +import { Form } from "@/components/ui/form"; +import { + addCredentialsFormSchema, + ApiError, + AWSCredentials, + AzureCredentials, + GCPCredentials, + KubernetesCredentials, +} from "@/types"; + +import { AWScredentialsForm } from "./via-credentials/aws-credentials-form"; +import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form"; +import { GCPcredentialsForm } from "./via-credentials/gcp-credentials-form"; +import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form"; + +type CredentialsFormSchema = z.infer< + ReturnType +>; + +// Add this type intersection to include all fields +type FormType = CredentialsFormSchema & + AWSCredentials & + AzureCredentials & + GCPCredentials & + KubernetesCredentials; + +export const UpdateViaCredentialsForm = ({ + searchParams, +}: { + searchParams: { type: string; id: string; secretId?: string }; +}) => { + const router = useRouter(); + const { toast } = useToast(); + + const searchParamsObj = useSearchParams(); + + // Handler for back button + const handleBackStep = () => { + const currentParams = new URLSearchParams(window.location.search); + currentParams.delete("via"); + router.push(`?${currentParams.toString()}`); + }; + + const providerType = searchParams.type; + const providerId = searchParams.id; + const providerSecretId = searchParams.secretId || ""; + const formSchema = addCredentialsFormSchema(providerType); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + providerId, + providerType, + ...(providerType === "aws" + ? { + aws_access_key_id: "", + aws_secret_access_key: "", + aws_session_token: "", + } + : providerType === "azure" + ? { + client_id: "", + client_secret: "", + tenant_id: "", + } + : providerType === "gcp" + ? { + client_id: "", + client_secret: "", + refresh_token: "", + } + : providerType === "kubernetes" + ? { + kubeconfig_content: "", + } + : {}), + }, + }); + + const isLoading = form.formState.isSubmitting; + + const onSubmitClient = async (values: FormType) => { + const formData = new FormData(); + + Object.entries(values).forEach( + ([key, value]) => value !== undefined && formData.append(key, value), + ); + + const data = await updateCredentialsProvider(providerSecretId, formData); + + if (data?.errors && data.errors.length > 0) { + data.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + switch (error.source.pointer) { + case "/data/attributes/secret/aws_access_key_id": + form.setError("aws_access_key_id", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/aws_secret_access_key": + form.setError("aws_secret_access_key", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/aws_session_token": + form.setError("aws_session_token", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/client_id": + form.setError("client_id", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/client_secret": + form.setError("client_secret", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/tenant_id": + form.setError("tenant_id", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/secret/kubeconfig_content": + form.setError("kubeconfig_content", { + type: "server", + message: errorMessage, + }); + break; + case "/data/attributes/name": + form.setError("secretName", { + type: "server", + message: errorMessage, + }); + break; + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + } else { + router.push( + `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`, + ); + } + }; + + return ( +
+ + + + +
+ {providerType && getProviderLogo(providerType as ProviderType)} + + {providerType + ? getProviderName(providerType as ProviderType) + : "Unknown Provider"} + +
+ + {providerType === "aws" && ( + } + /> + )} + {providerType === "azure" && ( + } + /> + )} + {providerType === "gcp" && ( + } + /> + )} + {providerType === "kubernetes" && ( + } + /> + )} + +
+ {searchParamsObj.get("via") === "credentials" && ( + } + isDisabled={isLoading} + > + Back + + )} + } + > + {isLoading ? <>Loading : Next} + +
+ + + ); +}; diff --git a/ui/components/providers/workflow/forms/update-via-role-form.tsx b/ui/components/providers/workflow/forms/update-via-role-form.tsx new file mode 100644 index 00000000000..ee57dc0759d --- /dev/null +++ b/ui/components/providers/workflow/forms/update-via-role-form.tsx @@ -0,0 +1,149 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Control, useForm } from "react-hook-form"; +import * as z from "zod"; + +import { updateCredentialsProvider } from "@/actions/providers/providers"; +import { useToast } from "@/components/ui"; +import { CustomButton } from "@/components/ui/custom"; +import { Form } from "@/components/ui/form"; +import { + addCredentialsRoleFormSchema, + ApiError, + AWSCredentialsRole, +} from "@/types"; + +import { AWSCredentialsRoleForm } from "./via-role/aws-role-form"; + +export const UpdateViaRoleForm = ({ + searchParams, +}: { + searchParams: { type: string; id: string; secretId?: string }; +}) => { + const router = useRouter(); + const { toast } = useToast(); + + const searchParamsObj = useSearchParams(); + + // Handler for back button + const handleBackStep = () => { + const currentParams = new URLSearchParams(window.location.search); + currentParams.delete("via"); + router.push(`?${currentParams.toString()}`); + }; + + const providerType = searchParams.type; + const providerId = searchParams.id; + const providerSecretId = searchParams.secretId || ""; + + const formSchema = addCredentialsRoleFormSchema(providerType); + type FormSchemaType = z.infer; + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + providerId, + providerType, + ...(providerType === "aws" + ? { + role_arn: "", + aws_access_key_id: "", + aws_secret_access_key: "", + aws_session_token: "", + session_duration: 3600, + external_id: "", + role_session_name: "", + } + : {}), + }, + }); + + const isLoading = form.formState.isSubmitting; + + const onSubmitClient = async (values: FormSchemaType) => { + const formData = new FormData(); + + Object.entries(values).forEach( + ([key, value]) => + value !== undefined && formData.append(key, String(value)), + ); + + const data = await updateCredentialsProvider(providerSecretId, formData); + + if (data?.errors && data.errors.length > 0) { + data.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + switch (error.source.pointer) { + case "/data/attributes/secret/role_arn": + form.setError("role_arn" as keyof FormSchemaType, { + type: "server", + message: errorMessage, + }); + break; + + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + } else { + router.push( + `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`, + ); + } + }; + + return ( +
+ + + + + {providerType === "aws" && ( + } + /> + )} + +
+ {searchParamsObj.get("via") === "role" && ( + } + isDisabled={isLoading} + > + Back + + )} + } + > + {isLoading ? <>Loading : Next} + +
+ + + ); +}; diff --git a/ui/components/providers/workflow/forms/via-credentials-form.tsx b/ui/components/providers/workflow/forms/via-credentials-form.tsx index 3ecb11da415..6c0bd0fe371 100644 --- a/ui/components/providers/workflow/forms/via-credentials-form.tsx +++ b/ui/components/providers/workflow/forms/via-credentials-form.tsx @@ -1,8 +1,8 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { ChevronRightIcon } from "lucide-react"; -import { useRouter } from "next/navigation"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; import { Control, useForm } from "react-hook-form"; import * as z from "zod"; @@ -46,6 +46,15 @@ export const ViaCredentialsForm = ({ const router = useRouter(); const { toast } = useToast(); + const searchParamsObj = useSearchParams(); + + // Handler for back button + const handleBackStep = () => { + const currentParams = new URLSearchParams(window.location.search); + currentParams.delete("via"); + router.push(`?${currentParams.toString()}`); + }; + const providerType = searchParams.type; const providerId = searchParams.id; const formSchema = addCredentialsFormSchema(providerType); @@ -199,6 +208,21 @@ export const ViaCredentialsForm = ({ )}
+ {searchParamsObj.get("via") === "credentials" && ( + } + isDisabled={isLoading} + > + Back + + )} { + const currentParams = new URLSearchParams(window.location.search); + currentParams.delete("via"); + router.push(`?${currentParams.toString()}`); + }; + const providerType = searchParams.type; const providerId = searchParams.id; @@ -54,7 +63,6 @@ export const ViaRoleForm = ({ const isLoading = form.formState.isSubmitting; const onSubmitClient = async (values: FormSchemaType) => { - console.log("via ROLE form", values); const formData = new FormData(); Object.entries(values).forEach( @@ -106,6 +114,21 @@ export const ViaRoleForm = ({ )}
+ {searchParamsObj.get("via") === "role" && ( + } + isDisabled={isLoading} + > + Back + + )} } + endContent={!isLoading && } > - {isLoading ? <>Loading : Save} + {isLoading ? <>Loading : Next}
diff --git a/ui/components/ui/sidebar/sidebar-wrap.tsx b/ui/components/ui/sidebar/sidebar-wrap.tsx index 4f3fba89df1..0f8900854bf 100644 --- a/ui/components/ui/sidebar/sidebar-wrap.tsx +++ b/ui/components/ui/sidebar/sidebar-wrap.tsx @@ -163,7 +163,7 @@ export const SidebarWrap = () => { scheduleDate: z.string(), }); +export const awsCredentialsTypeSchema = z.object({ + awsCredentialsType: z.string().min(1, { + message: "Please select the type of credentials you want to use", + }), +}); + export const addProviderFormSchema = z .object({ providerType: z.enum(["aws", "azure", "gcp", "kubernetes"], { @@ -43,9 +49,6 @@ export const addProviderFormSchema = z providerType: z.literal("aws"), providerAlias: z.string(), providerUid: z.string(), - awsCredentialsType: z.string().min(1, { - message: "Please select the type of credentials you want to use", - }), }), z.object({ providerType: z.literal("azure"),