diff --git a/actions/auth/auth.ts b/actions/auth/auth.ts index 28504d2c0c..394bdf4f3e 100644 --- a/actions/auth/auth.ts +++ b/actions/auth/auth.ts @@ -7,7 +7,7 @@ import { signIn, signOut } from "@/auth.config"; import { authFormSchema } from "@/types"; const formSchemaSignIn = authFormSchema("sign-in"); -// const formSchemaSignUp = authFormSchema("sign-up"); +const formSchemaSignUp = authFormSchema("sign-up"); const defaultValues: z.infer = { email: "", @@ -50,6 +50,46 @@ export async function authenticate( } } +export const createNewUser = async ( + formData: z.infer, +) => { + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/users`); + + const bodyData = { + data: { + type: "User", + attributes: { + name: formData.name, + company_name: formData.company, + email: formData.email, + password: formData.password, + }, + }, + }; + + try { + const response = await fetch(url.toString(), { + method: "POST", + headers: { + "Content-Type": "application/vnd.api+json", + Accept: "application/vnd.api+json", + }, + body: JSON.stringify(bodyData), + }); + + const parsedResponse = await response.json(); + + if (!response.ok) { + return parsedResponse; + } + + return parsedResponse; + } catch (error) { + return { errors: [{ detail: "Network error or server is unreachable" }] }; + } +}; + export const getToken = async (formData: z.infer) => { const keyServer = process.env.API_BASE_URL; const url = new URL(`${keyServer}/tokens`); diff --git a/auth.config.ts b/auth.config.ts index ffcfaad06d..556c7d40c0 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -74,7 +74,7 @@ export const authConfig = { const parsedCredentials = z .object({ email: z.string().email(), - password: z.string().min(6), + password: z.string().min(12), }) .safeParse(credentials); diff --git a/components/auth/auth-form.tsx b/components/auth/auth-form.tsx index de53e66f8b..1a9dbd9777 100644 --- a/components/auth/auth-form.tsx +++ b/components/auth/auth-form.tsx @@ -9,18 +9,19 @@ import { useFormState } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { authenticate } from "@/actions/auth"; +import { authenticate, createNewUser } from "@/actions/auth"; import { Form, FormControl, FormField, FormMessage, } from "@/components/ui/form"; -import { authFormSchema } from "@/types"; +import { ApiError, authFormSchema } from "@/types"; import { NotificationIcon, ProwlerExtended } from "../icons"; import { ThemeSwitch } from "../ThemeSwitch"; import { CustomButton, CustomInput } from "../ui/custom"; +import { useToast } from "../ui/toast"; export const AuthForm = ({ type }: { type: string }) => { const formSchema = authFormSchema(type); @@ -31,17 +32,20 @@ export const AuthForm = ({ type }: { type: string }) => { email: "", password: "", ...(type === "sign-up" && { - firstName: "", - companyName: "", + name: "", + company: "", + termsAndConditions: false, confirmPassword: "", }), }, }); - const isLoading = form.formState.isSubmitting; - const [state, dispatch] = useFormState(authenticate, undefined); + const { toast } = useToast(); + + const isLoading = form.formState.isSubmitting; + useEffect(() => { if (state?.message === "Success") { router.push("/"); @@ -49,20 +53,53 @@ export const AuthForm = ({ type }: { type: string }) => { }, [state]); const onSubmit = async (data: z.infer) => { - try { - // Sign-up logic will be here. - if (type === "sign-in") { - dispatch({ - email: data.email.toLowerCase(), - password: data.password, + if (type === "sign-in") { + dispatch({ + email: data.email.toLowerCase(), + password: data.password, + }); + } + if (type === "sign-up") { + const newUser = await createNewUser(data); + + if (newUser?.errors && newUser.errors.length > 0) { + newUser.errors.forEach((error: ApiError) => { + const errorMessage = error.detail; + + switch (error.source.pointer) { + case "/data/attributes/name": + form.setError("name", { type: "server", message: errorMessage }); + break; + case "/data/attributes/email": + form.setError("email", { type: "server", message: errorMessage }); + break; + case "/data/attributes/password": + form.setError("password", { + type: "server", + message: errorMessage, + }); + break; + default: + toast({ + variant: "destructive", + title: "Oops! Something went wrong", + description: errorMessage, + }); + } + }); + } else { + toast({ + title: "Success!", + description: "The user was registered successfully.", + }); + form.reset({ + name: "", + company: "", + email: "", + password: "", + termsAndConditions: false, }); } - if (type === "sign-up") { - // const newUser = await signUp(data); - } - } catch (error) { - // eslint-disable-next-line no-console - console.error(error); } }; @@ -107,18 +144,20 @@ export const AuthForm = ({ type }: { type: string }) => { <> )} @@ -128,9 +167,15 @@ export const AuthForm = ({ type }: { type: string }) => { type="email" label="Email" placeholder="Enter your email" + isInvalid={!!form.formState.errors.email} /> - + {type === "sign-in" && (
@@ -143,40 +188,40 @@ export const AuthForm = ({ type }: { type: string }) => {
)} {type === "sign-up" && ( - ( - <> - - - - field.onChange(e.target.checked ? "true" : "false") - } - > - I agree with the  - - Terms - -   and  - - Privacy Policy - - - - - - )} - /> + <> + + ( + <> + + field.onChange(e.target.checked)} + > + I agree with the  + + Terms + +   and  + + Privacy Policy + + + + + + )} + /> + )} {state?.message === "Credentials error" && ( @@ -186,8 +231,6 @@ export const AuthForm = ({ type }: { type: string }) => { )} - {isLoading &&

Loading...

} - { @@ -74,8 +74,8 @@ export const SidebarWrap = () => { Loading...

}>
diff --git a/types/authFormSchema.ts b/types/authFormSchema.ts index 7705b2e19f..6f3ab5a087 100644 --- a/types/authFormSchema.ts +++ b/types/authFormSchema.ts @@ -4,11 +4,9 @@ export const authFormSchema = (type: string) => z .object({ // Sign Up - companyName: - type === "sign-in" - ? z.string().optional() - : z.string().min(3).optional(), - firstName: + company: + type === "sign-in" ? z.string().optional() : z.string().optional(), + name: type === "sign-in" ? z.string().optional() : z @@ -20,20 +18,19 @@ export const authFormSchema = (type: string) => confirmPassword: type === "sign-in" ? z.string().optional() - : z.string().min(3, { + : z.string().min(12, { message: "It must contain at least 12 characters.", }), termsAndConditions: type === "sign-in" - ? z.enum(["true"]).optional() - : z.enum(["true"], { - errorMap: () => ({ - message: "You must accept the terms and conditions.", - }), + ? z.boolean().optional() + : z.boolean().refine((value) => value === true, { + message: "You must accept the terms and conditions.", }), + // Fields for Sign In and Sign Up email: z.string().email(), - password: z.string().min(3, { + password: z.string().min(12, { message: "It must contain at least 12 characters.", }), }) diff --git a/types/components.ts b/types/components.ts index 6bac7ce1d2..02018469dc 100644 --- a/types/components.ts +++ b/types/components.ts @@ -30,6 +30,15 @@ export interface SearchParamsProps { [key: string]: string | string[] | undefined; } +export interface ApiError { + detail: string; + status: string; + source: { + pointer: string; + }; + code: string; +} + export interface ProviderProps { id: string; type: "providers";