diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-actions.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-actions.tsx index 51c262be..2853c205 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-actions.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-actions.tsx @@ -8,13 +8,29 @@ type SlideOverCompletionActionsProps = { docStatus: CompletionDocStatus; rowVersion?: string | number | null; completionPending: boolean; + /** Completion documents require a selected primary service. */ + hasService: boolean; hasUploadHandler: boolean; pdfInputRef: RefObject; onPdfSelected: (files: FileList | null) => void; onOpenCompletionDoc: () => void; }; -function DocStatusButtonContent({ docStatus }: { docStatus: CompletionDocStatus }) { +function DocStatusButtonContent({ + docStatus, + hasService, +}: { + docStatus: CompletionDocStatus; + hasService: boolean; +}) { + if (!hasService) { + return ( + <> + + Select service + + ); + } if (docStatus === "Yes") { return ( <> @@ -43,6 +59,7 @@ export function SlideOverCompletionActions({ docStatus, rowVersion, completionPending, + hasService, hasUploadHandler, pdfInputRef, onPdfSelected, @@ -83,10 +100,17 @@ export function SlideOverCompletionActions({ + {!hasService && ( + + Select a service before opening the completion document. + + )} ); } diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx index c44465b3..cf89d2bf 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx @@ -6,6 +6,7 @@ import type { UploadableMediaCategory } from "@/domain/work-orders/mappers/work- import type { WorkOrderBoardCompletion } from "@/domain/work-orders/types/work-order-board-detail"; import type { MediaFile } from "@/domain/work-orders/types/work-order-media"; import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; +import { hasCompletionDocService } from "@/domain/work-orders/utils/completion-doc-service-gate"; import { hasCompletionMediaGallery } from "@/domain/work-orders/utils/partition-slide-over-media"; import { formatMediaSizeLimit, @@ -126,6 +127,7 @@ export function SlideOverCompletionTab({ docStatus={docStatus} rowVersion={row.rowVersion} completionPending={completionPending} + hasService={hasCompletionDocService(row.pm)} hasUploadHandler={Boolean(onUploadCompletionPdf)} pdfInputRef={pdfInputRef} onPdfSelected={handlePdfSelected} diff --git a/src/test/app/(protected)/workorders/slide-over-completion-service-gate.test.tsx b/src/test/app/(protected)/workorders/slide-over-completion-service-gate.test.tsx new file mode 100644 index 00000000..d87162fe --- /dev/null +++ b/src/test/app/(protected)/workorders/slide-over-completion-service-gate.test.tsx @@ -0,0 +1,46 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { SlideOverCompletionTab } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab"; +import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; + +function renderTab(pm: string, onOpenCompletionDoc = vi.fn()) { + const row = { id: "1", type: "PM", pm, docStatus: "No", rowVersion: "1" } as WorkOrderTableRow; + render( + , + ); + return onOpenCompletionDoc; +} + +describe("SlideOverCompletionTab service gate", () => { + it("disables the completion document button until a service is selected", () => { + const onOpen = renderTab(" "); + + const button = screen.getByRole("button", { name: /select service/i }); + expect(button).toBeDisabled(); + expect( + screen.getByText("Select a service before opening the completion document."), + ).toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: /open completion document/i }), + ).not.toBeInTheDocument(); + + fireEvent.click(button); + expect(onOpen).not.toHaveBeenCalled(); + }); + + it("opens the completion document when a service is selected", () => { + const onOpen = renderTab("HVAC Inspection"); + + const button = screen.getByRole("button", { name: /open completion document/i }); + expect(button).toBeEnabled(); + expect(screen.queryByText(/select service/i)).not.toBeInTheDocument(); + + fireEvent.click(button); + expect(onOpen).toHaveBeenCalledTimes(1); + }); +});