From 58a12a3c05e78dea8831c34ebfd739f29fdc2d42 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Thu, 30 Oct 2025 09:50:26 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=A2=D0=B5=D0=BB=D0=B5=D1=84=D0=BE?= =?UTF-8?q?=D0=BD=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D1=88=D0=B0=D0=B3=201=20(SMS=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8F)\n\n-=20?= =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=88=D0=B0=D0=B3=20Step1Phon?= =?UTF-8?q?e.tsx=20(=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0/?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20SMS)\n-=20Cl?= =?UTF-8?q?aimForm:=20=D0=BD=D0=BE=D0=B2=D0=B0=D1=8F=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D1=88=D0=B0=D0=B3=D0=BE=D0=B2?= =?UTF-8?q?=20(=D0=A2=D0=B5=D0=BB=D0=B5=D1=84=D0=BE=D0=BD=20->=20=D0=9F?= =?UTF-8?q?=D0=BE=D0=BB=D0=B8=D1=81=20->=20=D0=A2=D0=B8=D0=BF=20->=20?= =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20->=20?= =?UTF-8?q?=D0=9E=D0=BF=D0=BB=D0=B0=D1=82=D0=B0)\n-=20Step3Payment:=20?= =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=20=D0=B1=D0=BB=D0=BE=D0=BA=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=82=D0=B5=D0=BB=D0=B5=D1=84=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/form/Step1Phone.tsx | 172 +++++++++++++++ frontend/src/components/form/Step3Payment.tsx | 208 ++---------------- frontend/src/pages/ClaimForm.tsx | 22 +- 3 files changed, 209 insertions(+), 193 deletions(-) create mode 100644 frontend/src/components/form/Step1Phone.tsx diff --git a/frontend/src/components/form/Step1Phone.tsx b/frontend/src/components/form/Step1Phone.tsx new file mode 100644 index 0000000..3659566 --- /dev/null +++ b/frontend/src/components/form/Step1Phone.tsx @@ -0,0 +1,172 @@ +import { useState } from 'react'; +import { Form, Input, Button, message, Space } from 'antd'; +import { PhoneOutlined, SafetyOutlined, MailOutlined } from '@ant-design/icons'; + +interface Props { + formData: any; + updateFormData: (data: any) => void; + onNext: () => void; + setIsPhoneVerified: (verified: boolean) => void; + addDebugEvent?: (type: string, status: string, message: string, data?: any) => void; +} + +export default function Step1Phone({ + formData, + updateFormData, + onNext, + setIsPhoneVerified, + addDebugEvent +}: Props) { + const [form] = Form.useForm(); + const [codeSent, setCodeSent] = useState(false); + const [loading, setLoading] = useState(false); + const [verifyLoading, setVerifyLoading] = useState(false); + + const sendCode = async () => { + try { + const values = await form.validateFields(['phone', 'email']); + const phone = values.phone; + + setLoading(true); + addDebugEvent?.('sms', 'pending', `📱 Отправляю SMS на ${phone}...`, { phone }); + + const response = await fetch('http://147.45.146.17:8100/api/v1/sms/send', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ phone }) + }); + + const result = await response.json(); + + if (response.ok) { + addDebugEvent?.('sms', 'success', `✅ SMS отправлен (DEBUG mode)`, { + phone, + debug_code: result.debug_code, + message: result.message + }); + message.success('Код отправлен на ваш телефон'); + setCodeSent(true); + updateFormData({ phone: values.phone, email: values.email }); + if (result.debug_code) { + message.info(`DEBUG: Код ${result.debug_code}`); + } + } else { + addDebugEvent?.('sms', 'error', `❌ Ошибка SMS: ${result.detail}`, { error: result.detail }); + message.error(result.detail || 'Ошибка отправки кода'); + } + } catch (error) { + if ((error as any)?.errorFields) { + message.error('Введите телефон и email'); + } else { + message.error('Ошибка соединения с сервером'); + } + } finally { + setLoading(false); + } + }; + + const verifyCode = async () => { + try { + const values = await form.validateFields(['phone', 'smsCode']); + const phone = values.phone; + const code = values.smsCode; + + setVerifyLoading(true); + addDebugEvent?.('sms', 'pending', `🔐 Проверяю SMS код...`, { phone, code }); + + const response = await fetch('http://147.45.146.17:8100/api/v1/sms/verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ phone, code }) + }); + + const result = await response.json(); + + if (response.ok) { + addDebugEvent?.('sms', 'success', `✅ Телефон подтвержден успешно`, { phone, verified: true }); + message.success('Телефон подтвержден!'); + setIsPhoneVerified(true); + onNext(); + } else { + addDebugEvent?.('sms', 'error', `❌ Неверный код SMS`, { phone, code, error: result.detail }); + message.error(result.detail || 'Неверный код'); + } + } catch (error) { + if ((error as any)?.errorFields) { + message.error('Введите код из SMS'); + } else { + message.error('Ошибка соединения с сервером'); + } + } finally { + setVerifyLoading(false); + } + }; + + return ( +
+

📱 Подтверждение телефона

+ + + } + placeholder="+79001234567" + maxLength={12} + size="large" + /> + + + + } + placeholder="example@mail.ru" + size="large" + type="email" + /> + + + + {!codeSent ? ( + + ) : ( + + } + placeholder="123456" + maxLength={6} + style={{ width: '70%' }} + size="large" + name="smsCode" + onChange={(e) => form.setFieldValue('smsCode', e.target.value)} + /> + + + )} + +
+ ); +} + + diff --git a/frontend/src/components/form/Step3Payment.tsx b/frontend/src/components/form/Step3Payment.tsx index 766147f..1333098 100644 --- a/frontend/src/components/form/Step3Payment.tsx +++ b/frontend/src/components/form/Step3Payment.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Form, Input, Button, Select, message, Space, Divider } from 'antd'; -import { PhoneOutlined, SafetyOutlined, QrcodeOutlined, MailOutlined } from '@ant-design/icons'; +import { Form, Input, Button, Select, message, Divider } from 'antd'; +import { QrcodeOutlined } from '@ant-design/icons'; const { Option } = Select; @@ -24,96 +24,12 @@ export default function Step3Payment({ addDebugEvent }: Props) { const [form] = Form.useForm(); - const [codeSent, setCodeSent] = useState(false); - const [loading, setLoading] = useState(false); - const [verifyLoading, setVerifyLoading] = useState(false); + const [codeSent] = useState(false); + const [loading] = useState(false); + const [verifyLoading] = useState(false); const [submitting, setSubmitting] = useState(false); - const sendCode = async () => { - try { - const phone = form.getFieldValue('phone'); - if (!phone) { - message.error('Введите номер телефона'); - return; - } - - setLoading(true); - - addDebugEvent?.('sms', 'pending', `📱 Отправляю SMS на ${phone}...`, { phone }); - - const response = await fetch('http://147.45.146.17:8100/api/v1/sms/send', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ phone }), - }); - - const result = await response.json(); - - if (response.ok) { - addDebugEvent?.('sms', 'success', `✅ SMS отправлен (DEBUG mode)`, { - phone, - debug_code: result.debug_code, - message: result.message - }); - message.success('Код отправлен на ваш телефон'); - setCodeSent(true); - if (result.debug_code) { - message.info(`DEBUG: Код ${result.debug_code}`); - } - } else { - addDebugEvent?.('sms', 'error', `❌ Ошибка SMS: ${result.detail}`, { error: result.detail }); - message.error(result.detail || 'Ошибка отправки кода'); - } - } catch (error) { - message.error('Ошибка соединения с сервером'); - } finally { - setLoading(false); - } - }; - - const verifyCode = async () => { - try { - const phone = form.getFieldValue('phone'); - const code = form.getFieldValue('smsCode'); - - if (!code) { - message.error('Введите код из SMS'); - return; - } - - setVerifyLoading(true); - - addDebugEvent?.('sms', 'pending', `🔐 Проверяю SMS код...`, { phone, code }); - - const response = await fetch('http://147.45.146.17:8100/api/v1/sms/verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ phone, code }), - }); - - const result = await response.json(); - - if (response.ok) { - addDebugEvent?.('sms', 'success', `✅ Телефон подтвержден успешно`, { - phone, - verified: true - }); - message.success('Телефон подтвержден!'); - setIsPhoneVerified(true); - } else { - addDebugEvent?.('sms', 'error', `❌ Неверный код SMS`, { - phone, - code, - error: result.detail - }); - message.error(result.detail || 'Неверный код'); - } - } catch (error) { - message.error('Ошибка соединения с сервером'); - } finally { - setVerifyLoading(false); - } - }; + // Верификация телефона перенесена на шаг 1 const handleSubmit = async () => { try { @@ -143,106 +59,18 @@ export default function Step3Payment({ - {/* Блок верификации телефона */} -
-

📱 Подтверждение телефона

- - - } - placeholder="+79001234567" - disabled={isPhoneVerified} - maxLength={12} - size="large" - /> - - - - } - placeholder="example@mail.ru" - size="large" - type="email" - disabled={isPhoneVerified} - /> - - - {!isPhoneVerified && ( - <> - - - - - {codeSent && ( - - - } - placeholder="123456" - maxLength={6} - style={{ width: '70%' }} - size="large" - /> - - - - )} - - )} - - {isPhoneVerified && ( -
- ✅ Телефон подтвержден -
- )} -
+ {/* Блок верификации телефона перенесен на шаг 1 */} + {isPhoneVerified && ( +
+ ✅ Телефон подтвержден +
+ )} {/* Блок выплаты (показывается только после верификации) */} {isPhoneVerified && ( diff --git a/frontend/src/pages/ClaimForm.tsx b/frontend/src/pages/ClaimForm.tsx index 3aaaae2..bfaea4f 100644 --- a/frontend/src/pages/ClaimForm.tsx +++ b/frontend/src/pages/ClaimForm.tsx @@ -1,6 +1,7 @@ import { useState, useMemo, useCallback } from 'react'; import { Steps, Card, message, Row, Col } from 'antd'; import Step1Policy from '../components/form/Step1Policy'; +import Step1Phone from '../components/form/Step1Phone'; import Step2EventType from '../components/form/Step2EventType'; import StepDocumentUpload from '../components/form/StepDocumentUpload'; import Step3Payment from '../components/form/Step3Payment'; @@ -165,7 +166,22 @@ export default function ClaimForm() { const steps = useMemo(() => { const stepsArray: any[] = []; - // Шаг 1: Policy (всегда) + // Шаг 1: Подтверждение телефона (всегда) + stepsArray.push({ + title: 'Телефон', + description: 'Подтверждение по SMS', + content: ( + + ), + }); + + // Шаг 2: Policy (всегда) stepsArray.push({ title: 'Проверка полиса', description: 'Полис ERV', @@ -179,7 +195,7 @@ export default function ClaimForm() { ), }); - // Шаг 2: Event Type Selection (всегда) + // Шаг 3: Event Type Selection (всегда) stepsArray.push({ title: 'Тип события', description: 'Выбор случая', @@ -193,7 +209,7 @@ export default function ClaimForm() { ), }); - // Шаги 3+: Document Upload (динамически, если выбран eventType) + // Шаги 4+: Document Upload (динамически, если выбран eventType) if (formData.eventType && documentConfigs.length > 0) { documentConfigs.forEach((docConfig, index) => { stepsArray.push({