'use client' import { zodResolver } from "@hookform/resolvers/zod" import { signIn } from 'next-auth/react' import { useRouter, 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 { ToastAction } from "@/components/ui/toast" import { toast } from "@/components/ui/use-toast" import { cn } from '@/lib/utils' import { userAuthSchema } from "@/lib/validations/auth" import { sendVerificationEmail } from "@/lib/validations/sendVerificationEmail" interface UserAuthFormProps extends HTMLAttributes<HTMLDivElement> { type: "login" | "signup" } type FormData = z.infer<typeof userAuthSchema> export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { const { register, handleSubmit, setError, formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(userAuthSchema), }) const [isLoading, setIsLoading] = useState<boolean>(false) const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false) const router = useRouter() const searchParams = useSearchParams() //muss noch exportiert werden 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.' }) } setIsLoading(false) return toast({ variant: "destructive", title: "Uh oh! Something went wrong.", description: "Your sign up request failed. Please try again.", }) } await sendVerificationEmail(data.email!) } const signInResult = await signIn("credentials", { usernameOrEmail: data.email?.toLowerCase() || data.usernameOrEmail?.toLowerCase(), password: data.password, redirect: false, callbackUrl: searchParams?.get("from") || "/home", }) setIsLoading(false) 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.' }) } if (signInResult.error === "Email is not verified") { return toast({ title: "Please Verify your account.", description: "We send you a email to verify your Account. Please check your Mailbox!🚀", }) } return 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>, }) } router.push("/home") router.refresh() if (type !== "signup") { return toast({ title: "Login successful.", description: "You will be redirected shortly.", }) } } async function onGitHub() { setIsGitHubLoading(true) await signIn("github", { callbackUrl: searchParams?.get("from") || "/home" }) } return ( <div className={cn("grid gap-6", className)} {...props}> <form onSubmit={handleSubmit(onSubmit)}> <div className="grid gap-2"> {type === "login" ? <div className="grid gap-1"> <Label className="sr-only" htmlFor="usernameOrEmail"> Username or email </Label> <Input id="usernameOrEmail" placeholder="Your username or email" type="text" autoCapitalize="none" autoComplete="username email" autoCorrect="off" disabled={isLoading || isGitHubLoading} {...register("usernameOrEmail", { required: true })} /> {errors?.usernameOrEmail && ( <p className="px-1 text-xs text-red-600"> {errors.usernameOrEmail.message} </p> )} </div> : null} {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> )} </div> <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> <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={onGitHub} 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> ) }