104 lines
3.5 KiB
React
104 lines
3.5 KiB
React
import { useState } from 'react'
|
||
import { useAuth } from '../auth'
|
||
|
||
export default function AuthModal({ onClose }) {
|
||
const { login, register } = useAuth()
|
||
const [mode, setMode] = useState('login') // 'login' | 'register'
|
||
const [nickname, setNickname] = useState('')
|
||
const [password, setPassword] = useState('')
|
||
const [avatar, setAvatar] = useState(null)
|
||
const [avatarPreview, setAvatarPreview] = useState(null)
|
||
const [error, setError] = useState(null)
|
||
const [submitting, setSubmitting] = useState(false)
|
||
|
||
const handleAvatarChange = (e) => {
|
||
const file = e.target.files?.[0] || null
|
||
setAvatar(file)
|
||
setAvatarPreview(file ? URL.createObjectURL(file) : null)
|
||
}
|
||
|
||
const handleSubmit = async (e) => {
|
||
e.preventDefault()
|
||
setError(null)
|
||
setSubmitting(true)
|
||
try {
|
||
if (mode === 'login') {
|
||
await login(nickname.trim(), password)
|
||
} else {
|
||
await register(nickname.trim(), password, avatar)
|
||
}
|
||
onClose()
|
||
} catch (err) {
|
||
setError(err.message)
|
||
} finally {
|
||
setSubmitting(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="modal-overlay" onClick={onClose}>
|
||
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
||
<button className="modal-close" onClick={onClose} aria-label="Закрыть">
|
||
×
|
||
</button>
|
||
<h2 className="modal-title">{mode === 'login' ? 'Вход' : 'Регистрация'}</h2>
|
||
|
||
<form onSubmit={handleSubmit} className="auth-form">
|
||
<label className="field">
|
||
<span>Имя</span>
|
||
<input
|
||
type="text"
|
||
value={nickname}
|
||
onChange={(e) => setNickname(e.target.value)}
|
||
minLength={mode === 'register' ? 3 : undefined}
|
||
maxLength={mode === 'register' ? 24 : undefined}
|
||
required
|
||
autoFocus
|
||
/>
|
||
</label>
|
||
<label className="field">
|
||
<span>Пароль</span>
|
||
<input
|
||
type="password"
|
||
value={password}
|
||
onChange={(e) => setPassword(e.target.value)}
|
||
minLength={mode === 'register' ? 6 : undefined}
|
||
required
|
||
/>
|
||
</label>
|
||
|
||
{mode === 'register' && (
|
||
<label className="field avatar-field">
|
||
<span>Аватарка (необязательно)</span>
|
||
<div className="avatar-picker">
|
||
{avatarPreview ? (
|
||
<img src={avatarPreview} alt="Аватар" className="avatar-preview" />
|
||
) : (
|
||
<div className="avatar-preview avatar-placeholder">?</div>
|
||
)}
|
||
<input type="file" accept="image/png,image/jpeg,image/gif,image/webp" onChange={handleAvatarChange} />
|
||
</div>
|
||
</label>
|
||
)}
|
||
|
||
{error && <div className="form-error">{error}</div>}
|
||
|
||
<button type="submit" className="btn btn-primary" disabled={submitting}>
|
||
{submitting ? 'Подождите…' : mode === 'login' ? 'Войти' : 'Зарегистрироваться'}
|
||
</button>
|
||
</form>
|
||
|
||
<button
|
||
className="link-button"
|
||
onClick={() => {
|
||
setMode(mode === 'login' ? 'register' : 'login')
|
||
setError(null)
|
||
}}
|
||
>
|
||
{mode === 'login' ? 'Нет аккаунта? Зарегистрироваться' : 'Уже есть аккаунт? Войти'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|