// web/screens/auth.jsx — Вход и Регистрация (фаза 2, §3.1–3.2). // Самодостаточные экраны до авторизации: собственные инлайн-стили, не зависят // от темы приложения. api.auth.* → сохраняет токен → onLogin/onRegistered(me). const AUTH_SHELL = { minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "#f5f6f4", fontFamily: "inherit", }; const AUTH_CARD = { width: 380, background: "#fff", border: "1px solid #e4e6e1", borderRadius: 14, padding: "28px 28px 22px", boxShadow: "0 8px 30px rgba(0,0,0,.06)", }; const A_LABEL = { display: "block", fontSize: 12.5, color: "#5b6157", marginBottom: 5, fontWeight: 500 }; const A_INPUT = { width: "100%", boxSizing: "border-box", padding: "9px 11px", fontSize: 14, border: "1px solid #d7dad3", borderRadius: 8, outline: "none", background: "#fff", }; const A_BTN = { width: "100%", padding: "10px 14px", fontSize: 14, fontWeight: 600, cursor: "pointer", border: "none", borderRadius: 8, background: "#3f6f52", color: "#fff", }; const A_LINK = { background: "none", border: "none", color: "#3f6f52", cursor: "pointer", fontWeight: 600, padding: 0 }; function AuthBrand() { return (
P
pika
кейвординг для фотостоков
); } function LoginScreen({ onLogin, onGoRegister }) { const [ident, setIdent] = React.useState(""); const [password, setPassword] = React.useState(""); const [remember, setRemember] = React.useState(true); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(""); const submit = async (e) => { e.preventDefault(); setError(""); setLoading(true); try { const key = await api.auth.Login(ident.trim(), password, remember); api.auth.setToken(key); const me = await api.auth.Me(); onLogin(me); } catch (err) { setError(err.message || "Не удалось войти"); api.auth.logout(); } finally { setLoading(false); } }; return (

Вход

Войдите по email или логину, чтобы продолжить.

setIdent(e.target.value)} placeholder="you@mail.ru или ваш_логин" />
setPassword(e.target.value)} placeholder="••••••••" />
{error &&
{error}
}
Нет аккаунта?
); } function RegisterScreen({ onRegistered, onGoLogin }) { const [email, setEmail] = React.useState(""); const [login, setLogin] = React.useState(""); const [password, setPassword] = React.useState(""); const [password2, setPassword2] = React.useState(""); const [check, setCheck] = React.useState({ valid: false, free: null, reason: "" }); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(""); const [pending, setPending] = React.useState(false); // регистрация принята, ждёт ручной активации // Live-проверка логина (дебаунс 350мс) через auth.CheckLogin. React.useEffect(() => { if (!login) { setCheck({ valid: false, free: null, reason: "" }); return; } let alive = true; const t = setTimeout(() => { api.auth.CheckLogin(login.trim()) .then((r) => { if (alive) setCheck(r); }) .catch(() => {}); }, 350); return () => { alive = false; clearTimeout(t); }; }, [login]); const emailOk = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email); const passOk = password.length >= 8; const matchOk = password.length > 0 && password === password2; const canSubmit = emailOk && check.valid && check.free && passOk && matchOk && !loading; const submit = async (e) => { e.preventDefault(); if (!canSubmit) return; setError(""); setLoading(true); try { const key = await api.auth.Register(email.trim(), login.trim(), password); if (!key) { // Ворота ручной активации включены: аккаунт создан, но заблокирован до // одобрения админом. Не логиним — показываем «ждите активации». setPending(true); return; } api.auth.setToken(key); const me = await api.auth.Me(); onRegistered(me); } catch (err) { setError(err.message || "Не удалось зарегистрироваться"); api.auth.logout(); } finally { setLoading(false); } }; const loginBorder = login ? (check.valid && check.free ? "#3f9f6a" : check.valid ? "#c0392b" : "#d7dad3") : "#d7dad3"; if (pending) { return (

Заявка принята

Аккаунт {login.trim()} создан и ожидает активации администратором. Как только тебя активируют — сможешь войти под своим логином и паролем.

); } return (

Регистрация

Создайте аккаунт — сразу дадим пробный период.

setEmail(e.target.value)} placeholder="you@mail.ru" />
setLogin(e.target.value)} placeholder="ваш_логин" />
{login && check.reason ? check.reason : "латиница, цифры и _, без пробелов и @ · станет именем вашей папки"}
setPassword(e.target.value)} placeholder="минимум 8 символов" /> {password && !passOk &&
слишком короткий — нужно хотя бы 8
}
setPassword2(e.target.value)} placeholder="ещё раз" /> {password2 && !matchOk &&
пароли не совпадают
}
{error &&
{error}
}
Уже есть аккаунт?
); } window.LoginScreen = LoginScreen; window.RegisterScreen = RegisterScreen;