nakama/frontend/src/pages/VerifyEmail.jsx

220 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}