import { PureComponent } from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import Tooltip from '@mui/material/Tooltip'; import CloseIcon from '@mui/icons-material/Close'; import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined'; import VerticalAlignTopIcon from '@mui/icons-material/VerticalAlignTop'; import VerticalAlignBottomIcon from '@mui/icons-material/VerticalAlignBottom'; import ViewSidebarIcon from '@mui/icons-material/ViewSidebar'; import { enqueueSnackbar } from 'notistack'; import { PanelTile } from 'dashgui/panels'; import { delivery as deliveryApi } from '../../api'; import { SURFACE_BASE, SURFACE_WASH, WHITE, } from '../../colors'; /** * Inline PDF viewer rendered as a panel tile, mirroring TextEditorPanel's * "window" behaviour: it can sit above, right of, or below the article list, * and stays sticky when docked to the right. Mount it only while a PDF * attachment is selected; unmounting closes it. */ export class PdfViewerPanel extends PureComponent { constructor(props) { super(props); this.state = { url: null, loading: false, error: null, }; this.handleOpenExternal = this.handleOpenExternal.bind(this); this.handleDownload = this.handleDownload.bind(this); } componentDidMount() { this.loadUrl(); } componentDidUpdate(prevProps) { if (this.props.attachment !== prevProps.attachment) { this.revokeUrl(); this.loadUrl(); } } componentWillUnmount() { this.revokeUrl(); } revokeUrl() { if (this._objectUrl) { URL.revokeObjectURL(this._objectUrl); this._objectUrl = null; } } async loadUrl() { const { attachment, docId } = this.props; if (!attachment) return; if ( (attachment.file instanceof Blob || attachment.file instanceof File) && attachment.file.size > 0 ) { this._objectUrl = URL.createObjectURL(attachment.file); this.setState({ url: this._objectUrl, loading: false, error: null }); return; } const effectiveDocId = docId ?? attachment.manifestId; const fileId = attachment.fileId ?? attachment.id; if (effectiveDocId != null && fileId != null) { this.setState({ loading: true, error: null }); try { const blob = await deliveryApi.getFile(effectiveDocId, fileId); this._objectUrl = URL.createObjectURL(blob); this.setState({ url: this._objectUrl, loading: false, error: null }); return; } catch (err) { console.error('Fehler beim Laden der PDF-Datei:', err); enqueueSnackbar('Fehler beim Laden der Datei', { variant: 'error' }); this.setState({ loading: false, error: 'Fehler beim Laden der Datei' }); return; } } this.setState({ url: null, loading: false, error: null }); } handleOpenExternal() { const { url } = this.state; if (!url) return; window.open(url, '_blank'); } handleDownload() { const { attachment } = this.props; const { url } = this.state; if (!url) return; const link = document.createElement('a'); link.href = url; link.download = attachment?.name || 'Dokument.pdf'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } render() { const { onClose, attachment, wide = 3, position = 'top', onPositionChange, height = '60vh', } = this.props; if (!attachment) return null; const { url, loading, error } = this.state; const filename = attachment?.name || 'Dokument.pdf'; return ( {filename} PDF-Vorschau onPositionChange?.('top')} > onPositionChange?.('right')} > onPositionChange?.('below')} > {loading && ( )} {error ? ( {error} ) : ( url && ( ) )} ); } } export default PdfViewerPanel;