import { ApiError, api, apiFetch, apiFetchBlob } from '../../api/client'; import type { FileOut, Page, UploadResult } from '../../types/api'; export function isUploadResult(body: unknown): body is UploadResult { return ( typeof body === 'object' && body !== null && Array.isArray((body as UploadResult).accepted) && Array.isArray((body as UploadResult).rejected) ); } /** Маршрут файлов ЮЛ: с консолидацией — файлы в контексте консолидации, без неё — файлы ЮЛ (design D-2). */ export function filesPath(legalEntityId: string, consolidationId: string | null): string { return consolidationId ? `/consolidations/${encodeURIComponent(consolidationId)}/legal-entities/${legalEntityId}/files` : `/legal-entities/${legalEntityId}/files`; } /** Загрузка файлов ЮЛ одним запросом (в консолидацию или без контекста); 422 «ни один не принят» возвращается как результат, прочие ошибки — throw. */ export async function uploadFiles(legalEntityId: string, files: File[], consolidationId: string | null): Promise { const form = new FormData(); for (const file of files) { form.append('files', file); } try { return await apiFetch(filesPath(legalEntityId, consolidationId), { method: 'POST', body: form }); } catch (e: unknown) { if (e instanceof ApiError && e.status === 422 && isUploadResult(e.body)) { return e.body; } throw e; } } export function summarizeUpload(result: UploadResult): { accepted: string[]; rejected: { name: string; reason: string }[] } { return { accepted: result.accepted.map((a) => a.name), rejected: result.rejected.map((r) => ({ name: r.name, reason: r.message })), }; } /** Размер страницы: столько файлов панель загружает сразу и при каждой прокрутке до конца списка. Не больше 200 — `MAX_PAGE_LIMIT` бэка. */ export const FILES_PAGE_LIMIT = 200; /** Страница файлов ЮЛ одним запросом. Бэк сортирует по дате загрузки и `id` — порядок стабилен. */ export function listLegalEntityFiles(le: string, cons: string | null, offset = 0, limit = FILES_PAGE_LIMIT) { return apiFetch>(`${filesPath(le, cons)}?limit=${limit}&offset=${offset}`); } /** * Файлы ЮЛ начиная с номера `from` запросами по `FILES_PAGE_LIMIT`, пока не наберём `count` * или файлы не кончатся. Пустая страница раньше `total` — файлы удалили между запросами, дальше не идём. */ export async function listLegalEntityFilesFrom( le: string, cons: string | null, from: number, count: number, ): Promise<{ items: FileOut[]; total: number }> { const items: FileOut[] = []; let total = 0; let lastPageEmpty = false; do { const page = await listLegalEntityFiles(le, cons, from + items.length); items.push(...page.items); total = page.total; lastPageEmpty = page.items.length === 0; } while (items.length < count && from + items.length < total && !lastPageEmpty); return { items, total }; } export function hasPendingFiles(files: FileOut[]): boolean { return files.some((f) => f.status === 'UPLOADED' || f.status === 'PROCESSING'); } export function fetchFileContent(fileId: string): Promise<{ blob: Blob; filename: string | null }> { return apiFetchBlob(`/files/${fileId}/content?disposition=inline`); } export function getFile(id: string): Promise { return apiFetch(`/files/${id}`); } export function removeFile(fileId: string) { return api.del(`/files/${fileId}`); }