2025-11-15 18:48:15 +03:00
|
|
|
|
import { Form, Input, Button, Typography, message, Checkbox } from 'antd';
|
2025-11-14 19:06:36 +03:00
|
|
|
|
import { useEffect, useState } from 'react';
|
2025-11-15 18:48:15 +03:00
|
|
|
|
import wizardPlanSample from '../../mocks/wizardPlanSample';
|
2025-11-14 19:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
const { TextArea } = Input;
|
|
|
|
|
|
const { Paragraph } = Typography;
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
formData: any;
|
|
|
|
|
|
updateFormData: (data: any) => void;
|
|
|
|
|
|
onPrev: () => void;
|
|
|
|
|
|
onNext: () => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function StepDescription({
|
|
|
|
|
|
formData,
|
|
|
|
|
|
updateFormData,
|
|
|
|
|
|
onPrev,
|
|
|
|
|
|
onNext,
|
|
|
|
|
|
}: Props) {
|
|
|
|
|
|
const [form] = Form.useForm();
|
|
|
|
|
|
const [submitting, setSubmitting] = useState(false);
|
2025-11-15 18:48:15 +03:00
|
|
|
|
const [useMockWizard, setUseMockWizard] = useState(true);
|
|
|
|
|
|
|
|
|
|
|
|
const buildPrefillMap = (prefill?: Array<{ name: string; value: any }>) => {
|
|
|
|
|
|
if (!prefill) {
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}
|
|
|
|
|
|
return prefill.reduce<Record<string, any>>((acc, item) => {
|
|
|
|
|
|
if (item?.name) {
|
|
|
|
|
|
acc[item.name] = item.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
};
|
2025-11-14 19:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
form.setFieldsValue({
|
|
|
|
|
|
problemDescription: formData.problemDescription ?? '',
|
|
|
|
|
|
});
|
|
|
|
|
|
}, [form, formData.problemDescription]);
|
|
|
|
|
|
|
|
|
|
|
|
const handleContinue = async () => {
|
|
|
|
|
|
try {
|
2025-11-15 18:48:15 +03:00
|
|
|
|
let problemDescription = form.getFieldValue('problemDescription');
|
|
|
|
|
|
if (!useMockWizard) {
|
|
|
|
|
|
const values = await form.validateFields();
|
|
|
|
|
|
problemDescription = values.problemDescription;
|
|
|
|
|
|
}
|
|
|
|
|
|
const safeDescription = problemDescription || '';
|
2025-11-14 19:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
if (!formData.session_id) {
|
|
|
|
|
|
message.error('Не найден session_id. Попробуйте обновить страницу.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-15 18:48:15 +03:00
|
|
|
|
if (!formData.claim_id) {
|
|
|
|
|
|
message.error('Не удалось определить номер обращения. Вернитесь на шаг с телефоном.');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-14 19:06:36 +03:00
|
|
|
|
|
|
|
|
|
|
setSubmitting(true);
|
|
|
|
|
|
|
2025-11-15 18:48:15 +03:00
|
|
|
|
if (useMockWizard && wizardPlanSample?.wizard_plan) {
|
|
|
|
|
|
const mockPrefill = buildPrefillMap(wizardPlanSample.answers_prefill);
|
|
|
|
|
|
const mockClaimId = wizardPlanSample.claim_id || formData.claim_id;
|
|
|
|
|
|
|
|
|
|
|
|
updateFormData({
|
|
|
|
|
|
problemDescription: safeDescription,
|
|
|
|
|
|
claim_id: mockClaimId,
|
|
|
|
|
|
wizardPlan: wizardPlanSample.wizard_plan,
|
|
|
|
|
|
wizardPlanStatus: 'ready',
|
|
|
|
|
|
wizardPrefill: mockPrefill,
|
|
|
|
|
|
wizardPrefillArray: wizardPlanSample.answers_prefill,
|
|
|
|
|
|
wizardCoverageReport: wizardPlanSample.coverage_report,
|
|
|
|
|
|
wizardAnswers: undefined,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
message.success('Загружены сохранённые рекомендации (DEV).');
|
|
|
|
|
|
onNext();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-14 19:06:36 +03:00
|
|
|
|
const response = await fetch('/api/v1/claims/description', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
session_id: formData.session_id,
|
|
|
|
|
|
claim_id: formData.claim_id,
|
|
|
|
|
|
phone: formData.phone,
|
|
|
|
|
|
email: formData.email,
|
2025-11-15 18:48:15 +03:00
|
|
|
|
problem_description: safeDescription,
|
2025-11-14 19:06:36 +03:00
|
|
|
|
}),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
throw new Error(`Ошибка API: ${response.status}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-15 18:48:15 +03:00
|
|
|
|
message.success('Описание отправлено, подбираем рекомендации...');
|
|
|
|
|
|
updateFormData({
|
|
|
|
|
|
problemDescription: safeDescription,
|
|
|
|
|
|
wizardPlan: undefined,
|
|
|
|
|
|
wizardPlanStatus: 'pending',
|
|
|
|
|
|
wizardAnswers: undefined,
|
|
|
|
|
|
wizardPrefill: undefined,
|
|
|
|
|
|
wizardPrefillArray: undefined,
|
|
|
|
|
|
});
|
2025-11-14 19:06:36 +03:00
|
|
|
|
onNext();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(error);
|
|
|
|
|
|
message.error('Не получилось сохранить описание. Попробуйте ещё раз.');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setSubmitting(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{ marginTop: 24 }}>
|
|
|
|
|
|
<Button onClick={onPrev} size="large">
|
|
|
|
|
|
← Назад
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginTop: 24,
|
|
|
|
|
|
padding: 24,
|
|
|
|
|
|
background: '#f6f8fa',
|
2025-11-19 18:46:48 +03:00
|
|
|
|
borderRadius: 8,
|
2025-11-14 19:06:36 +03:00
|
|
|
|
border: '1px solid #e0e6ed',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Paragraph style={{ fontSize: 18, fontWeight: 600, marginBottom: 8 }}>
|
|
|
|
|
|
📄 Опишите проблему
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
<Paragraph type="secondary" style={{ marginBottom: 24 }}>
|
|
|
|
|
|
Расскажите, что произошло, в свободной форме. Чем больше деталей —
|
|
|
|
|
|
тем быстрее команда сможет разобраться, какие документы нужны и куда
|
|
|
|
|
|
направить заявку.
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
|
|
|
|
|
|
<Form layout="vertical" form={form}>
|
|
|
|
|
|
<Form.Item
|
|
|
|
|
|
label="Описание ситуации"
|
|
|
|
|
|
name="problemDescription"
|
|
|
|
|
|
rules={[
|
|
|
|
|
|
{
|
2025-11-15 18:48:15 +03:00
|
|
|
|
validator: (_, value) => {
|
|
|
|
|
|
if (useMockWizard) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
|
return Promise.reject(new Error('Поле обязательно'));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (value.length < 20) {
|
|
|
|
|
|
return Promise.reject(
|
|
|
|
|
|
new Error('Опишите, пожалуйста, минимум в пару предложений')
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
},
|
2025-11-14 19:06:36 +03:00
|
|
|
|
},
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<TextArea
|
2025-11-15 18:48:15 +03:00
|
|
|
|
disabled={useMockWizard}
|
2025-11-14 19:06:36 +03:00
|
|
|
|
autoSize={{ minRows: 6 }}
|
|
|
|
|
|
maxLength={3000}
|
2025-11-15 18:48:15 +03:00
|
|
|
|
showCount={!useMockWizard}
|
2025-11-14 19:06:36 +03:00
|
|
|
|
placeholder="Например: заключил договор на оказание услуг..., деньги списали..., услугу не выполнили..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
|
2025-11-15 18:48:15 +03:00
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginTop: 12,
|
|
|
|
|
|
padding: 12,
|
|
|
|
|
|
borderRadius: 8,
|
2025-11-19 18:46:48 +03:00
|
|
|
|
background: '#fafafa',
|
|
|
|
|
|
border: '1px dashed #d9d9d9',
|
2025-11-15 18:48:15 +03:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Checkbox
|
|
|
|
|
|
checked={useMockWizard}
|
|
|
|
|
|
onChange={(e) => setUseMockWizard(e.target.checked)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Использовать сохранённые рекомендации (DEV)
|
|
|
|
|
|
</Checkbox>
|
|
|
|
|
|
<Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 4 }}>
|
|
|
|
|
|
Если включено, план вопросов берётся из локального файла и не запускает модель.
|
|
|
|
|
|
</Paragraph>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-11-14 19:06:36 +03:00
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
|
|
|
|
|
|
<Button type="primary" size="large" onClick={handleContinue} loading={submitting}>
|
|
|
|
|
|
Продолжить →
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|