fixed code
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useState, useCallback } from "react";
|
||||
// src/components/AuthModal.jsx
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
import { useAuthContext } from "../context/AuthContext";
|
||||
import { API_URL } from "../config/authConstants";
|
||||
import { saveTokens } from "../api/apiClient";
|
||||
import { login as apiLogin, register as apiRegister } from "../api/apiClient";
|
||||
|
||||
/**
|
||||
* AuthModal — модальное окно входа и регистрации прямо на странице ленты.
|
||||
@@ -15,32 +15,31 @@ export default function AuthModal({ onClose }) {
|
||||
const { login: saveAuth } = useAuthContext();
|
||||
|
||||
return (
|
||||
<div className="f-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
|
||||
<div className="f-modal" style={{ width: 420 }}>
|
||||
{/* Header */}
|
||||
<div className="f-modal-head">
|
||||
<div className="f-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
|
||||
<div className="f-modal auth-modal">
|
||||
<div className="f-modal-head">
|
||||
<span className="f-modal-title">
|
||||
{mode === "login" ? t('auth.loginModalTitle') : t('auth.registerModalTitle')}
|
||||
</span>
|
||||
<button className="f-close-btn" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
<button className="f-close-btn" type="button" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{mode === "login"
|
||||
? <LoginFormInline
|
||||
saveAuth={saveAuth}
|
||||
onClose={onClose}
|
||||
onSwitch={() => setMode("register")}
|
||||
/>
|
||||
: <RegisterFormInline
|
||||
saveAuth={saveAuth}
|
||||
onClose={onClose}
|
||||
onSwitch={() => setMode("login")}
|
||||
/>
|
||||
}
|
||||
{mode === "login"
|
||||
? <LoginFormInline
|
||||
saveAuth={saveAuth}
|
||||
onClose={onClose}
|
||||
onSwitch={() => setMode("register")}
|
||||
/>
|
||||
: <RegisterFormInline
|
||||
saveAuth={saveAuth}
|
||||
onClose={onClose}
|
||||
onSwitch={() => setMode("login")}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,25 +51,16 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!login || !password) { setError(t('auth.enterLoginPassword')); return; }
|
||||
setLoading(true); setError("");
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/api/v1/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ login, password }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.message || t('auth.invalidCredentials'));
|
||||
}
|
||||
|
||||
const { token, refreshToken, user } = await res.json();
|
||||
saveTokens(token, refreshToken);
|
||||
saveAuth(user, token);
|
||||
const { token, refreshToken, user } = await apiLogin(
|
||||
login, password, t('auth.invalidCredentials')
|
||||
);
|
||||
saveAuth(user, token, refreshToken);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
@@ -80,56 +70,43 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.loginLabel')}</label>
|
||||
<input
|
||||
className="f-m-field"
|
||||
type="text"
|
||||
placeholder={t('auth.yourLogin')}
|
||||
value={login}
|
||||
onChange={e => { setLogin(e.target.value); setError(""); }}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.loginLabel')}</label>
|
||||
<input
|
||||
className="f-m-field"
|
||||
type="text"
|
||||
placeholder={t('auth.yourLogin')}
|
||||
value={login}
|
||||
onChange={e => { setLogin(e.target.value); setError(""); }}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.passwordLabel')}</label>
|
||||
<input
|
||||
className="f-m-field"
|
||||
type="password"
|
||||
placeholder={t('auth.yourPassword')}
|
||||
value={password}
|
||||
onChange={e => { setPassword(e.target.value); setError(""); }}
|
||||
onKeyDown={e => e.key === "Enter" && handleSubmit()}
|
||||
/>
|
||||
</div>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.passwordLabel')}</label>
|
||||
<input
|
||||
className="f-m-field"
|
||||
type="password"
|
||||
placeholder={t('auth.yourPassword')}
|
||||
value={password}
|
||||
onChange={e => { setPassword(e.target.value); setError(""); }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && <p className="f-err-msg" style={{ marginBottom: 10 }}>{error}</p>}
|
||||
{error && <p className="f-err-msg auth-form-error">{error}</p>}
|
||||
|
||||
<SubmitButton
|
||||
loading={loading}
|
||||
label={t('auth.login')}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<SubmitButton loading={loading} label={t('auth.login')} />
|
||||
|
||||
<p style={{
|
||||
textAlign: "center", marginTop: 14,
|
||||
color: "rgba(255,255,255,.45)", fontSize: ".85rem"
|
||||
}}>
|
||||
{t('auth.noAccount')}{" "}
|
||||
<button
|
||||
onClick={onSwitch}
|
||||
style={{
|
||||
background: "none", border: "none", color: "#2de0c8",
|
||||
cursor: "pointer", fontWeight: 700, fontSize: ".85rem",
|
||||
fontFamily: "var(--font)",
|
||||
}}
|
||||
>
|
||||
{t('auth.register')}
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
<p style={{textAlign: "center", marginTop: 14, color: "rgba(255,255,255,.45)", fontSize: ".85rem"}}>
|
||||
{t('auth.noAccount')}{" "}
|
||||
<button onClick={onSwitch}
|
||||
style={{background: "none", border: "none", color: "#2de0c8", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}
|
||||
>
|
||||
{t('auth.register')}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,53 +120,39 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
|
||||
const [error, setError] = useState("");
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
const setField = useCallback((key, val) => {
|
||||
const setField = (key, val) => {
|
||||
setForm(p => ({ ...p, [key]: val }));
|
||||
setErrors(p => ({ ...p, [key]: "" }));
|
||||
}, []);
|
||||
};
|
||||
|
||||
const validate = () => {
|
||||
const e = {};
|
||||
if (!form.name.trim()) e.name = t('auth.validateNameRequired');
|
||||
if (!form.login.trim()) e.login = t('auth.validateLoginRequired');
|
||||
else if (form.login.length < 3) e.login = t('auth.loginMinChars');
|
||||
if (form.password.length < 6) e.password = t('auth.validatePasswordMin');
|
||||
if (form.password !== form.password2) e.password2 = t('auth.validatePasswordMatch');
|
||||
if (!form.name.trim()) e.name = t('auth.validateNameRequired');
|
||||
if (!form.login.trim()) e.login = t('auth.validateLoginRequired');
|
||||
else if (form.login.length < 3) e.login = t('auth.loginMinChars');
|
||||
if (form.password.length < 6) e.password = t('auth.validatePasswordMin');
|
||||
if (form.password !== form.password2) e.password2 = t('auth.validatePasswordMatch');
|
||||
setErrors(e);
|
||||
return !Object.keys(e).length;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!validate()) return;
|
||||
setLoading(true); setError("");
|
||||
|
||||
try {
|
||||
// 1. Регистрация
|
||||
const regRes = await fetch(`${API_URL}/api/v1/auth/register`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: form.name,
|
||||
login: form.login,
|
||||
password: form.password,
|
||||
}),
|
||||
});
|
||||
await apiRegister(form.name, form.login, form.password, t('auth.registrationError'));
|
||||
|
||||
const regData = await regRes.json();
|
||||
if (!regRes.ok) throw new Error(regData.message || t('auth.registrationError'));
|
||||
let session;
|
||||
try {
|
||||
session = await apiLogin(form.login, form.password, t('auth.autoLoginFailed'));
|
||||
} catch {
|
||||
throw new Error(t('auth.accountCreatedLoginManually'));
|
||||
}
|
||||
|
||||
// 2. Автоматический вход после регистрации
|
||||
const loginRes = await fetch(`${API_URL}/api/v1/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ login: form.login, password: form.password }),
|
||||
});
|
||||
|
||||
if (!loginRes.ok) throw new Error("Аккаунт создан. Войдите вручную.");
|
||||
|
||||
const { token, refreshToken, user } = await loginRes.json();
|
||||
saveTokens(token, refreshToken);
|
||||
saveAuth(user, token);
|
||||
const { token, refreshToken, user } = session;
|
||||
saveAuth(user, token, refreshToken);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
@@ -199,97 +162,79 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.nameLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.name ? " err" : ""}`}
|
||||
type="text"
|
||||
placeholder={t('auth.namePlaceholder')}
|
||||
value={form.name}
|
||||
onChange={e => setField("name", e.target.value)}
|
||||
/>
|
||||
{errors.name && <p className="f-err-msg">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.loginLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.login ? " err" : ""}`}
|
||||
type="text"
|
||||
placeholder={t('auth.loginPlaceholder')}
|
||||
value={form.login}
|
||||
onChange={e => setField("login", e.target.value)}
|
||||
/>
|
||||
{errors.login && <p className="f-err-msg">{errors.login}</p>}
|
||||
</div>
|
||||
|
||||
<div className="f-m-row">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.passwordLabel')}</label>
|
||||
<label className="f-m-label">{t('auth.nameLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.password ? " err" : ""}`}
|
||||
type="password"
|
||||
placeholder={t('auth.passwordMinShort')}
|
||||
value={form.password}
|
||||
onChange={e => setField("password", e.target.value)}
|
||||
className={`f-m-field${errors.name ? " err" : ""}`}
|
||||
type="text"
|
||||
placeholder={t('auth.namePlaceholder')}
|
||||
value={form.name}
|
||||
onChange={e => setField("name", e.target.value)}
|
||||
/>
|
||||
{errors.password && <p className="f-err-msg">{errors.password}</p>}
|
||||
{errors.name && <p className="f-err-msg">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.repeatLabel')}</label>
|
||||
<label className="f-m-label">{t('auth.loginLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.password2 ? " err" : ""}`}
|
||||
type="password"
|
||||
placeholder={t('auth.repeatPlaceholder')}
|
||||
value={form.password2}
|
||||
onChange={e => setField("password2", e.target.value)}
|
||||
className={`f-m-field${errors.login ? " err" : ""}`}
|
||||
type="text"
|
||||
placeholder={t('auth.loginPlaceholder')}
|
||||
value={form.login}
|
||||
onChange={e => setField("login", e.target.value)}
|
||||
/>
|
||||
{errors.password2 && <p className="f-err-msg">{errors.password2}</p>}
|
||||
{errors.login && <p className="f-err-msg">{errors.login}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <p className="f-err-msg" style={{ marginBottom: 10 }}>{error}</p>}
|
||||
<div className="f-m-row">
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.passwordLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.password ? " err" : ""}`}
|
||||
type="password"
|
||||
placeholder={t('auth.passwordMinShort')}
|
||||
value={form.password}
|
||||
onChange={e => setField("password", e.target.value)}
|
||||
/>
|
||||
{errors.password && <p className="f-err-msg">{errors.password}</p>}
|
||||
</div>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{t('auth.repeatLabel')}</label>
|
||||
<input
|
||||
className={`f-m-field${errors.password2 ? " err" : ""}`}
|
||||
type="password"
|
||||
placeholder={t('auth.repeatPlaceholder')}
|
||||
value={form.password2}
|
||||
onChange={e => setField("password2", e.target.value)}
|
||||
/>
|
||||
{errors.password2 && <p className="f-err-msg">{errors.password2}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SubmitButton
|
||||
loading={loading}
|
||||
label={t('auth.createAccountBtn').replace(' ✓', '')}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
{error && <p className="f-err-msg auth-form-error">{error}</p>}
|
||||
|
||||
<p style={{
|
||||
textAlign: "center", marginTop: 14,
|
||||
color: "rgba(255,255,255,.45)", fontSize: ".85rem"
|
||||
}}>
|
||||
{t('auth.hasAccount')}{" "}
|
||||
<button
|
||||
onClick={onSwitch}
|
||||
style={{
|
||||
background: "none", border: "none", color: "#2de0c8",
|
||||
cursor: "pointer", fontWeight: 700, fontSize: ".85rem",
|
||||
fontFamily: "var(--font)",
|
||||
}}
|
||||
>
|
||||
{t('auth.loginLink')}
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
<SubmitButton loading={loading} label={t('auth.createAccountBtn')} />
|
||||
|
||||
<p style={{textAlign: "center", marginTop: 14, color: "rgba(255,255,255,.45)", fontSize: ".85rem"}}>
|
||||
{t('auth.hasAccount')}{" "}
|
||||
<button onClick={onSwitch}
|
||||
style={{background: "none", border: "none", color: "#2de0c8", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}>
|
||||
{t('auth.loginLink')}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Shared submit button ──────────────────────────────────────────────────
|
||||
function SubmitButton({ loading, label, onClick }) {
|
||||
function SubmitButton({ loading, label }) {
|
||||
return (
|
||||
<button
|
||||
className="f-m-submit"
|
||||
onClick={onClick}
|
||||
disabled={loading}
|
||||
style={{ marginTop: 6 }}
|
||||
>
|
||||
<div className="f-m-submit-row">
|
||||
{loading && <div className="f-m-spinner" />}
|
||||
{label}
|
||||
</div>
|
||||
</button>
|
||||
<button className="f-m-submit auth-submit-btn" type="submit" disabled={loading}>
|
||||
<div className="f-m-submit-row">
|
||||
{loading && <div className="f-m-spinner" />}
|
||||
{label}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user