merge origin/feat/SH-165-unscheduled-pin

This commit is contained in:
arthur.bassi 2026-08-18 17:44:39 -03:00
commit b0b94e8a53
3 changed files with 44 additions and 24 deletions

View file

@ -23,8 +23,14 @@ 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 && Boolean(tableData.unassignedLoading) && !tableData.unassignedError;
tableData.unassignedOpen &&
unassignedRows.length === 0 &&
!tableData.unassignedError &&
tableData.unassignedLoading;
const showUnassignedEmpty =
tableData.unassignedOpen &&
unassignedRows.length === 0 &&
@ -53,10 +59,10 @@ export function WoTableBody({ tableData, renderRow, renderInlineRow }: WoTableBo
</tr>
)}
{showUnassignedLoading && (
<tr>
<tr aria-busy="true">
<td colSpan={WO_TABLE_COLS.length} style={{ padding: "8px 14px" }}>
<Text variant="feedback" tone="muted">
Loading unassigned work orders.
Loading unassigned work orders…
</Text>
</td>
</tr>

View file

@ -76,7 +76,7 @@ export function useWorkOrdersBoardSource(filters: FiltersReturn): WorkOrdersBoar
} = useWorkOrdersBoardSearch(resolvedSearchParams ?? { datePreset: "ThisWeek" }, advActive);
const {
data: queueData,
isPending: queuePending,
isLoading: queueLoading,
isFetching: queueFetching,
error: queueError,
refetch: refetchQueue,
@ -96,7 +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),
unassignedLoading: advActive ? false : queuePending,
// 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,

View file

@ -309,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(
<table>
<WoTableBody
tableData={baseTableData({ unassignedLoading: true })}
renderRow={() => null}
renderInlineRow={() => null}
/>
</table>,
);
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(
<table>
<WoTableBody
tableData={baseTableData({ unassignedLoading: false })}
renderRow={() => null}
renderInlineRow={() => null}
/>
</table>,
);
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(
<table>
@ -351,25 +381,6 @@ describe("WoTableBody Unassigned queue (SH-165)", () => {
expect(screen.queryByTestId(/row-/)).not.toBeInTheDocument();
});
it("shows a loading state instead of empty while the unassigned queue is pending", () => {
render(
<table>
<WoTableBody
tableData={baseTableData({ unassignedLoading: true })}
renderRow={(row) => (
<tr key={String(row.id)} data-testid={`row-${row.id}`}>
<td>{row.woNumber}</td>
</tr>
)}
renderInlineRow={() => null}
/>
</table>,
);
expect(screen.getByText("Loading unassigned work orders.")).toBeInTheDocument();
expect(screen.queryByText("No unassigned work orders.")).not.toBeInTheDocument();
});
it("removes a pinned row immediately after assigning through the board PATCH path", async () => {
const user = userEvent.setup();
const pinned = makeRow({