/** * Export Button Component * * Reusable component for exporting data to CSV or Excel */ 'use client'; import { useState, useRef, useEffect } from 'react'; import { useTranslations, useLocale } from 'next-intl'; import { Download, FileSpreadsheet, FileText, ChevronDown, Lock } from 'lucide-react'; import { useSubscription } from '@/lib/context/subscription-context'; import { useToast } from '@/components/ui/toast'; interface ExportButtonProps { data: T[]; filename: string; columns: { key: keyof T | string; label: string; format?: (value: any, row: T) => string; }[]; disabled?: boolean; } export default function ExportButton>({ data, filename, columns, disabled = false, }: ExportButtonProps) { const { toast } = useToast(); const t = useTranslations('components.exportButton'); const locale = useLocale(); const dateLocale = locale === 'fr' ? 'fr-FR' : 'en-US'; const { hasFeature } = useSubscription(); const canExport = hasFeature('csv_export'); const [isOpen, setIsOpen] = useState(false); const [isExporting, setIsExporting] = useState(false); const dropdownRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const formatValue = (value: any): string => { if (value === null || value === undefined) return ''; if (typeof value === 'boolean') return value ? t('boolean.true') : t('boolean.false'); if (value instanceof Date) return value.toLocaleDateString(dateLocale); if (typeof value === 'object') return JSON.stringify(value); return String(value); }; const getNestedValue = (obj: T, path: string): any => { return path.split('.').reduce((acc, part) => acc?.[part], obj as any); }; const generateCSV = (): string => { const headers = columns.map(col => `"${col.label.replace(/"/g, '""')}"`).join(';'); const rows = data.map(row => { return columns .map(col => { const value = getNestedValue(row, col.key as string); const formattedValue = col.format ? col.format(value, row) : formatValue(value); return `"${formattedValue.replace(/"/g, '""')}"`; }) .join(';'); }); return [headers, ...rows].join('\n'); }; const downloadFile = (content: string, mimeType: string, extension: string) => { const BOM = '\uFEFF'; const blob = new Blob([BOM + content], { type: `${mimeType};charset=utf-8` }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${filename}_${new Date().toISOString().split('T')[0]}.${extension}`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }; const handleExportCSV = async () => { setIsExporting(true); try { const csv = generateCSV(); downloadFile(csv, 'text/csv', 'csv'); } catch (error) { console.error('Export CSV error:', error); toast.error(t('errorCsv')); } finally { setIsExporting(false); setIsOpen(false); } }; const handleExportExcel = async () => { setIsExporting(true); try { const xmlHeader = ` `; const columnWidths = columns .map(() => '') .join(''); const headerRow = ` ${columns.map(col => `${escapeXml(col.label)}`).join('')} `; const dataRows = data .map(row => { const cells = columns .map(col => { const value = getNestedValue(row, col.key as string); const formattedValue = col.format ? col.format(value, row) : formatValue(value); const type = typeof value === 'number' ? 'Number' : 'String'; return `${escapeXml(formattedValue)}`; }) .join(''); return `${cells}`; }) .join(''); const xmlFooter = `
`; const xmlContent = xmlHeader + columnWidths + headerRow + dataRows + xmlFooter; downloadFile(xmlContent, 'application/vnd.ms-excel', 'xls'); } catch (error) { console.error('Export Excel error:', error); toast.error(t('errorExcel')); } finally { setIsExporting(false); setIsOpen(false); } }; const escapeXml = (str: string): string => { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; return (
{/* Dropdown Menu */} {isOpen && !isExporting && canExport && (

{t('rowsToExport', { count: data.length })}

)}
); }