feat: Полный флоу для создания контакта через CreateWebContact

- docker-compose.yml: убраны локальные postgres/redis, только внешние
- Frontend: телефон в формате 79001234567 (без +)
- Готово к интеграции с n8n webhook для создания контакта в CRM
- CreateWebContact: только создание или возврат ID, БЕЗ обновления
This commit is contained in:
AI Assistant
2025-10-30 19:22:14 +03:00
parent 6708092662
commit 7b554c0ad2
3 changed files with 226 additions and 72 deletions

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { Form, Input, Button, Select, message, Divider } from 'antd';
import { QrcodeOutlined, MailOutlined } from '@ant-design/icons';
import { Form, Input, Button, Select, message, Space, Divider } from 'antd';
import { PhoneOutlined, SafetyOutlined, QrcodeOutlined, MailOutlined } from '@ant-design/icons';
const { Option } = Select;
@@ -24,12 +24,96 @@ export default function Step3Payment({
addDebugEvent
}: Props) {
const [form] = Form.useForm();
const [codeSent] = useState(false);
const [loading] = useState(false);
const [verifyLoading] = useState(false);
const [codeSent, setCodeSent] = useState(false);
const [loading, setLoading] = useState(false);
const [verifyLoading, setVerifyLoading] = useState(false);
const [submitting, setSubmitting] = useState(false);
// Верификация телефона перенесена на шаг 1
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);
}
};
const handleSubmit = async () => {
try {
@@ -59,35 +143,106 @@ export default function Step3Payment({
</Button>
</div>
{/* Блок верификации телефона перенесен на шаг 1 */}
{isPhoneVerified && (
<div style={{
padding: 12,
background: '#f0f9ff',
borderRadius: 8,
border: '1px solid #91d5ff',
marginBottom: 24
}}>
Телефон подтвержден
</div>
)}
{/* Блок верификации телефона */}
<div style={{
padding: 16,
background: '#f6f8fa',
borderRadius: 8,
marginBottom: 24
}}>
<h3 style={{ marginTop: 0 }}>📱 Подтверждение телефона</h3>
<Form.Item
label="Номер телефона"
name="phone"
rules={[
{ required: true, message: 'Введите номер телефона' },
{ pattern: /^\+7\d{10}$/, message: 'Формат: +79001234567' }
]}
>
<Input
prefix={<PhoneOutlined />}
placeholder="+79001234567"
disabled={isPhoneVerified}
maxLength={12}
size="large"
/>
</Form.Item>
{/* Email собираем на последнем шаге */}
<Form.Item
label="Электронная почта"
name="email"
rules={[
{ required: true, message: 'Введите email' },
{ type: 'email', message: 'Неверный формат email' }
]}
>
<Input
prefix={<MailOutlined />}
placeholder="example@mail.ru"
size="large"
type="email"
/>
</Form.Item>
<Form.Item
label="Электронная почта"
name="email"
rules={[
{ required: true, message: 'Введите email' },
{ type: 'email', message: 'Неверный формат email' }
]}
>
<Input
prefix={<MailOutlined />}
placeholder="example@mail.ru"
size="large"
type="email"
disabled={isPhoneVerified}
/>
</Form.Item>
{!isPhoneVerified && (
<>
<Form.Item>
<Button
type="primary"
onClick={sendCode}
loading={loading}
disabled={codeSent}
block
>
{codeSent ? 'Код отправлен' : 'Отправить код'}
</Button>
</Form.Item>
{codeSent && (
<Form.Item
label="Код из SMS"
name="smsCode"
rules={[
{ required: true, message: 'Введите код' },
{ len: 6, message: '6 цифр' }
]}
>
<Space.Compact style={{ width: '100%' }}>
<Input
prefix={<SafetyOutlined />}
placeholder="123456"
maxLength={6}
style={{ width: '70%' }}
size="large"
/>
<Button
type="primary"
onClick={verifyCode}
loading={verifyLoading}
style={{ width: '30%' }}
size="large"
>
Проверить
</Button>
</Space.Compact>
</Form.Item>
)}
</>
)}
{isPhoneVerified && (
<div style={{
padding: 12,
background: '#f0f9ff',
borderRadius: 8,
border: '1px solid #91d5ff'
}}>
Телефон подтвержден
</div>
)}
</div>
{/* Блок выплаты (показывается только после верификации) */}
{isPhoneVerified && (
@@ -186,7 +341,7 @@ export default function Step3Payment({
const devData = {
fullName: 'Тест Тестов',
email: 'test@test.ru',
phone: '79991234567', // БЕЗ +
phone: '+79991234567',
paymentMethod: 'sbp',
bankName: 'sberbank',
};
@@ -206,7 +361,7 @@ export default function Step3Payment({
const devData = {
fullName: 'Тест Тестов',
email: 'test@test.ru',
phone: '79991234567', // БЕЗ +
phone: '+79991234567',
paymentMethod: 'sbp',
bankName: 'sberbank',
};