import React, { useEffect, useMemo, useState } from 'react'; import { useForm } from '@inertiajs/react'; import { useTranslation } from 'react-i18next'; import { LoaderCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import InputError from '@/components/input-error'; import TextLink from '@/components/text-link'; import { Alert, AlertDescription } from '@/components/ui/alert'; interface LoginFormProps { onSuccess?: (payload: any) => void; canResetPassword?: boolean; } const getCsrfToken = () => (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement | null)?.content ?? ''; const parseJson = async (response: Response) => { if (response.headers.get('Content-Type')?.includes('application/json')) { const json = await response.json().catch(() => null); if (json) return json; } const text = await response.text(); throw new Error(text || 'Invalid server response (unexpected end of data or non-JSON).'); }; export default function LoginForm({ onSuccess, canResetPassword = true }: LoginFormProps) { const { t } = useTranslation('auth'); const csrfToken = useMemo(getCsrfToken, []); const { data, setData, errors, setError, clearErrors, reset } = useForm({ login: '', password: '', remember: false, }); const [hasTriedSubmit, setHasTriedSubmit] = useState(false); const [submitting, setSubmitting] = useState(false); const [formError, setFormError] = useState(null); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); setHasTriedSubmit(true); setSubmitting(true); setFormError(null); clearErrors(); try { const response = await fetch('/purchase/auth/login', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-CSRF-TOKEN': csrfToken, 'X-Requested-With': 'XMLHttpRequest', }, body: JSON.stringify({ login: data.login, password: data.password, remember: data.remember, }), }); if (response.ok) { const payload = await parseJson(response); reset({ login: payload?.user?.email ?? data.login, password: '', remember: false }); setHasTriedSubmit(false); if (onSuccess) { onSuccess(payload); } return; } if (response.status === 422) { const body = await parseJson(response); const validationErrors = body.errors ?? {}; let fallbackMessage: string | null = body.message ?? null; Object.entries(validationErrors as Record).forEach(([key, value]) => { const message = Array.isArray(value) ? value[0] : value; if (typeof message === 'string') { setError(key as keyof typeof data, message); if (!fallbackMessage) { fallbackMessage = message; } } }); if (fallbackMessage) { setFormError(fallbackMessage); } return; } setFormError(t('login.generic_error', { defaultValue: 'Login failed. Please try again.' })); } catch (error) { setFormError(t('login.generic_error', { defaultValue: 'Login failed. Please try again.' })); } finally { setSubmitting(false); } }; useEffect(() => { if (!hasTriedSubmit) { return; } const errorKeys = Object.keys(errors); if (errorKeys.length === 0) { return; } const field = document.querySelector(`[name="${errorKeys[0]}"]`); if (field) { field.scrollIntoView({ behavior: 'smooth', block: 'center' }); field.focus(); } }, [errors, hasTriedSubmit]); return (
{ setData('login', event.target.value); if (errors.login) { clearErrors('login'); } }} />
{canResetPassword && ( {t('login.forgot')} )}
{ setData('password', event.target.value); if (errors.password) { clearErrors('password'); } }} />
setData('remember', Boolean(checked))} />
{(formError || Object.keys(errors).length > 0) && ( {formError || Object.values(errors).join(' ')} )}
); }