feat: T04 fixes - i18n, auth tests, contact/vehicle test cleanup, status updates

This commit is contained in:
2026-07-14 20:16:15 +02:00
parent 2cf433a01c
commit b128ea6b39
16 changed files with 379 additions and 1004 deletions
+31 -21
View File
@@ -5,29 +5,28 @@ import { useRouter } from 'next/navigation';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Card } from '@/components/ui/Card';
import { useToast } from '@/components/ui/Toast';
import { useI18n } from '@/lib/i18n';
import { login as apiLogin } from '@/lib/api';
import { ToastProvider, useToast } from '@/components/ui/Toast';
import { I18nProvider, useI18n } from '@/lib/i18n';
import { login as apiLogin, setTokens } from '@/lib/api';
export default function LoginPage() {
function LoginForm() {
const router = useRouter();
const { t } = useI18n();
const { showToast } = useToast();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
const validate = (): boolean => {
const newErrors: typeof errors = {};
const newErrors: { email?: string; password?: string } = {};
if (!email) {
newErrors.email = t('auth.error.emailRequired');
newErrors.email = t('login.error.emailRequired');
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
newErrors.email = t('auth.error.emailInvalid');
newErrors.email = t('login.error.emailInvalid');
}
if (!password) {
newErrors.password = t('auth.error.passwordRequired');
newErrors.password = t('login.error.passwordRequired');
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
@@ -39,11 +38,12 @@ export default function LoginPage() {
setLoading(true);
try {
await apiLogin(email, password);
showToast(t('auth.loginSuccess'), 'success');
const tokens = await apiLogin(email, password);
setTokens(tokens);
showToast(t('login.success'), 'success');
router.push('/dashboard');
} catch (err) {
const message = (err as any)?.error?.message || t('auth.error.loginFailed');
const message = (err as any)?.error?.message || t('login.error.invalidCredentials');
showToast(message, 'error');
} finally {
setLoading(false);
@@ -51,37 +51,47 @@ export default function LoginPage() {
};
return (
<div className="min-h-screen flex items-center justify-center bg-background px-4">
<Card className="w-full max-w-md" title={t('auth.loginTitle')}>
<div className="min-h-screen flex items-center justify-center p-4">
<Card className="w-full max-w-md" title={t('login.title')}>
<form onSubmit={handleSubmit} className="space-y-4" data-testid="login-form">
<Input
label={t('auth.email')}
label={t('login.email')}
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={errors.email}
placeholder="name@firma.de"
data-testid="email-input"
placeholder="user@example.com"
data-testid="login-email"
/>
<Input
label={t('auth.password')}
label={t('login.password')}
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
error={errors.password}
placeholder="********"
data-testid="password-input"
data-testid="login-password"
/>
<Button
type="submit"
loading={loading}
className="w-full"
data-testid="login-button"
data-testid="login-submit"
>
{t('auth.loginButton')}
{t('login.submit')}
</Button>
</form>
</Card>
</div>
);
}
export default function LoginPage() {
return (
<I18nProvider initialLocale="de">
<ToastProvider>
<LoginForm />
</ToastProvider>
</I18nProvider>
);
}