arm-web/src/widgets/FilesDrawer/FileWarningsPopover.tsx

107 lines
3.6 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 { Popover, Spin, Tag } from 'antd';
import { useState } from 'react';
import { getDocument } from '../../services/api/documents';
import type { IssueOut, Severity } from '../../types/api';
import { severityLabel } from '../../utils/labels';
import { SeverityIcon } from '../DocumentReview/IssuesPanel';
import type { DocumentRow } from './documentRows';
import { warningText } from './fileWarnings';
type IssuesState = { status: 'loading' } | { status: 'error' } | { status: 'done'; issues: IssueOut[] };
const tagColor = (s: Severity) => (s === 'error' ? 'error' : 'processing');
function DocumentIssues({ state }: { state: IssuesState | null }) {
if (state === null || state.status === 'loading') return <Spin size='small' />;
if (state.status === 'error') return <span style={{ color: '#cf1322' }}>Не удалось загрузить</span>;
if (state.issues.length === 0) return <span style={{ color: '#8c8c8c' }}>Нет</span>;
return (
<>
{state.issues.map((i) => (
<div key={i.id}>
<Tag color={tagColor(i.severity)}>{severityLabel(i.severity)}</Tag>
{i.message}
</div>
))}
</>
);
}
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div style={{ marginBottom: 8 }}>
<div style={{ fontWeight: 600, marginBottom: 4 }}>{title}</div>
{children}
</div>
);
}
/** Значок с числом предупреждений под плашкой статуса; по клику — подробности (файл, обработка, документ). */
export function FileWarningsPopover({ row, badge }: { row: DocumentRow; badge: { count: number; level: Severity } | null }) {
const [issues, setIssues] = useState<IssuesState | null>(null);
if (!badge) return null;
// предупреждения документа грузим один раз за открытие: при закрытии сбрасываем, повторное открытие запрашивает заново
const onOpenChange = (open: boolean) => {
if (!open) {
setIssues(null);
return;
}
if (!row.documentId) return;
setIssues({ status: 'loading' });
getDocument(row.documentId)
.then((d) => setIssues({ status: 'done', issues: d.issues }))
.catch(() => setIssues({ status: 'error' }));
};
const content = (
<div style={{ maxWidth: 420, maxHeight: 320, overflowY: 'auto' }}>
{row.fileWarnings.length > 0 && (
<Section title='Предупреждения файла'>
{row.fileWarnings.map((w, i) => (
<div key={`${w.code}-${i}`}>
<Tag color={tagColor(w.severity)}>{severityLabel(w.severity)}</Tag>
{warningText(w)}
</div>
))}
</Section>
)}
{row.mappingWarnings.length > 0 && (
<Section title='Предупреждения обработки'>
{row.mappingWarnings.map((w) => (
<div key={w}>
<Tag color={tagColor('info')}>{severityLabel('info')}</Tag>
{w}
</div>
))}
</Section>
)}
{row.documentId && (
<Section title='Предупреждения документа'>
<DocumentIssues state={issues} />
</Section>
)}
</div>
);
return (
<Popover trigger='click' title='Предупреждения' content={content} onOpenChange={onOpenChange}>
<button
type='button'
onClick={(e) => e.stopPropagation()}
style={{
cursor: 'pointer',
background: 'none',
border: 'none',
padding: 0,
fontSize: 12,
whiteSpace: 'nowrap',
color: badge.level === 'error' ? '#cf1322' : '#1677ff',
}}>
<SeverityIcon severity={badge.level} /> {badge.count}
</button>
</Popover>
);
}