Files
Stalwart-webui/src/pages/LoginPage.tsx
T
2026-04-20 15:02:57 +02:00

90 lines
2.9 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
import { type FormEvent, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { ArrowRight, Loader2 } from 'lucide-react';
import Logo from '@/components/common/Logo';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { startAuthFlow } from '@/services/auth/oauth';
export default function LoginPage() {
const { t } = useTranslation();
const location = useLocation();
const originalPath = (location.state as { from?: string } | null)?.from ?? null;
const [username, setUsername] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: FormEvent) {
e.preventDefault();
const trimmed = username.trim();
if (!trimmed) return;
setError(null);
setLoading(true);
try {
await startAuthFlow(trimmed, originalPath);
} catch (err) {
setError(err instanceof Error ? err.message : t('login.error', 'An unexpected error occurred'));
setLoading(false);
}
}
return (
<div className="flex min-h-screen items-center justify-center bg-content-background px-4">
<Card className="w-full max-w-sm shadow-sm">
<CardHeader className="items-center text-center">
<Logo />
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<p className="text-center text-sm text-muted-foreground">
{t('login.prompt', 'Enter your account name to continue')}
</p>
<Input
id="username"
type="text"
autoComplete="username"
autoFocus
placeholder={t('login.usernamePlaceholder', 'user@example.com')}
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={loading}
aria-label={t('login.prompt', 'Enter your account name to continue')}
/>
</div>
{error && (
<p className="text-sm text-destructive" role="alert">
{error}
</p>
)}
<Button type="submit" className="w-full" disabled={loading || !username.trim()}>
{loading ? (
<Loader2 className="animate-spin" />
) : (
<>
{t('login.continue', 'Continue')}
<ArrowRight />
</>
)}
</Button>
</form>
</CardContent>
</Card>
</div>
);
}