Files
igny8/frontend/src/utils/table-import-export.ts
alorig 8d47d6a555 Revert "sad"
This reverts commit 550a8f26a2.
2025-11-30 06:14:54 +05:00

252 lines
6.8 KiB
TypeScript

/**
* Global Table Import/Export Utility
* Handles CSV/JSON export and import for any table-based page
*
* Usage:
* ```typescript
* import { useTableImportExport } from '../../utils/table-import-export';
*
* const { handleExport, handleImport } = useTableImportExport({
* endpoint: '/v1/planner/keywords/export/',
* filename: 'keywords',
* columns: columns, // Column config from TablePageTemplate
* filters: filterValues, // Current filter values
* });
* ```
*/
import { API_BASE_URL } from '../services/api';
export interface ExportConfig {
endpoint: string;
filename: string;
format?: 'csv' | 'json';
columns?: Array<{ key: string; label: string }>;
filters?: Record<string, any>;
}
export interface ImportConfig {
endpoint: string;
acceptedFormats?: string[];
maxFileSize?: number;
queryParams?: Record<string, any>; // Additional query params (e.g., site_id, sector_id)
onSuccess?: (result: any) => void;
onError?: (error: Error) => void;
}
/**
* Build export URL with filters as query parameters
*/
export const buildExportUrl = (config: ExportConfig): string => {
const params = new URLSearchParams();
// Add filters as query params
if (config.filters) {
Object.entries(config.filters).forEach(([key, value]) => {
if (value !== '' && value !== null && value !== undefined) {
params.append(key, String(value));
}
});
}
const queryString = params.toString();
const endpoint = config.endpoint.endsWith('/')
? config.endpoint
: `${config.endpoint}/`;
const fullUrl = `${API_BASE_URL}${endpoint}${queryString ? `?${queryString}` : ''}`;
return fullUrl;
};
/**
* Export table data to CSV or JSON
*/
export const exportTableData = async (
config: ExportConfig,
onProgress?: (progress: string) => void,
onError?: (error: Error) => void
): Promise<void> => {
const format = config.format || 'csv';
const url = buildExportUrl(config);
onProgress?.(`Exporting ${format.toUpperCase()}...`);
try {
const response = await fetch(url, {
method: 'GET',
credentials: 'include',
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Export failed: ${response.statusText} - ${errorText}`);
}
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `${config.filename}.${format}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
onProgress?.(`Export successful: ${config.filename}.${format}`);
} catch (error) {
const err = error instanceof Error ? error : new Error('Export failed');
onError?.(err);
throw err;
}
};
/**
* Import table data from file
*/
export const importTableData = async (
file: File,
config: ImportConfig,
onProgress?: (progress: string) => void,
onError?: (error: Error) => void
): Promise<any> => {
// Validate file format
const acceptedFormats = config.acceptedFormats || ['.csv'];
const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase();
if (!acceptedFormats.includes(fileExtension)) {
const error = new Error(
`Invalid file format. Accepted formats: ${acceptedFormats.join(', ')}`
);
onError?.(error);
throw error;
}
// Validate file size
const maxSize = config.maxFileSize || 5 * 1024 * 1024; // 5MB default
if (file.size > maxSize) {
const error = new Error(`File size exceeds ${maxSize / 1024 / 1024}MB limit`);
onError?.(error);
throw error;
}
onProgress?.(`Importing ${file.name}...`);
const formData = new FormData();
formData.append('file', file);
try {
const endpoint = config.endpoint.endsWith('/')
? config.endpoint
: `${config.endpoint}/`;
// Build query string from queryParams
const params = new URLSearchParams();
if (config.queryParams) {
Object.entries(config.queryParams).forEach(([key, value]) => {
if (value !== '' && value !== null && value !== undefined) {
params.append(key, String(value));
}
});
}
const queryString = params.toString();
const url = `${API_BASE_URL}${endpoint}${queryString ? `?${queryString}` : ''}`;
const response = await fetch(url, {
method: 'POST',
body: formData,
credentials: 'include',
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Import failed: ${response.statusText} - ${errorText}`);
}
const result = await response.json();
onProgress?.(`Import successful: ${result.imported || 0} rows imported`);
config.onSuccess?.(result);
return result;
} catch (error) {
const err = error instanceof Error ? error : new Error('Import failed');
onError?.(err);
throw err;
}
};
/**
* React hook for table import/export functionality
*/
export const useTableImportExport = (exportConfig: ExportConfig) => {
const handleExport = async (
format: 'csv' | 'json' = 'csv',
onProgress?: (progress: string) => void,
onError?: (error: Error) => void
) => {
await exportTableData(
{ ...exportConfig, format },
onProgress,
onError
);
};
const handleImport = async (
file: File,
importConfig: ImportConfig,
onProgress?: (progress: string) => void,
onError?: (error: Error) => void
) => {
return await importTableData(file, importConfig, onProgress, onError);
};
return {
handleExport,
handleImport,
};
};
/**
* Generate CSV from table data (client-side fallback)
* Used when backend doesn't provide export endpoint
*/
export const generateCSVFromTable = (
data: any[],
columns: Array<{ key: string; label: string }>,
filename: string = 'export'
): void => {
// Create header row
const headers = columns.map((col) => col.label).join(',');
// Create data rows
const rows = data.map((row) =>
columns
.map((col) => {
const value = row[col.key];
// Escape commas and quotes in CSV
const stringValue = value?.toString() || '';
if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
return `"${stringValue.replace(/"/g, '""')}"`;
}
return stringValue;
})
.join(',')
);
const csvContent = [headers, ...rows].join('\n');
// Create download
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `${filename}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
};