marco.pms.web/src/utils/tableExportUtils.jsx

187 lines
6.4 KiB
JavaScript

import * as XLSX from "xlsx";
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
/**
* Export JSON data to CSV
* @param {Array} data - Array of objects to export
* @param {string} fileName - File name without extension
*/
export const exportToCSV = (data, fileName = "data") => {
const headers = Object.keys(data[0] || {});
const csvContent = [
headers.join(","), // header row
...data.map(row => headers.map(field => `"${row[field] ?? ""}"`).join(",")),
].join("\n");
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", `${fileName}.csv`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
/**
* Export JSON data to Excel
* @param {Array} data - Array of objects to export
* @param {string} fileName - File name without extension
*/
export const exportToExcel = (data, fileName = "data") => {
const ws = XLSX.utils.json_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, `${fileName}.xlsx`);
};
/**
* Export JSON data to PDF using pdf-lib
* @param {Array} data - Array of objects to export
* @param {string} fileName - File name for the PDF (optional)
*/
export const exportToPDF = async (data, fileName = "data") => {
if (!data || data.length === 0) return;
// Create a new PDF document
const pdfDoc = await PDFDocument.create();
// Set up the font
const font = await pdfDoc.embedFont(StandardFonts.Helvetica); // Use Helvetica font
// Calculate column widths dynamically based on data content
const headers = Object.keys(data[0]);
const rows = data.map(item => headers.map(header => item[header] || ''));
const getMaxColumnWidth = (columnIndex) => {
let maxWidth = font.widthOfTextAtSize(headers[columnIndex], 12);
rows.forEach(row => {
const cellText = row[columnIndex].toString();
maxWidth = Math.max(maxWidth, font.widthOfTextAtSize(cellText, 10));
});
return maxWidth + 10; // Padding for better spacing
};
const columnWidths = headers.map((_, index) => getMaxColumnWidth(index));
const tableX = 30; // X-coordinate for the table start
const rowHeight = 20; // Height of each row (can be adjusted)
const maxPageHeight = 750; // Max available height for content (before a new page is added)
const pageMargin = 30; // Margin from the top of the page
let tableY = maxPageHeight; // Start Y position for the table
const maxPageWidth = 600; // Max available width for content (before a new page is added)
// Add the headers and rows to the page
const addHeadersToPage = (page, scaleFactor) => {
let xPosition = tableX;
headers.forEach((header, index) => {
page.drawText(header, {
x: xPosition,
y: tableY,
font,
size: 12 * scaleFactor, // Scale the header font size
color: rgb(0, 0, 0),
});
xPosition += columnWidths[index] * scaleFactor; // Adjust X position based on scaling
});
tableY -= rowHeight; // Move down after adding headers
};
// Add a new page and reset the table position
const addNewPage = (scaleFactor) => {
const page = pdfDoc.addPage([600, 800]);
tableY = maxPageHeight; // Reset Y position for the new page
addHeadersToPage(page, scaleFactor); // Re-add headers to the new page
return page;
};
// Create the first page and add headers
let page = pdfDoc.addPage([600, 800]);
// Check if the content fits within the page width, scale if necessary
const checkPageWidth = (row) => {
let totalWidth = columnWidths.reduce((acc, width) => acc + width, 0);
let scaleFactor = 1;
if (totalWidth > maxPageWidth) {
scaleFactor = maxPageWidth / totalWidth; // Scale down if necessary
}
return scaleFactor;
};
// Function to check for page breaks when adding a new row
const checkPageBreak = () => {
if (tableY - rowHeight < pageMargin) {
page = addNewPage(scaleFactor); // Add a new page if there is no space for the next row
}
};
// Add rows to the PDF with pagination and horizontal scaling
rows.forEach(row => {
checkPageBreak(); // Check for page break before adding each row
const scaleFactor = checkPageWidth(row); // Get the scaling factor for the row
// Add headers to the first page and each new page with the same scale factor
if (tableY === maxPageHeight) {
addHeadersToPage(page, scaleFactor); // Add headers only on the first page
}
let xPosition = tableX;
row.forEach((value, index) => {
page.drawText(value.toString(), {
x: xPosition,
y: tableY,
font,
size: 10 * scaleFactor, // Scale the font size
color: rgb(0, 0, 0),
});
xPosition += columnWidths[index] * scaleFactor; // Adjust X position based on scaling
});
tableY -= rowHeight; // Move down to the next row position
});
// Serialize the document to bytes
const pdfBytes = await pdfDoc.save();
// Trigger a download of the PDF
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `${fileName}.pdf`;
link.click();
};
/**
* Print the HTML table by accepting the table element or a reference.
* @param {HTMLElement} table - The table element (or ref) to print
*/
export const printTable = (table) => {
if (table) {
const newWindow = window.open("", "", "width=600,height=600"); // Open a new window
// Inject styles for the table and body
newWindow.document.write("<html><head><title>Print Table</title>");
const style = document.createElement('style');
style.innerHTML = `
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
`;
newWindow.document.head.appendChild(style);
newWindow.document.write("</head><body>");
newWindow.document.write(table.outerHTML); // Write the table HTML to the new window
newWindow.document.write("</body></html>");
newWindow.document.close(); // Close the document stream
// Wait for the document to load before triggering print
newWindow.onload = () => {
newWindow.print(); // Trigger the print dialog after the content is loaded
};
}
};