Commit 6420b58b authored by ThinhNC's avatar ThinhNC

Merge fix/export-custom-filename: pass custom fileName and customFileName to...

Merge fix/export-custom-filename: pass custom fileName and customFileName to export endpoint and parse Content-Disposition on download
parents a4285bee d4e84772
Pipeline #53755 passed with stage
in 1 minute and 21 seconds
...@@ -115,10 +115,12 @@ export function CreateExportModal({ ...@@ -115,10 +115,12 @@ export function CreateExportModal({
e.preventDefault(); e.preventDefault();
if (!selectedJobId) return; if (!selectedJobId) return;
const trimmedFileName = customFileName.trim();
setCustomFileName("");
await createExportMutation.mutateAsync({ await createExportMutation.mutateAsync({
jobId: selectedJobId, jobId: selectedJobId,
exportType: selectedFormat, exportType: selectedFormat,
fileName: customFileName.trim() ? customFileName.trim() : undefined, fileName: trimmedFileName ? trimmedFileName : undefined,
}); });
onClose(); onClose();
}; };
......
...@@ -19,6 +19,7 @@ export const createExportSchema = z.object({ ...@@ -19,6 +19,7 @@ export const createExportSchema = z.object({
jobId: z.string().uuid("Mã Job không hợp lệ"), jobId: z.string().uuid("Mã Job không hợp lệ"),
exportType: exportTypeEnum.default("JSON"), exportType: exportTypeEnum.default("JSON"),
fileName: z.string().trim().optional(), fileName: z.string().trim().optional(),
customFileName: z.string().trim().optional(),
}); });
export const exportQuerySchema = z.object({ export const exportQuerySchema = z.object({
......
...@@ -135,6 +135,7 @@ export class CrawlExportService { ...@@ -135,6 +135,7 @@ export class CrawlExportService {
const response = await apiClient.post<ApiResponse<CrawlExport>>(`/crawl-jobs/${jobId}/exports`, { const response = await apiClient.post<ApiResponse<CrawlExport>>(`/crawl-jobs/${jobId}/exports`, {
exportType: dto.exportType, exportType: dto.exportType,
fileName: dto.fileName, fileName: dto.fileName,
customFileName: dto.fileName,
}); });
if (response.data?.data) { if (response.data?.data) {
const created = response.data.data; const created = response.data.data;
...@@ -178,8 +179,21 @@ export class CrawlExportService { ...@@ -178,8 +179,21 @@ export class CrawlExportService {
responseType: "blob", responseType: "blob",
}); });
const headerContentType = response.headers?.["content-type"]; const headerContentType = response.headers?.["content-type"];
let downloadName = defaultFilename;
const contentDisposition = response.headers?.["content-disposition"];
if (typeof contentDisposition === "string") {
const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
if (utf8Match && utf8Match[1]) {
downloadName = decodeURIComponent(utf8Match[1]);
} else {
const match = contentDisposition.match(/filename="?([^";]+)"?/i);
if (match && match[1]) {
downloadName = match[1];
}
}
}
downloadBlob(response.data, { downloadBlob(response.data, {
filename: defaultFilename, filename: downloadName,
mimeType: typeof headerContentType === "string" ? headerContentType : "application/octet-stream", mimeType: typeof headerContentType === "string" ? headerContentType : "application/octet-stream",
}); });
} catch { } catch {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment