Files
aiform_prod/frontend/src/components/DebugPanel.tsx
AI Assistant d2777aeabf feat: Step2 переделан + улучшен Debug Panel с полными S3 URL
Step2Details (по скриншоту):
 Индикатор ' Полис найден' вверху
 Select с типами событий из erv_ticket:
   - Задержка авиарейса (более 3 часов)
   - Отмена авиарейса
   - Пропуск стыковочного рейса
   - Посадка на запасной аэродром
   - Задержка отправки поезда
   - Отмена поезда
   - Задержка/отмена парома/круизного судна

 Дата наступления страхового случая (DatePicker)
 Номер рейса/поезда/парома
 Загрузка подтверждающих документов:
   - Посадочный талон, билет, справка и т.д.
   - До 10 файлов по 15MB
   - HEIC, PDF, фото

Debug Panel улучшения:
 Полные S3 URL (не обрезанные)
 Кнопка '🔗 Открыть в новой вкладке'
 word-break: break-all для длинных URL
 Показывает все файлы из массива
 Для каждого файла:
   - Filename
   - File ID (UUID)
   - Size (KB)
   - Полный S3 URL (кликабельный)

Теперь в Debug видно КУДА загрузилось:
https://s3.twcstorage.ru/f9825c87-.../policies/20251024_213045_abc123_file.jpg

Можно кликнуть и посмотреть глазами! 👀
2025-10-25 09:27:56 +03:00

271 lines
12 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 { Card, Timeline, Tag, Descriptions } from 'antd';
import { CheckCircleOutlined, LoadingOutlined, ExclamationCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
interface DebugEvent {
timestamp: string;
type: 'policy_check' | 'ocr' | 'ai_analysis' | 'upload' | 'error';
status: 'pending' | 'success' | 'warning' | 'error';
message: string;
data?: any;
}
interface Props {
events: DebugEvent[];
formData: any;
}
export default function DebugPanel({ events, formData }: Props) {
const getIcon = (status: string) => {
switch (status) {
case 'success': return <CheckCircleOutlined style={{ color: '#52c41a' }} />;
case 'pending': return <LoadingOutlined style={{ color: '#1890ff' }} />;
case 'warning': return <ExclamationCircleOutlined style={{ color: '#faad14' }} />;
case 'error': return <CloseCircleOutlined style={{ color: '#f5222d' }} />;
default: return <CheckCircleOutlined />;
}
};
const getColor = (status: string) => {
switch (status) {
case 'success': return 'success';
case 'pending': return 'processing';
case 'warning': return 'warning';
case 'error': return 'error';
default: return 'default';
}
};
return (
<div style={{
position: 'sticky',
top: 20,
height: 'calc(100vh - 40px)',
overflowY: 'auto'
}}>
<Card
title="🔧 Debug Console"
size="small"
style={{
background: '#1e1e1e',
color: '#d4d4d4',
border: '1px solid #333'
}}
headStyle={{
background: '#252526',
color: '#fff',
borderBottom: '1px solid #333'
}}
bodyStyle={{ padding: 12 }}
>
{/* Текущие данные формы */}
<div style={{ marginBottom: 16, padding: 12, background: '#2d2d30', borderRadius: 4 }}>
<div style={{ fontSize: 12, color: '#9cdcfe', marginBottom: 8, fontFamily: 'monospace' }}>
<strong>Form Data:</strong>
</div>
<pre style={{
margin: 0,
fontSize: 11,
color: '#ce9178',
fontFamily: 'Consolas, monospace',
maxHeight: 150,
overflow: 'auto'
}}>
{JSON.stringify(formData, null, 2)}
</pre>
</div>
{/* События */}
<div style={{ marginBottom: 8, fontSize: 12, color: '#9cdcfe', fontFamily: 'monospace' }}>
<strong>Events Log:</strong>
</div>
<Timeline style={{ marginTop: 16 }}>
{events.length === 0 && (
<Timeline.Item color="gray">
<span style={{ color: '#888', fontSize: 12 }}>Нет событий...</span>
</Timeline.Item>
)}
{events.map((event, index) => (
<Timeline.Item
key={index}
dot={getIcon(event.status)}
>
<div style={{ fontSize: 11, fontFamily: 'monospace' }}>
<div style={{ color: '#888', marginBottom: 4 }}>
{event.timestamp}
</div>
<div style={{ marginBottom: 8 }}>
<Tag color={getColor(event.status)} style={{ fontSize: 11 }}>
{event.type.toUpperCase()}
</Tag>
<span style={{ color: '#d4d4d4' }}>{event.message}</span>
</div>
{event.data && (
<div style={{
marginTop: 8,
padding: 8,
background: '#1e1e1e',
borderRadius: 4,
border: '1px solid #333'
}}>
{event.type === 'policy_check' && event.data.found !== undefined && (
<Descriptions size="small" column={1} bordered>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Found</span>}
labelStyle={{ background: '#252526', color: '#9cdcfe' }}
contentStyle={{ background: '#1e1e1e', color: event.data.found ? '#4ec9b0' : '#f48771' }}
>
{event.data.found ? 'TRUE' : 'FALSE'}
</Descriptions.Item>
{event.data.holder_name && (
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Holder</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#ce9178' }}
>
{event.data.holder_name}
</Descriptions.Item>
)}
</Descriptions>
)}
{event.type === 'ocr' && (
<pre style={{
margin: 0,
fontSize: 10,
color: '#ce9178',
maxHeight: 100,
overflow: 'auto',
whiteSpace: 'pre-wrap'
}}>
{event.data.text?.substring(0, 300)}...
</pre>
)}
{event.type === 'ai_analysis' && (
<Descriptions size="small" column={1} bordered>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Type</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#4ec9b0' }}
>
{event.data.document_type}
</Descriptions.Item>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Valid</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: event.data.is_valid ? '#4ec9b0' : '#f48771' }}
>
{event.data.is_valid ? 'TRUE' : 'FALSE'}
</Descriptions.Item>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Confidence</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#dcdcaa' }}
>
{(event.data.confidence * 100).toFixed(0)}%
</Descriptions.Item>
{event.data.extracted_data && Object.keys(event.data.extracted_data).length > 0 && (
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Extracted</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e' }}
>
<pre style={{
margin: 0,
fontSize: 10,
color: '#ce9178',
whiteSpace: 'pre-wrap'
}}>
{JSON.stringify(event.data.extracted_data, null, 2)}
</pre>
</Descriptions.Item>
)}
</Descriptions>
)}
{event.type === 'upload' && event.data.files && (
<div>
{event.data.files.map((file: any, idx: number) => (
<Descriptions key={idx} size="small" column={1} bordered style={{ marginBottom: 8 }}>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>File</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#ce9178', fontSize: 10 }}
>
{file.filename}
</Descriptions.Item>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>File ID</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#569cd6', fontSize: 9, fontFamily: 'monospace' }}
>
{file.file_id}
</Descriptions.Item>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Size</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#dcdcaa' }}
>
{(file.size / 1024).toFixed(1)} KB
</Descriptions.Item>
{file.url && (
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>S3 URL</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', fontSize: 9, wordBreak: 'break-all' }}
>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#4ec9b0', textDecoration: 'underline' }}
>
{file.url}
</a>
<div style={{ marginTop: 4 }}>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
style={{
fontSize: 10,
color: '#569cd6',
background: '#1e1e1e',
padding: '2px 6px',
borderRadius: 3,
border: '1px solid #333'
}}
>
🔗 Открыть в новой вкладке
</a>
</div>
</Descriptions.Item>
)}
</Descriptions>
))}
</div>
)}
</div>
)}
</div>
</Timeline.Item>
))}
</Timeline>
{events.length > 0 && (
<div style={{ marginTop: 16, padding: 8, background: '#2d2d30', borderRadius: 4, textAlign: 'center' }}>
<span style={{ fontSize: 11, color: '#888' }}>
Total events: {events.length}
</span>
</div>
)}
</Card>
</div>
);
}