/** * RegisterForm Component * Handles user registration with validation * Integrates with backend API and auth store */ 'use client'; import { useState } from 'react'; import Link from 'next/link'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Alert } from '@/components/ui/alert'; import { useRegister } from '@/lib/api/hooks/useAuth'; import { getGeneralError, getFieldErrors } from '@/lib/api/errors'; import type { APIError } from '@/lib/api/errors'; import config from '@/config/app.config'; // ============================================================================ // Validation Schema // ============================================================================ const registerSchema = z .object({ email: z .string() .min(1, 'Email is required') .email('Please enter a valid email address'), first_name: z .string() .min(1, 'First name is required') .min(2, 'First name must be at least 2 characters') .max(50, 'First name must not exceed 50 characters'), last_name: z .string() .max(50, 'Last name must not exceed 50 characters') .optional() .or(z.literal('')), // Allow empty string password: z .string() .min(1, 'Password is required') .min(8, 'Password must be at least 8 characters') .regex(/[0-9]/, 'Password must contain at least one number') .regex(/[A-Z]/, 'Password must contain at least one uppercase letter'), confirmPassword: z .string() .min(1, 'Please confirm your password'), }) .refine((data) => data.password === data.confirmPassword, { message: 'Passwords do not match', path: ['confirmPassword'], }); type RegisterFormData = z.infer; // ============================================================================ // Component // ============================================================================ interface RegisterFormProps { /** Optional callback after successful registration */ onSuccess?: () => void; /** Show login link */ showLoginLink?: boolean; /** Custom className for form container */ className?: string; } /** * RegisterForm - User registration form * * Features: * - Email, name, and password validation * - Password confirmation matching * - Password strength requirements display * - Loading states * - Server error display * - Link to login page * * @example * ```tsx * router.push('/dashboard')} * /> * ``` */ export function RegisterForm({ onSuccess, showLoginLink = true, className, }: RegisterFormProps) { const [serverError, setServerError] = useState(null); const registerMutation = useRegister(); const form = useForm({ resolver: zodResolver(registerSchema), defaultValues: { email: '', first_name: '', last_name: '', password: '', confirmPassword: '', }, }); const onSubmit = async (data: RegisterFormData) => { try { // Clear previous errors setServerError(null); form.clearErrors(); // Prepare data for API (exclude confirmPassword) // eslint-disable-next-line @typescript-eslint/no-unused-vars const { confirmPassword, ...registerData } = data; // Attempt registration await registerMutation.mutateAsync(registerData); // Success callback onSuccess?.(); } catch (error) { // Handle API errors const errors = error as APIError[]; // Set general error message const generalError = getGeneralError(errors); if (generalError) { setServerError(generalError); } // Set field-specific errors const fieldErrors = getFieldErrors(errors); Object.entries(fieldErrors).forEach(([field, message]) => { if (field in form.getValues()) { form.setError(field as keyof RegisterFormData, { message }); } }); } }; const isSubmitting = form.formState.isSubmitting || registerMutation.isPending; // Watch password to show strength requirements const password = form.watch('password'); const hasMinLength = password.length >= 8; const hasNumber = /[0-9]/.test(password); const hasUppercase = /[A-Z]/.test(password); return (
{/* Server Error Alert */} {serverError && (

{serverError}

)} {/* First Name Field */}
{form.formState.errors.first_name && (

{form.formState.errors.first_name.message}

)}
{/* Last Name Field */}
{form.formState.errors.last_name && (

{form.formState.errors.last_name.message}

)}
{/* Email Field */}
{form.formState.errors.email && (

{form.formState.errors.email.message}

)}
{/* Password Field */}
{form.formState.errors.password && (

{form.formState.errors.password.message}

)} {/* Password Strength Indicator */} {password.length > 0 && !form.formState.errors.password && (

{hasMinLength ? '✓' : '○'} At least 8 characters

{hasNumber ? '✓' : '○'} Contains a number

{hasUppercase ? '✓' : '○'} Contains an uppercase letter

)}
{/* Confirm Password Field */}
{form.formState.errors.confirmPassword && (

{form.formState.errors.confirmPassword.message}

)}
{/* Submit Button */} {/* Login Link */} {showLoginLink && (

Already have an account?{' '} Sign in

)}
); }