feat(tender-details): add documents section and enhance document handling

- Introduced a new documents section in the tender details page for better organization of related documents.
- Updated the API service to support downloading individual and bulk documents.
- Enhanced the TenderDetailsContent component to manage document data and loading states effectively.
- Refactored tender navigation items to include the new documents section.
- Improved type definitions for better clarity and type safety in document handling.
This commit is contained in:
AmirReza Jamali
2026-05-11 17:41:38 +03:30
parent 88bc939956
commit 2477215fce
9 changed files with 282 additions and 4 deletions
+32
View File
@@ -1,4 +1,5 @@
import { ILabelValue } from "@/types/shared";
import type { AxiosResponse } from "axios";
import * as moment from "moment";
import { z } from "zod";
export const unixToDate = ({
@@ -160,4 +161,35 @@ export const formatFileSize = (bytes: number) => {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};
/** Saves a blob from an axios GET with `responseType: "blob"` (uses Content-Disposition when present). */
export const saveBlobFromAxiosResponse = (
response: AxiosResponse<Blob>,
fallbackFilename: string,
) => {
const disposition = response.headers["content-disposition"] as
| string
| undefined;
let filename = fallbackFilename;
if (disposition) {
const utf8Match = /filename\*=UTF-8''([^;\n]+)/i.exec(disposition);
const asciiMatch = /filename="([^"]+)"/i.exec(disposition);
const raw = utf8Match?.[1] ?? asciiMatch?.[1];
if (raw) {
try {
filename = decodeURIComponent(raw.replace(/['"]/g, ""));
} catch {
filename = raw;
}
}
}
const url = URL.createObjectURL(response.data);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = filename;
document.body.appendChild(anchor);
anchor.click();
anchor.remove();
URL.revokeObjectURL(url);
};