85cf8be705
Better Auth does not auto-resend on 403 (verified against Resend logs), so request a fresh link via authClient.sendVerificationEmail before telling the user a new email was sent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
132 lines
4.8 KiB
TypeScript
132 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import Link from "next/link";
|
|
import { signIn, authClient } from "@/lib/auth-client";
|
|
|
|
export function LoginForm() {
|
|
const router = useRouter();
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [error, setError] = useState("");
|
|
const [needsVerification, setNeedsVerification] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault();
|
|
setError("");
|
|
setNeedsVerification(false);
|
|
setLoading(true);
|
|
|
|
const result = await signIn.email({ email, password });
|
|
|
|
if (result.error) {
|
|
if (result.error.status === 403) {
|
|
// Креды верны, но email не подтверждён. Better Auth сам письмо
|
|
// не переотправляет (проверено) — запрашиваем свежую ссылку явно.
|
|
await authClient.sendVerificationEmail({ email, callbackURL: "/dashboard" });
|
|
setNeedsVerification(true);
|
|
} else if (result.error.status === 429) {
|
|
setError("Слишком много попыток входа. Подождите минуту и попробуйте снова.");
|
|
} else {
|
|
setError("Неверный email или пароль");
|
|
}
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
const session = await authClient.getSession();
|
|
const role = session.data?.user?.role;
|
|
|
|
if (role === "admin") {
|
|
router.push("/admin/dashboard");
|
|
} else if (role === "curator") {
|
|
router.push("/curator/dashboard");
|
|
} else {
|
|
router.push("/dashboard");
|
|
}
|
|
router.refresh();
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="space-y-5">
|
|
<div className="space-y-1.5">
|
|
<label className="block text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
|
|
Email
|
|
</label>
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
required
|
|
className="w-full px-3 py-2 text-sm bg-transparent"
|
|
style={{
|
|
border: "2px solid var(--border)",
|
|
color: "var(--foreground)",
|
|
fontFamily: "var(--font-sans)",
|
|
outline: "none",
|
|
}}
|
|
onFocus={(e) => (e.target.style.borderColor = "var(--foreground)")}
|
|
onBlur={(e) => (e.target.style.borderColor = "var(--border)")}
|
|
placeholder="you@example.com"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<label className="block text-xs uppercase tracking-widest font-bold" style={{ color: "var(--muted-foreground)" }}>
|
|
Пароль
|
|
</label>
|
|
<input
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
className="w-full px-3 py-2 text-sm bg-transparent"
|
|
style={{
|
|
border: "2px solid var(--border)",
|
|
color: "var(--foreground)",
|
|
fontFamily: "var(--font-sans)",
|
|
outline: "none",
|
|
}}
|
|
onFocus={(e) => (e.target.style.borderColor = "var(--foreground)")}
|
|
onBlur={(e) => (e.target.style.borderColor = "var(--border)")}
|
|
placeholder="••••••••"
|
|
/>
|
|
</div>
|
|
{needsVerification && (
|
|
<div
|
|
className="text-xs py-3 px-3 space-y-1"
|
|
style={{ border: "2px solid var(--border)", color: "var(--foreground)" }}
|
|
>
|
|
<p className="font-bold">Email не подтверждён</p>
|
|
<p style={{ color: "var(--muted-foreground)" }}>
|
|
Мы отправили новое письмо со ссылкой подтверждения на <strong>{email}</strong>.
|
|
Проверьте почту (и папку «Спам»), затем войдите снова.
|
|
</p>
|
|
</div>
|
|
)}
|
|
{error && (
|
|
<p className="text-sm" style={{ color: "var(--destructive)" }}>
|
|
{error}
|
|
</p>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="btn-aubade w-full justify-center"
|
|
style={loading ? { opacity: 0.6, cursor: "not-allowed" } : undefined}
|
|
>
|
|
{loading ? "Вход..." : "Войти"}
|
|
</button>
|
|
<div className="flex items-center justify-between text-xs" style={{ color: "var(--muted-foreground)" }}>
|
|
<Link href="/forgot-password" className="underline" style={{ color: "var(--muted-foreground)" }}>
|
|
Забыли пароль?
|
|
</Link>
|
|
<Link href="/register" className="underline" style={{ color: "var(--foreground)" }}>
|
|
Зарегистрироваться
|
|
</Link>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|