import { useMemo, useState, useEffect } from 'react'; import type { JSX } from 'react'; import { useNavigate } from 'react-router-dom'; import { CloseIcon, LoaderIcon, CheckIcon, InfoIcon, WarningIcon, BottombarCollapseIcon, BottombarExpandIcon, } from '../ui/icons'; import PanelHeader from '../ui/PanelHeader'; type UploadStatus = 'pending' | 'uploading' | 'success' | 'duplicate' | 'error' | (string & {}); interface UploadQueueItem { id: string | number; name: string; status: UploadStatus; error?: string | null; document?: { id?: string | number; title?: string }; conflictDocumentId?: string | number; } interface UploadQueueOverlayProps { queue?: UploadQueueItem[]; onClearQueue?: () => void; } const STATUS_META: Record = { pending: { label: 'Queued', tone: 'muted', icon: , }, uploading: { label: 'Uploading', tone: 'accent', icon: , }, success: { label: 'Uploaded', tone: 'success', icon: , }, duplicate: { label: 'Duplicate', tone: 'info', icon: , }, error: { label: 'Failed', tone: 'danger', icon: , }, }; const UploadQueueOverlay = ({ queue = [], onClearQueue }: UploadQueueOverlayProps): JSX.Element | null => { const navigate = useNavigate(); const [collapsed, setCollapsed] = useState(false); const [dismissed, setDismissed] = useState(false); useEffect(() => { if (queue.length > 0) { setDismissed(false); } }, [queue.length]); const summary = useMemo(() => { if (!queue.length) { return 'No uploads'; } const uploadingCount = queue.filter((item) => item.status === 'uploading').length; const pendingCount = queue.filter((item) => item.status === 'pending').length; const errorCount = queue.filter((item) => item.status === 'error').length; if (uploadingCount > 0 || pendingCount > 0) { return `${uploadingCount} uploading · ${pendingCount} queued`; } if (errorCount > 0) { return `${errorCount} failed · ${queue.length} total`; } return `${queue.length} completed`; }, [queue]); const hasActiveUploads = queue.some((item) => item.status === 'uploading' || item.status === 'pending'); const handleDismissOverlay = () => { if (!queue.length) { setDismissed(true); return; } if (hasActiveUploads) { const confirmed = window.confirm('Uploads are still running. Clear the queue and hide the overlay?'); if (!confirmed) { return; } } onClearQueue?.(); setDismissed(true); }; if (!queue.length || dismissed) { return null; } return (
Uploads {summary} )} actions={(
)} /> {!collapsed ? (
    {[...queue] .slice() .reverse() .map((item) => { const meta = STATUS_META[item.status] || STATUS_META.pending; const fileLabel = item.name; const documentTitle = item.document?.title || null; const duplicateLabel = item.status === 'duplicate' ? documentTitle : null; const documentId = item.document?.id || item.conflictDocumentId || null; const hasLink = Boolean(documentId); const handleNavigate = () => { if (!documentId) { return; } navigate(`/documents/${documentId}`); }; return (
  • {meta.icon}
    {item.status === 'success' && hasLink ? ( ) : (
    {fileLabel}
    )}
    {item.status === 'duplicate' && duplicateLabel ? ( Duplicate of{' '} ) : item.status === 'error' && item.error ? ( {item.error} ) : ( {meta.label} )}
  • ); })}
) : null}
); }; export default UploadQueueOverlay;