Files
image-prompt-studio/components/login-form.tsx
2026-04-25 11:26:33 +08:00

114 lines
3.1 KiB
TypeScript

"use client";
import { startTransition, useMemo, useState, type FormEvent } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import type { UserRole } from "@/lib/types";
interface LoginResponse {
ok?: boolean;
error?: string;
user?: {
role: UserRole;
};
}
function getErrorMessage(error: unknown, fallbackMessage: string): string {
return error instanceof Error ? error.message : fallbackMessage;
}
export default function LoginForm() {
const router = useRouter();
const searchParams = useSearchParams();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const [isPending, setIsPending] = useState(false);
const notice = useMemo(() => {
const reason = searchParams.get("reason");
if (reason === "inactive") {
return "当前账号不可用,请联系管理员。";
}
return "使用管理员或已创建的普通用户登录。";
}, [searchParams]);
async function handleSubmit(
event: FormEvent<HTMLFormElement>,
): Promise<void> {
event.preventDefault();
setErrorMessage("");
setIsPending(true);
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username,
password,
}),
});
const payload = (await response.json()) as LoginResponse;
if (!response.ok || payload.ok !== true || !payload.user) {
throw new Error(payload.error || "登录失败,请稍后重试。");
}
const currentUser = payload.user;
startTransition(() => {
router.push(currentUser.role === "admin" ? "/admin" : "/studio");
router.refresh();
});
} catch (error) {
setErrorMessage(getErrorMessage(error, "登录失败,请稍后重试。"));
} finally {
setIsPending(false);
}
}
return (
<form className="login-form" onSubmit={handleSubmit}>
<div className="login-copy">
<p className="eyebrow">Secure Access</p>
<h1>使</h1>
<p>{notice}</p>
</div>
<label className="field">
<span></span>
<input
type="text"
autoComplete="username"
placeholder="admin"
value={username}
onChange={(event) => setUsername(event.target.value)}
required
/>
</label>
<label className="field">
<span></span>
<input
type="password"
autoComplete="current-password"
placeholder="输入密码"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
/>
</label>
{errorMessage ? <div className="error-banner">{errorMessage}</div> : null}
<button type="submit" className="primary-button" disabled={isPending}>
{isPending ? "登录中..." : "登录"}
</button>
</form>
);
}