diff --git a/actions/auth.ts b/actions/auth.ts index e6c0968967..34e7fa36c4 100644 --- a/actions/auth.ts +++ b/actions/auth.ts @@ -2,9 +2,7 @@ import { AuthError } from "next-auth"; -import { signIn } from "@/auth.config"; - -// ... +import { signIn, signOut } from "@/auth.config"; export async function authenticate( prevState: string | undefined, @@ -26,3 +24,7 @@ export async function authenticate( throw error; } } + +export async function logOut() { + await signOut(); +} diff --git a/auth.config.ts b/auth.config.ts index 58e112ef3c..1171b2ae42 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -1,3 +1,4 @@ +import bcryptjs from "bcryptjs"; import NextAuth, { type NextAuthConfig } from "next-auth"; import Credentials from "next-auth/providers/credentials"; import { z } from "zod"; @@ -17,9 +18,21 @@ export const authConfig = { if (!parsedCredentials.success) return null; const { email, password } = parsedCredentials.data; - console.log("AuthConfig.ts"); - console.log({ email, password }); - return null; + console.log({ email, password }, "from AuthConfig.ts"); + + // Check the user using the email + + // const user = await getUser(email); + // if (!user) return null; + + // Compare passwords + + // if (!bcryptjs.compareSync(password, user.password)) return null; + + // Return the user object without the password field + + // const { password: _, ...rest } = user; + // return rest; }, }), ], diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx index 63b0e3acd8..f87458b2c9 100644 --- a/components/auth/AuthForm.tsx +++ b/components/auth/AuthForm.tsx @@ -1,23 +1,47 @@ "use client"; +import { zodResolver } from "@hookform/resolvers/zod"; import { Icon } from "@iconify/react"; import { Button, Checkbox, Divider, Input, Link } from "@nextui-org/react"; import { useState } from "react"; import { useFormState } from "react-dom"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; import { authenticate } from "@/actions"; +import { + Form, + FormControl, + FormField, + FormMessage, +} from "@/components/ui/form"; +import { authFormSchema } from "@/types"; import { ProwlerExtended } from "../icons"; import { ThemeSwitch } from "../ThemeSwitch"; +import { CustomInput } from "../ui/custom"; import { AuthButton } from "./AuthButton"; export const AuthForm = ({ type }: { type: string }) => { - const [isVisible, setIsVisible] = useState(false); + const [user, setUser] = useState(null); const [state, dispath] = useFormState(authenticate, undefined); - console.log(state); - const toggleVisibility = () => setIsVisible(!isVisible); + const form = useForm>({ + resolver: zodResolver(authFormSchema), + defaultValues: { + username: "", + password: "", + }, + }); + + const onSubmit = (values: z.infer) => { + // Do something with the form values + // this will be type-safe and validated + console.log(values); + }; + + console.log(state); return (
{ {/* Testimonial */}
-

+

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eget - augue nec massa volutpat aliquet. + Open Cloud Security

- {/* Login Form */}

{type === "sign-in" ? "Sign In" : "Sign Up"}

-
- - - {isVisible ? ( - - ) : ( - - )} - - } - label="Password" - name="password" - placeholder="Enter your password" - type={isVisible ? "text" : "password"} - variant="bordered" - /> -
- - Remember me - - - Forgot password? - -
- - + {/* Sign UP Form */} + {type === "sign-up" && ( +
+ + + + + {/* + {isConfirmVisible ? ( + + ) : ( + + )} + + } + label="Confirm Password" + name="confirmPassword" + placeholder="Confirm your password" + type={isConfirmVisible ? "text" : "password"} + variant="bordered" + /> */} + + I agree with the  + + Terms + +   and  + + Privacy Policy + + + + + + )} + {/* Sign IN Form */} {type === "sign-in" && ( <> +
+ + +
+ + Remember me + + + Forgot password? + +
+ +

OR

@@ -123,6 +184,7 @@ export const AuthForm = ({ type }: { type: string }) => {
)} + {type === "sign-in" ? (

Need to create an account?  diff --git a/components/ui/custom/CustomInput.tsx b/components/ui/custom/CustomInput.tsx new file mode 100644 index 0000000000..07ea994a46 --- /dev/null +++ b/components/ui/custom/CustomInput.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { Icon } from "@iconify/react"; +import { Input } from "@nextui-org/react"; +import React, { useState } from "react"; +import { Control, FieldPath } from "react-hook-form"; +import { z } from "zod"; + +import { FormControl, FormField, FormMessage } from "@/components/ui/form"; +import { authFormSchema } from "@/types"; + +type CustomInputProps = + | { + control: Control>; + name: FieldPath>; + password?: false; + label: string; + type: "text" | "email"; + placeholder: string; + isRequired?: boolean; + } + | { + control: Control>; + password: true; + name?: never; + label?: never; + type?: never; + placeholder?: never; + isRequired?: never; + }; + +export const CustomInput = ({ + control, + name, + type, + label, + placeholder, + password = false, + isRequired = true, +}: CustomInputProps) => { + const [isVisible, setIsVisible] = useState(false); + + const toggleVisibility = () => setIsVisible(!isVisible); + + const inputProps = password + ? { + name: "password", + label: "Password", + type: isVisible ? "text" : "password", + placeholder: "Enter your password", + isRequired: true, + } + : { name, label, type, placeholder, isRequired }; + + return ( + ( + <> + + + + + ) + } + /> + + + + )} + /> + ); +}; diff --git a/components/ui/custom/index.ts b/components/ui/custom/index.ts index 00732c48c8..126110ade6 100644 --- a/components/ui/custom/index.ts +++ b/components/ui/custom/index.ts @@ -1 +1,2 @@ export * from "./CustomBox"; +export * from "./CustomInput"; diff --git a/components/ui/form/Form.tsx b/components/ui/form/Form.tsx new file mode 100644 index 0000000000..c598b08b8f --- /dev/null +++ b/components/ui/form/Form.tsx @@ -0,0 +1,186 @@ +"use client"; + +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import * as React from "react"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/lib/utils"; + +import { Label } from "./label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +

+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +