Files
aiform_prod/frontend/src/components/form/Step3Payment.tsx
AI Assistant 4c8fda5f55 Добавлено логирование для отладки черновиков
- Добавлены логи в frontend (ClaimForm.tsx) для отслеживания unified_id и запросов к API
- Добавлены логи в backend (claims.py) для отладки SQL запросов
- Создан лог сессии с описанием проблемы и текущего состояния
- Проблема: API возвращает 0 черновиков, хотя в БД есть данные
2025-11-19 18:46:48 +03:00

426 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react';
import { Form, Input, Button, Select, message, Space, Divider } from 'antd';
import { PhoneOutlined, SafetyOutlined, QrcodeOutlined, MailOutlined, CopyOutlined } from '@ant-design/icons';
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8200';
const { Option } = Select;
interface Props {
formData: any;
updateFormData: (data: any) => void;
onPrev: () => void;
onSubmit: () => void;
isPhoneVerified: boolean;
setIsPhoneVerified: (verified: boolean) => void;
addDebugEvent?: (type: string, status: string, message: string, data?: any) => void;
}
export default function Step3Payment({
formData,
updateFormData,
onPrev,
onSubmit,
isPhoneVerified,
setIsPhoneVerified,
addDebugEvent
}: Props) {
const [form] = Form.useForm();
const [codeSent, setCodeSent] = useState(false);
const [loading, setLoading] = useState(false);
const [verifyLoading, setVerifyLoading] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [debugCode, setDebugCode] = useState<string | null>(formData.smsDebugCode ?? null);
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(`${API_BASE_URL}/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) {
setDebugCode(result.debug_code);
updateFormData({ smsDebugCode: 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(`${API_BASE_URL}/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('Телефон подтвержден!');
setDebugCode(null);
updateFormData({ smsDebugCode: undefined });
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 {
const values = await form.validateFields();
updateFormData(values);
setSubmitting(true);
await onSubmit();
} catch (error) {
message.error('Заполните все обязательные поля');
} finally {
setSubmitting(false);
}
};
return (
<Form
form={form}
layout="vertical"
initialValues={formData}
style={{ marginTop: 24 }}
>
{/* Скрытые технические поля */}
<Form.Item name="clientIp" hidden>
<Input type="hidden" />
</Form.Item>
<Form.Item name="smsCode" hidden>
<Input type="hidden" />
</Form.Item>
{/* Кнопка Назад вверху */}
<div style={{ marginBottom: 16 }}>
<Button onClick={onPrev} size="large">
Назад
</Button>
</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>
<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>
)}
{debugCode && !isPhoneVerified && (
<div
style={{
marginTop: 8,
padding: 12,
background: '#fafafa',
borderRadius: 8,
border: '1px dashed #d9d9d9',
display: 'flex',
alignItems: 'center',
gap: 12,
}}
>
<span>
<strong>DEBUG код:</strong> {debugCode}
</span>
<Button
icon={<CopyOutlined />}
size="small"
onClick={() => {
navigator.clipboard.writeText(debugCode);
message.success('Код скопирован');
}}
>
Скопировать
</Button>
</div>
)}
</>
)}
{isPhoneVerified && (
<div style={{
padding: 12,
background: '#fafafa',
borderRadius: 8,
border: '1px solid #d9d9d9'
}}>
Телефон подтвержден
</div>
)}
</div>
{/* Блок выплаты (показывается только после верификации) */}
{isPhoneVerified && (
<>
<Divider />
<h3>💳 Способ получения выплаты</h3>
<Form.Item
label="Способ выплаты"
name="paymentMethod"
initialValue="sbp"
>
<div style={{
padding: '12px',
background: '#fafafa',
borderRadius: '8px',
border: '1px solid #d9d9d9'
}}>
<QrcodeOutlined style={{ fontSize: 20, color: '#595959', marginRight: 8 }} />
<strong>СБП (Система быстрых платежей)</strong>
<p style={{ margin: '8px 0 0 0', color: '#666', fontSize: 13 }}>
Выплата поступит на ваш счет в течение нескольких минут
</p>
</div>
</Form.Item>
<Form.Item
label="Выберите ваш банк"
name="bankName"
rules={[{ required: true, message: 'Выберите банк для получения выплаты' }]}
>
<Select
placeholder="Выберите банк"
size="large"
showSearch
filterOption={(input: string, option: any) => {
const children = option?.children;
if (typeof children === 'string') {
return children.toLowerCase().includes(input.toLowerCase());
}
return false;
}}
>
<Option value="sberbank">🟢 Сбербанк</Option>
<Option value="tinkoff">🟡 Тинькофф</Option>
<Option value="vtb">🔵 ВТБ</Option>
<Option value="alfabank">🔴 Альфа-Банк</Option>
<Option value="raiffeisen">🟡 Райффайзенбанк</Option>
<Option value="gazprombank">🔵 Газпромбанк</Option>
<Option value="rosbank">🔴 Росбанк</Option>
<Option value="sovcombank">🟢 Совкомбанк</Option>
<Option value="otkritie">🔵 Открытие</Option>
<Option value="other">💳 Другой банк</Option>
</Select>
</Form.Item>
<Form.Item>
<div style={{ display: 'flex', gap: 8, marginTop: 32 }}>
<Button onClick={onPrev} size="large">Назад</Button>
<Button
type="primary"
onClick={handleSubmit}
loading={submitting}
style={{ flex: 1 }}
size="large"
>
Отправить заявку
</Button>
</div>
</Form.Item>
{/* 🔧 Технические кнопки для разработки */}
<div style={{
marginTop: 24,
padding: 16,
background: '#f0f0f0',
borderRadius: 8,
border: '2px dashed #999'
}}>
<div style={{ marginBottom: 8, fontSize: 12, color: '#666', fontWeight: 'bold' }}>
🔧 DEV MODE - Быстрая навигация (без валидации)
</div>
<div style={{ display: 'flex', gap: 8 }}>
<Button
onClick={onPrev}
size="small"
>
Назад (Step 2)
</Button>
<Button
type="dashed"
onClick={() => {
// Пропускаем валидацию телефона
setIsPhoneVerified(true);
const devData = {
fullName: 'Тест Тестов',
email: 'test@test.ru',
phone: '+79991234567',
paymentMethod: 'sbp',
bankName: 'sberbank',
};
updateFormData(devData);
message.success('DEV: Телефон автоматически подтверждён');
}}
size="small"
style={{ flex: 1 }}
>
Автоподтверждение телефона [dev]
</Button>
<Button
type="primary"
onClick={() => {
// Автоматически отправляем заявку
setIsPhoneVerified(true);
const devData = {
fullName: 'Тест Тестов',
email: 'test@test.ru',
phone: '+79991234567',
paymentMethod: 'sbp',
bankName: 'sberbank',
};
updateFormData(devData);
onSubmit();
}}
size="small"
>
🚀 Отправить [пропустить]
</Button>
</div>
</div>
</>
)}
</Form>
);
}