Files
fotospiel-app/resources/js/pages/auth/LoginForm.tsx

133 lines
3.9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { useForm } from '@inertiajs/react';
import { useTranslation } from 'react-i18next';
import { LoaderCircle, Mail, Lock } 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';
interface LoginFormProps {
onSuccess?: (userData: any) => void;
canResetPassword?: boolean;
}
export default function LoginForm({ onSuccess, canResetPassword = true }: LoginFormProps) {
const [hasTriedSubmit, setHasTriedSubmit] = useState(false);
const { t } = useTranslation('auth');
const { data, setData, post, processing, errors, clearErrors, reset } = useForm({
email: '',
password: '',
remember: false,
});
const submit = (e: React.FormEvent) => {
e.preventDefault();
setHasTriedSubmit(true);
post('/login', {
preserveScroll: true,
onSuccess: () => {
if (onSuccess) {
onSuccess({ user: { email: data.email } }); // Pass basic user info; full user from props in parent
}
reset();
},
});
};
useEffect(() => {
if (!hasTriedSubmit) {
return;
}
const errorKeys = Object.keys(errors);
if (errorKeys.length === 0) {
return;
}
const field = document.querySelector<HTMLInputElement>(`[name="${errorKeys[0]}"]`);
if (field) {
field.scrollIntoView({ behavior: 'smooth', block: 'center' });
field.focus();
}
}, [errors, hasTriedSubmit]);
return (
<div className="flex flex-col gap-6">
<div className="grid gap-6">
<div className="grid gap-2">
<Label htmlFor="email">{t('login.email')}</Label>
<Input
id="email"
type="email"
name="email"
required
autoFocus
placeholder={t('login.email_placeholder')}
value={data.email}
onChange={(e) => {
setData('email', e.target.value);
if (errors.email) {
clearErrors('email');
}
}}
/>
<InputError message={errors.email} />
</div>
<div className="grid gap-2">
<div className="flex items-center">
<Label htmlFor="password">{t('login.password')}</Label>
{canResetPassword && (
<TextLink href="/forgot-password" className="ml-auto text-sm">
{t('login.forgot')}
</TextLink>
)}
</div>
<Input
id="password"
type="password"
name="password"
required
placeholder={t('login.password_placeholder')}
value={data.password}
onChange={(e) => {
setData('password', e.target.value);
if (errors.password) {
clearErrors('password');
}
}}
/>
<InputError message={errors.password} />
</div>
<div className="flex items-center space-x-3">
<Checkbox
id="remember"
name="remember"
checked={data.remember}
onCheckedChange={(checked) => setData('remember', Boolean(checked))}
/>
<Label htmlFor="remember">{t('login.remember')}</Label>
</div>
<Button type="button" onClick={submit} className="w-full" disabled={processing}>
{processing && <LoaderCircle className="h-4 w-4 animate-spin mr-2" />}
{t('login.submit')}
</Button>
</div>
{Object.keys(errors).length > 0 && (
<div className="p-4 bg-red-50 border border-red-200 rounded-md">
<p className="text-sm text-red-800">
{Object.values(errors).join(' ')}
</p>
</div>
)}
</div>
);
}