diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-body.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-body.tsx index bfbde81a..7e51c960 100644 --- a/src/app/(protected)/workorders/_components/list/table/wo-table-body.tsx +++ b/src/app/(protected)/workorders/_components/list/table/wo-table-body.tsx @@ -23,8 +23,19 @@ export function WoTableBody({ tableData, renderRow, renderInlineRow }: WoTableBo const unassignedRows = tableData.unassigned ?? []; const showUnscheduled = (tableData.unscheduled?.length ?? 0) > 0 || tableData.draftDayKey === UNSCHEDULED_DAY_KEY; + // SH-165: "still loading" and "zero unassigned" are distinct states. Without + // this split the pin announced "No unassigned work orders." while the queue + // was still paging board/search on first mount. + const showUnassignedLoading = + tableData.unassignedOpen && + unassignedRows.length === 0 && + !tableData.unassignedError && + tableData.unassignedLoading; const showUnassignedEmpty = - tableData.unassignedOpen && unassignedRows.length === 0 && !tableData.unassignedError; + tableData.unassignedOpen && + unassignedRows.length === 0 && + !tableData.unassignedError && + !tableData.unassignedLoading; return ( @@ -47,6 +58,15 @@ export function WoTableBody({ tableData, renderRow, renderInlineRow }: WoTableBo )} + {showUnassignedLoading && ( + + + + Loading unassigned work orders… + + + + )} {showUnassignedEmpty && ( diff --git a/src/app/(protected)/workorders/_hooks/use-work-orders-board-source.ts b/src/app/(protected)/workorders/_hooks/use-work-orders-board-source.ts index 11771ca7..914bf835 100644 --- a/src/app/(protected)/workorders/_hooks/use-work-orders-board-source.ts +++ b/src/app/(protected)/workorders/_hooks/use-work-orders-board-source.ts @@ -23,6 +23,7 @@ export type WorkOrdersBoardSource = { data: WorkOrderBoardListResult | undefined; unassignedQueue: WorkOrderTableRow[]; unassignedError: Error | null; + unassignedLoading: boolean; isLoading: boolean; isFetching: boolean; error: Error | null; @@ -75,6 +76,7 @@ export function useWorkOrdersBoardSource(filters: FiltersReturn): WorkOrdersBoar } = useWorkOrdersBoardSearch(resolvedSearchParams ?? { datePreset: "ThisWeek" }, advActive); const { data: queueData, + isLoading: queueLoading, isFetching: queueFetching, error: queueError, refetch: refetchQueue, @@ -94,6 +96,10 @@ export function useWorkOrdersBoardSource(filters: FiltersReturn): WorkOrdersBoar data: advActive ? searchData : boardData, unassignedQueue: advActive ? EMPTY_UNASSIGNED_QUEUE : (queueData ?? EMPTY_UNASSIGNED_QUEUE), unassignedError: advActive ? null : (queueError ?? null), + // SH-165: the pinned queue has its own first-load state. Folding it into + // isFetching only made "still paging board/search" indistinguishable from + // "zero unassigned", so the pin announced an empty queue while loading. + unassignedLoading: advActive ? false : queueLoading, isLoading: advActive ? searchLoading : boardLoading, isFetching: advActive ? searchFetching : boardFetching || queueFetching, error: advActive ? searchError : boardError, diff --git a/src/app/(protected)/workorders/_hooks/use-work-orders-table-data.ts b/src/app/(protected)/workorders/_hooks/use-work-orders-table-data.ts index 469fd465..752272de 100644 --- a/src/app/(protected)/workorders/_hooks/use-work-orders-table-data.ts +++ b/src/app/(protected)/workorders/_hooks/use-work-orders-table-data.ts @@ -26,6 +26,7 @@ export function useWorkOrdersTableData(filters: FiltersReturn) { data, unassignedQueue, unassignedError, + unassignedLoading, isLoading, isFetching, error, @@ -107,6 +108,7 @@ export function useWorkOrdersTableData(filters: FiltersReturn) { advResults, unassigned, unassignedError, + unassignedLoading, unscheduled, displayGroups, searchActive, diff --git a/src/test/app/(protected)/workorders/wo-table-unassigned-queue.test.tsx b/src/test/app/(protected)/workorders/wo-table-unassigned-queue.test.tsx index d010346d..66a61208 100644 --- a/src/test/app/(protected)/workorders/wo-table-unassigned-queue.test.tsx +++ b/src/test/app/(protected)/workorders/wo-table-unassigned-queue.test.tsx @@ -76,6 +76,7 @@ function baseTableData( advResults: [], unassigned: [], unassignedError: null, + unassignedLoading: false, unscheduled: [], displayGroups: [], searchActive: false, @@ -308,6 +309,36 @@ describe("WoTableBody Unassigned queue (SH-165)", () => { expect(screen.queryByText("WED · 07/15")).not.toBeInTheDocument(); }); + it("distinguishes a still-loading queue from an empty one", () => { + render( + + null} + renderInlineRow={() => null} + /> +
, + ); + + expect(screen.getByText(/Loading unassigned work orders/)).toBeInTheDocument(); + expect(screen.queryByText("No unassigned work orders.")).not.toBeInTheDocument(); + }); + + it("announces an empty queue only once loading has settled", () => { + render( + + null} + renderInlineRow={() => null} + /> +
, + ); + + expect(screen.getByText("No unassigned work orders.")).toBeInTheDocument(); + expect(screen.queryByText(/Loading unassigned work orders/)).not.toBeInTheDocument(); + }); + it("keeps the Unassigned pin visible when the queue query fails", () => { render(