Skip to content

Signed WOPC thumbnail in tx-details renders weirdly

Why

Owner observation (2026-06-21, verbatim):

"The web app currently shows the signed WOPC thumbnail in the transaction details on the accounting page quite weirdly"

The "weirdly" needs concrete repro before a fix scope can be set β€” opening this task with the unknowns documented so a future agent doesn't re-discover them from scratch.

What's known about the surface (without the live repro)

The thumbnail in question is rendered by WOPCHoverPreview at components/accounting/transactions/TransactionTitleWithLinks.tsx:307-327. It's a popover content component shown when a user hovers a WOPC-matched transaction's title in the Bank Transactions table or detail drawer.

Implementation today:

function WOPCHoverPreview({ transactionId, wopcRefNumber }) {
  return (
    <DocumentPreviewShell caption={…}>
      <div style={{ width: "100%", height: DOCUMENT_PREVIEW_FRAME_HEIGHT, … }}>
        <iframe
          src={`/accounting/payment-confirmation/${transactionId}/preview?standalone=1&embed=1`}
          width="100%"
          height="100%"
          style={{ border: "none" }}
          title="WOPC Preview"
        />
      </div>
    </DocumentPreviewShell>
  )
}

Notes that may matter once the bug is reproduced:

  • The preview is an <iframe> loading a route by transactionId (not wopcRef directly). The route renders the WOPC PDF preview in embed=1 / standalone=1 mode.
  • DOCUMENT_PREVIEW_FRAME_HEIGHT is a shared constant β€” sibling preview components (ArchivePdfHoverPreview at line ~295 for Workspace invoices, InvoiceHoverPreview for project invoices) use the same shell.
  • Sibling previews use a different shape β€” ArchivePdfHoverPreview takes a documentId directly; only WOPC uses transactionId + route lookup. That asymmetry is a candidate explanation if the WOPC preview behaves differently from the others.
  • Per AGENTS.md UI conventions: empty/missing data β†’ render as N/A, retrieval failures as Error, unavailable date/numeric as -. If the route's data fetch fails for a transactionId with no resolved WOPC (or no signed PDF on Drive), the iframe may render something visually broken instead of a clear "N/A"/"Error" placeholder.
  • Adjacent context that may compound: the WOPC's signed-PDF Drive id lives on the signing-request doc (wopcSigningRequests/{id}.signedDriveFileId) for 30/32 of the 32 production WOPCs β€” only 2 have WOPC.pdfFileId/WOPC.pdfStoragePath mirrored on the WOPC doc itself. The preview route likely resolves through both paths.

What this task needs

  1. Reproduce the "weird" rendering in the live app (Records / Bank Transactions tab, hover a WOPC-matched tx, observe the WOPC thumbnail popover). Capture: screenshot, browser console errors, network tab for the /accounting/payment-confirmation/{txId}/preview request, and the affected tx + WOPC ids.
  2. Diagnose — is the issue iframe sizing (height/scrollbar), embed-mode CSS, slow-load placeholder, broken aspect ratio relative to the actual signed PDF, transactionId→WOPC resolution returning the wrong source, or something else?
  3. Decide if the fix folds into T-083's Phase 1b accessor migration (the preview's underlying lookup is one of the WOPC reader sites being switched onto the dual-read accessor) or stands alone as a CSS / iframe-config tweak.

Decision log

2026-06-21 β€” task opened (diagnosis pending)

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md (tip e5e90b3a); checked the board scope-not-UID (no existing task covers this β€” T-055 was a different WOPC bug about quantity/unit display, T-073 covers infra not the preview surface).
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea (retrofitted 2026-06-21 after the Source-line convention was added to AGENTS.md mid-session)
  • What changed: opened T-084 to capture the owner's observation before it gets lost; surface + likely-relevant context documented so the eventual diagnosis is faster.
  • Proposed by: the owner.
  • Approved by: the owner β€” opening this follow-up was explicitly directed: "Proceed with T-083 while please create a new task for the transaction thumbnail issue I mentioned previously" (2026-06-21).
  • Rationale: AGENTS.md's "Capture the WHY" requires the why be on record at whatever point it becomes known. Opening the task now with the owner's concern + the technical surface captures what's known. The diagnosis (the actual "what's weird") gets added in the next entry once the repro happens.
  • Out of scope: any fix work. This task is in todo until the bug is reproduced and a concrete fix scope can be written.

2026-06-21 β€” renumbered T-084 β†’ T-086 (UID collision resolved)

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md; renumbering, not editing scope.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • What changed: task file moved T-084.md β†’ T-086.md; frontmatter uid: T-084 β†’ uid: T-086; related: [T-083] β†’ related: [T-089] (the sibling WOPC-restructure task on this branch was renumbered in the same commit).
  • Why: PR #777 (a fresh branch off nightly, opened while this branch was in draft) took the then-next-free T-084 for the Records expense display regressions, before this branch could merge. Renumbering this task to T-086 frees T-084 for that other PR; commit messages on this branch that still mention "T-084" are historical and stay as-is.
  • Proposed by: Accounting (Diagnostics). Approved by: the owner β€” "proceed on both please" (2026-06-21).

2026-07-01 β€” reassigned to Accounting (Infrastructure) β†’ diagnosed + fixed β†’ done

  • βœ… Attestation (Accounting (Infrastructure)): read AGENTS.md; the owner routed this to me (relayed via EOP Local Assistance (fork), with the cause pre-pinned from code). Branch signal is main 🟑🟑 (Vercel, manual) Β· nightly πŸ”΄ β†’ committed to main only, no auto-deploy (Vercel deploys are manual). Mirrored to taskboard.
  • Source: Accounting (Infrastructure) Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM
  • Diagnosis (confirmed in code): WOPCHoverPreview (TransactionTitleWithLinks.tsx:307) iframed the full standalone preview PAGE (/accounting/payment-confirmation/{txId}/preview?standalone=1&embed=1) into the fixed 300px DOCUMENT_PREVIEW_FRAME_HEIGHT box. That route is a full app page with its own layout/margins, so squeezed into a thumbnail it renders awkwardly. Its siblings (ArchivePdfHoverPreview for receipts, WorkspaceInvoiceHoverPreview) instead iframe a raw PDF (…?redirect=pdf#toolbar=0&navpanes=0&scrollbar=0), which the browser's PDF viewer fits cleanly into the box. (Aside: the old iframe passed no signingRequestId, so it rendered the unsigned template anyway β€” the preview page only composes the signature when that param is present, per preview/client.tsx:176-181.)
  • Fix: WOPCHoverPreview now iframes a raw PDF with the same chrome-hiding fragment as its siblings. It prefers the canonical WOPC-PDF resolver keyed by ref (/api/records/wopcs/pdf?ref=<ref>) β€” which serves the actual signed (or voided) Drive copy when one exists (fast + what "signed WOPC" implies; same source the Records download uses), and falls back to the by-transaction on-the-fly render (/api/accounting/transactions/{txId}/payment-confirmation-pdf, application/pdf inline) when wopcRefNumber is absent (it's optional at this call site; transactionId is always present).
  • Verification: npx tsc --noEmit clean. Visual confirmation needs the live app (can't render a browser in-sandbox) β€” the fix mirrors the already-working sibling pattern exactly and both endpoints serve Content-Disposition: inline PDFs, so confidence is high; owner to eyeball on the next deploy.
  • Commit(s): 0dd3098a on main.
  • Blast radius: one component (WOPCHoverPreview in TransactionTitleWithLinks.tsx) β€” the WOPC hover thumbnail on the Bank Transactions table + detail drawer. No data, no schema, no other surface. Deploy is manual (Vercel) β€” pushed to main, not auto-deployed.
  • Optional follow-up (flagged, not done): if the owner wants the by-transaction fallback to also show the signed copy (not just the unsigned render), payment-confirmation-pdf.ts would need to resolve the signed Drive bytes (like wopcs/pdf) rather than renderWopcPdfBuffer. Rare path (only when no ref is available), so left out to keep the fix scoped.

2026-07-01 β€” re-verified against live main (no overlap) + orphaned-endpoint flag

  • βœ… Attestation (Accounting (Infrastructure)): read AGENTS.md; the owner asked me to re-check this against the current repo, since multiple agents committed to main this session and T-086 was originally Accounting (Diagnostics)'s task. Branch signal main 🟑🟑 (Vercel, manual) Β· nightly πŸ”΄. Mirrored to taskboard.
  • Source (edit): Accounting (Infrastructure) Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM
  • No overlap β€” verified. 0dd3098a is the sole change to WOPCHoverPreview; no parallel agent touched it (git log on TransactionTitleWithLinks.tsx shows only my commit, prior touch was T-010 routing, unrelated). The fix is live on main and intact.
  • Related-but-separate finding (worth a cleanup, not blocking): there is an orphaned endpoint pages/api/accounting/transactions/[id]/wopc-thumbnail.ts β€” a React-SSR HTML render of the WOPC that nothing references (repo-wide grep for wopc-thumbnail finds no caller). WOPCHoverPreview never used it (it iframed the full preview page before this fix, and the raw PDF after). Note the irony: commit f913bd60 (T-073 #2) bumped that endpoint's cache 60sβ†’600s "to avoid re-rendering on every preview hover" β€” but it's a dead path, so that optimisation is moot. Suggest whoever owns the WOPC cleanup either wire it up or delete it; left untouched here (out of T-086's scope, and not mine to delete unprompted).
  • The other …/preview?standalone=1 reference at TransactionTitleWithLinks.tsx:~595 is the "open full WOPC in a popup window" button (900Γ—700), NOT the thumbnail β€” intentionally the full page there (a real full-window view), so it correctly stays as-is.

Commit index

  • 0dd3098a 2026-07-01 β€” fix(T-086): WOPC hover thumbnail β€” iframe raw PDF, not the full preview page