Spaces:
Sleeping
Sleeping
| 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> | |
| ); | |
| } | |