From 767a7657b1b2d255aaa37e95a655ba2f60a948c0 Mon Sep 17 00:00:00 2001 From: Daniel Bedeleanu Date: Wed, 22 Apr 2026 17:43:16 +0300 Subject: [PATCH] feat(5-03-04): create useExport hook for managing export API calls and file downloads --- frontend/hooks/useExport.ts | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 frontend/hooks/useExport.ts diff --git a/frontend/hooks/useExport.ts b/frontend/hooks/useExport.ts new file mode 100644 index 00000000..45102447 --- /dev/null +++ b/frontend/hooks/useExport.ts @@ -0,0 +1,118 @@ +import { useState } from "react"; +import axios from "axios"; + +interface UseExportReturn { + exportSnapshot: (format: "csv" | "xlsx") => Promise; + exportAuditTrail: (format: "csv" | "xlsx") => Promise; + isLoading: boolean; + error: string | null; +} + +/** + * Custom hook for managing inventory export operations. + * Handles API calls, file downloads, and error management. + * + * @returns Object with export functions and state + */ +export function useExport(): UseExportReturn { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + /** + * Extract filename from Content-Disposition header. + * Handles both inline and attachment disposition. + */ + const extractFilenameFromHeader = (contentDisposition: string): string => { + const match = contentDisposition.match(/filename="?([^"]+)"?/); + return match ? match[1] : `export_${Date.now()}`; + }; + + /** + * Trigger file download from blob response. + */ + const downloadFile = (blob: Blob, filename: string) => { + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + /** + * Export inventory snapshot in specified format. + */ + const exportSnapshot = async (format: "csv" | "xlsx"): Promise => { + setIsLoading(true); + setError(null); + + try { + const response = await axios.post( + `/api/admin/exports/inventory-snapshot?format=${format}`, + {}, + { + responseType: "blob", + } + ); + + // Extract filename from response headers + const contentDisposition = response.headers["content-disposition"] || ""; + const filename = contentDisposition + ? extractFilenameFromHeader(contentDisposition) + : `inventory_snapshot_${new Date().toISOString().split("T")[0]}.${format}`; + + // Trigger download + downloadFile(response.data, filename); + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : "Failed to export inventory snapshot"; + setError(errorMessage); + throw err; + } finally { + setIsLoading(false); + } + }; + + /** + * Export audit trail in specified format. + */ + const exportAuditTrail = async (format: "csv" | "xlsx"): Promise => { + setIsLoading(true); + setError(null); + + try { + const response = await axios.post( + `/api/admin/exports/audit-trail?format=${format}`, + {}, + { + responseType: "blob", + } + ); + + // Extract filename from response headers + const contentDisposition = response.headers["content-disposition"] || ""; + const filename = contentDisposition + ? extractFilenameFromHeader(contentDisposition) + : `audit_trail_${new Date().toISOString().split("T")[0]}.${format}`; + + // Trigger download + downloadFile(response.data, filename); + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : "Failed to export audit trail"; + setError(errorMessage); + throw err; + } finally { + setIsLoading(false); + } + }; + + return { + exportSnapshot, + exportAuditTrail, + isLoading, + error, + }; +}