mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 06:53:12 +00:00
fix(work-orders): toast after assigned-to save on the board
Show the save toast only after the board PATCH succeeds.
This commit is contained in:
parent
0ee2879234
commit
7e1dcb7a36
5 changed files with 219 additions and 3 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import type { CSSProperties } from "react";
|
||||
import type { LocationOption, WorkOrderUser } from "@/domain/work-orders/types/work-order";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { applyAssignedToTableSave } from "@/domain/work-orders/utils/work-order-feedback-toasts";
|
||||
import { AssignedToCell } from "./cells/assigned-to-cell";
|
||||
import { DateCell } from "./cells/date-cell";
|
||||
import { DueDateCell } from "./cells/due-date-cell";
|
||||
|
|
@ -111,9 +112,7 @@ export function WoTableRowCells({
|
|||
dispatcherId={row.dispatcherId}
|
||||
users={users}
|
||||
highlight={!row.dispatcherId}
|
||||
onSave={(id, name) =>
|
||||
handlers.onPatch(row.id, { dispatcherId: id, dispatcherName: name })
|
||||
}
|
||||
onSave={(id, name) => applyAssignedToTableSave(handlers.onPatch, row.id, id, name)}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
|
|
|
|||
|
|
@ -80,3 +80,12 @@ export function applyVendorTableSave(
|
|||
onSuccess: isAssignedVendorId(fields.vendorId) ? notifyVendorAssigned : notifyWorkOrderSaved,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyAssignedToTableSave(
|
||||
onPatch: PersistPatchFn,
|
||||
id: string | number,
|
||||
dispatcherId: string,
|
||||
dispatcherName: string,
|
||||
) {
|
||||
onPatch(id, { dispatcherId, dispatcherName }, { onSuccess: notifyWorkOrderSaved });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import { fireEvent, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { WoTableRowCells } from "@/app/(protected)/workorders/_components/list/table/wo-table-row-cells";
|
||||
import type { WoTableRowHandlers } from "@/app/(protected)/workorders/_components/list/table/wo-table-row";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { notifyWorkOrderSaved } from "@/domain/work-orders/utils/work-order-feedback-toasts";
|
||||
import { renderWithProviders } from "@/test/test-utils";
|
||||
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/site-cell", () => ({
|
||||
SiteCell: () => null,
|
||||
}));
|
||||
vi.mock(
|
||||
"@/app/(protected)/workorders/_components/list/table/cells/editable-wo-number-cell",
|
||||
() => ({
|
||||
EditableWONumberCell: () => null,
|
||||
}),
|
||||
);
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/type-cell", () => ({
|
||||
TypeCell: () => null,
|
||||
}));
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/date-cell", () => ({
|
||||
DateCell: () => null,
|
||||
}));
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/due-date-cell", () => ({
|
||||
DueDateCell: () => null,
|
||||
}));
|
||||
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell", () => ({
|
||||
AssignedToCell: ({ onSave }: { onSave: (id: string, name: string) => void }) => (
|
||||
<button type="button" onClick={() => onSave("u2", "Admin test")}>
|
||||
persist-assigned
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
const row = { id: 7, dispatcherId: "u1" } as WorkOrderTableRow;
|
||||
|
||||
describe("WoTableRowCells assigned-to persist", () => {
|
||||
it("forwards assigned-to save through patch onSuccess instead of onPatchRow", () => {
|
||||
const onPatch = vi.fn();
|
||||
const onPatchRow = vi.fn();
|
||||
const handlers = { onPatch } as unknown as WoTableRowHandlers;
|
||||
|
||||
renderWithProviders(
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<WoTableRowCells
|
||||
row={row}
|
||||
allRows={[]}
|
||||
users={[]}
|
||||
sites={[]}
|
||||
handlers={handlers}
|
||||
onPatchRow={onPatchRow}
|
||||
/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>,
|
||||
{ withAuth: false },
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "persist-assigned" }));
|
||||
|
||||
expect(onPatchRow).not.toHaveBeenCalled();
|
||||
expect(onPatch).toHaveBeenCalledWith(
|
||||
7,
|
||||
{ dispatcherId: "u2", dispatcherName: "Admin test" },
|
||||
{ onSuccess: notifyWorkOrderSaved },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { act, renderHook, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { toast } from "react-toastify";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
import { useWorkOrderTableMutations } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
|
||||
import {
|
||||
applyAssignedToTableSave,
|
||||
workOrderSavedMessage,
|
||||
} from "@/domain/work-orders/utils/work-order-feedback-toasts";
|
||||
|
||||
const patchBoardField = vi.fn();
|
||||
|
||||
vi.mock("@/domain/work-orders/api/work-orders-api", () => ({
|
||||
workOrdersApi: {
|
||||
patchBoardField: (...args: unknown[]) => patchBoardField(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("react-toastify", () => ({
|
||||
toast: { error: vi.fn(), success: vi.fn(), warning: vi.fn() },
|
||||
}));
|
||||
|
||||
const ROW = {
|
||||
id: 7,
|
||||
dispatcherId: "u1",
|
||||
dispatcherName: "Admin",
|
||||
rowVersion: "v1",
|
||||
} as WorkOrderTableRow;
|
||||
|
||||
function makeWrapper() {
|
||||
const client = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
|
||||
});
|
||||
return function Wrapper({ children }: { children: ReactNode }) {
|
||||
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
|
||||
};
|
||||
}
|
||||
|
||||
describe("useWorkOrderTableMutations assigned-to toast", () => {
|
||||
beforeEach(() => {
|
||||
patchBoardField.mockReset();
|
||||
vi.mocked(toast.success).mockClear();
|
||||
vi.mocked(toast.error).mockClear();
|
||||
});
|
||||
|
||||
it("toasts assigned-to save only after the board PATCH succeeds", async () => {
|
||||
let release: () => void = () => undefined;
|
||||
const gate = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
patchBoardField.mockImplementation(async () => {
|
||||
await gate;
|
||||
return { ...ROW, dispatcherId: "u2", dispatcherName: "Admin test", rowVersion: "v2" };
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useWorkOrderTableMutations({
|
||||
onPatch: () => undefined,
|
||||
clearPatch: () => undefined,
|
||||
getRow: () => ROW,
|
||||
}),
|
||||
{ wrapper: makeWrapper() },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
applyAssignedToTableSave(result.current.patchField, 7, "u2", "Admin test");
|
||||
});
|
||||
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
release();
|
||||
await waitFor(() => expect(toast.success).toHaveBeenCalledWith(workOrderSavedMessage()));
|
||||
});
|
||||
|
||||
it("does not toast assigned-to save when the board PATCH fails", async () => {
|
||||
patchBoardField.mockRejectedValue(new Error("Failed to update work order"));
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useWorkOrderTableMutations({
|
||||
onPatch: () => undefined,
|
||||
clearPatch: () => undefined,
|
||||
getRow: () => ROW,
|
||||
}),
|
||||
{ wrapper: makeWrapper() },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
applyAssignedToTableSave(result.current.patchField, 7, "u2", "Admin test");
|
||||
});
|
||||
|
||||
await waitFor(() => expect(toast.error).toHaveBeenCalled());
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -2,10 +2,12 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|||
import { toast } from "react-toastify";
|
||||
import { toVendorTablePatch } from "@/domain/work-orders/utils/vendor-assignment-patch";
|
||||
import {
|
||||
applyAssignedToTableSave,
|
||||
applyVendorTableSave,
|
||||
isVendorAssignmentPatch,
|
||||
notifySlideOverSaveSuccess,
|
||||
notifyVendorAssigned,
|
||||
notifyWorkOrderSaved,
|
||||
patchWorkOrderAsCanceled,
|
||||
patchWorkOrderAsCompleted,
|
||||
vendorAssignedMessage,
|
||||
|
|
@ -137,6 +139,45 @@ describe("work-order-feedback-toasts", () => {
|
|||
expect(toast.success).not.toHaveBeenCalledWith(vendorAssignedMessage());
|
||||
});
|
||||
|
||||
it("toasts generic save after assigned-to persist onSuccess", () => {
|
||||
const onPatch = vi.fn();
|
||||
applyAssignedToTableSave(onPatch, 7, "u2", "Admin test");
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith(
|
||||
7,
|
||||
{ dispatcherId: "u2", dispatcherName: "Admin test" },
|
||||
{ onSuccess: notifyWorkOrderSaved },
|
||||
);
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
|
||||
const { onSuccess } = onPatch.mock.calls[0][2] as { onSuccess: () => void };
|
||||
onSuccess();
|
||||
expect(toast.success).toHaveBeenCalledWith(workOrderSavedMessage());
|
||||
expect(toast.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("toasts the same save copy after unassign persist onSuccess", () => {
|
||||
const onPatch = vi.fn();
|
||||
applyAssignedToTableSave(onPatch, 7, "", "");
|
||||
|
||||
expect(onPatch).toHaveBeenCalledWith(
|
||||
7,
|
||||
{ dispatcherId: "", dispatcherName: "" },
|
||||
{ onSuccess: notifyWorkOrderSaved },
|
||||
);
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
|
||||
const { onSuccess } = onPatch.mock.calls[0][2] as { onSuccess: () => void };
|
||||
onSuccess();
|
||||
expect(toast.success).toHaveBeenCalledWith(workOrderSavedMessage());
|
||||
});
|
||||
|
||||
it("does not toast assigned-to save when persist onSuccess is skipped", () => {
|
||||
const onPatch = vi.fn();
|
||||
applyAssignedToTableSave(onPatch, 7, "u2", "Admin test");
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("completes a work order without a success toast", () => {
|
||||
const patchField = vi.fn();
|
||||
patchWorkOrderAsCompleted(patchField, 42);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue