diff options
Diffstat (limited to 'frontend/src/components/auth')
-rw-r--r-- | frontend/src/components/auth/LoginForm.js | 99 | ||||
-rw-r--r-- | frontend/src/components/auth/RegisterForm.js | 167 |
2 files changed, 266 insertions, 0 deletions
diff --git a/frontend/src/components/auth/LoginForm.js b/frontend/src/components/auth/LoginForm.js new file mode 100644 index 0000000..fd5aeb7 --- /dev/null +++ b/frontend/src/components/auth/LoginForm.js @@ -0,0 +1,99 @@ +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useAuth } from '@/context/auth-context'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; + +// Form validation schema +const formSchema = z.object({ + email: z.string().email('Invalid email address'), + password: z.string().min(6, 'Password must be at least 6 characters'), +}); + +const LoginForm = () => { + const { login } = useAuth(); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + email: '', + password: '', + }, + }); + + const onSubmit = async (values) => { + try { + setIsLoading(true); + setError(''); + await login({ + email: values.email, + password: values.password, + }); + } catch (error) { + setError(error.message || 'Login failed. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( + <div className="w-full max-w-md mx-auto"> + <div className="bg-white dark:bg-slate-800 p-8 rounded-lg shadow-md"> + <h2 className="text-2xl font-bold mb-6 text-center">Login</h2> + + {error && ( + <div className="bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-3 rounded-md mb-4"> + {error} + </div> + )} + + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> + <FormField + control={form.control} + name="email" + render={({ field }) => ( + <FormItem> + <FormLabel>Email</FormLabel> + <FormControl> + <Input placeholder="[email protected]" type="email" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="password" + render={({ field }) => ( + <FormItem> + <FormLabel>Password</FormLabel> + <FormControl> + <Input placeholder="••••••••" type="password" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <Button + type="submit" + className="w-full" + disabled={isLoading} + > + {isLoading ? 'Logging in...' : 'Login'} + </Button> + </form> + </Form> + </div> + </div> + ); +}; + +export default LoginForm;
\ No newline at end of file diff --git a/frontend/src/components/auth/RegisterForm.js b/frontend/src/components/auth/RegisterForm.js new file mode 100644 index 0000000..3c321ea --- /dev/null +++ b/frontend/src/components/auth/RegisterForm.js @@ -0,0 +1,167 @@ +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useAuth } from '@/context/auth-context'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; + +// Form validation schema +const formSchema = z.object({ + name: z.string().min(2, 'Name must be at least 2 characters'), + email: z.string().email('Invalid email address'), + password: z.string().min(6, 'Password must be at least 6 characters'), + confirmPassword: z.string().min(6, 'Password must be at least 6 characters'), + phone: z.string().optional(), + address: z.string().optional(), +}).refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ['confirmPassword'], +}); + +const RegisterForm = () => { + const { register } = useAuth(); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + name: '', + email: '', + password: '', + confirmPassword: '', + phone: '', + address: '', + }, + }); + + const onSubmit = async (values) => { + try { + setIsLoading(true); + setError(''); + + // Remove confirmPassword from the data sent to API + const { confirmPassword, ...userData } = values; + + await register(userData); + } catch (error) { + setError(error.message || 'Registration failed. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( + <div className="w-full max-w-md mx-auto"> + <div className="bg-white dark:bg-slate-800 p-8 rounded-lg shadow-md"> + <h2 className="text-2xl font-bold mb-6 text-center">Create Account</h2> + + {error && ( + <div className="bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-3 rounded-md mb-4"> + {error} + </div> + )} + + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> + <FormField + control={form.control} + name="name" + render={({ field }) => ( + <FormItem> + <FormLabel>Name</FormLabel> + <FormControl> + <Input placeholder="John Doe" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="email" + render={({ field }) => ( + <FormItem> + <FormLabel>Email</FormLabel> + <FormControl> + <Input placeholder="[email protected]" type="email" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="password" + render={({ field }) => ( + <FormItem> + <FormLabel>Password</FormLabel> + <FormControl> + <Input placeholder="••••••••" type="password" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="confirmPassword" + render={({ field }) => ( + <FormItem> + <FormLabel>Confirm Password</FormLabel> + <FormControl> + <Input placeholder="••••••••" type="password" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="phone" + render={({ field }) => ( + <FormItem> + <FormLabel>Phone (Optional)</FormLabel> + <FormControl> + <Input placeholder="(123) 456-7890" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="address" + render={({ field }) => ( + <FormItem> + <FormLabel>Address (Optional)</FormLabel> + <FormControl> + <Input placeholder="123 Main St, City, State" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + + <Button + type="submit" + className="w-full" + disabled={isLoading} + > + {isLoading ? 'Creating Account...' : 'Register'} + </Button> + </form> + </Form> + </div> + </div> + ); +}; + +export default RegisterForm;
\ No newline at end of file |