'use client'; import { useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { GibsAuthSignInButton } from '@/components/layout/auth/buttons'; import { useAuthActions } from '@convex-dev/auth/react'; import { zodResolver } from '@hookform/resolvers/zod'; import { ConvexError } from 'convex/values'; import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; import { z } from 'zod'; import { PASSWORD_MAX, PASSWORD_MIN, PASSWORD_REGEX } from '@gib/backend/types'; import { Card, CardContent, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Separator, SubmitButton, Tabs, TabsContent, TabsList, TabsTrigger, } from '@gib/ui'; const signInFormSchema = z.object({ email: z.email({ message: 'Please enter a valid email address.', }), password: z.string().regex(PASSWORD_REGEX, { message: 'Incorrect password. Does not meet requirements.', }), }); const signUpFormSchema = z .object({ name: z.string().min(2, { message: 'Name must be at least 2 characters.', }), email: z.email({ message: 'Please enter a valid email address.', }), password: z .string() .min(PASSWORD_MIN, { message: `Password must be at least ${PASSWORD_MIN} characters.`, }) .max(PASSWORD_MAX, { message: `Password must be no more than ${PASSWORD_MAX} characters.`, }) .regex(/^\S+$/, { message: 'Password must not contain whitespace.', }) .regex(/[0-9]/, { message: 'Password must contain at least one digit.', }) .regex(/[a-z]/, { message: 'Password must contain at least one lowercase letter.', }) .regex(/[A-Z]/, { message: 'Password must contain at least one uppercase letter.', }) .regex(/[\p{P}\p{S}]/u, { message: 'Password must contain at least one symbol.', }), confirmPassword: z.string().min(PASSWORD_MIN, { message: `Password must be at least ${PASSWORD_MIN} characters.`, }), }) .refine((data) => data.password === data.confirmPassword, { message: 'Passwords do not match!', path: ['confirmPassword'], }); const verifyEmailFormSchema = z.object({ code: z.string({ message: 'Invalid code.' }), }); const SignIn = () => { const { signIn } = useAuthActions(); const [flow, setFlow] = useState<'signIn' | 'signUp' | 'email-verification'>( 'signIn', ); const [email, setEmail] = useState(''); const [code, setCode] = useState(''); const [loading, setLoading] = useState(false); const router = useRouter(); const signInForm = useForm>({ resolver: zodResolver(signInFormSchema), defaultValues: { email: '', password: '' }, }); const signUpForm = useForm>({ resolver: zodResolver(signUpFormSchema), defaultValues: { name: '', email, password: '', confirmPassword: '', }, }); const verifyEmailForm = useForm>({ resolver: zodResolver(verifyEmailFormSchema), defaultValues: { code }, }); const handleSignIn = async (values: z.infer) => { const formData = new FormData(); formData.append('email', values.email); formData.append('password', values.password); formData.append('flow', flow); setLoading(true); try { await signIn('password', formData).then(() => router.push('/')); } catch (error) { console.error('Error signing in:', error); toast.error('Error signing in.'); } finally { signInForm.reset(); setLoading(false); } }; const handleSignUp = async (values: z.infer) => { const formData = new FormData(); formData.append('email', values.email); formData.append('password', values.password); formData.append('flow', flow); formData.append('name', values.name); setLoading(true); try { if (values.confirmPassword !== values.password) throw new ConvexError('Passwords do not match.'); await signIn('password', formData).then(() => { setEmail(values.email); setFlow('email-verification'); }); } catch (error) { console.error('Error signing up:', error); toast.error('Error signing up.'); } finally { signUpForm.reset(); setLoading(false); } }; const handleVerifyEmail = async ( values: z.infer, ) => { const formData = new FormData(); formData.append('code', code); formData.append('flow', flow); formData.append('email', email); setLoading(true); try { await signIn('password', formData).then(() => router.push('/')); } catch (error) { console.error('Error verifying email:', error); toast.error('Error verifying email.'); } finally { verifyEmailForm.reset(); setLoading(false); } }; if (flow === 'email-verification') { return (

Verify Your Email

We sent a code to {email}

( Code setCode(value)} > Please enter the one-time password sent to your email.
)} /> Verify Email
); } return (
setFlow(value as 'signIn' | 'signUp')} className='items-center' > Sign In Sign Up
( Email
)} /> (
Password Forgot Password?
)} /> Sign In
or
( Name
)} /> ( Email
)} /> ( Password
)} /> ( Confirm Passsword
)} /> Sign Up
or
); }; export default SignIn;