fix(workorders): say Site dialog saves to the work order only when the site did not load

When the site detail request fails, or the user types before it loads,
the dialog falls back to saving this work order only and never calls
updateContactInfo. The POC helper text still said contacts and notes were
saved to the site record, so a dispatcher could believe every work order
at the site now had the new contact. The helper text now follows the
site-record sync state and says the change applies to this work order
only whenever that fallback is active, including after a later refetch
succeeds.
This commit is contained in:
Alexandre Brandizzi 2026-09-25 13:39:25 -03:00
parent 7efc9e8914
commit c0e484275d
4 changed files with 29 additions and 5 deletions

View file

@ -10,12 +10,23 @@ type SiteDialogPocSectionProps = {
primaryRequired?: boolean;
/** Edits go to the site record rather than only this work order. */
editsSiteRecord?: boolean;
/** The site record did not load, so Save writes this work order only. */
siteRecordUnavailable?: boolean;
onPocNameChange: (value: string) => void;
onPocPhoneChange: (value: string) => void;
onPocNotesChange: (value: string) => void;
onAdditionalContactsChange: (contacts: WorkOrderAdditionalContact[]) => void;
};
function helperText(editsSiteRecord: boolean, siteRecordUnavailable: boolean): string {
if (!editsSiteRecord) {
return "Auto-filled from site records when empty. Additional contacts persist on save.";
}
return siteRecordUnavailable
? "Site record unavailable — changes apply to this work order only."
: "Contacts and notes are saved to the site record and apply to all its work orders.";
}
export function SiteDialogPocSection({
pocName,
pocPhone,
@ -24,6 +35,7 @@ export function SiteDialogPocSection({
showPrimaryErrors = false,
primaryRequired = false,
editsSiteRecord = false,
siteRecordUnavailable = false,
onPocNameChange,
onPocPhoneChange,
onPocNotesChange,
@ -41,11 +53,7 @@ export function SiteDialogPocSection({
onPocPhoneChange={onPocPhoneChange}
onPocNotesChange={onPocNotesChange}
onAdditionalContactsChange={onAdditionalContactsChange}
helperText={
editsSiteRecord
? "Contacts and notes are saved to the site record and apply to all its work orders."
: "Auto-filled from site records when empty. Additional contacts persist on save."
}
helperText={helperText(editsSiteRecord, siteRecordUnavailable)}
/>
);
}

View file

@ -139,6 +139,7 @@ export function SiteDialog({
showPrimaryErrors={s.requiresPoc && s.showErrors}
primaryRequired={s.requiresPoc}
editsSiteRecord={s.editsSiteRecord}
siteRecordUnavailable={s.siteRecordUnavailable}
onPocNameChange={s.setPn}
onPocPhoneChange={s.setPp}
onPocNotesChange={s.setNotes}

View file

@ -95,6 +95,9 @@ export function useSiteDialogState({
locationDetailError: liveLocationDetailError,
fields,
});
// The site failed to load (or the user typed before it did), so Save writes this work order only.
const siteRecordUnavailable =
editsSiteRecord && Boolean(locId) && !siteRecord.synced && !siteRecord.loading;
const updateSite = useUpdateSiteContactInfo();
const pocMissing = !pn.trim() || !pp.trim();
const requiresPoc = createMode || editsSiteRecord;
@ -166,6 +169,7 @@ export function useSiteDialogState({
saving: updateSite.isPending,
siteRecordLoading: siteRecord.loading,
editsSiteRecord,
siteRecordUnavailable,
requiresPoc,
setPn: fields.setPnDirty,
setPp: fields.setPpDirty,

View file

@ -34,6 +34,10 @@ const dallas: Location = {
const sites = [{ id: "1", name: "DAL1" }];
const SITE_RECORD_COPY =
"Contacts and notes are saved to the site record and apply to all its work orders.";
const SITE_UNAVAILABLE_COPY = "Site record unavailable — changes apply to this work order only.";
function renderDialog(overrides: Partial<Parameters<typeof SiteDialog>[0]> = {}) {
const onSave = vi.fn();
const onOpenChange = vi.fn();
@ -87,6 +91,8 @@ describe("Work order Site dialog edits the site record", () => {
expect(screen.getByTitle("Site location map")).toBeInTheDocument();
expect(screen.getByText("(214) 555-0100")).toBeInTheDocument();
expect(screen.getByText("dal1@acme.test")).toBeInTheDocument();
expect(screen.getByText(SITE_RECORD_COPY)).toBeInTheDocument();
expect(screen.queryByText(SITE_UNAVAILABLE_COPY)).not.toBeInTheDocument();
});
it("locks the contact fields until the site loads, so typed input is never replaced", async () => {
@ -118,12 +124,17 @@ describe("Work order Site dialog edits the site record", () => {
const { onSave, queryClient } = renderDialog();
await waitFor(() => expect(primaryField("POC name")).toBeEnabled());
expect(screen.getByText(SITE_UNAVAILABLE_COPY)).toBeInTheDocument();
expect(screen.queryByText(SITE_RECORD_COPY)).not.toBeInTheDocument();
fireEvent.change(primaryField("POC name"), { target: { value: "John" } });
await queryClient.refetchQueries({ queryKey: queryKeys.locations.detail("1") });
await waitFor(() => expect(getById).toHaveBeenCalledTimes(2));
await screen.findByText("(214) 555-0100");
// The typed input stays on the work order, so the copy still says so after the refetch.
expect(screen.getByText(SITE_UNAVAILABLE_COPY)).toBeInTheDocument();
expect(screen.queryByText(SITE_RECORD_COPY)).not.toBeInTheDocument();
expect(primaryField("POC name")).toHaveValue("John");
expect(primaryField("POC name")).toBeEnabled();
expect(primaryField("POC phone")).toHaveValue("(000) 000-0000");