'use client'; import { useEffect, useRef, useState } from 'react'; import { Loader2 } from 'lucide-react'; /** * In-app .docx viewer. * * Renders Word OOXML (.docx) client-side via `docx-preview` (lazy-loaded * so the ~library cost only lands on routes that actually preview a docx). * We fetch the bytes from our own storage URL and render them in-browser — * deliberately NOT delegating to Microsoft's hosted Office viewer, which * requires a publicly-reachable URL and so can't render documents stored * in our private object store. * * Legacy .doc / .xls / .xlsx are not handled here (docx-preview is OOXML- * Word only); the preview dialog routes those to a download CTA instead. */ export function DocxViewer({ url, fileName }: { url: string; fileName?: string }) { // Key-based remount on url change keeps render state (loading/error + // the imperatively-populated container) re-initialised from scratch, // mirroring PdfViewer. return ; } function DocxViewerBody({ url, fileName }: { url: string; fileName?: string }) { const containerRef = useRef(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; async function render() { try { const res = await fetch(url); if (!res.ok) throw new Error(`Failed to load document (${res.status})`); const blob = await res.blob(); if (cancelled) return; const { renderAsync } = await import('docx-preview'); const container = containerRef.current; if (!container) return; container.innerHTML = ''; await renderAsync(blob, container, undefined, { className: 'docx', inWrapper: true, // Let the document flow to the container width rather than // forcing fixed A4 page metrics that overflow the dialog. ignoreWidth: true, ignoreHeight: true, breakPages: true, }); if (!cancelled) setError(null); } catch (err) { if (!cancelled) { setError(err instanceof Error ? err.message : 'Failed to render document'); } } finally { if (!cancelled) setLoading(false); } } void render(); return () => { cancelled = true; }; }, [url]); return (
{loading && (
Rendering document…
)} {error && !loading && (
{error}
)}
); }