import { useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useNavigate, useSearch } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { useAuth } from '@/contexts/auth-context'; import { NetworkError, ProblemDetailsError } from '@/lib/api-client'; export function LoginPage() { const { t } = useTranslation(); const { login } = useAuth(); const navigate = useNavigate(); const search = useSearch({ strict: false }) as { redirect?: string }; const [serverError, setServerError] = useState(null); const schema = useMemo( () => z.object({ email: z .string() .min(1, t('login.errors.emailRequired')) .email(t('login.errors.emailInvalid')), password: z.string().min(1, t('login.errors.passwordRequired')), }), [t], ); type FormValues = z.infer; const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(schema), defaultValues: { email: '', password: '' }, }); const onSubmit = handleSubmit(async (values) => { setServerError(null); try { await login(values.email, values.password); await navigate({ to: search.redirect ?? '/dashboard' }); } catch (err) { if (err instanceof ProblemDetailsError && err.status === 401) { setServerError(t('login.errors.invalidCredentials')); } else if (err instanceof NetworkError) { setServerError(t('errors.network')); } else { setServerError(t('login.errors.generic')); } } }); return (
{t('login.title')} {t('login.subtitle')}
{serverError !== null && (
{serverError}
)}
{errors.email && (

{errors.email.message}

)}
{errors.password && (

{errors.password.message}

)}
); }