Spaces:
Sleeping
Sleeping
File size: 5,835 Bytes
eddc354 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import { useState, type FormEvent } from "react";
import { KeyRound, ShieldCheck } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { login, setPermanentPassword, type StoredUser } from "@/lib/auth-service";
export default function LoginDialog({ onSignedIn }: { onSignedIn: (user: StoredUser) => void }) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [remember, setRemember] = useState(true);
const [error, setError] = useState<string | null>(null);
/** Пользователь вошёл по временному паролю — требуется постоянный. */
const [pending, setPending] = useState<StoredUser | null>(null);
const [next, setNext] = useState("");
const [confirm, setConfirm] = useState("");
const submitLogin = (e: FormEvent) => {
e.preventDefault();
const result = login(username, password, remember);
if (!result.ok) {
setError(result.error);
return;
}
setError(null);
if (result.mustChangePassword) {
setPending(result.user);
return;
}
onSignedIn(result.user);
};
const submitNewPassword = (e: FormEvent) => {
e.preventDefault();
if (!pending) return;
if (next !== confirm) {
setError("Пароли не совпадают.");
return;
}
const result = setPermanentPassword(pending.id, next);
if (!result.ok) {
setError(result.error ?? "Не удалось сохранить пароль.");
return;
}
onSignedIn({ ...pending, temporary: false, status: "active" });
};
if (pending) {
return (
<Dialog open>
<DialogContent className="max-w-sm [&>button]:hidden">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 font-display text-2xl">
<ShieldCheck className="h-6 w-6 text-accent" /> Новый пароль
</DialogTitle>
<DialogDescription>
Вы вошли по временному паролю. Задайте постоянный пароль, чтобы продолжить работу.
</DialogDescription>
</DialogHeader>
<form onSubmit={submitNewPassword} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="new-password">Новый пароль</Label>
<Input
id="new-password"
autoFocus
type="password"
value={next}
onChange={(e) => {
setNext(e.target.value);
setError(null);
}}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirm-password">Повторите пароль</Label>
<Input
id="confirm-password"
type="password"
value={confirm}
onChange={(e) => {
setConfirm(e.target.value);
setError(null);
}}
className="h-12"
/>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<Button type="submit" size="lg" className="h-12 w-full">
Сохранить и войти
</Button>
</form>
</DialogContent>
</Dialog>
);
}
return (
<Dialog open>
<DialogContent className="max-w-sm [&>button]:hidden">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 font-display text-2xl">
<KeyRound className="h-6 w-6 text-accent" /> RealKnot Studio
</DialogTitle>
<DialogDescription>Войдите в аккаунт, чтобы открыть студию и свои проекты.</DialogDescription>
</DialogHeader>
<form onSubmit={submitLogin} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="login">Логин</Label>
<Input
id="login"
autoFocus
autoComplete="username"
placeholder="admin"
value={username}
onChange={(e) => {
setUsername(e.target.value);
setError(null);
}}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Пароль</Label>
<Input
id="password"
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
setError(null);
}}
className="h-12"
/>
</div>
{error && <p className="text-sm text-destructive">{error}</p>}
<label className="flex cursor-pointer items-center gap-2 text-sm">
<Checkbox checked={remember} onCheckedChange={(v) => setRemember(v === true)} />
Запомнить меня
</label>
<Button type="submit" size="lg" className="h-12 w-full">
Войти в студию
</Button>
<p className="text-center text-xs text-muted-foreground">
Аккаунт создаёт администратор в «Панели админа».
</p>
</form>
</DialogContent>
</Dialog>
);
}
|