'use client' import React, { useState } from 'react' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { Label } from '@/components/ui/label' import { Alert, AlertDescription } from '@/components/ui/alert' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Progress } from '@/components/ui/progress' import { Download, FileText, CheckCircle, AlertTriangle } from 'lucide-react' import Papa from 'papaparse' interface ExportColumn { key: string label: string transform?: (value: any, row?: any) => string required?: boolean } interface CsvExportProps { title: string description: string columns: ExportColumn[] onExport: ( selectedColumns: string[], filters?: any, onProgress?: (progress: number) => void ) => Promise<{ success: boolean data?: any[] message?: string }> filters?: { label: string key: string options: { value: string; label: string }[] }[] maxRecords?: number filename?: string children?: React.ReactNode } export default function CsvExport({ title, description, columns, onExport, filters = [], maxRecords = 10000, filename, children }: CsvExportProps) { const [isOpen, setIsOpen] = useState(false) const [selectedColumns, setSelectedColumns] = useState( columns.filter(col => col.required).map(col => col.key) ) const [filterValues, setFilterValues] = useState>({}) const [exporting, setExporting] = useState(false) const [progress, setProgress] = useState(0) const [result, setResult] = useState<{ success: boolean message?: string recordCount?: number } | null>(null) const handleColumnToggle = (columnKey: string, checked: boolean) => { const column = columns.find(col => col.key === columnKey) if (column?.required) return // Don't allow unchecking required columns if (checked) { setSelectedColumns(prev => [...prev, columnKey]) } else { setSelectedColumns(prev => prev.filter(key => key !== columnKey)) } } const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedColumns(columns.map(col => col.key)) } else { setSelectedColumns(columns.filter(col => col.required).map(col => col.key)) } } const handleFilterChange = (filterKey: string, value: string) => { setFilterValues(prev => ({ ...prev, [filterKey]: value })) } const generateFilename = () => { if (filename) return filename const timestamp = new Date().toISOString().split('T')[0] return `${title.toLowerCase().replace(/\s+/g, '_')}_export_${timestamp}.csv` } const downloadCsv = (data: any[]) => { const selectedColumnData = columns.filter(col => selectedColumns.includes(col.key)) // Prepare headers const headers = selectedColumnData.map(col => col.label) // Prepare data rows const rows = data.map(row => { return selectedColumnData.map(col => { const value = row[col.key] if (col.transform) { return col.transform(value, row) } return value?.toString() || '' }) }) // Generate CSV const csvContent = Papa.unparse([headers, ...rows]) // 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', generateFilename()) link.style.visibility = 'hidden' document.body.appendChild(link) link.click() document.body.removeChild(link) } const handleExport = async () => { if (selectedColumns.length === 0) { setResult({ success: false, message: 'Please select at least one column to export' }) return } setExporting(true) setProgress(0) setResult(null) try { const exportResult = await onExport(selectedColumns, filterValues, setProgress) if (exportResult.success && exportResult.data) { downloadCsv(exportResult.data) setResult({ success: true, message: 'Export completed successfully', recordCount: exportResult.data.length }) } else { setResult({ success: false, message: exportResult.message || 'Export failed' }) } } catch (error) { setResult({ success: false, message: error instanceof Error ? error.message : 'Export failed' }) } finally { setExporting(false) setProgress(0) } } const resetDialog = () => { setResult(null) setProgress(0) setFilterValues({}) } const selectedCount = selectedColumns.length const allSelected = selectedCount === columns.length const someSelected = selectedCount > 0 && selectedCount < columns.length return ( { setIsOpen(open) if (!open) resetDialog() }}> {children || ( )} {title} {description}
{/* Filters */} {filters.length > 0 && (

Filters

{filters.map(filter => (
))}
)} {/* Column Selection */}

Select Columns to Export

{ if (ref && ref instanceof HTMLButtonElement) { (ref as any).indeterminate = someSelected } }} onCheckedChange={handleSelectAll} />
{columns.map(column => (
handleColumnToggle(column.key, !!checked)} disabled={column.required} />
))}
{/* Export Info */}

Export Information

  • • {selectedCount} columns selected
  • • Maximum {maxRecords.toLocaleString()} records
  • • File format: CSV (UTF-8)
  • • Filename: {generateFilename()}
{/* Export Progress */} {exporting && (
Exporting... {Math.round(progress)}%
)} {/* Export Result */} {result && ( {result.success ? ( ) : ( )}

{result.message}

{result.recordCount !== undefined && (

Exported {result.recordCount.toLocaleString()} records

)}
)} {/* Action Buttons */}
) }