arm-web/src/services/api/files.ts
Andrey Epifancev 79278589d6 api: загрузка и список файлов по маршрутам консолидации (#26)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 00:53:59 +04:00

59 lines
2.4 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 { ApiError, 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<UploadResult> {
const form = new FormData();
for (const file of files) {
form.append('files', file);
}
try {
return await apiFetch<UploadResult>(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 })),
};
}
export async function listLegalEntityFiles(legalEntityId: string, consolidationId: string | null): Promise<FileOut[]> {
const page = await apiFetch<Page<FileOut>>(`${filesPath(legalEntityId, consolidationId)}?limit=100`);
return page.items;
}
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<FileOut> {
return apiFetch<FileOut>(`/files/${id}`);
}