Initial commit
This commit is contained in:
184
frontend/src/utils/export.ts
Normal file
184
frontend/src/utils/export.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
// Export utilities for CSV and PDF
|
||||
|
||||
export interface ExportData {
|
||||
headers: string[];
|
||||
rows: (string | number)[][];
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data to CSV
|
||||
*/
|
||||
export function exportToCSV(data: ExportData, filename: string = 'export.csv'): void {
|
||||
const { headers, rows } = data;
|
||||
|
||||
// Create CSV content
|
||||
const csvContent = [
|
||||
headers.join(','),
|
||||
...rows.map((row) => row.map((cell) => `"${cell}"`).join(',')),
|
||||
].join('\n');
|
||||
|
||||
// Create blob and download
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export table data to CSV
|
||||
*/
|
||||
export function exportTableToCSV(
|
||||
tableId: string,
|
||||
filename: string = 'table-export.csv'
|
||||
): void {
|
||||
const table = document.getElementById(tableId) as HTMLTableElement;
|
||||
if (!table) {
|
||||
console.error('Table not found:', tableId);
|
||||
return;
|
||||
}
|
||||
|
||||
const headers: string[] = [];
|
||||
const rows: (string | number)[][] = [];
|
||||
|
||||
// Get headers
|
||||
const headerRow = table.querySelector('thead tr');
|
||||
if (headerRow) {
|
||||
headerRow.querySelectorAll('th').forEach((th) => {
|
||||
headers.push(th.textContent?.trim() || '');
|
||||
});
|
||||
}
|
||||
|
||||
// Get rows
|
||||
table.querySelectorAll('tbody tr').forEach((tr) => {
|
||||
const row: (string | number)[] = [];
|
||||
tr.querySelectorAll('td').forEach((td) => {
|
||||
row.push(td.textContent?.trim() || '');
|
||||
});
|
||||
if (row.length > 0) {
|
||||
rows.push(row);
|
||||
}
|
||||
});
|
||||
|
||||
exportToCSV({ headers, rows }, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data to JSON
|
||||
*/
|
||||
export function exportToJSON(data: any, filename: string = 'export.json'): void {
|
||||
const jsonContent = JSON.stringify(data, null, 2);
|
||||
const blob = new Blob([jsonContent], { type: 'application/json;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate PDF using browser print (simple approach)
|
||||
* For more advanced PDF generation, consider using libraries like jsPDF or pdfmake
|
||||
*/
|
||||
export function exportToPDF(
|
||||
elementId: string,
|
||||
filename: string = 'export.pdf',
|
||||
title?: string
|
||||
): void {
|
||||
const element = document.getElementById(elementId);
|
||||
if (!element) {
|
||||
console.error('Element not found:', elementId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new window for printing
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) {
|
||||
console.error('Could not open print window');
|
||||
return;
|
||||
}
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>${title || 'Export'}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${element.innerHTML}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
printWindow.focus();
|
||||
|
||||
// Wait for content to load, then print
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
// Optionally close after printing
|
||||
// printWindow.close();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format data for export from DataTable component
|
||||
*/
|
||||
export function formatDataForExport<T extends Record<string, any>>(
|
||||
data: T[],
|
||||
columns: Array<{ key: string; header: string; render?: (row: T) => any }>
|
||||
): ExportData {
|
||||
const headers = columns.map((col) => col.header);
|
||||
const rows = data.map((row) =>
|
||||
columns.map((col) => {
|
||||
if (col.render) {
|
||||
// For rendered cells, try to extract meaningful text
|
||||
const rendered = col.render(row);
|
||||
if (typeof rendered === 'string' || typeof rendered === 'number') {
|
||||
return rendered;
|
||||
}
|
||||
// For React elements, fall back to raw value
|
||||
return String(row[col.key] || '');
|
||||
}
|
||||
const value = row[col.key];
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
return String(value);
|
||||
})
|
||||
);
|
||||
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
29
frontend/src/utils/format.ts
Normal file
29
frontend/src/utils/format.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// Formatting utilities
|
||||
export function formatCurrency(value: number, currency: string = 'USD'): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
export function formatNumber(value: number, decimals: number = 2): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
minimumFractionDigits: decimals,
|
||||
maximumFractionDigits: decimals,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
export function formatPercentage(value: number, decimals: number = 2): string {
|
||||
return `${formatNumber(value, decimals)}%`;
|
||||
}
|
||||
|
||||
export function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user