Skip to content
Snippets Groups Projects
user-auth-form.tsx 10 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 { useRouter, useSearchParams } from "next/navigation"
Yusuf Akgül's avatar
Yusuf Akgül committed
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'
Yusuf Akgül's avatar
Yusuf Akgül committed
import { ToastAction } from "@/components/ui/toast"
import { toast } from "@/components/ui/use-toast"
Yusuf Akgül's avatar
Yusuf Akgül committed
import { cn } from '@/lib/utils'
Yusuf Akgül's avatar
Yusuf Akgül committed
import { userAuthSchema } from "@/lib/validations/auth"
Caner's avatar
Caner committed
import { sendVerificationEmail } from "@/lib/validations/sendVerificationEmail"
Yusuf Akgül's avatar
Yusuf Akgül committed

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

type FormData = z.infer<typeof userAuthSchema>

Caner's avatar
Caner committed

Yusuf Akgül's avatar
Yusuf Akgül committed
export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) {
    const {
        register,
        handleSubmit,
Yusuf Akgül's avatar
Yusuf Akgül committed
        formState: { errors },
    } = useForm<FormData>({
        resolver: zodResolver(userAuthSchema),
    })
    const [isLoading, setIsLoading] = useState<boolean>(false)
    const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false)
Yusuf Akgül's avatar
Yusuf Akgül committed
    const searchParams = useSearchParams()
Caner's avatar
Caner committed
    //muss noch exportiert werden
Caner's avatar
Caner committed

Yusuf Akgül's avatar
Yusuf Akgül committed
    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) {
                if (res.status === 422) {
                    setError('email', { type: 'manual', message: 'This email is already in use. Please choose another one.' })
Yusuf Akgül's avatar
Yusuf Akgül committed
                setIsLoading(false)
Yusuf Akgül's avatar
Yusuf Akgül committed
                    variant: "destructive",
                    title: "Uh oh! Something went wrong.",
                    description: "Your sign up request failed. Please try again.",
                })
            }
Caner's avatar
Caner committed
            await sendVerificationEmail(data.email!)
Yusuf Akgül's avatar
Yusuf Akgül committed
        }
Yusuf Akgül's avatar
Yusuf Akgül committed

Yusuf Akgül's avatar
Yusuf Akgül committed
        const signInResult = await signIn("credentials", {
Yusuf Akgül's avatar
Yusuf Akgül committed
            usernameOrEmail: data.email?.toLowerCase() || data.usernameOrEmail?.toLowerCase(),
Yusuf Akgül's avatar
Yusuf Akgül committed
            password: data.password,
Yusuf Akgül's avatar
Yusuf Akgül committed
            callbackUrl: searchParams?.get("from") || "/home",
Yusuf Akgül's avatar
Yusuf Akgül committed

        setIsLoading(false)

Yusuf Akgül's avatar
Yusuf Akgül committed
        if (signInResult?.error) {
            if (signInResult.error === "user not found") {
                setError('usernameOrEmail', {
                    type: 'manual',
                    message: 'Sorry, we couldn\'t find an account with the provided email / username. Please double-check your input or create a new account.'
            }
            if (signInResult.error === "invalid password") {
                setError('password', {
                    type: 'manual',
                    message: 'Sorry, but it seems like the password you entered is invalid. Please try again.'
Caner's avatar
Caner committed
            if (signInResult.error === "Email is not verified") {
                return toast({
                    title: "Please verify your account.",
Caner's avatar
Caner committed
                    description: "We send you a email to verify your Account. Please check your Mailbox!🚀",
                })
            }
Yusuf Akgül's avatar
Yusuf Akgül committed
            return toast({
Yusuf Akgül's avatar
Yusuf Akgül committed
                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>,
            })
        }

Yusuf Akgül's avatar
Yusuf Akgül committed
        router.refresh()
Caner's avatar
Caner committed
        if (type !== "signup") {
Yusuf Akgül's avatar
Yusuf Akgül committed
            return toast({
                title: "Login successful.",
Yusuf Akgül's avatar
Yusuf Akgül committed
                description: "You will be redirected shortly.",
            })
        }
Yusuf Akgül's avatar
Yusuf Akgül committed
    async function onGitHub() {
        setIsGitHubLoading(true)
        await signIn("github", { callbackUrl: searchParams?.get("from") || "/home" })
    }

Yusuf Akgül's avatar
Yusuf Akgül committed
    return (
Yusuf Akgül's avatar
Yusuf Akgül committed
        <div className={cn("grid gap-6", className)} {...props}>
            <form onSubmit={handleSubmit(onSubmit)}>
                <div className="grid gap-2">
                    {type === "login" ?
Yusuf Akgül's avatar
Yusuf Akgül committed
                        <div className="grid gap-1">
Yusuf Akgül's avatar
Yusuf Akgül committed
                            <Label className="sr-only" htmlFor="usernameOrEmail">
                                Username or email
Yusuf Akgül's avatar
Yusuf Akgül committed
                            </Label>
                            <Input
Yusuf Akgül's avatar
Yusuf Akgül committed
                                id="usernameOrEmail"
                                placeholder="Your username or email"
                                type="text"
Yusuf Akgül's avatar
Yusuf Akgül committed
                                autoCapitalize="none"
Yusuf Akgül's avatar
Yusuf Akgül committed
                                autoComplete="username email"
Yusuf Akgül's avatar
Yusuf Akgül committed
                                autoCorrect="off"
                                disabled={isLoading || isGitHubLoading}
Yusuf Akgül's avatar
Yusuf Akgül committed
                                {...register("usernameOrEmail", { required: true })}
Yusuf Akgül's avatar
Yusuf Akgül committed
                            />
Yusuf Akgül's avatar
Yusuf Akgül committed
                            {errors?.usernameOrEmail && (
Yusuf Akgül's avatar
Yusuf Akgül committed
                                <p className="px-1 text-xs text-red-600">
Yusuf Akgül's avatar
Yusuf Akgül committed
                                    {errors.usernameOrEmail.message}
Yusuf Akgül's avatar
Yusuf Akgül committed
                                </p>
                            )}
                        </div> : null}
Yusuf Akgül's avatar
Yusuf Akgül committed
                    {type === "signup" ?
                        <>
                            <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", { required: true })}
                                />
                                {errors?.username && (
                                    <p className="px-1 text-xs text-red-600">
                                        {errors.username.message}
                                    </p>
                                )}
                            </div>
                            <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", { required: true })}
                                />
                                {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", { required: true })}
                        />
                        {errors?.password && (
                            <p className="px-1 text-xs text-red-600">
                                {errors.password.message}
                            </p>
                        )}
Yusuf Akgül's avatar
Yusuf Akgül committed
                    </div>
Yusuf Akgül's avatar
Yusuf Akgül committed
                    <Button disabled={isLoading} type="submit">
                        {isLoading && (
                            <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
                        )}
                        {type === "signup" ? "Sign Up" : "Log In"}
                    </Button>
                </div>
            </form>
Yusuf Akgül's avatar
Yusuf Akgül committed

Yusuf Akgül's avatar
Yusuf Akgül committed
            <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>
Yusuf Akgül's avatar
Yusuf Akgül committed
                </div>
            </div>
Yusuf Akgül's avatar
Yusuf Akgül committed
            <Button
                variant="outline"
                type="button"
Yusuf Akgül's avatar
Yusuf Akgül committed
                onClick={onGitHub}
Yusuf Akgül's avatar
Yusuf Akgül committed
                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>
Yusuf Akgül's avatar
Yusuf Akgül committed
    )
}