From 7eec1337da80b647c560d1d876dff56ed83b77f8 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:10:06 +0200 Subject: [PATCH] feat(contracts): preview PDF before sending Add a 'Preview PDF' button on draft contracts that renders a fresh PDF via the existing no-write /preview endpoint, so the admin can check layout + signature blocks before sending (no audit trail created). --- frontend/src/i18n/locales/de.json | 1 + frontend/src/i18n/locales/en.json | 1 + .../admin/contracts/ContractDetailPage.tsx | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index 4f7506b1..b1866d74 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -4153,6 +4153,7 @@ "new": "Neuer Vertrag" }, "detail": { + "previewPdf": "PDF-Vorschau", "integrity": { "title": "PDF-Integritätsprüfung", "help": "Berechnet die SHA-256-Hashes der unsignierten und signierten PDFs neu und vergleicht sie mit den beim Ausstellen gespeicherten Werten. Deckt Backup-Beschädigungen oder nachträgliche Änderungen an der Kundenkopie auf.", diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 2bf1fd1b..64a55130 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -4151,6 +4151,7 @@ "new": "New contract" }, "detail": { + "previewPdf": "Preview PDF", "integrity": { "title": "PDF integrity check", "help": "Re-hashes the unsigned + signed PDFs on disk and compares them to the SHA-256 stored when the document was issued. Catches backup corruption or manual edits since the customer received their copy.", diff --git a/frontend/src/pages/admin/contracts/ContractDetailPage.tsx b/frontend/src/pages/admin/contracts/ContractDetailPage.tsx index c322b1a6..fc4325fc 100644 --- a/frontend/src/pages/admin/contracts/ContractDetailPage.tsx +++ b/frontend/src/pages/admin/contracts/ContractDetailPage.tsx @@ -197,6 +197,25 @@ export const ContractDetailPage: React.FC = () => { } } + // Pre-send preview: renders a fresh PDF from the current draft without + // writing/sending anything, so the admin can sanity-check layout + + // signature blocks before committing to send (no audit trail created). + async function handlePdfPreview() { + if (!numericId) return; + const previewWindow = window.open('about:blank', '_blank'); + if (!previewWindow) { + toast.error(t('contracts.detail.popupBlocked', 'Allow pop-ups for this site to preview the PDF.') as string); + return; + } + try { + const url = await contractsService.previewPdfUrl(numericId); + previewWindow.location.href = url; + } catch (err: any) { + previewWindow.close(); + toast.error(err?.response?.data?.error || 'Preview failed'); + } + } + async function handleSignedPdfDownload() { if (!numericId) return; const previewWindow = window.open('about:blank', '_blank'); @@ -241,6 +260,10 @@ export const ContractDetailPage: React.FC = () => { {t('contracts.detail.edit', 'Edit')} +