'use client' import { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' import { validateEmail, validatePassword, sanitizeString } from '@/lib/validation' import { registerUser } from '@/lib/auth-actions' import { useToast } from './ToastContainer' interface RegisterFormProps { callbackUrl?: string } export default function RegisterForm({ callbackUrl = '/' }: RegisterFormProps) { const router = useRouter() const { showToast } = useToast() const [name, setName] = useState('') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [errors, setErrors] = useState<{ name?: string email?: string password?: string confirmPassword?: string }>({}) const [touched, setTouched] = useState<{ name: boolean email: boolean password: boolean confirmPassword: boolean }>({ name: false, email: false, password: false, confirmPassword: false, }) // Inline validation when fields change useEffect(() => { if (touched.name) { const sanitized = sanitizeString(name) if (!sanitized) { setErrors((prev) => ({ ...prev, name: 'Name is required' })) } else if (sanitized.length > 100) { setErrors((prev) => ({ ...prev, name: 'Name must be less than 100 characters' })) } else { setErrors((prev) => ({ ...prev, name: undefined })) } } }, [name, touched.name]) useEffect(() => { if (touched.email) { const result = validateEmail(email) setErrors((prev) => ({ ...prev, email: result.isValid ? undefined : result.error, })) } }, [email, touched.email]) useEffect(() => { if (touched.password) { const result = validatePassword(password) setErrors((prev) => ({ ...prev, password: result.isValid ? undefined : result.error, })) } }, [password, touched.password]) useEffect(() => { if (touched.confirmPassword) { if (!confirmPassword) { setErrors((prev) => ({ ...prev, confirmPassword: 'Please confirm your password' })) } else if (password !== confirmPassword) { setErrors((prev) => ({ ...prev, confirmPassword: 'Passwords do not match' })) } else { setErrors((prev) => ({ ...prev, confirmPassword: undefined })) } } }, [confirmPassword, password, touched.confirmPassword]) const handleNameBlur = () => { setTouched((prev) => ({ ...prev, name: true })) } const handleEmailBlur = () => { setTouched((prev) => ({ ...prev, email: true })) } const handlePasswordBlur = () => { setTouched((prev) => ({ ...prev, password: true })) } const handleConfirmPasswordBlur = () => { setTouched((prev) => ({ ...prev, confirmPassword: true })) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() // Validate all fields before submission const sanitizedName = sanitizeString(name) const emailResult = validateEmail(email) const passwordResult = validatePassword(password) const newErrors: { name?: string email?: string password?: string confirmPassword?: string } = {} if (!sanitizedName) { newErrors.name = 'Name is required' } else if (sanitizedName.length > 100) { newErrors.name = 'Name must be less than 100 characters' } if (!emailResult.isValid) { newErrors.email = emailResult.error } if (!passwordResult.isValid) { newErrors.password = passwordResult.error } if (!confirmPassword) { newErrors.confirmPassword = 'Please confirm your password' } else if (password !== confirmPassword) { newErrors.confirmPassword = 'Passwords do not match' } if (Object.keys(newErrors).length > 0) { setErrors(newErrors) setTouched({ name: true, email: true, password: true, confirmPassword: true }) return } setIsSubmitting(true) setErrors({}) try { const result = await registerUser(email.trim(), password, sanitizedName) if (!result.success) { if (result.error === 'Email already exists') { setErrors({ email: 'This email is already registered' }) showToast('Email already exists', 'error') } else { showToast(result.error || 'Registration failed', 'error') } } else { showToast('Registration successful! Logging you in...', 'success') router.push(callbackUrl) router.refresh() } } catch (err) { console.error('Registration error:', err) showToast('An error occurred. Please try again.', 'error') } finally { setIsSubmitting(false) } } return (
setName(e.target.value)} onBlur={handleNameBlur} className={`w-full px-3 sm:px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 text-sm sm:text-base ${ errors.name && touched.name ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-blue-500' }`} placeholder="Enter your name" disabled={isSubmitting} autoComplete="name" autoFocus maxLength={100} aria-invalid={errors.name && touched.name ? 'true' : 'false'} aria-describedby={errors.name && touched.name ? 'name-error' : undefined} /> {errors.name && touched.name && (

{errors.name}

)}
setEmail(e.target.value)} onBlur={handleEmailBlur} className={`w-full px-3 sm:px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 text-sm sm:text-base ${ errors.email && touched.email ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-blue-500' }`} placeholder="Enter your email" disabled={isSubmitting} autoComplete="email" aria-invalid={errors.email && touched.email ? 'true' : 'false'} aria-describedby={errors.email && touched.email ? 'email-error' : undefined} /> {errors.email && touched.email && (

{errors.email}

)}
setPassword(e.target.value)} onBlur={handlePasswordBlur} className={`w-full px-3 sm:px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 text-sm sm:text-base ${ errors.password && touched.password ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-blue-500' }`} placeholder="At least 8 characters" disabled={isSubmitting} autoComplete="new-password" aria-invalid={errors.password && touched.password ? 'true' : 'false'} aria-describedby={errors.password && touched.password ? 'password-error' : undefined} /> {errors.password && touched.password && (

{errors.password}

)} {!errors.password && touched.password && (

Password must be at least 8 characters

)}
setConfirmPassword(e.target.value)} onBlur={handleConfirmPasswordBlur} className={`w-full px-3 sm:px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 text-sm sm:text-base ${ errors.confirmPassword && touched.confirmPassword ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-blue-500' }`} placeholder="Re-enter your password" disabled={isSubmitting} autoComplete="new-password" aria-invalid={errors.confirmPassword && touched.confirmPassword ? 'true' : 'false'} aria-describedby={ errors.confirmPassword && touched.confirmPassword ? 'confirm-password-error' : undefined } /> {errors.confirmPassword && touched.confirmPassword && (

{errors.confirmPassword}

)}
) }