mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 14:23:13 +00:00
fix(work-orders): align Past Due overlay and copy to Schedule On (SH-185)
Dispatchers were told to change Due Date, which cannot clear Past Due. Drive the table overlay from Schedule On and match the 422 contract.
This commit is contained in:
parent
4a95da32d2
commit
8d1a3340a8
4 changed files with 65 additions and 27 deletions
|
|
@ -49,7 +49,7 @@ export function StatusCell({
|
|||
|
||||
setAnchorEl(e.currentTarget);
|
||||
}}
|
||||
title="This WO missed its deadline. Update the Due Date to unlock status updates."
|
||||
title="This WO missed its deadline. Update Schedule On to unlock status updates."
|
||||
className="group/cell flex w-full cursor-pointer items-center gap-1.5 whitespace-nowrap leading-none transition-opacity hover:opacity-80"
|
||||
style={{ fontSize: 12.5, fontWeight: 600, color: "var(--status-pastdue-text)" }}
|
||||
>
|
||||
|
|
@ -83,7 +83,7 @@ export function StatusCell({
|
|||
color: "var(--status-pending-text)",
|
||||
}}
|
||||
>
|
||||
This work order is past due. Update the Due Date before the status can be updated.
|
||||
This work order is past due. Update Schedule On before the status can be updated.
|
||||
</Text>
|
||||
|
||||
<button
|
||||
|
|
@ -97,7 +97,7 @@ export function StatusCell({
|
|||
}}
|
||||
className="mt-2 block text-[11px] font-medium text-[var(--color-primary)]"
|
||||
>
|
||||
Update Due Date →
|
||||
Update Schedule On →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ import { weekRange } from "@/domain/work-orders/utils/list-week-utils";
|
|||
const TERMINAL_STATUSES = new Set(["Completed", "Canceled", "Done", "Cancelled"]);
|
||||
|
||||
export function computeIsPastDue(row: WorkOrderTableRow): boolean {
|
||||
if (!row.dueDate || TERMINAL_STATUSES.has(row.status)) {
|
||||
if (!row.scheduledOn || TERMINAL_STATUSES.has(row.status)) {
|
||||
return false;
|
||||
}
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
return row.dueDate < today;
|
||||
return row.scheduledOn.slice(0, 10) < today;
|
||||
}
|
||||
|
||||
export function enrichTableRow(row: WorkOrderTableRow): WorkOrderTableRow {
|
||||
return {
|
||||
...row,
|
||||
// Recompute from dueDate (same date-only rule as BE). Avoid sticky
|
||||
// `apiTrue || compute` so a future Due Date clears Past Due immediately.
|
||||
// Recompute from Schedule On (SH-185). Avoid sticky `apiTrue || compute`
|
||||
// so a future Schedule On clears Past Due immediately.
|
||||
isPastDue: computeIsPastDue(row),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { StatusCell } from "@/app/(protected)/workorders/_components/list/table/cells/status-cell";
|
||||
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
||||
|
||||
const pastDueRow = {
|
||||
id: "wo-1",
|
||||
status: "Scheduled",
|
||||
dispatcherId: "",
|
||||
dispatcherName: "",
|
||||
company: "",
|
||||
tech: "",
|
||||
completedDate: "",
|
||||
woNumber: "",
|
||||
pm: "",
|
||||
docStatus: "",
|
||||
mediaCount: 0,
|
||||
type: "PM",
|
||||
isPastDue: true,
|
||||
scheduledOn: "2026-06-01",
|
||||
dueDate: "2026-08-01",
|
||||
} as WorkOrderTableRow;
|
||||
|
||||
describe("StatusCell Past Due copy (SH-185)", () => {
|
||||
it("directs the dispatcher to update Schedule On, not Due Date", () => {
|
||||
render(<StatusCell wo={pastDueRow} onChangeStatus={vi.fn()} onOpenSlideOver={vi.fn()} />);
|
||||
|
||||
const trigger = screen.getByRole("button", { name: /past due/i });
|
||||
expect(trigger.getAttribute("title") ?? "").toContain("Schedule On");
|
||||
expect(trigger.getAttribute("title") ?? "").not.toMatch(/due date/i);
|
||||
|
||||
fireEvent.click(trigger);
|
||||
|
||||
expect(screen.getByText(/update schedule on before the status can be updated/i)).toBeTruthy();
|
||||
expect(screen.getByRole("button", { name: /update schedule on/i })).toBeTruthy();
|
||||
expect(screen.queryByText(/due date/i)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -156,43 +156,43 @@ describe("matchesAdvancedFilters", () => {
|
|||
});
|
||||
|
||||
describe("computeIsPastDue", () => {
|
||||
it("is true when dueDate is before today even if scheduledOn is future", () => {
|
||||
it("is true when scheduledOn is before today even if dueDate is future", () => {
|
||||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
dueDate: shiftIso(-1),
|
||||
scheduledOn: shiftIso(5),
|
||||
dueDate: shiftIso(5),
|
||||
scheduledOn: shiftIso(-1),
|
||||
status: "Scheduled",
|
||||
}),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is false when dueDate is future even if scheduledOn is past", () => {
|
||||
it("is false when scheduledOn is future even if dueDate is past", () => {
|
||||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
dueDate: shiftIso(5),
|
||||
scheduledOn: shiftIso(-1),
|
||||
dueDate: shiftIso(-1),
|
||||
scheduledOn: shiftIso(5),
|
||||
status: "Incomplete",
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when dueDate is empty", () => {
|
||||
it("is false when scheduledOn is empty", () => {
|
||||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
dueDate: "",
|
||||
scheduledOn: shiftIso(-3),
|
||||
dueDate: shiftIso(-3),
|
||||
scheduledOn: "",
|
||||
status: "Scheduled",
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is false for terminal statuses even with past dueDate", () => {
|
||||
it("is false for terminal statuses even with past scheduledOn", () => {
|
||||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
|
|
@ -205,19 +205,19 @@ describe("computeIsPastDue", () => {
|
|||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
dueDate: shiftIso(-2),
|
||||
scheduledOn: shiftIso(-2),
|
||||
status: "Canceled",
|
||||
}),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when dueDate is today", () => {
|
||||
it("is false when scheduledOn is today", () => {
|
||||
expect(
|
||||
computeIsPastDue(
|
||||
makeRow({
|
||||
dueDate: todayIso(),
|
||||
scheduledOn: shiftIso(-1),
|
||||
dueDate: shiftIso(-1),
|
||||
scheduledOn: todayIso(),
|
||||
status: "Scheduled",
|
||||
}),
|
||||
),
|
||||
|
|
@ -226,11 +226,11 @@ describe("computeIsPastDue", () => {
|
|||
});
|
||||
|
||||
describe("enrichTableRow", () => {
|
||||
it("recomputes isPastDue from dueDate and ignores sticky API true", () => {
|
||||
it("recomputes isPastDue from scheduledOn and ignores sticky API true", () => {
|
||||
const enriched = enrichTableRow(
|
||||
makeRow({
|
||||
dueDate: shiftIso(5),
|
||||
scheduledOn: shiftIso(-1),
|
||||
dueDate: shiftIso(-1),
|
||||
scheduledOn: shiftIso(5),
|
||||
isPastDue: true,
|
||||
status: "Scheduled",
|
||||
}),
|
||||
|
|
@ -238,11 +238,11 @@ describe("enrichTableRow", () => {
|
|||
expect(enriched.isPastDue).toBe(false);
|
||||
});
|
||||
|
||||
it("sets isPastDue true from past dueDate even when API flag is false", () => {
|
||||
it("sets isPastDue true from past scheduledOn even when API flag is false", () => {
|
||||
const enriched = enrichTableRow(
|
||||
makeRow({
|
||||
dueDate: shiftIso(-1),
|
||||
scheduledOn: shiftIso(5),
|
||||
dueDate: shiftIso(5),
|
||||
scheduledOn: shiftIso(-1),
|
||||
isPastDue: false,
|
||||
status: "Incomplete",
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue