108 lines
3.6 KiB
React
108 lines
3.6 KiB
React
|
|
import { useState } from 'react'
|
|||
|
|
import dayjs from 'dayjs'
|
|||
|
|
import { FileDown } from 'lucide-react'
|
|||
|
|
import { useAuth } from '../../context/AuthContext'
|
|||
|
|
import { useProfile } from '../../context/ProfileContext'
|
|||
|
|
import { exportDashboardToPdf } from '../../utils/dashboardPdfExport'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param {{ reportExportConfig: { document_title: string, subtitle: string, capture_scale: number } }} props
|
|||
|
|
*/
|
|||
|
|
export default function ReportExportWidget({ reportExportConfig }) {
|
|||
|
|
const { canExport } = useAuth()
|
|||
|
|
const { activeProfile } = useProfile()
|
|||
|
|
const [busy, setBusy] = useState(false)
|
|||
|
|
const [err, setErr] = useState(null)
|
|||
|
|
|
|||
|
|
const profileName = activeProfile?.name?.trim() || 'Profil'
|
|||
|
|
const title =
|
|||
|
|
reportExportConfig.document_title || `${profileName} – Übersicht`
|
|||
|
|
const subtitle =
|
|||
|
|
reportExportConfig.subtitle ||
|
|||
|
|
`Erstellt am ${dayjs().format('DD.MM.YYYY HH:mm')} · Mitai Jinkendo`
|
|||
|
|
|
|||
|
|
const runExport = async () => {
|
|||
|
|
setErr(null)
|
|||
|
|
setBusy(true)
|
|||
|
|
try {
|
|||
|
|
const slug = (reportExportConfig.document_title || profileName).replace(/\s+/g, '-').slice(0, 80)
|
|||
|
|
await exportDashboardToPdf({
|
|||
|
|
scale: reportExportConfig.capture_scale,
|
|||
|
|
filenameBase: `bericht-${slug}-${dayjs().format('YYYY-MM-DD')}`,
|
|||
|
|
})
|
|||
|
|
} catch (e) {
|
|||
|
|
setErr(e?.message || 'PDF-Export fehlgeschlagen.')
|
|||
|
|
} finally {
|
|||
|
|
setBusy(false)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="card" style={{ marginBottom: 16 }}>
|
|||
|
|
<div
|
|||
|
|
className="card-title"
|
|||
|
|
style={{ display: 'flex', alignItems: 'center', gap: 8 }}
|
|||
|
|
data-dashboard-pdf-exclude="true"
|
|||
|
|
>
|
|||
|
|
<FileDown size={18} color="var(--accent)" aria-hidden />
|
|||
|
|
Übersicht als Bild-PDF
|
|||
|
|
</div>
|
|||
|
|
<div style={{ marginTop: 12 }}>
|
|||
|
|
<h2
|
|||
|
|
style={{
|
|||
|
|
fontSize: 17,
|
|||
|
|
fontWeight: 650,
|
|||
|
|
margin: '0 0 6px',
|
|||
|
|
color: 'var(--text1)',
|
|||
|
|
lineHeight: 1.35,
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
{title}
|
|||
|
|
</h2>
|
|||
|
|
<p style={{ margin: 0, fontSize: 13, color: 'var(--text2)', lineHeight: 1.5 }}>{subtitle}</p>
|
|||
|
|
</div>
|
|||
|
|
<div data-dashboard-pdf-exclude="true">
|
|||
|
|
<p style={{ fontSize: 12, color: 'var(--text3)', marginTop: 14, lineHeight: 1.55 }}>
|
|||
|
|
<strong>Layout-Schnappschuss:</strong> Die sichtbare Übersicht wird im Browser gerastert (html2canvas).
|
|||
|
|
Für einen <strong>datenbasierten Bericht</strong> unabhängig vom Dashboard nutze{' '}
|
|||
|
|
<strong>Einstellungen → PDF-Bericht (strukturiert)</strong>.
|
|||
|
|
</p>
|
|||
|
|
<div style={{ marginTop: 14 }}>
|
|||
|
|
{!canExport ? (
|
|||
|
|
<p style={{ fontSize: 13, color: '#D85A30', margin: 0 }}>
|
|||
|
|
PDF-Export ist für dieses Profil nicht freigeschaltet.
|
|||
|
|
</p>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
{err && (
|
|||
|
|
<p style={{ fontSize: 13, color: '#D85A30', margin: '0 0 10px' }} role="alert">
|
|||
|
|
{err}
|
|||
|
|
</p>
|
|||
|
|
)}
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
className="btn btn-primary"
|
|||
|
|
disabled={busy}
|
|||
|
|
onClick={runExport}
|
|||
|
|
style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}
|
|||
|
|
>
|
|||
|
|
{busy ? (
|
|||
|
|
<>
|
|||
|
|
<span className="spinner" style={{ width: 18, height: 18 }} aria-hidden />
|
|||
|
|
PDF wird erzeugt…
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
<FileDown size={18} />
|
|||
|
|
PDF-Schnappschuss herunterladen
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</button>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|