fix(work-orders): parse start-only appointment times and refresh visual baselines
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 184 KiB |
|
|
@ -151,6 +151,13 @@ export function parseApptTime(appt: string): { start: string; end: string } {
|
|||
}
|
||||
return { start: `${pad2(h)}:${single[2]}`, end: "" };
|
||||
}
|
||||
const startOnly = appt.trim().match(/^(\d{1,2}):(\d{2})$/);
|
||||
if (startOnly) {
|
||||
return {
|
||||
start: `${startOnly[1].padStart(2, "0")}:${startOnly[2]}`,
|
||||
end: "",
|
||||
};
|
||||
}
|
||||
return { start: "", end: "" };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ describe("commitApptTimeSelection", () => {
|
|||
expect(onSave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clears a stored start-only time when Start is empty", () => {
|
||||
const onSave = vi.fn();
|
||||
commitApptTimeSelection("", "", "09:00", onSave);
|
||||
expect(onSave).toHaveBeenCalledWith("");
|
||||
});
|
||||
|
||||
it("saves start only when end is omitted", () => {
|
||||
const onSave = vi.fn();
|
||||
commitApptTimeSelection("09:00", "", "", onSave);
|
||||
|
|
@ -93,4 +99,16 @@ describe("ApptTimeCell", () => {
|
|||
|
||||
expect(onSave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("hydrates start-only stored times so Done can clear them", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onSave = vi.fn();
|
||||
render(<ApptTimeCell apptTime="09:00" onSave={onSave} />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: fmt12("09:00") }));
|
||||
expect(await screen.findByText("Pick end (optional)")).toBeInTheDocument();
|
||||
await user.click(screen.getByRole("button", { name: /^done$/i }));
|
||||
|
||||
expect(onSave).toHaveBeenCalledWith("09:00");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,10 +3,21 @@ import {
|
|||
fmtDateRange,
|
||||
fmtShortDate,
|
||||
pad2,
|
||||
parseApptTime,
|
||||
rangeDayCount,
|
||||
todayIso,
|
||||
} from "@/domain/work-orders/utils/wizard-date-utils";
|
||||
|
||||
describe("parseApptTime", () => {
|
||||
it("parses a 24-hour start-only value", () => {
|
||||
expect(parseApptTime("09:00")).toEqual({ start: "09:00", end: "" });
|
||||
});
|
||||
|
||||
it("leaves free-text labels unparsed", () => {
|
||||
expect(parseApptTime("AM before 2pm")).toEqual({ start: "", end: "" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("fmtShortDate", () => {
|
||||
it("formats ISO dates as Mon D", () => {
|
||||
expect(fmtShortDate("2026-06-08")).toBe("Jun 8");
|
||||
|
|
|
|||