import React, { useMemo } from 'react'; import { formatFileSize } from '../utils/format'; import { DownloadIcon, TextScanIcon, AnalyzeIcon, CloseIcon } from '../ui/icons'; import { openOcrTextInNewTab } from '../utils/ocr'; const PreviewWorkspace = ({ document, previewEntry, }) => { if (!document) { return null; } const title = document.title || document.original_name || 'Document'; const mime = previewEntry?.contentType || document.content_type || 'application/pdf'; const sizeBytes = Number(document.current_version?.size_bytes) || 0; const sizeLabel = sizeBytes > 0 ? formatFileSize(sizeBytes) : null; const metadata = document.metadata && Object.keys(document.metadata).length > 0 ? document.metadata : null; const folderName = document.folder_path || document.folder_name || null; const issuedAt = document.issued_at || document.current_version?.issued_at || null; const createdAt = document.created_at || null; const updatedAt = document.updated_at || null; const tags = Array.isArray(document.tags) ? document.tags : []; const correspondents = Array.isArray(document.correspondents) ? document.correspondents : []; const metadataSummary = useMemo(() => { const rows = []; if (mime) rows.push(['Type', mime]); if (sizeLabel) rows.push(['Size', sizeLabel]); if (issuedAt) rows.push(['Issued', new Date(issuedAt).toLocaleString()]); if (createdAt) rows.push(['Created', new Date(createdAt).toLocaleString()]); if (updatedAt) rows.push(['Updated', new Date(updatedAt).toLocaleString()]); if (folderName) rows.push(['Folder', folderName]); if (tags.length) { rows.push(['Tags', tags.map((tag) => tag.label || tag.name || tag.slug).filter(Boolean).join(', ')]); } if (correspondents.length) { rows.push([ 'Correspondents', correspondents .map((entry) => entry.name || entry.label || entry.slug) .filter(Boolean) .join(', '), ]); } return rows; }, [mime, sizeLabel, issuedAt, createdAt, updatedAt, folderName, tags, correspondents]); return ( {!previewEntry?.url ? ( Loading preview… ) : ( )} ); }; export default PreviewWorkspace; export const createPreviewWorkspaceHeaderActions = ({ document, ensureAssetUrl, getDocumentAsset, resolveApiPath, notifyApiError, onRegenerate, }) => { if (!document) { return null; } const downloadHref = document.current_version?.download_path ? resolveApiPath(document.current_version.download_path) : null; const ocrAsset = getDocumentAsset(document, 'ocr-text'); const hasOcr = Boolean(ocrAsset); const handleOcrClick = async () => { try { const success = await openOcrTextInNewTab({ document, getDocumentAsset, ensureAssetUrl, }); if (!success) { throw new Error('OCR text URL unavailable.'); } } catch (error) { notifyApiError(error, 'Unable to open OCR text.'); } }; return ( <> {downloadHref ? ( ) : null} {hasOcr ? ( ) : null} onRegenerate(document.id)} aria-label="Re-run analysis" title="Re-run analysis" > > ); }; export const createPreviewSurface = ({ document, previewEntry, ensureAssetUrl, getDocumentAsset, resolveApiPath, notifyApiError, onRegenerate, onClose, renderSidebarToggle, }) => { if (!document) { return null; } const title = document.title || document.original_name || 'Document preview'; const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const closeButton = onClose ? ( onClose?.()} aria-label="Close preview" title="Close preview" > ) : null; const leading = sidebarToggle || closeButton ? ( <> {sidebarToggle} {closeButton} > ) : null; const header = { title, subtitle: null, leading, actions: createPreviewWorkspaceHeaderActions({ document, ensureAssetUrl, getDocumentAsset, resolveApiPath, notifyApiError, onRegenerate, }), }; return { key: 'preview', variant: 'preview', header, content: , supportsDetail: false, }; };