feat(ticket_form): add wizard plan step and dev cache

This commit is contained in:
AI Assistant
2025-11-15 18:48:15 +03:00
parent 3306d01e0d
commit cbab1c0fe6
7 changed files with 1285 additions and 12 deletions

View File

@@ -1,5 +1,6 @@
import { Form, Input, Button, Typography, message } from 'antd';
import { Form, Input, Button, Typography, message, Checkbox } from 'antd';
import { useEffect, useState } from 'react';
import wizardPlanSample from '../../mocks/wizardPlanSample';
const { TextArea } = Input;
const { Paragraph } = Typography;
@@ -19,6 +20,19 @@ export default function StepDescription({
}: Props) {
const [form] = Form.useForm();
const [submitting, setSubmitting] = useState(false);
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;
}, {});
};
useEffect(() => {
form.setFieldsValue({
@@ -28,15 +42,44 @@ export default function StepDescription({
const handleContinue = async () => {
try {
const values = await form.validateFields();
let problemDescription = form.getFieldValue('problemDescription');
if (!useMockWizard) {
const values = await form.validateFields();
problemDescription = values.problemDescription;
}
const safeDescription = problemDescription || '';
if (!formData.session_id) {
message.error('Не найден session_id. Попробуйте обновить страницу.');
return;
}
if (!formData.claim_id) {
message.error('Не удалось определить номер обращения. Вернитесь на шаг с телефоном.');
return;
}
setSubmitting(true);
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;
}
const response = await fetch('/api/v1/claims/description', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -45,7 +88,7 @@ export default function StepDescription({
claim_id: formData.claim_id,
phone: formData.phone,
email: formData.email,
problem_description: values.problemDescription,
problem_description: safeDescription,
}),
});
@@ -53,8 +96,15 @@ export default function StepDescription({
throw new Error(`Ошибка API: ${response.status}`);
}
message.success('Описание отправлено, продолжаем заполнение');
updateFormData(values);
message.success('Описание отправлено, подбираем рекомендации...');
updateFormData({
problemDescription: safeDescription,
wizardPlan: undefined,
wizardPlanStatus: 'pending',
wizardAnswers: undefined,
wizardPrefill: undefined,
wizardPrefillArray: undefined,
});
onNext();
} catch (error) {
console.error(error);
@@ -93,22 +143,54 @@ export default function StepDescription({
label="Описание ситуации"
name="problemDescription"
rules={[
{ required: true, message: 'Поле обязательно' },
{
min: 20,
message: 'Опишите, пожалуйста, минимум в пару предложений',
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();
},
},
]}
>
<TextArea
disabled={useMockWizard}
autoSize={{ minRows: 6 }}
maxLength={3000}
showCount
showCount={!useMockWizard}
placeholder="Например: заключил договор на оказание услуг..., деньги списали..., услугу не выполнили..."
/>
</Form.Item>
</Form>
<div
style={{
marginTop: 12,
padding: 12,
borderRadius: 8,
background: '#eef2ff',
border: '1px dashed #c7d2fe',
}}
>
<Checkbox
checked={useMockWizard}
onChange={(e) => setUseMockWizard(e.target.checked)}
>
Использовать сохранённые рекомендации (DEV)
</Checkbox>
<Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 4 }}>
Если включено, план вопросов берётся из локального файла и не запускает модель.
</Paragraph>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
<Button type="primary" size="large" onClick={handleContinue} loading={submitting}>
Продолжить