diff --git a/src/components/orders-review-modal.test.tsx b/src/components/orders-review-modal.test.tsx index d8e0718..f265bbe 100644 --- a/src/components/orders-review-modal.test.tsx +++ b/src/components/orders-review-modal.test.tsx @@ -248,4 +248,89 @@ describe("OrdersReviewModal", () => { // Cost should now show 33.33. expect(screen.getByDisplayValue("33.33")).toBeInTheDocument(); }); + + it("renders PDF preview via iframe when first attachment is a PDF", () => { + render( + , + ); + const iframe = document.querySelector("iframe"); + expect(iframe).toBeInTheDocument(); + expect(iframe?.getAttribute("src")).toBe( + "/api/v1/files/coll-1/att-1/acme-token", + ); + }); + + it("renders image preview via when first attachment is an image", () => { + render( + , + ); + const img = document.querySelector("img"); + expect(img).toBeInTheDocument(); + expect(img?.getAttribute("src")).toBe( + "/api/v1/files/coll-1/att-1/photo-token", + ); + }); + + it("renders 'no preview' list when attachments are not PDF/image", () => { + render( + , + ); + expect(screen.getByText(/no preview/i)).toBeInTheDocument(); + expect(screen.getByText("doc.docx")).toBeInTheDocument(); + }); }); diff --git a/src/components/orders-review-modal.tsx b/src/components/orders-review-modal.tsx index 711ffd9..cbcb44c 100644 --- a/src/components/orders-review-modal.tsx +++ b/src/components/orders-review-modal.tsx @@ -514,7 +514,10 @@ export function OrdersReviewModal({ ); } -// --- Source-of-truth panel (filled out in Task 5) ------------------------- +// --- Source-of-truth panel ----------------------------------------------- +// Inline preview of the source attachment so the operator can scan +// the actual PDF/image against the order items in one screen. +// Falls back to the email body when no attachments exist. function SourceOfTruth({ quoteEmail, attachments, @@ -523,32 +526,143 @@ function SourceOfTruth({ attachments: QuoteAttachment[]; }) { if (attachments.length === 0) { - return ( -
-
- - Email -
-

{quoteEmail.subject || "(no subject)"}

-
-          {quoteEmail.body || "(no body)"}
-        
-
- ); + return ; } + + // Pick the first PDF if any, else first image, else the first + // entry of any other kind (which gets the "no preview" treatment). + const firstPdf = attachments.find((a) => a.kind === "pdf"); + const firstImage = attachments.find((a) => a.kind === "image"); + const firstOther = attachments.find( + (a) => a.kind !== "pdf" && a.kind !== "image", + ); + + const previewTarget = firstPdf ?? firstImage ?? null; + const othersAreUnpreviewable = attachments.some( + (a) => a.kind !== "pdf" && a.kind !== "image", + ); + return ( -
-
+
+
- Attachments ({attachments.length}) — preview added in Task 5 + Attachments ({attachments.length})
-
    + + {/* Tab strip — one chip per attachment, primary chip = previewTarget */} + {attachments.length > 1 && ( +
    + {attachments.map((a) => { + const isActive = previewTarget && a.id === previewTarget.id; + const previewable = a.kind === "pdf" || a.kind === "image"; + return ( + + {previewable ? "●" : "○"} {a.filename} + + ); + })} +
    + )} + + {previewTarget && previewTarget.kind === "pdf" && ( +