import { PureComponent } from 'react'; import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import TextSnippetOutlinedIcon from '@mui/icons-material/TextSnippetOutlined'; import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; import SyncIcon from '@mui/icons-material/Sync'; import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined'; import { tileBaseSx, TILE_SIZE } from 'dashgui/tiles'; import { generateTextThumbnail } from '../../utils/textThumbnail'; import { notifyErrorMessage } from '../../utils/notify'; export class Attachment extends PureComponent { state = { generatedThumbnail: null, }; componentDidMount() { this.ensureThumbnail(); } componentDidUpdate(prevProps) { if (this.props.attachment !== prevProps.attachment) { this.ensureThumbnail(); } } async ensureThumbnail() { const { attachment } = this.props; if (!attachment || attachment.thumbnail) return; const isCsv = attachment.type === 'text/csv' || attachment.name?.toLowerCase().endsWith('.csv'); const isText = attachment.type?.startsWith('text/') || attachment.name?.toLowerCase().endsWith('.txt') || isCsv; if (!isText) return; const source = attachment.text || attachment.file; if (!source) return; try { const thumb = await generateTextThumbnail(source); if (thumb && this.props.attachment === attachment) { this.setState({ generatedThumbnail: thumb }); } } catch (err) { console.error('Fehler beim Generieren des Text-Thumbnails:', err); } } handleRescan = (e) => { e?.stopPropagation?.(); if (this.props.hasArticles) { notifyErrorMessage( 'Neu analysieren nicht möglich, solange Artikel vorhanden sind. Bitte entfernen Sie zuerst alle Zeilen.', ); return; } const { attachment, onRescan } = this.props; if (attachment && onRescan) { onRescan(attachment); } }; handleDelete = (e) => { e?.stopPropagation?.(); const { attachment, onDelete } = this.props; if (attachment && onDelete) { onDelete(attachment); } }; handleTileClick = () => { const { attachment, onClick } = this.props; if (onClick && attachment) { onClick(attachment); } else if (attachment?.file instanceof Blob || attachment?.file instanceof File) { if (attachment.file.size > 0) { const url = URL.createObjectURL(attachment.file); window.open(url, '_blank'); } else { const docId = attachment.manifestId; const fileId = attachment.fileId ?? attachment.id; if (docId != null && fileId != null) { window.open( `/api/delivery/${encodeURIComponent(docId)}/file/${encodeURIComponent(fileId)}`, '_blank', ); } } } }; formatFileSize(bytes) { if (bytes == null || Number.isNaN(bytes)) return ''; if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } render() { const { attachment, disabled, sx, hasArticles } = this.props; const thumbnail = attachment?.thumbnail || this.state.generatedThumbnail; const name = attachment?.name || 'Dokument'; const size = attachment?.size; const isPdf = attachment?.type === 'application/pdf' || name.toLowerCase().endsWith('.pdf'); const isCsv = attachment?.type === 'text/csv' || name.toLowerCase().endsWith('.csv'); const isText = attachment?.type?.startsWith('text/') || name.toLowerCase().endsWith('.txt') || isCsv; return ( {/* Thumbnail, Text preview, or Fallback Icon */} {thumbnail ? ( ) : isText ? ( {isCsv ? 'CSV' : 'TXT'} {attachment?.text ? attachment.text.slice(0, 300) : 'Textdatei'} ) : ( )} {/* Action buttons: Rescan (for PDF) and Delete */} e.stopPropagation()} > {isPdf || isText ? ( { e.stopPropagation(); notifyErrorMessage( 'Neu analysieren nicht möglich, solange Artikel vorhanden sind. Bitte entfernen Sie zuerst alle Zeilen.', ); } : undefined } > ) : null} {/* Bottom file metadata overlay */} {name} {size ? ( {this.formatFileSize(size)} ) : null} ); } } export default Attachment;