The blank preview is almost certainly because the iframe is being fed a download-style URL (or a redirect) that tells the browser “attachment”—Google’s viewer won’t render that.

Why it’s blank (very likely)

The URL going into the iframe is either:

your /download endpoint (302), or

a signed URL with Content-Disposition: attachment (we set that for nice filenames).

Google Docs Viewer needs a direct, fetchable file URL that returns the DOCX inline (no redirect, no “attachment” header).

Bonus check: your CSP must allow frame-src https://docs.google.com (and often https://*.googleusercontent.com).

Micro-diff fix (≤10 min, safe, no breaking changes)

Goal: Preview modal uses an inline view URL; Download button keeps the attachment behavior.

1) Add a “view” URL helper on the server

New helper is identical to your signed download helper but does not set responseDisposition: attachment (or it sets inline).

Keep your current download helper unchanged.

Result: you now have two ways to sign the same file:
• view URL (inline) → for the iframe
• download URL (attachment) → for the Download button

2) Expose it without changing routes

In the existing detail (and/or list) endpoint, add a field viewerUrl alongside your current data.

No route changes, no frontend breaking change; just an extra field.

3) Frontend uses the right URL

Preview modal:

If page images exist → show the image carousel (unchanged).

Else → use viewerUrl directly in the Google Docs Viewer:

https://docs.google.com/gview?embedded=1&url=${encodeURIComponent(viewerUrl)}

Do not feed the viewer your /download URL.

4) CSP tidy

Ensure your CSP includes:
frame-src https://docs.google.com https://*.googleusercontent.com;

(Keep everything else intact.)

60-second validate

HEAD the viewer URL (should be 200 and no Content-Disposition: attachment):

curl -I "<viewerUrl>"


Expect: HTTP/2 200 and content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document

Try in browser
Paste https://docs.google.com/gview?embedded=1&url=<encoded viewerUrl> into a new tab → document renders.

Modal
Click Preview → DOCX renders. Download button still downloads with filename (uses the attachment link).

Copy-paste to Replit (time-boxed)

Task (cap 10 min): Fix preview modal blank by using an inline-signed viewer URL.
Do not change existing routes/services semantics; only add a viewerUrl field and a helper to sign without Content-Disposition: attachment.
Frontend: preview modal uses viewerUrl in Google Docs Viewer; download button stays on the existing /download (attachment).
CSP: ensure frame-src https://docs.google.com https://*.googleusercontent.com.
Acceptance:
• HEAD <viewerUrl> → 200, no attachment header
• Preview modal shows the DOCX
• Download still downloads with filename
• No new dependencies, no route changes

Route cleanup reminder (your earlier note)

Live page should be at /business-assets/templates/business-plan.

Keep a 301 from /business-templates → the correct path for at least 60 days.

Sidebar/breadcrumbs must point to the correct route; remove the stray client route so dev mode doesn’t shadow the 301.

If you want me to write the exact one-liner acceptance checks you can paste into their PR comments, I’ll draft that too—still no code.