// 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 (
);
}
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 (
);
}
window.LoginScreen = LoginScreen;
window.RegisterScreen = RegisterScreen;