8511aebb53
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { useEffect, useState, type FormEvent } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import { useLogin } from "../api/queries";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
export function LoginPage() {
|
|
const { t } = useTranslation();
|
|
const navigate = useNavigate();
|
|
const login = useLogin();
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
|
|
useEffect(() => {
|
|
document.title = t("app.name");
|
|
}, [t]);
|
|
|
|
const onSubmit = (event: FormEvent) => {
|
|
event.preventDefault();
|
|
login.mutate(
|
|
{ email, password },
|
|
{ onSuccess: () => navigate("/objects", { replace: true }) },
|
|
);
|
|
};
|
|
|
|
const errorKey = login.error
|
|
? login.error.message === "invalid"
|
|
? "auth.invalid"
|
|
: "auth.networkError"
|
|
: null;
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center p-4">
|
|
<form onSubmit={onSubmit} className="w-full max-w-sm space-y-4">
|
|
<h1 className="text-2xl font-semibold">{t("app.name")}</h1>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email">{t("auth.email")}</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={email}
|
|
onChange={(event) => setEmail(event.target.value)}
|
|
autoComplete="username"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="password">{t("auth.password")}</Label>
|
|
<Input
|
|
id="password"
|
|
type="password"
|
|
value={password}
|
|
onChange={(event) => setPassword(event.target.value)}
|
|
autoComplete="current-password"
|
|
/>
|
|
</div>
|
|
{errorKey && (
|
|
<p role="alert" className="text-sm text-destructive">
|
|
{t(errorKey)}
|
|
</p>
|
|
)}
|
|
<Button type="submit" className="w-full" disabled={login.isPending}>
|
|
{t("auth.signIn")}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|