Files
aiform_dev/frontend/src/components/DebugPanel.tsx
AI Assistant 720d4ebdd9 feat: Split-screen с Debug панелью в реальном времени!
Новый UI:
 Split-screen layout:
   - Слева (60%): форма заявки
   - Справа (40%): Debug Console в реальном времени

Компонент DebugPanel.tsx:
 Темная тема (VS Code style)
 Timeline с событиями
 Real-time обновления
 Показывает:
   - Form Data (JSON в реальном времени)
   - Events Log с иконками и цветами
   - Детали каждого события

События которые отображаются:
1. policy_check:
   -  Полис найден в MySQL БД
   - ⚠️ Полис не найден
   - Показывает: voucher, found status

2. upload:
   - 📤 Загружаю X файлов в S3
   -  Загружено в S3: X/Y
   - Показывает: file_id, size, S3 URL

3. ocr:
   - 🔍 Запущен OCR
   - 📄 OCR завершен: XXX символов
   - Показывает: текст preview

4. ai_analysis:
   - 🤖 AI: policy/garbage, confidence: 95%
   - 🗑️ ШЛЯПА DETECTED! (пользователю не говорим)
   - Показывает: document_type, is_valid, confidence, extracted_data

5. sms:
   - 📱 Отправляю SMS
   -  SMS отправлен (DEBUG mode)
   - 🔐 Проверяю код
   -  Телефон подтвержден
   - Показывает: phone, debug_code

UX:
- Sticky panel (прилипает при скролле)
- Monospace шрифт для данных
- Цветовая кодировка статусов
- JSON форматирование

Layout:
- Row + Col от Ant Design
- Responsive: mobile = 1 column, desktop = split

Теперь видно ВСЁ что происходит в реальном времени! 🔍
2025-10-24 22:13:52 +03:00

237 lines
9.7 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' && (
<Descriptions size="small" column={1} bordered>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>File ID</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#569cd6', fontSize: 10 }}
>
{event.data.file_id}
</Descriptions.Item>
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>Size</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', color: '#dcdcaa' }}
>
{(event.data.size / 1024).toFixed(1)} KB
</Descriptions.Item>
{event.data.url && (
<Descriptions.Item
label={<span style={{ color: '#9cdcfe' }}>S3 URL</span>}
labelStyle={{ background: '#252526' }}
contentStyle={{ background: '#1e1e1e', fontSize: 9 }}
>
<a href={event.data.url} target="_blank" rel="noopener noreferrer" style={{ color: '#4ec9b0' }}>
{event.data.url.substring(0, 50)}...
</a>
</Descriptions.Item>
)}
</Descriptions>
)}
</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>
);
}