nakama/frontend/src/pages/VerifyEmail.jsx

220 lines
6.8 KiB
React
Raw Normal View History

2026-01-01 19:39:12 +00:00
import { useState, useEffect } from 'react'
import { useSearchParams, useNavigate } from 'react-router-dom'
import { verifyMagicLink, setPassword } from '../utils/api'
import { X } from 'lucide-react'
import './VerifyEmail.css'
export default function VerifyEmail() {
const [searchParams] = useSearchParams()
const navigate = useNavigate()
const token = searchParams.get('token')
const [loading, setLoading] = useState(true)
const [requiresPassword, setRequiresPassword] = useState(false)
const [error, setError] = useState('')
const [formData, setFormData] = useState({
password: '',
confirmPassword: '',
username: '',
firstName: ''
})
const [submitting, setSubmitting] = useState(false)
useEffect(() => {
if (!token) {
setError('Токен не указан')
setLoading(false)
return
}
const checkToken = async () => {
try {
const result = await verifyMagicLink(token)
if (result.requiresPassword) {
setRequiresPassword(true)
} else {
// Уже авторизован, перенаправляем
window.location.href = '/feed'
}
} catch (err) {
setError(err.response?.data?.error || 'Неверная или устаревшая ссылка')
} finally {
setLoading(false)
}
}
checkToken()
}, [token])
const handleSubmit = async (e) => {
e.preventDefault()
setError('')
// Валидация
if (!formData.password || formData.password.length < 8) {
setError('Пароль должен быть не менее 8 символов')
return
}
if (formData.password !== formData.confirmPassword) {
setError('Пароли не совпадают')
return
}
if (!formData.username || formData.username.trim().length < 3) {
setError('Юзернейм должен быть не менее 3 символов')
return
}
if (!formData.firstName || formData.firstName.trim().length < 1) {
setError('Никнейм обязателен')
return
}
try {
setSubmitting(true)
await setPassword(
token,
formData.password,
formData.username.trim().toLowerCase(),
formData.firstName.trim()
)
// Успешная регистрация, перенаправляем
window.location.href = '/feed'
} catch (err) {
setError(err.response?.data?.error || 'Ошибка регистрации')
} finally {
setSubmitting(false)
}
}
if (loading) {
return (
<div className="verify-email-page">
<div className="verify-email-container">
<div className="spinner" />
<p>Проверка ссылки...</p>
</div>
</div>
)
}
if (error && !requiresPassword) {
return (
<div className="verify-email-page">
<div className="verify-email-container">
<div className="error-state">
<h2>Ошибка</h2>
<p>{error}</p>
<button className="btn-primary" onClick={() => navigate('/feed')}>
На главную
</button>
</div>
</div>
</div>
)
}
if (requiresPassword) {
return (
<div className="verify-email-page">
<div className="verify-email-container">
<div className="verify-email-card">
<h2>Завершение регистрации</h2>
<p className="verify-email-subtitle">Установите пароль и заполните данные профиля</p>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Юзернейм *</label>
<input
type="text"
className="form-input"
placeholder="username"
value={formData.username}
onChange={e => {
setFormData({ ...formData, username: e.target.value.replace(/[^a-z0-9_]/gi, '').toLowerCase() })
setError('')
}}
disabled={submitting}
required
minLength={3}
maxLength={20}
/>
<p className="form-hint">Только латинские буквы, цифры и _. Нельзя изменить после регистрации</p>
</div>
<div className="form-group">
<label>Никнейм *</label>
<input
type="text"
className="form-input"
placeholder="Ваше имя"
value={formData.firstName}
onChange={e => {
setFormData({ ...formData, firstName: e.target.value })
setError('')
}}
disabled={submitting}
required
maxLength={50}
/>
<p className="form-hint">Можно изменить в настройках профиля</p>
</div>
<div className="form-group">
<label>Пароль *</label>
<input
type="password"
className="form-input"
placeholder="Минимум 8 символов"
value={formData.password}
onChange={e => {
setFormData({ ...formData, password: e.target.value })
setError('')
}}
disabled={submitting}
required
minLength={8}
maxLength={24}
/>
</div>
<div className="form-group">
<label>Подтвердите пароль *</label>
<input
type="password"
className="form-input"
placeholder="Повторите пароль"
value={formData.confirmPassword}
onChange={e => {
setFormData({ ...formData, confirmPassword: e.target.value })
setError('')
}}
disabled={submitting}
required
/>
</div>
{error && (
<div className="error-message">{error}</div>
)}
<button
type="submit"
className="btn-primary"
disabled={submitting || !formData.password || !formData.username || !formData.firstName}
>
{submitting ? 'Регистрация...' : 'Завершить регистрацию'}
</button>
</form>
</div>
</div>
</div>
)
}
return null
}