'use client'; import { useState, useCallback, useRef, useEffect } from 'react'; import { Copy, Check, Download, ChevronDown } from 'lucide-react'; import { StructuredLog } from './index'; import { ExportFormat, copyToClipboard, formatLogsForExport, downloadAsFile, getExportFileInfo, } from '@/lib/copy-utils'; export interface CopyToolbarProps { logs: StructuredLog[]; selectedIndices?: Set; onCopySuccess?: () => void; } const FORMAT_OPTIONS: { value: ExportFormat; label: string }[] = [ { value: 'text', label: 'Text' }, { value: 'json', label: 'JSON' }, { value: 'csv', label: 'CSV' }, ]; /** * Toast notification component for copy success feedback */ function Toast({ message, isVisible, onClose, }: { message: string; isVisible: boolean; onClose: () => void; }) { useEffect(() => { if (isVisible) { const timer = setTimeout(onClose, 2500); return () => clearTimeout(timer); } }, [isVisible, onClose]); if (!isVisible) return null; return (
{message}
); } /** * Format dropdown component */ function FormatDropdown({ value, onChange, disabled, }: { value: ExportFormat; onChange: (format: ExportFormat) => void; disabled?: boolean; }) { const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); // Close dropdown when clicking outside useEffect(() => { function handleClickOutside(event: MouseEvent) { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } } if (isOpen) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [isOpen]); const selectedOption = FORMAT_OPTIONS.find((opt) => opt.value === value); return (
{isOpen && (
{FORMAT_OPTIONS.map((option) => ( ))}
)}
); } /** * CopyToolbar - Toolbar for copying and exporting logs * * Features: * - Copy All button to copy all logs * - Copy Selected button to copy only selected logs * - Format dropdown to choose Text, JSON, or CSV * - Download button for exporting as file * - Toast notification on copy success */ export default function CopyToolbar({ logs, selectedIndices = new Set(), onCopySuccess, }: CopyToolbarProps) { const [format, setFormat] = useState('text'); const [toastMessage, setToastMessage] = useState(''); const [showToast, setShowToast] = useState(false); const [copyingAll, setCopyingAll] = useState(false); const [copyingSelected, setCopyingSelected] = useState(false); const hasSelection = selectedIndices.size > 0; const hasLogs = logs.length > 0; // Get selected logs from indices const getSelectedLogs = useCallback((): StructuredLog[] => { return logs.filter((_, index) => selectedIndices.has(index)); }, [logs, selectedIndices]); // Show toast notification const showToastMessage = useCallback((message: string) => { setToastMessage(message); setShowToast(true); }, []); // Handle copy all logs const handleCopyAll = useCallback(async () => { if (!hasLogs) return; setCopyingAll(true); try { const content = formatLogsForExport(logs, format); const success = await copyToClipboard(content); if (success) { showToastMessage(`Copied ${logs.length} log${logs.length !== 1 ? 's' : ''}`); onCopySuccess?.(); } else { showToastMessage('Failed to copy'); } } finally { setCopyingAll(false); } }, [logs, format, hasLogs, showToastMessage, onCopySuccess]); // Handle copy selected logs const handleCopySelected = useCallback(async () => { if (!hasSelection) return; setCopyingSelected(true); try { const selectedLogs = getSelectedLogs(); const content = formatLogsForExport(selectedLogs, format); const success = await copyToClipboard(content); if (success) { showToastMessage(`Copied ${selectedLogs.length} log${selectedLogs.length !== 1 ? 's' : ''}`); onCopySuccess?.(); } else { showToastMessage('Failed to copy'); } } finally { setCopyingSelected(false); } }, [getSelectedLogs, format, hasSelection, showToastMessage, onCopySuccess]); // Handle download const handleDownload = useCallback(() => { const logsToExport = hasSelection ? getSelectedLogs() : logs; if (logsToExport.length === 0) return; const content = formatLogsForExport(logsToExport, format); const { extension, mimeType } = getExportFileInfo(format); const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const filename = `logs-${timestamp}.${extension}`; downloadAsFile(content, filename, mimeType); showToastMessage(`Downloaded ${logsToExport.length} log${logsToExport.length !== 1 ? 's' : ''}`); }, [logs, format, hasSelection, getSelectedLogs, showToastMessage]); return (
{/* Toast notification */} setShowToast(false)} /> {/* Copy All button */} {/* Copy Selected button */} {/* Separator */}
{/* Format dropdown */}
Format:
{/* Separator */}
{/* Download button */} {/* Selection info */} {hasSelection && ( {selectedIndices.size} of {logs.length} selected )}
); }