feat(documents): implement document upload functionality for companies
- Added DocumentItem component to display uploaded documents in CompanyDetailsPage. - Introduced useUploadCompanyDocuments hook for handling document uploads. - Updated CreateCompany form to include document upload capabilities and validation for file types and sizes. - Enhanced API services to support batch document uploads and retrieval of document information. - Improved response handling in the file service for better error management and user feedback.
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
TCompaniesResponse,
|
||||
TCompanyCategoryApiResponse,
|
||||
TCreateCompanyCategoryCredentials,
|
||||
UploadDocumentsData,
|
||||
} from "../types";
|
||||
|
||||
import { ICreateCompanyCredentials } from "../types";
|
||||
@@ -51,6 +52,45 @@ export const companiesService = {
|
||||
const response = await api.get(API_ENDPOINTS.COMPANIES.DETAILS(id));
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads documents and links them to a company in one step. New file IDs are
|
||||
* appended to the company's existing `document_file_ids` (server-side), and
|
||||
* any per-file failures are returned in `data.failed`.
|
||||
*/
|
||||
uploadDocuments: async ({
|
||||
id,
|
||||
files,
|
||||
onProgress,
|
||||
}: {
|
||||
id: string;
|
||||
files: File[];
|
||||
onProgress?: (percent: number) => void;
|
||||
}): Promise<ApiResponse<UploadDocumentsData>> => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
files.forEach((file) => formData.append("files", file, file.name));
|
||||
|
||||
const response = await api.post(
|
||||
API_ENDPOINTS.COMPANIES.DOCUMENTS(id),
|
||||
formData,
|
||||
{
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (!progressEvent.total) return;
|
||||
onProgress?.(
|
||||
Math.round((progressEvent.loaded * 100) / progressEvent.total),
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error("ERROR caught in Companies Services Upload Documents:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
deleteCompany: async (id: string) => {
|
||||
try {
|
||||
return (await api.delete(API_ENDPOINTS.COMPANIES.DELETE(id))).data;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import api from "../axios";
|
||||
import { buildFileDownloadUrl } from "../config";
|
||||
import { API_ENDPOINTS } from "../endpoints";
|
||||
import { BatchUploadResult, FileInfo } from "../types";
|
||||
|
||||
interface IUploadFileParams {
|
||||
file: File;
|
||||
@@ -10,6 +11,23 @@ interface IUploadFileParams {
|
||||
onProgress?: (percent: number) => void;
|
||||
}
|
||||
|
||||
interface IUploadBatchParams {
|
||||
files: File[];
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
description?: string;
|
||||
onProgress?: (percent: number) => void;
|
||||
}
|
||||
|
||||
const trackUploadProgress =
|
||||
(onProgress?: (percent: number) => void) =>
|
||||
(progressEvent: { loaded: number; total?: number }) => {
|
||||
if (!progressEvent.total) return;
|
||||
onProgress?.(
|
||||
Math.round((progressEvent.loaded * 100) / progressEvent.total),
|
||||
);
|
||||
};
|
||||
|
||||
export const fileService = {
|
||||
/**
|
||||
* Uploads a binary file to the shared filestore endpoint and returns the
|
||||
@@ -36,13 +54,7 @@ export const fileService = {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (!progressEvent.total) return;
|
||||
const percent = Math.round(
|
||||
(progressEvent.loaded * 100) / progressEvent.total,
|
||||
);
|
||||
onProgress?.(percent);
|
||||
},
|
||||
onUploadProgress: trackUploadProgress(onProgress),
|
||||
});
|
||||
|
||||
const fileId = response?.data?.file_id as string | undefined;
|
||||
@@ -56,4 +68,72 @@ export const fileService = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads multiple files in one request. Returns the batch body directly
|
||||
* (this endpoint is not wrapped in the `success`/`data` envelope).
|
||||
*/
|
||||
uploadBatch: async ({
|
||||
files,
|
||||
category,
|
||||
tags = [],
|
||||
description,
|
||||
onProgress,
|
||||
}: IUploadBatchParams): Promise<BatchUploadResult> => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
files.forEach((file) => formData.append("files", file, file.name));
|
||||
if (category) formData.append("category", category);
|
||||
tags.forEach((tag) => formData.append("tags", tag));
|
||||
if (description) formData.append("description", description);
|
||||
|
||||
const response = await api.post(
|
||||
API_ENDPOINTS.FILES.UPLOAD_BATCH,
|
||||
formData,
|
||||
{
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
onUploadProgress: trackUploadProgress(onProgress),
|
||||
},
|
||||
);
|
||||
|
||||
return response.data as BatchUploadResult;
|
||||
} catch (error) {
|
||||
console.error("ERROR caught in File Service Upload Batch", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/** Normalized metadata for a stored file, tolerant of envelope/key variations. */
|
||||
getFileInfo: async (fileId: string): Promise<FileInfo> => {
|
||||
try {
|
||||
const response = await api.get(API_ENDPOINTS.FILES.INFO(fileId));
|
||||
const info = response.data?.data ?? response.data ?? {};
|
||||
|
||||
return {
|
||||
file_id: fileId,
|
||||
filename: info.filename ?? info.name ?? "Document",
|
||||
content_type: info.content_type ?? info.contentType ?? "",
|
||||
size: Number(info.size ?? info.length ?? 0),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("ERROR caught in File Service Get File Info", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/** Downloads a stored file as a blob (caller saves it). */
|
||||
downloadFile: (fileId: string) =>
|
||||
api.get<Blob>(API_ENDPOINTS.FILES.DOWNLOAD(fileId), {
|
||||
responseType: "blob",
|
||||
}),
|
||||
|
||||
/** Permanently removes a file from storage. */
|
||||
deleteFile: async (fileId: string) => {
|
||||
try {
|
||||
return (await api.delete(API_ENDPOINTS.FILES.DELETE(fileId))).data;
|
||||
} catch (error) {
|
||||
console.error("ERROR caught in File Service Delete File", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user