mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-01 10:53:11 +00:00
fix(work-orders): distinguish loading from empty in Unassigned pin (SH-165)
The pinned Unassigned section folded the queue query's pending state into isFetching only, which the section never read. On first mount, once GET /board resolved while the queue was still paging GET /board/search, the pin announced "No unassigned work orders." instead of a loading state. Expose the queue query's own isLoading as unassignedLoading, thread it through the table data hook, and render an aria-busy loading row while it is true so the loading and empty states are separately observable.
This commit is contained in:
parent
dc9e146080
commit
ac9ce34101
4 changed files with 60 additions and 1 deletions
|
|
@ -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 (
|
||||
<tbody>
|
||||
|
|
@ -47,6 +58,15 @@ export function WoTableBody({ tableData, renderRow, renderInlineRow }: WoTableBo
|
|||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{showUnassignedLoading && (
|
||||
<tr aria-busy="true">
|
||||
<td colSpan={WO_TABLE_COLS.length} style={{ padding: "8px 14px" }}>
|
||||
<Text variant="feedback" tone="muted">
|
||||
Loading unassigned work orders…
|
||||
</Text>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{showUnassignedEmpty && (
|
||||
<tr>
|
||||
<td colSpan={WO_TABLE_COLS.length} style={{ padding: "8px 14px" }}>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue