Reaching a finishing point!

This commit is contained in:
2025-06-04 14:33:39 -05:00
parent bfb6e9e648
commit 04dceb93bd
16 changed files with 462 additions and 240 deletions

View File

@ -1,53 +1,184 @@
'use client';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import Link from 'next/link';
import { signUp } from '@/lib/actions';
import { FormMessage, type Message, SubmitButton } from '@/components/default';
import { Input, Label } from '@/components/ui';
import { redirect } from 'next/navigation';
import { getUser } from '@/lib/actions';
import { StatusMessage, SubmitButton } from '@/components/default';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
Input,
} from '@/components/ui';
import { useEffect, useState } from 'react';
const SignUp = async (props: { searchParams: Promise<Message> }) => {
const searchParams = await props.searchParams;
const user = await getUser();
if (user.success) redirect('/profile');
if ('message' in searchParams) {
return (
<div
className='w-full flex-1 flex items-center h-screen
sm:max-w-md justify-center gap-2 p-4'
>
<FormMessage message={searchParams} />
</div>
);
} else {
return (
<form className='flex flex-col min-w-64 max-w-64 mx-auto'>
<h1 className='text-2xl font-medium'>Sign up</h1>
<p className='text-sm text text-foreground'>
const formSchema = z
.object({
name: z.string().min(2, {
message: 'Name must be at least 2 characters.',
}),
email: z.string().email({
message: 'Please enter a valid email address.',
}),
password: z.string().min(8, {
message: 'Password must be at least 8 characters.',
}),
confirmPassword: z.string().min(8, {
message: 'Password must be at least 8 characters.',
}),
})
.refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match!',
path: ['confirmPassword'],
});
const SignUp = () => {
const router = useRouter();
const { isAuthenticated, isLoading, refreshUserData } = useAuth();
const [statusMessage, setStatusMessage] = useState('');
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: '',
email: '',
password: '',
confirmPassword: '',
},
mode: 'onChange',
});
// Redirect if already authenticated
useEffect(() => {
if (isAuthenticated) {
router.push('/');
}
}, [isAuthenticated, router]);
const handleSignUp = async (values: z.infer<typeof formSchema>) => {
try {
setStatusMessage('');
const formData = new FormData();
formData.append('name', values.name);
formData.append('email', values.email);
formData.append('password', values.password);
const result = await signUp(formData);
if (result?.success) {
await refreshUserData();
setStatusMessage(
result.data ??
'Thanks for signing up! Please check your email for a verification link.'
);
form.reset();
router.push('');
} else {
setStatusMessage(`Error: ${result.error}`)
}
} catch (error) {
setStatusMessage(
`Error: ${error instanceof Error ? error.message : 'Could not sign in!'}`
);
}
};
return (
<Card className='min-w-xs md:min-w-sm'>
<CardHeader>
<CardTitle className='text-2xl font-medium'>
Sign Up
</CardTitle>
<CardDescription className='text text-foreground'>
Already have an account?{' '}
<Link className='text-primary font-medium underline' href='/sign-in'>
Sign in
</Link>
</p>
<div className='flex flex-col gap-2 [&>input]:mb-3 mt-8'>
<Label htmlFor='name'>Name</Label>
<Input name='name' placeholder='Full Name' required />
<Label htmlFor='email'>Email</Label>
<Input name='email' placeholder='you@example.com' required />
<Label htmlFor='password'>Password</Label>
<Input
type='password'
name='password'
placeholder='Your password'
minLength={6}
required
/>
<SubmitButton formAction={signUp} pendingText='Signing up...'>
Sign up
</SubmitButton>
<FormMessage message={searchParams} />
</div>
</form>
);
}
</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSignUp)} className='flex flex-col mx-auto space-y-4'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input type='text' placeholder='Full Name' {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name='email'
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type='email' placeholder='you@example.com' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='password'
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type='password' placeholder='Your password' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='confirmPassword'
render={({ field }) => (
<FormItem>
<FormLabel>Confirm Password</FormLabel>
<FormControl>
<Input type='password' placeholder='Confirm password' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{statusMessage && (
statusMessage.includes('Error') ||
statusMessage.includes('error') ||
statusMessage.includes('failed') ||
statusMessage.includes('invalid')
? <StatusMessage message={{error: statusMessage}} />
: <StatusMessage message={{ success: statusMessage }} />
)}
<SubmitButton
disabled={isLoading}
pendingText='Signing Up...'
>
Sign up
</SubmitButton>
</form>
</Form>
</CardContent>
</Card>
);
};
export default SignUp;