diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab.tsx index cf231f2a..eee8b3ec 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab.tsx @@ -50,16 +50,23 @@ type SlideOverExtrasTabProps = { onCategorize?: (id: string, category: UploadableMediaCategory) => void; }; -let lastExtraDocObjectUrl: string | null = null; - async function openExtraDocContent(workOrderId: string | number, mediaId: string): Promise { - const blob = await workOrdersApi.getMediaContent(workOrderId, mediaId); - if (lastExtraDocObjectUrl) { - URL.revokeObjectURL(lastExtraDocObjectUrl); + const tab = window.open("about:blank", "_blank"); + if (!tab) { + toast.error("Unable to open this document. Please allow pop-ups and try again."); + return; + } + + try { + const blob = await workOrdersApi.getMediaContent(workOrderId, mediaId); + const objectUrl = URL.createObjectURL(blob); + tab.opener = null; + tab.location.href = objectUrl; + window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000); + } catch { + tab.close(); + toast.error("Unable to open this document."); } - const objectUrl = URL.createObjectURL(blob); - lastExtraDocObjectUrl = objectUrl; - window.open(objectUrl, "_blank", "noopener,noreferrer"); } function ExtraDocRow({ @@ -98,9 +105,7 @@ function ExtraDocRow({ if (workOrderId == null) { return; } - void openExtraDocContent(workOrderId, file.id).catch(() => { - toast.error("Unable to open this document."); - }); + void openExtraDocContent(workOrderId, file.id); }} className="min-w-0 flex-1 truncate text-left hover:underline disabled:cursor-not-allowed disabled:opacity-50" style={{ fontSize: 12, color: "var(--foreground)" }} diff --git a/src/test/app/(protected)/workorders/slide-over-extras-aveta.test.tsx b/src/test/app/(protected)/workorders/slide-over-extras-aveta.test.tsx index d3a6d09c..f788e250 100644 --- a/src/test/app/(protected)/workorders/slide-over-extras-aveta.test.tsx +++ b/src/test/app/(protected)/workorders/slide-over-extras-aveta.test.tsx @@ -1,5 +1,6 @@ import { fireEvent, screen } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { toast } from "react-toastify"; import { SlideOverExtrasTab } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab"; import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api"; import type { MediaFile } from "@/domain/work-orders/types/work-order-media"; @@ -82,7 +83,12 @@ describe("SlideOverExtrasTab Aveta", () => { const getMediaContent = vi.spyOn(workOrdersApi, "getMediaContent").mockResolvedValue(blob); const createObjectURL = vi.fn(() => "blob:authorized"); vi.stubGlobal("URL", { ...URL, createObjectURL, revokeObjectURL: vi.fn() }); - const open = vi.fn(); + const tab = { + opener: window, + location: { href: "about:blank" }, + close: vi.fn(), + }; + const open = vi.fn(() => tab); vi.stubGlobal("open", open); renderWithProviders( @@ -103,7 +109,35 @@ describe("SlideOverExtrasTab Aveta", () => { expect(getMediaContent).toHaveBeenCalledWith(10, "m1"); }); expect(createObjectURL).toHaveBeenCalledWith(blob); - expect(open).toHaveBeenCalledWith("blob:authorized", "_blank", "noopener,noreferrer"); + expect(open).toHaveBeenCalledWith("about:blank", "_blank"); + expect(tab.opener).toBeNull(); + expect(tab.location.href).toBe("blob:authorized"); + }); + + it("shows an error when the browser blocks the document tab", async () => { + const getMediaContent = vi.spyOn(workOrdersApi, "getMediaContent"); + const toastError = vi.spyOn(toast, "error").mockImplementation(vi.fn()); + vi.stubGlobal( + "open", + vi.fn(() => null), + ); + + renderWithProviders( + , + ); + + fireEvent.click(screen.getByRole("button", { name: "safety-form.pdf" })); + + expect(toastError).toHaveBeenCalledWith( + "Unable to open this document. Please allow pop-ups and try again.", + ); + expect(getMediaContent).not.toHaveBeenCalled(); }); it("shows Extra Docs accept copy with PDF DOC and 50MB", () => {