feat: add i18n and avatar upload
This commit is contained in:
@@ -11,8 +11,11 @@ import { Label } from '@/components/ui/Label';
|
||||
import { useAuthStore } from '@/features/auth/authStore';
|
||||
import { ApiError } from '@/lib/api';
|
||||
import { Footer } from '@/components/Layout/Footer';
|
||||
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
||||
import { translateValidation, useI18n } from '@/i18n/i18n';
|
||||
|
||||
export function LoginPage() {
|
||||
const { t } = useI18n();
|
||||
const { user, login } = useAuthStore();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -39,29 +42,32 @@ export function LoginPage() {
|
||||
navigate(from, { replace: true });
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) toast.error(err.message);
|
||||
else toast.error('Login failed');
|
||||
else toast.error(t('login.failed'));
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<div className="container-page flex justify-end pt-6">
|
||||
<LanguageSwitcher />
|
||||
</div>
|
||||
<div className="container-page flex flex-1 items-center justify-center py-12">
|
||||
<div className="w-full max-w-md animate-fade-in-up">
|
||||
<div className="mb-6 flex items-center justify-center gap-2">
|
||||
<span className="inline-flex h-11 w-11 items-center justify-center rounded-md bg-primary text-primary-foreground shadow-card">
|
||||
<Gift className="h-5 w-5" />
|
||||
</span>
|
||||
<h1 className="font-display text-3xl">Family Wishlist</h1>
|
||||
<h1 className="font-display text-3xl">{t('app.name')}</h1>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-border bg-surface p-6 shadow-card sm:p-8">
|
||||
<h2 className="mb-1 text-xl font-semibold">Welcome back</h2>
|
||||
<h2 className="mb-1 text-xl font-semibold">{t('login.title')}</h2>
|
||||
<p className="mb-6 text-sm text-muted">
|
||||
Sign in to manage your wishlist. Credentials are set up via the server environment.
|
||||
{t('login.description')}
|
||||
</p>
|
||||
<form className="grid gap-4" onSubmit={submit}>
|
||||
<div className="field">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Label htmlFor="username">{t('login.username')}</Label>
|
||||
<Input
|
||||
id="username"
|
||||
autoComplete="username"
|
||||
@@ -69,11 +75,13 @@ export function LoginPage() {
|
||||
{...register('username')}
|
||||
/>
|
||||
{errors.username && (
|
||||
<span className="field__error">{errors.username.message}</span>
|
||||
<span className="field__error">
|
||||
{translateValidation(t, errors.username.message)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="field">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Label htmlFor="password">{t('login.password')}</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
@@ -81,12 +89,14 @@ export function LoginPage() {
|
||||
{...register('password')}
|
||||
/>
|
||||
{errors.password && (
|
||||
<span className="field__error">{errors.password.message}</span>
|
||||
<span className="field__error">
|
||||
{translateValidation(t, errors.password.message)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button type="submit" size="lg" disabled={isSubmitting}>
|
||||
{isSubmitting && <Loader2 className="h-4 w-4 animate-spin" />}
|
||||
Sign in
|
||||
{t('login.submit')}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user