shinkan-jinkendo/frontend/src/components/MediaPreviewModal.jsx

141 lines
5.0 KiB
React
Raw Normal View History

/**
* Gemeinsamer Medienvorschau-Dialog.
*
* Props:
* title – Anzeigename des Mediums (Dateiname o.ä.)
* media – Medienobjekt (mime_type, embed_url, asset_legal_hold_active, media_type)
* fileUrl – Bereits aufgelöste Datei-URL (null für Embed-only-Medien)
* onClose – Pflicht
* onReport – optional; wenn gesetzt, erscheint "Melden"-Button
* onEdit – optional; wenn gesetzt, erscheint "Bearbeiten"-Button
*/
import React from 'react'
export default function MediaPreviewModal({ title, media, fileUrl, onClose, onReport, onEdit }) {
const mlow = (media?.mime_type || '').toLowerCase()
const isHeic = mlow.includes('heic') || mlow.includes('heif')
const isImage = !isHeic && (media?.mime_type?.startsWith('image/') || media?.media_type === 'image')
const isVideo = media?.mime_type?.startsWith('video/') || media?.media_type === 'video'
const isPdf = mlow.includes('pdf')
const isLegalHold = !!media?.asset_legal_hold_active
const canReport = onReport && !isLegalHold
function handleBackdrop(e) {
if (e.target === e.currentTarget) onClose()
}
function handleKeyDown(e) {
if (e.key === 'Escape') onClose()
}
return (
<div
role="dialog"
aria-modal="true"
aria-label="Medienvorschau"
style={{
position: 'fixed',
inset: 0,
background: 'rgba(0,0,0,0.55)',
zIndex: 1100,
overflow: 'auto',
padding: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
onClick={handleBackdrop}
onKeyDown={handleKeyDown}
>
<div
className="card"
style={{ maxWidth: 720, width: '100%', maxHeight: '90vh', overflow: 'auto', padding: '1.25rem' }}
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem', gap: '0.5rem' }}>
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{title || 'Vorschau'}
</h3>
<div style={{ display: 'flex', gap: '0.4rem', flexShrink: 0 }}>
{canReport && (
<button type="button" className="btn btn-secondary" style={{ fontSize: '0.85rem', padding: '5px 10px' }} onClick={onReport}>
Melden
</button>
)}
{onEdit && (
<button type="button" className="btn btn-secondary" style={{ fontSize: '0.85rem', padding: '5px 10px' }} onClick={onEdit}>
Bearbeiten
</button>
)}
<button
type="button"
className="btn btn-secondary"
style={{ fontSize: '0.85rem', padding: '5px 10px' }}
onClick={onClose}
aria-label="Schließen"
>
✕
</button>
</div>
</div>
{/* Body */}
{isLegalHold ? (
<p style={{ color: 'var(--danger)', fontSize: '0.9rem' }}>
Dieses Medium ist gesperrt und steht nicht zur Verfügung.
</p>
) : media?.embed_url ? (
<p style={{ fontSize: '14px', wordBreak: 'break-all' }}>
<a href={media.embed_url} target="_blank" rel="noreferrer">{media.embed_url}</a>
</p>
) : isHeic ? (
<div>
<p style={{ color: 'var(--text2)', fontSize: '0.9rem' }}>
HEIC/HEIF — in diesem Browser oft keine eingebettete Vorschau. Zum Ansehen herunterladen oder im neuen Tab öffnen.
</p>
{fileUrl && (
<a className="btn btn-secondary" href={fileUrl} target="_blank" rel="noopener noreferrer">
Datei öffnen
</a>
)}
</div>
) : isImage ? (
<img
key={media?.id}
alt=""
src={fileUrl}
style={{ maxWidth: '100%', borderRadius: '8px', maxHeight: '70vh', objectFit: 'contain', display: 'block' }}
/>
) : isVideo ? (
<video
key={media?.id}
src={fileUrl}
controls
playsInline
preload="metadata"
style={{ width: '100%', borderRadius: '8px', maxHeight: '70vh' }}
>
Wiedergabe nicht unterstützt.
</video>
) : isPdf ? (
<div>
<p style={{ color: 'var(--text2)', fontSize: '0.9rem' }}>PDF — zur Ansicht im neuen Tab öffnen.</p>
{fileUrl && (
<a className="btn btn-secondary" href={fileUrl} target="_blank" rel="noopener noreferrer">
PDF öffnen
</a>
)}
</div>
) : fileUrl ? (
<p style={{ fontSize: '14px' }}>
<a href={fileUrl} target="_blank" rel="noreferrer">Datei öffnen</a>
</p>
) : (
<p style={{ color: 'var(--text2)', fontSize: '0.9rem' }}>Keine Vorschau verfügbar.</p>
)}
</div>
</div>
)
}