import { useState, useRef, useEffect, KeyboardEvent, ClipboardEvent } from 'react'; import { useNavigate, useLocation } from 'react-router'; import { ArrowLeft, RefreshCw, AlertCircle } from 'lucide-react'; const CODE_LENGTH = 6; // Правильный код для демо const DEMO_CODE = '123456'; export function AuthOtp() { const navigate = useNavigate(); const location = useLocation(); const phone = (location.state as { phone?: string })?.phone ?? '+7 (___) ___ __ __'; const [digits, setDigits] = useState(Array(CODE_LENGTH).fill('')); const [error, setError] = useState(null); const [shake, setShake] = useState(false); const [countdown, setCountdown] = useState(59); const [canResend, setCanResend] = useState(false); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); useEffect(() => { inputRefs.current[0]?.focus(); }, []); useEffect(() => { if (countdown === 0) { setCanResend(true); return; } const t = setTimeout(() => setCountdown(c => c - 1), 1000); return () => clearTimeout(t); }, [countdown]); function handleChange(index: number, value: string) { const digit = value.replace(/\D/g, '').slice(-1); const next = [...digits]; next[index] = digit; setDigits(next); setError(null); if (digit && index < CODE_LENGTH - 1) { inputRefs.current[index + 1]?.focus(); } // Автоматическая проверка когда введены все цифры if (next.every(d => d !== '')) { verifyCode(next.join('')); } } function handleKeyDown(index: number, e: KeyboardEvent) { if (e.key === 'Backspace') { if (digits[index]) { const next = [...digits]; next[index] = ''; setDigits(next); setError(null); } else if (index > 0) { inputRefs.current[index - 1]?.focus(); const next = [...digits]; next[index - 1] = ''; setDigits(next); setError(null); } } if (e.key === 'ArrowLeft' && index > 0) inputRefs.current[index - 1]?.focus(); if (e.key === 'ArrowRight' && index < CODE_LENGTH - 1) inputRefs.current[index + 1]?.focus(); } function handlePaste(e: ClipboardEvent) { e.preventDefault(); const pasted = e.clipboardData.getData('text').replace(/\D/g, '').slice(0, CODE_LENGTH); if (!pasted) return; const next = Array(CODE_LENGTH).fill(''); pasted.split('').forEach((d, i) => { next[i] = d; }); setDigits(next); setError(null); const focusIdx = Math.min(pasted.length, CODE_LENGTH - 1); inputRefs.current[focusIdx]?.focus(); if (next.every(d => d !== '')) verifyCode(next.join('')); } function verifyCode(code: string) { if (code === DEMO_CODE) { navigate('/auth/loading', { state: { phone } }); } else { setError('Неверный код. Проверьте и попробуйте снова'); setShake(true); setTimeout(() => setShake(false), 500); // Очищаем поля после ошибки setTimeout(() => { setDigits(Array(CODE_LENGTH).fill('')); inputRefs.current[0]?.focus(); }, 600); } } function handleResend() { if (!canResend) return; setDigits(Array(CODE_LENGTH).fill('')); setError(null); setCountdown(59); setCanResend(false); inputRefs.current[0]?.focus(); } const isComplete = digits.every(d => d !== ''); return (
{/* Назад */} {/* Заголовок */}

Введите код

Отправили SMS на номер{' '} {phone}

{/* Поля ввода OTP */}
{digits.map((digit, i) => ( { inputRefs.current[i] = el; }} type="text" inputMode="numeric" maxLength={1} value={digit} onChange={e => handleChange(i, e.target.value)} onKeyDown={e => handleKeyDown(i, e)} onPaste={handlePaste} className={` w-12 h-14 text-center text-xl font-semibold rounded-xl border transition-all focus:outline-none focus:ring-2 ${error ? 'border-destructive bg-destructive/5 text-destructive focus:ring-destructive/20' : digit ? 'border-primary/50 bg-primary/5 text-foreground focus:ring-primary/30 focus:border-primary/60' : 'border-border bg-input-background text-foreground focus:ring-primary/30 focus:border-primary/50' } `} /> ))}
{/* Блок ошибки */} {error && (

{error}

Для демо введите код: 123456

)}
{/* Повторная отправка */}
{canResend ? ( ) : (

Отправить повторно через{' '} 0:{countdown.toString().padStart(2, '0')}

)}
{/* Кнопка подтверждения */}
); }