Skip to content
Snippets Groups Projects
user-auth-form.tsx 7.4 KiB
Newer Older
Yusuf Akgül's avatar
Yusuf Akgül committed
'use client'

import { zodResolver } from "@hookform/resolvers/zod"
import { signIn } from 'next-auth/react'
import { useSearchParams } from 'next/navigation'
import { HTMLAttributes, useState } from 'react'
import { useForm } from 'react-hook-form'
import * as z from "zod"

import { Icons } from '@/components/icons'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useToast } from '@/components/ui/use-toast'
import { cn } from '@/lib/utils'
import { userAuthSchema } from '@/lib/validations/auth'
import { ToastAction } from "./ui/toast"

interface UserAuthFormProps extends HTMLAttributes<HTMLDivElement> {
    type: "login" | "signup"
}

type FormData = z.infer<typeof userAuthSchema>

export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) {
    const {
        register,
        handleSubmit,
        formState: { errors },
    } = useForm<FormData>({
        resolver: zodResolver(userAuthSchema),
    })
    const [isLoading, setIsLoading] = useState<boolean>(false)
    const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false)
    const searchParams = useSearchParams()

    const { toast } = useToast()

    async function onSubmit(data: FormData) {
        setIsLoading(true)

        if (type === "signup") {
            const res = await fetch('/api/signup', {
                method: 'POST',
                body: JSON.stringify({
                    username: data.username,
                    email: data.email,
                    password: data.password
                }),
                headers: {
                    'Content-Type': 'application/json'
                }
            })

            if (!res.ok) {
                setIsLoading(false)
                toast({
                    variant: "destructive",
                    title: "Uh oh! Something went wrong.",
                    description: "Your sign up request failed. Please try again.",
                })
            }
        }

        const signInResult = await signIn("credentials", {
            username: data.username,
            email: data.email,
            password: data.password,
            redirect: false,
            callbackUrl: "/home",
        });

        setIsLoading(false)

        if (!signInResult?.ok) {
            toast({
                variant: "destructive",
                title: "Uh oh! Something went wrong.",
                description: "Your log in request failed. Please try again.",
                action: <ToastAction altText="Try again">Try again</ToastAction>,
            })
        }

        // toast({
        //     title: "Check your email.",
        //     description: "We sent you a login link. Be sure to check your spam too.",
        // })
    }

    return (
        <>
            <div className={cn("grid gap-6", className)} {...props}>
                <form onSubmit={handleSubmit(onSubmit)}>
                    <div className="grid gap-2">
                        <div className="grid gap-1">
                            <Label className="sr-only" htmlFor="username">
                                Username
                            </Label>
                            <Input
                                id="username"
                                placeholder="Your username"
                                type="username"
                                autoCapitalize="none"
                                autoComplete="username"
                                autoCorrect="off"
                                disabled={isLoading || isGitHubLoading}
                                {...register("username")}
                            />
                            {errors?.username && (
                                <p className="px-1 text-xs text-red-600">
                                    {errors.username.message}
                                </p>
                            )}
                        </div>
                        {type === "signup" ? <div className="grid gap-1">
                            <Label className="sr-only" htmlFor="email">
                                Email
                            </Label>
                            <Input
                                id="email"
                                placeholder="Your email"
                                type="email"
                                autoCapitalize="none"
                                autoComplete="email"
                                autoCorrect="off"
                                disabled={isLoading || isGitHubLoading}
                                {...register("email")}
                            />
                            {errors?.email && (
                                <p className="px-1 text-xs text-red-600">
                                    {errors.email.message}
                                </p>
                            )}
                        </div> : null}
                        <div className="grid gap-1">
                            <Label className="sr-only" htmlFor="password">
                                Password
                            </Label>
                            <Input
                                id="password"
                                placeholder="Your password"
                                type="password"
                                autoCapitalize="none"
                                autoComplete="new-password"
                                autoCorrect="off"
                                disabled={isLoading || isGitHubLoading}
                                {...register("password")}
                            />
                            {errors?.password && (
                                <p className="px-1 text-xs text-red-600">
                                    {errors.password.message}
                                </p>
                            )}
                        </div>
                        <Button disabled={isLoading}>
                            {isLoading && (
                                <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
                            )}
                            {type === "signup" ? "Sign Up" : "Log In"}
                        </Button>
                    </div>
                </form>

                <div className="relative">
                    <div className="absolute inset-0 flex items-center">
                        <span className="w-full border-t" />
                    </div>
                    <div className="relative flex justify-center text-xs uppercase">
                        <span className="bg-background px-2 text-muted-foreground">
                            Or continue with
                        </span>
                    </div>
                </div>
                <Button
                    variant="outline"
                    type="button"
                    onClick={() => {
                        setIsGitHubLoading(true)
                        signIn("github")
                    }}
                    disabled={isLoading || isGitHubLoading}
                >
                    {isGitHubLoading ? (
                        <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
                    ) : (
                        <Icons.github className="mr-2 h-4 w-4" />
                    )}{" "}
                    Github
                </Button>
            </div>
        </>
    )
}