// PhoneLogin — SMS-OTP login flow. Two-step: phone → code. // Calls onSuccess(jwt) on successful verify. function PhoneLogin({ onSuccess }) { const [step, setStep] = React.useState('phone'); const [phone, setPhone] = React.useState(''); const [code, setCode] = React.useState(''); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(''); const [success, setSuccess] = React.useState(''); const [resendIn, setResendIn] = React.useState(0); // Countdown ticker — clean up on unmount. React.useEffect(() => { if (resendIn <= 0) return undefined; const t = setTimeout(() => setResendIn(s => Math.max(0, s - 1)), 1000); return () => clearTimeout(t); }, [resendIn]); const isValidPhone = (raw) => { const cleaned = (raw || '').replace(/[^\d+]/g, ''); if (/^\+\d{10,15}$/.test(cleaned)) return true; if (/^\d{10,11}$/.test(cleaned)) return true; return false; }; const requestCode = async () => { if (!phone) return setError('Введите номер телефона'); if (!isValidPhone(phone)) return setError('Введите номер, например +79001234567'); setLoading(true); setError(''); setSuccess(''); try { await api.post('/api/auth/phone/request-code', { phone }); setStep('code'); setSuccess('Код отправлен по SMS'); setResendIn(60); } catch (e) { if (e.status === 429) { setError('Слишком много запросов, подождите'); // If retry_after is provided, use it as countdown so UI matches. if (e.retry_after) setResendIn(Number(e.retry_after)); } else if (e.status === 400) { setError(e.message || 'Неверный формат телефона'); } else if (e.status === 502) { setError('Не удалось отправить SMS, попробуйте позже'); } else { setError(e.message || 'Ошибка отправки кода'); } } finally { setLoading(false); } }; const verify = async () => { if (!code || code.length < 4) return setError('Введите код из SMS'); setLoading(true); setError(''); try { const data = await api.post('/api/auth/phone/verify', { phone, code }); if (data && data.access_token) { onSuccess(data.access_token); } else { setError('Неверный ответ сервера'); } } catch (e) { if (e.status === 400) setError(e.message || 'Неверный или истёкший код'); else setError(e.message || 'Ошибка проверки кода'); } finally { setLoading(false); } }; if (step === 'phone') { return (