feat(frontend): i18n common + nav + auth (Layout, LoginPage, ProtectedRoute)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, type FormEvent } from 'react';
|
||||
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { extractApiError } from '@/api/client';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { FormField, TextInput } from '@/components/FormField';
|
||||
@@ -14,6 +15,7 @@ export function LoginPage(): JSX.Element {
|
||||
const location = useLocation();
|
||||
const fromPath = (location.state as LocationState | null)?.from;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
@@ -34,7 +36,7 @@ export function LoginPage(): JSX.Element {
|
||||
await login(username, password);
|
||||
navigate(fromPath ?? '/engagements', { replace: true });
|
||||
} catch (err) {
|
||||
setError(extractApiError(err, 'Invalid credentials'));
|
||||
setError(extractApiError(err, t('auth.login.invalid')));
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -48,10 +50,10 @@ export function LoginPage(): JSX.Element {
|
||||
<span className="inline-block h-8 w-8 rotate-12 bg-primary" aria-hidden />
|
||||
<h1 className="text-[28px] font-medium leading-none">Mimic</h1>
|
||||
</div>
|
||||
<p className="text-[16px] text-charcoal">Sign in to access your engagements.</p>
|
||||
<p className="text-[16px] text-charcoal">{t('auth.login.subtitle')}</p>
|
||||
|
||||
<form onSubmit={onSubmit} noValidate className="flex flex-col gap-md">
|
||||
<FormField label="Username" htmlFor="login-username" required>
|
||||
<FormField label={t('auth.login.username')} htmlFor="login-username" required>
|
||||
<TextInput
|
||||
id="login-username"
|
||||
name="username"
|
||||
@@ -62,7 +64,7 @@ export function LoginPage(): JSX.Element {
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Password" htmlFor="login-password" required>
|
||||
<FormField label={t('auth.login.password')} htmlFor="login-password" required>
|
||||
<TextInput
|
||||
id="login-password"
|
||||
type="password"
|
||||
@@ -81,7 +83,7 @@ export function LoginPage(): JSX.Element {
|
||||
) : null}
|
||||
|
||||
<button type="submit" className="btn-primary" disabled={submitting}>
|
||||
{submitting ? 'Signing in…' : 'Sign in'}
|
||||
{submitting ? t('auth.login.signingIn') : t('auth.login.signIn')}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user