Files
igny8/frontend/src/config/import-export.config.tsx
IGNY8 VPS (Salman) 4f7ab9c606 stlyes fixes
2025-12-29 19:52:51 +00:00

223 lines
6.4 KiB
TypeScript

/**
* Global Import/Export Configuration System
* Provides reusable import/export configs for table pages
*
* Usage:
* ```typescript
* import { useImportExport } from '@/config/import-export.config';
*
* const { handleExport, handleImport, ImportModal } = useImportExport({
* exportEndpoint: '/v1/planner/keywords/export/',
* importEndpoint: '/v1/planner/keywords/import_keywords/',
* filename: 'keywords',
* formats: ['csv', 'json'],
* onImportSuccess: () => loadData(),
* });
* ```
*/
import React, { useState, useCallback } from 'react';
import { exportTableData, importTableData, ExportConfig, ImportConfig } from '../utils/table-import-export';
import { Modal } from '../components/ui/modal';
import Button from '../components/ui/button/Button';
import FileInput from '../components/form/input/FileInput';
import Label from '../components/form/Label';
export interface ImportExportConfigOptions {
exportEndpoint: string;
importEndpoint: string;
filename: string;
formats?: ('csv' | 'json')[];
acceptedFormats?: string[];
maxFileSize?: number;
importQueryParams?: Record<string, any>; // Query params for import (e.g., site_id, sector_id)
onImportSuccess?: (result: any) => void;
onExportSuccess?: () => void;
onError?: (error: Error) => void;
}
export interface ImportExportHandlers {
handleExport: (format?: 'csv' | 'json', filters?: Record<string, any>) => Promise<void>;
handleImportClick: () => void;
ImportModal: React.FC;
}
/**
* React hook for import/export functionality
*/
export function useImportExport(
options: ImportExportConfigOptions
): ImportExportHandlers {
const {
exportEndpoint,
importEndpoint,
filename,
formats = ['csv'],
acceptedFormats = ['.csv'],
maxFileSize = 5 * 1024 * 1024, // 5MB default
onImportSuccess,
onExportSuccess,
onError,
} = options;
const [isImportModalOpen, setIsImportModalOpen] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);
const handleExport = useCallback(async (
format: 'csv' | 'json' = 'csv',
filters: Record<string, any> = {}
) => {
setIsProcessing(true);
try {
const exportConfig: ExportConfig = {
endpoint: exportEndpoint,
filename,
format,
filters,
};
await exportTableData(
exportConfig,
(progress) => console.log(progress),
(error) => {
onError?.(error);
throw error;
}
);
onExportSuccess?.();
} catch (error) {
const err = error instanceof Error ? error : new Error('Export failed');
onError?.(err);
throw err;
} finally {
setIsProcessing(false);
}
}, [exportEndpoint, filename, onError, onExportSuccess]);
const handleImport = useCallback(async (file: File) => {
setIsProcessing(true);
try {
const importConfig: ImportConfig = {
endpoint: importEndpoint,
acceptedFormats,
maxFileSize,
queryParams: options.importQueryParams,
onSuccess: (result) => {
onImportSuccess?.(result);
},
};
const result = await importTableData(
file,
importConfig,
(progress) => console.log(progress),
(error) => {
onError?.(error);
throw error;
}
);
setIsImportModalOpen(false);
return result;
} catch (error) {
const err = error instanceof Error ? error : new Error('Import failed');
onError?.(err);
throw err;
} finally {
setIsProcessing(false);
}
}, [importEndpoint, acceptedFormats, maxFileSize, onImportSuccess, onError]);
const handleImportFileChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
await handleImport(file);
// Reset file input
e.target.value = '';
}, [handleImport]);
const ImportModalComponent: React.FC = () => (
<Modal
isOpen={isImportModalOpen}
onClose={() => setIsImportModalOpen(false)}
className="max-w-md"
>
<div className="p-6">
<h2 className="text-xl font-bold mb-6 text-gray-800 dark:text-white">
Import {filename.charAt(0).toUpperCase() + filename.slice(1)}
</h2>
<div className="space-y-4">
<div>
<Label>CSV File</Label>
<FileInput
onChange={handleImportFileChange}
accept={acceptedFormats.join(',')}
disabled={isProcessing}
/>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
Upload a CSV file (max {maxFileSize / 1024 / 1024}MB)
</p>
{filename === 'keywords' && (
<p className="text-xs text-gray-600 dark:text-gray-300 mt-2 p-2 bg-brand-50 dark:bg-brand-900/20 rounded border border-brand-200 dark:border-brand-800">
<strong>Expected columns:</strong> keyword, volume, difficulty, country, status
</p>
)}
</div>
<div className="flex justify-end gap-4 pt-4">
<Button
variant="outline"
onClick={() => setIsImportModalOpen(false)}
disabled={isProcessing}
>
Close
</Button>
</div>
</div>
</div>
</Modal>
);
return {
handleExport,
handleImportClick: () => setIsImportModalOpen(true),
ImportModal: ImportModalComponent,
};
}
/**
* Pre-configured import/export hooks for common modules
*/
export const useKeywordsImportExport = (
onImportSuccess?: () => void,
onError?: (error: Error) => void,
importQueryParams?: Record<string, any>
) => {
return useImportExport({
exportEndpoint: '/v1/planner/keywords/export/',
importEndpoint: '/v1/planner/keywords/import_keywords/',
filename: 'keywords',
formats: ['csv', 'json'],
acceptedFormats: ['.csv'],
maxFileSize: 5 * 1024 * 1024,
importQueryParams,
onImportSuccess,
onError,
});
};
export const useClustersImportExport = (onImportSuccess?: () => void, onError?: (error: Error) => void) => {
return useImportExport({
exportEndpoint: '/v1/planner/clusters/export/',
importEndpoint: '/v1/planner/clusters/import_clusters/',
filename: 'clusters',
formats: ['csv', 'json'],
acceptedFormats: ['.csv'],
maxFileSize: 5 * 1024 * 1024,
onImportSuccess,
onError,
});
};