english version update
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { Turnstile } from '@marsidev/react-turnstile';
|
||||
@@ -6,11 +6,16 @@ import type { TurnstileInstance } from '@marsidev/react-turnstile';
|
||||
import useAuth from '../context/useAuth';
|
||||
import { API_URL } from '../config/api';
|
||||
import { useToast } from '../hooks/useToast';
|
||||
import { useTranslation } from '../i18n';
|
||||
import { useLocalePath } from '../middleware';
|
||||
import { validateEmail } from '../utils/emailValidation';
|
||||
|
||||
const RegisterPage = () => {
|
||||
const { addToast } = useToast();
|
||||
const [username, setUsername] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [emailError, setEmailError] = useState<string | null>(null);
|
||||
const [emailSuggestion, setEmailSuggestion] = useState<string | null>(null);
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -19,10 +24,36 @@ const RegisterPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { login } = useAuth();
|
||||
const { t, locale } = useTranslation();
|
||||
const localePath = useLocalePath();
|
||||
|
||||
const siteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY;
|
||||
|
||||
// Обработка OAuth токена из URL
|
||||
// Email validation on blur
|
||||
const handleEmailBlur = useCallback(() => {
|
||||
if (!email.trim()) {
|
||||
setEmailError(null);
|
||||
setEmailSuggestion(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = validateEmail(email, locale);
|
||||
setEmailError(result.isValid ? null : result.error ?? null);
|
||||
setEmailSuggestion(result.suggestion ?? null);
|
||||
}, [email, locale]);
|
||||
|
||||
// Apply email suggestion
|
||||
const applySuggestion = useCallback(() => {
|
||||
if (emailSuggestion) {
|
||||
const match = emailSuggestion.match(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/);
|
||||
if (match) {
|
||||
setEmail(match[1]);
|
||||
setEmailSuggestion(null);
|
||||
}
|
||||
}
|
||||
}, [emailSuggestion]);
|
||||
|
||||
// Handle OAuth token from URL
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const token = params.get('token');
|
||||
@@ -30,48 +61,57 @@ const RegisterPage = () => {
|
||||
|
||||
if (token) {
|
||||
login(token);
|
||||
navigate('/dashboard', { replace: true });
|
||||
navigate(localePath('/dashboard'), { replace: true });
|
||||
}
|
||||
|
||||
if (authError) {
|
||||
setError('Ошибка авторизации через социальную сеть. Попробуйте снова.');
|
||||
setError(locale === 'en'
|
||||
? 'Social login error. Please try again.'
|
||||
: 'Ошибка авторизации через социальную сеть. Попробуйте снова.');
|
||||
}
|
||||
}, [location, login, navigate]);
|
||||
}, [location, login, navigate, localePath, locale]);
|
||||
|
||||
const handleRegister = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(''); // Очищаем предыдущие ошибки
|
||||
setError('');
|
||||
|
||||
// Validate email before submit
|
||||
const emailValidation = validateEmail(email, locale);
|
||||
if (!emailValidation.isValid) {
|
||||
setEmailError(emailValidation.error ?? t('auth.register.invalidEmail'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!turnstileToken) {
|
||||
setError('Пожалуйста, подтвердите, что вы не робот.');
|
||||
setError(t('auth.register.captchaRequired'));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await axios.post(`${API_URL}/api/auth/register`, {
|
||||
await axios.post(`${API_URL}/api/auth/register`, {
|
||||
username: username,
|
||||
email: email,
|
||||
password: password,
|
||||
turnstileToken: turnstileToken,
|
||||
});
|
||||
|
||||
addToast('Регистрация прошла успешно! Теперь вы можете войти.', 'success');
|
||||
navigate('/login');
|
||||
addToast(t('auth.register.success'), 'success');
|
||||
navigate(localePath('/login'));
|
||||
|
||||
} catch (err) {
|
||||
// Сброс капчи при ошибке
|
||||
// Reset captcha on error
|
||||
if (turnstileRef.current) {
|
||||
turnstileRef.current.reset();
|
||||
}
|
||||
setTurnstileToken(null);
|
||||
|
||||
if (axios.isAxiosError(err) && err.response) {
|
||||
const errorMsg = err.response.data.message || 'Неизвестная ошибка регистрации.';
|
||||
const errorMsg = err.response.data.message || t('auth.register.unknownError');
|
||||
setError(errorMsg);
|
||||
} else {
|
||||
setError('Произошла ошибка сети. Пожалуйста, попробуйте позже.');
|
||||
setError(t('auth.register.networkError'));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -85,27 +125,50 @@ const RegisterPage = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
|
||||
<div className="bg-white p-8 md:p-10 rounded-3xl shadow-2xl w-full max-w-md text-center">
|
||||
<h1 className="text-3xl font-bold text-gray-800 mb-6">Регистрация</h1>
|
||||
<h1 className="text-3xl font-bold text-gray-800 mb-6">{t('auth.register.title')}</h1>
|
||||
<form onSubmit={handleRegister}>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="Имя пользователя"
|
||||
className="w-full px-5 py-3 mb-4 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-ospab-primary"
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Электронная почта"
|
||||
placeholder={t('auth.register.usernamePlaceholder')}
|
||||
className="w-full px-5 py-3 mb-4 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-ospab-primary"
|
||||
/>
|
||||
<div className="relative mb-4">
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
setEmailError(null);
|
||||
setEmailSuggestion(null);
|
||||
}}
|
||||
onBlur={handleEmailBlur}
|
||||
placeholder={t('auth.register.emailPlaceholder')}
|
||||
className={`w-full px-5 py-3 border rounded-full focus:outline-none focus:ring-2 ${
|
||||
emailError
|
||||
? 'border-red-500 focus:ring-red-500'
|
||||
: 'border-gray-300 focus:ring-ospab-primary'
|
||||
}`}
|
||||
/>
|
||||
{emailError && (
|
||||
<p className="mt-1 text-sm text-red-500 text-left px-3">{emailError}</p>
|
||||
)}
|
||||
{emailSuggestion && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={applySuggestion}
|
||||
className="mt-1 text-sm text-blue-600 hover:text-blue-800 text-left px-3 cursor-pointer"
|
||||
>
|
||||
{emailSuggestion}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Пароль"
|
||||
placeholder={t('auth.register.passwordPlaceholder')}
|
||||
className="w-full px-5 py-3 mb-6 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-ospab-primary"
|
||||
required
|
||||
disabled={isLoading}
|
||||
@@ -119,7 +182,7 @@ const RegisterPage = () => {
|
||||
onSuccess={(token: string) => setTurnstileToken(token)}
|
||||
onError={() => {
|
||||
setTurnstileToken(null);
|
||||
setError('Ошибка загрузки капчи. Попробуйте обновить страницу.');
|
||||
setError(t('auth.register.captchaError'));
|
||||
}}
|
||||
onExpire={() => setTurnstileToken(null)}
|
||||
/>
|
||||
@@ -127,24 +190,24 @@ const RegisterPage = () => {
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading || !turnstileToken}
|
||||
disabled={isLoading || !turnstileToken || !!emailError}
|
||||
className="w-full px-5 py-3 rounded-full text-white font-bold transition-colors transform hover:scale-105 bg-ospab-primary hover:bg-ospab-accent disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isLoading ? 'Регистрируем...' : 'Зарегистрироваться'}
|
||||
{isLoading ? t('auth.register.loading') : t('auth.register.submit')}
|
||||
</button>
|
||||
</form>
|
||||
{error && (
|
||||
<p className="mt-4 text-sm text-red-500">{error}</p>
|
||||
)}
|
||||
|
||||
{/* Социальные сети */}
|
||||
{/* Social networks */}
|
||||
<div className="mt-6">
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300"></div>
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="px-2 bg-white text-gray-500">Или зарегистрироваться через</span>
|
||||
<span className="px-2 bg-white text-gray-500">{t('auth.register.orRegisterWith')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -185,9 +248,9 @@ const RegisterPage = () => {
|
||||
</div>
|
||||
|
||||
<p className="mt-6 text-gray-600">
|
||||
Уже есть аккаунт?{' '}
|
||||
<Link to="/login" className="text-ospab-primary font-bold hover:underline">
|
||||
Войти
|
||||
{t('auth.register.haveAccount')}{' '}
|
||||
<Link to={localePath('/login')} className="text-ospab-primary font-bold hover:underline">
|
||||
{t('auth.register.loginLink')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user