diff --git a/src/widgets/FilesDrawer/FileWarningsPopover.tsx b/src/widgets/FilesDrawer/FileWarningsPopover.tsx index 5328f4b..8bfd2d7 100644 --- a/src/widgets/FilesDrawer/FileWarningsPopover.tsx +++ b/src/widgets/FilesDrawer/FileWarningsPopover.tsx @@ -1,5 +1,5 @@ import { Popover, Spin, Tag } from 'antd'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { getDocument } from '../../services/api/documents'; import type { IssueOut, Severity } from '../../types/api'; import { severityLabel } from '../../utils/labels'; @@ -27,6 +27,16 @@ function DocumentIssues({ state }: { state: IssuesState | null }) { ); } +/** Ключи для строк, которые могут повторяться: к тексту добавляется порядковый номер повтора (индекс массива в key линтер запрещает). */ +function uniqueKeys(list: string[]): { key: string; text: string }[] { + const seen = new Map(); + return list.map((text) => { + const n = (seen.get(text) ?? 0) + 1; + seen.set(text, n); + return { key: `${text}-${n}`, text }; + }); +} + function Section({ title, children }: { title: string; children: React.ReactNode }) { return (
@@ -39,11 +49,15 @@ function Section({ title, children }: { title: string; children: React.ReactNode /** Значок с числом предупреждений под плашкой статуса; по клику — подробности (файл, обработка, документ). */ export function FileWarningsPopover({ row, badge }: { row: DocumentRow; badge: { count: number; level: Severity } | null }) { const [issues, setIssues] = useState(null); + // номер текущего открытия: ответ от прошлого открытия или после закрытия игнорируется + const requestNo = useRef(0); if (!badge) return null; // предупреждения документа грузим один раз за открытие: при закрытии сбрасываем, повторное открытие запрашивает заново const onOpenChange = (open: boolean) => { + requestNo.current += 1; + const mine = requestNo.current; if (!open) { setIssues(null); return; @@ -51,12 +65,16 @@ export function FileWarningsPopover({ row, badge }: { row: DocumentRow; badge: { if (!row.documentId) return; setIssues({ status: 'loading' }); getDocument(row.documentId) - .then((d) => setIssues({ status: 'done', issues: d.issues })) - .catch(() => setIssues({ status: 'error' })); + .then((d) => mine === requestNo.current && setIssues({ status: 'done', issues: d.issues })) + .catch(() => mine === requestNo.current && setIssues({ status: 'error' })); }; const content = ( -
+
e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + style={{ maxWidth: 420, maxHeight: 320, overflowY: 'auto' }}> {row.fileWarnings.length > 0 && (
{row.fileWarnings.map((w, i) => ( @@ -69,10 +87,10 @@ export function FileWarningsPopover({ row, badge }: { row: DocumentRow; badge: { )} {row.mappingWarnings.length > 0 && (
- {row.mappingWarnings.map((w) => ( -
+ {uniqueKeys(row.mappingWarnings).map((w) => ( +
{severityLabel('info')} - {w} + {w.text}
))}
@@ -89,6 +107,7 @@ export function FileWarningsPopover({ row, badge }: { row: DocumentRow; badge: {