Portal: drop draft-agreement print fallback; surface the render error

Per Connor: the enterprise-agreement PDF is rendered server-side, so a
download failure means the render service (WeasyPrint) is unavailable —
not something the buyer can retry around with a browser print. Removed
the hidden-iframe print fallback and now show a clear error instead. The
flow can be exercised end to end once WeasyPrint is available (Docker or
a local install).
This commit is contained in:
Connor Yoh
2026-07-15 11:31:12 +01:00
parent b7c9b4f67d
commit e22940008a
2 changed files with 4 additions and 52 deletions
@@ -7704,7 +7704,7 @@ upload = "Upload"
agreeCta = "Agree & subscribe"
confirm = "I have read and agree to this Agreement, and I represent that I am authorized to sign it on behalf of the organization named above."
downloadAgreement = "Agreement"
downloadDraftError = "Could not open the agreement for download. Please allow pop-ups and try again."
downloadDraftError = "Could not generate the agreement PDF — the document renderer is unavailable. Please try again shortly or contact support."
downloadError = "Could not download the signed agreement. Please try again."
downloadQuote = "Quote"
draftBadge = "Draft — for review"
@@ -51,55 +51,6 @@ export function ProcurementAgreement({
const [downloadError, setDownloadError] = useState(false);
const docRef = useRef<HTMLDivElement>(null);
/**
* Fall back to the browser's print-to-PDF when the server can't render the agreement — the
* WeasyPrint pipeline isn't available in every environment (e.g. local dev), and the buyer must
* still be able to keep a copy of the draft. Prints the already-rendered agreement body from a
* hidden iframe so it isn't popup-blocked. Returns false if the document isn't in the DOM yet.
*/
const printDraftFallback = (): boolean => {
const body = docRef.current?.querySelector(
".portal-agreement__md",
)?.innerHTML;
if (!body) return false;
const iframe = document.createElement("iframe");
iframe.setAttribute("aria-hidden", "true");
iframe.style.cssText =
"position:fixed;right:0;bottom:0;width:0;height:0;border:0;";
document.body.appendChild(iframe);
const cw = iframe.contentWindow;
const cd = iframe.contentDocument ?? cw?.document;
if (!cw || !cd) {
iframe.remove();
return false;
}
const draftNotice =
doc && doc.status !== "final"
? `<p class="draft">${t("portal.procurement.agreement.draftBadge")}</p>`
: "";
cd.open();
cd.write(
`<!doctype html><html><head><meta charset="utf-8">` +
`<title>stirling-enterprise-agreement</title><style>` +
`body{font:14px/1.6 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;` +
`color:#111;max-width:44rem;margin:2rem auto;padding:0 1.5rem;}` +
`h1,h2,h3{line-height:1.3;}table{border-collapse:collapse;width:100%;}` +
`td,th{border:1px solid #ccc;padding:6px 8px;text-align:left;}` +
`.draft{color:#b45309;font-weight:600;font-size:12px;text-transform:uppercase;` +
`letter-spacing:.04em;margin-bottom:1.25rem;}</style></head><body>` +
`${draftNotice}${body}</body></html>`,
);
cd.close();
const print = () => {
cw.focus();
cw.print();
setTimeout(() => iframe.remove(), 1000);
};
if (cd.readyState === "complete") setTimeout(print, 100);
else cw.addEventListener("load", print);
return true;
};
const downloadMsa = async () => {
setDownloadingMsa(true);
setDownloadError(false);
@@ -114,8 +65,9 @@ export function ProcurementAgreement({
a.remove();
setTimeout(() => URL.revokeObjectURL(url), 60_000);
} catch {
// Server render unavailable — keep the buyer whole with the browser print fallback.
if (!printDraftFallback()) setDownloadError(true);
// Surface the failure — the PDF is rendered server-side, so a failure here means the
// render service is unavailable rather than something the buyer can retry around.
setDownloadError(true);
} finally {
setDownloadingMsa(false);
}