Перенос в arm-core/web правок RSHB/arm-web PR #27 «Панель «Первичные документы»: рабочий стол консолидации и загрузка в консолидацию» (ветка test, диапазон коммитов fc75edf..100fbfb), только src/: - services/api/files.ts: filesPath, маршруты /consolidations/{cid}/legal-entities/{le}/files вместо ?consolidation_version_id=, которое ядро игнорирует; - store/slices/consolidations/selectors.ts: текущая консолидация и статус версии (новый файл); - widgets/FilesDrawer: рабочий стол консолидации, переключатель «Все документы организации», подпись консолидации у файла (consolidationLabel.ts), загрузка только в версии «в работе»; - widgets/Sidebar: перечитывание списка консолидаций после создания; - типы api.ts и consolidations.ts, тесты. Закрывает находку I-1 из #98: загрузка из встроенного фронта снова переводит версию INITIATED -> DOCUMENTS_PROCESSING. Спеки, CLAUDE.md и scripts из arm-web не переносятся. После переноса web/src и arm-web@test идентичны (diff -rq пуст). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
import { message } from 'antd';
|
||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||
import { ApiError } from '../../api/client';
|
||
import { hasPendingFiles, listLegalEntityFiles } from '../../services/api/files';
|
||
import type { FileOut } from '../../types/api';
|
||
|
||
const POLL_MS = 5000;
|
||
|
||
/**
|
||
* Файлы рабочего стола консолидации (`consolidationId`) или все файлы ЮЛ (`null`);
|
||
* пока панель открыта и есть UPLOADED/PROCESSING — перечитываем раз в 5 с.
|
||
*/
|
||
export function useLegalEntityFiles(legalEntityId: string | null, consolidationId: string | null, open: boolean) {
|
||
const [files, setFiles] = useState<FileOut[]>([]);
|
||
const [loading, setLoading] = useState(false);
|
||
const requestRef = useRef(0);
|
||
const inFlightRef = useRef(false);
|
||
const errorShownRef = useRef(false);
|
||
|
||
const reload = useCallback(async () => {
|
||
if (!legalEntityId) return;
|
||
const requestId = ++requestRef.current;
|
||
inFlightRef.current = true;
|
||
setLoading(true);
|
||
try {
|
||
const items = await listLegalEntityFiles(legalEntityId, consolidationId);
|
||
if (requestId === requestRef.current) {
|
||
setFiles(items);
|
||
errorShownRef.current = false;
|
||
}
|
||
} catch (e: unknown) {
|
||
// при опросе показываем ошибку один раз на серию; флаг сбрасывается после первого успешного ответа
|
||
if (requestId === requestRef.current && !errorShownRef.current) {
|
||
errorShownRef.current = true;
|
||
message.error(e instanceof ApiError ? e.message : 'Не удалось получить файлы');
|
||
}
|
||
} finally {
|
||
if (requestId === requestRef.current) {
|
||
inFlightRef.current = false;
|
||
setLoading(false);
|
||
}
|
||
}
|
||
}, [legalEntityId, consolidationId]);
|
||
|
||
// смена режима (consolidationId) приходит через reload: он пересоздаётся и перезапускает этот эффект
|
||
useEffect(() => {
|
||
// при смене ЮЛ, режима (консолидация/все) или закрытии панели: старые ответы не применяем, прежние файлы очищаем
|
||
requestRef.current++;
|
||
inFlightRef.current = false;
|
||
errorShownRef.current = false;
|
||
setFiles([]);
|
||
if (open && legalEntityId) void reload();
|
||
}, [open, legalEntityId, reload]);
|
||
|
||
const pending = hasPendingFiles(files);
|
||
useEffect(() => {
|
||
if (!open || !pending) return;
|
||
const timer = window.setInterval(() => {
|
||
if (!inFlightRef.current) void reload(); // не копим параллельные запросы при медленном ответе
|
||
}, POLL_MS);
|
||
return () => window.clearInterval(timer);
|
||
}, [open, pending, reload]);
|
||
|
||
return { files, loading, reload };
|
||
}
|