a5e7b20699
Users can now request a password reset link via email. Better Auth sendResetPassword callback sends a branded email via Resend. Login page shows success notice after password is set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
109 lines
3.5 KiB
TypeScript
109 lines
3.5 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 [loading, setLoading] = useState(false);
|
||
|
||
async function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
setError("");
|
||
setLoading(true);
|
||
|
||
const result = await signIn.email({ email, password });
|
||
|
||
if (result.error) {
|
||
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>
|
||
{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>
|
||
);
|
||
}
|