'use client' import { useState, useEffect } from 'react' import { signIn } from 'next-auth/react' import { useRouter } from 'next/navigation' import { validateEmail, validatePassword } from '@/lib/validation' import { useToast } from './ToastContainer' interface LoginFormProps { callbackUrl?: string } export default function LoginForm({ callbackUrl = '/' }: LoginFormProps) { const router = useRouter() const { showToast } = useToast() const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [errors, setErrors] = useState<{ email?: string; password?: string }>({}) const [touched, setTouched] = useState<{ email: boolean; password: boolean }>({ email: false, password: false, }) // Inline validation when fields change 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]) const handleEmailBlur = () => { setTouched((prev) => ({ ...prev, email: true })) const result = validateEmail(email) setErrors((prev) => ({ ...prev, email: result.isValid ? undefined : result.error, })) } const handlePasswordBlur = () => { setTouched((prev) => ({ ...prev, password: true })) const result = validatePassword(password) setErrors((prev) => ({ ...prev, password: result.isValid ? undefined : result.error, })) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() // Validate all fields before submission const emailResult = validateEmail(email) const passwordResult = validatePassword(password) const newErrors: { email?: string; password?: string } = {} if (!emailResult.isValid) { newErrors.email = emailResult.error } if (!passwordResult.isValid) { newErrors.password = passwordResult.error } if (Object.keys(newErrors).length > 0) { setErrors(newErrors) setTouched({ email: true, password: true }) return } setIsSubmitting(true) setErrors({}) try { const result = await signIn('credentials', { email: email.trim(), password, redirect: false, }) if (result?.error) { showToast('Invalid email or password', 'error') setErrors({ email: 'Invalid credentials', password: 'Invalid credentials' }) } else if (result?.ok) { showToast('Login successful', 'success') router.push(callbackUrl) router.refresh() } } catch (err) { console.error('Login error:', err) showToast('An error occurred. Please try again.', 'error') } finally { setIsSubmitting(false) } } return (
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" autoFocus 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="Enter your password" disabled={isSubmitting} autoComplete="current-password" aria-invalid={errors.password && touched.password ? 'true' : 'false'} aria-describedby={errors.password && touched.password ? 'password-error' : undefined} /> {errors.password && touched.password && (

{errors.password}

)}
) }