256 lines
9.1 KiB
TypeScript
256 lines
9.1 KiB
TypeScript
|
|
import { useEffect, useState } from 'react';
|
|||
|
|
import { Button, Card, List, Typography, Space, Empty, Popconfirm, message, Spin, Tag } from 'antd';
|
|||
|
|
import { FileTextOutlined, DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
|
|||
|
|
// Форматирование даты без date-fns (если библиотека не установлена)
|
|||
|
|
const formatDate = (dateStr: string) => {
|
|||
|
|
try {
|
|||
|
|
const date = new Date(dateStr);
|
|||
|
|
const day = date.getDate();
|
|||
|
|
const month = date.toLocaleDateString('ru-RU', { month: 'long' });
|
|||
|
|
const year = date.getFullYear();
|
|||
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|||
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|||
|
|
return `${day} ${month} ${year}, ${hours}:${minutes}`;
|
|||
|
|
} catch {
|
|||
|
|
return dateStr;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const { Title, Text, Paragraph } = Typography;
|
|||
|
|
|
|||
|
|
interface Draft {
|
|||
|
|
id: string;
|
|||
|
|
claim_id: string;
|
|||
|
|
session_token: string;
|
|||
|
|
status_code: string;
|
|||
|
|
created_at: string;
|
|||
|
|
updated_at: string;
|
|||
|
|
problem_description?: string;
|
|||
|
|
wizard_plan: boolean;
|
|||
|
|
wizard_answers: boolean;
|
|||
|
|
has_documents: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface Props {
|
|||
|
|
phone: string;
|
|||
|
|
session_id?: string;
|
|||
|
|
onSelectDraft: (claimId: string) => void;
|
|||
|
|
onNewClaim: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function StepDraftSelection({
|
|||
|
|
phone,
|
|||
|
|
session_id,
|
|||
|
|
onSelectDraft,
|
|||
|
|
onNewClaim,
|
|||
|
|
}: Props) {
|
|||
|
|
const [drafts, setDrafts] = useState<Draft[]>([]);
|
|||
|
|
const [loading, setLoading] = useState(true);
|
|||
|
|
const [deletingId, setDeletingId] = useState<string | null>(null);
|
|||
|
|
|
|||
|
|
const loadDrafts = async () => {
|
|||
|
|
try {
|
|||
|
|
setLoading(true);
|
|||
|
|
const params = new URLSearchParams();
|
|||
|
|
if (session_id) {
|
|||
|
|
params.append('session_id', session_id);
|
|||
|
|
} else if (phone) {
|
|||
|
|
params.append('phone', phone);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const response = await fetch(`/api/v1/claims/drafts/list?${params.toString()}`);
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error('Не удалось загрузить черновики');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = await response.json();
|
|||
|
|
setDrafts(data.drafts || []);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Ошибка загрузки черновиков:', error);
|
|||
|
|
message.error('Не удалось загрузить список черновиков');
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
loadDrafts();
|
|||
|
|
}, [phone, session_id]);
|
|||
|
|
|
|||
|
|
const handleDelete = async (claimId: string) => {
|
|||
|
|
try {
|
|||
|
|
setDeletingId(claimId);
|
|||
|
|
const response = await fetch(`/api/v1/claims/drafts/${claimId}`, {
|
|||
|
|
method: 'DELETE',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error('Не удалось удалить черновик');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message.success('Черновик удален');
|
|||
|
|
await loadDrafts();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Ошибка удаления черновика:', error);
|
|||
|
|
message.error('Не удалось удалить черновик');
|
|||
|
|
} finally {
|
|||
|
|
setDeletingId(null);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
const getProgressInfo = (draft: Draft) => {
|
|||
|
|
const parts: string[] = [];
|
|||
|
|
if (draft.problem_description) parts.push('Описание');
|
|||
|
|
if (draft.wizard_plan) parts.push('План вопросов');
|
|||
|
|
if (draft.wizard_answers) parts.push('Ответы');
|
|||
|
|
if (draft.has_documents) parts.push('Документы');
|
|||
|
|
return parts.length > 0 ? parts.join(', ') : 'Начато';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div style={{ maxWidth: 800, margin: '0 auto', padding: '24px 0' }}>
|
|||
|
|
<Card
|
|||
|
|
style={{
|
|||
|
|
borderRadius: 8,
|
|||
|
|
border: '1px solid #d9d9d9',
|
|||
|
|
background: '#fff',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
|||
|
|
<div>
|
|||
|
|
<Title level={3} style={{ marginBottom: 8 }}>
|
|||
|
|
Продолжить заполнение или создать новую заявку?
|
|||
|
|
</Title>
|
|||
|
|
<Paragraph type="secondary">
|
|||
|
|
У вас есть незавершенные черновики. Вы можете продолжить заполнение или создать новую заявку.
|
|||
|
|
</Paragraph>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{loading ? (
|
|||
|
|
<div style={{ textAlign: 'center', padding: '40px 0' }}>
|
|||
|
|
<Spin size="large" />
|
|||
|
|
</div>
|
|||
|
|
) : drafts.length === 0 ? (
|
|||
|
|
<Empty
|
|||
|
|
description="У вас нет незавершенных черновиков"
|
|||
|
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|||
|
|
>
|
|||
|
|
<Button type="primary" icon={<PlusOutlined />} onClick={onNewClaim} size="large">
|
|||
|
|
Создать новую заявку
|
|||
|
|
</Button>
|
|||
|
|
</Empty>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
<List
|
|||
|
|
dataSource={drafts}
|
|||
|
|
renderItem={(draft) => (
|
|||
|
|
<List.Item
|
|||
|
|
style={{
|
|||
|
|
padding: '16px',
|
|||
|
|
border: '1px solid #d9d9d9',
|
|||
|
|
borderRadius: 8,
|
|||
|
|
marginBottom: 12,
|
|||
|
|
background: '#fff',
|
|||
|
|
}}
|
|||
|
|
actions={[
|
|||
|
|
<Button
|
|||
|
|
key="continue"
|
|||
|
|
type="primary"
|
|||
|
|
onClick={() => onSelectDraft(draft.claim_id!)}
|
|||
|
|
icon={<FileTextOutlined />}
|
|||
|
|
>
|
|||
|
|
Продолжить
|
|||
|
|
</Button>,
|
|||
|
|
<Popconfirm
|
|||
|
|
key="delete"
|
|||
|
|
title="Удалить черновик?"
|
|||
|
|
description="Это действие нельзя отменить"
|
|||
|
|
onConfirm={() => handleDelete(draft.claim_id!)}
|
|||
|
|
okText="Да, удалить"
|
|||
|
|
cancelText="Отмена"
|
|||
|
|
>
|
|||
|
|
<Button
|
|||
|
|
danger
|
|||
|
|
icon={<DeleteOutlined />}
|
|||
|
|
loading={deletingId === draft.claim_id}
|
|||
|
|
disabled={deletingId === draft.claim_id}
|
|||
|
|
>
|
|||
|
|
Удалить
|
|||
|
|
</Button>
|
|||
|
|
</Popconfirm>,
|
|||
|
|
]}
|
|||
|
|
>
|
|||
|
|
<List.Item.Meta
|
|||
|
|
avatar={<FileTextOutlined style={{ fontSize: 24, color: '#595959' }} />}
|
|||
|
|
title={
|
|||
|
|
<Space>
|
|||
|
|
<Text strong>Черновик {draft.claim_id}</Text>
|
|||
|
|
<Tag color="default">Черновик</Tag>
|
|||
|
|
</Space>
|
|||
|
|
}
|
|||
|
|
description={
|
|||
|
|
<Space direction="vertical" size="small" style={{ width: '100%' }}>
|
|||
|
|
<Text type="secondary" style={{ fontSize: 12 }}>
|
|||
|
|
Обновлен: {formatDate(draft.updated_at)}
|
|||
|
|
</Text>
|
|||
|
|
{draft.problem_description && (
|
|||
|
|
<Text
|
|||
|
|
ellipsis={{ tooltip: draft.problem_description }}
|
|||
|
|
style={{ fontSize: 13 }}
|
|||
|
|
>
|
|||
|
|
{draft.problem_description}
|
|||
|
|
</Text>
|
|||
|
|
)}
|
|||
|
|
<Space size="small">
|
|||
|
|
<Tag color={draft.wizard_plan ? 'green' : 'default'}>
|
|||
|
|
{draft.wizard_plan ? '✓ План' : 'План'}
|
|||
|
|
</Tag>
|
|||
|
|
<Tag color={draft.wizard_answers ? 'green' : 'default'}>
|
|||
|
|
{draft.wizard_answers ? '✓ Ответы' : 'Ответы'}
|
|||
|
|
</Tag>
|
|||
|
|
<Tag color={draft.has_documents ? 'green' : 'default'}>
|
|||
|
|
{draft.has_documents ? '✓ Документы' : 'Документы'}
|
|||
|
|
</Tag>
|
|||
|
|
</Space>
|
|||
|
|
<Text type="secondary" style={{ fontSize: 12 }}>
|
|||
|
|
Прогресс: {getProgressInfo(draft)}
|
|||
|
|
</Text>
|
|||
|
|
</Space>
|
|||
|
|
}
|
|||
|
|
/>
|
|||
|
|
</List.Item>
|
|||
|
|
)}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<div style={{ textAlign: 'center', marginTop: 24 }}>
|
|||
|
|
<Button
|
|||
|
|
type="dashed"
|
|||
|
|
icon={<PlusOutlined />}
|
|||
|
|
onClick={onNewClaim}
|
|||
|
|
size="large"
|
|||
|
|
style={{ width: '100%' }}
|
|||
|
|
>
|
|||
|
|
Создать новую заявку
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style={{ textAlign: 'center' }}>
|
|||
|
|
<Button
|
|||
|
|
type="link"
|
|||
|
|
icon={<ReloadOutlined />}
|
|||
|
|
onClick={loadDrafts}
|
|||
|
|
loading={loading}
|
|||
|
|
>
|
|||
|
|
Обновить список
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</Space>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|