Copying only the version left the cached detail with pre-save fields, so a
queued second panel save could send them with a fresh version and silently
revert what the first save wrote (e.g. POC name).
Keep the last saved row per work order so a queued panel save does not depend
on the board-detail cache, and pass the completion-doc row as a fallback so
saving a completion document works after the order leaves the list.
A save from the detail panel left the cached detail on the pre-save version
until its refetch settled, so a quick second save, Cancel WO or Complete could
conflict. The saved row's version now updates the detail cache before the patch
queue releases, and panel patches read their base at run time.
The board patch read the work order version from the loaded list only. Once a
save moved the order out of that list (e.g. to another week), the next save,
Cancel or Complete from the open panel failed for lack of a version. The panel
now passes its server row as the fallback patch base.
All six vendor-portal dispatch mutation hooks lacked meta.suppressErrorToast,
so a failed checklist toggle, signoff, comment, or uplift request/withdraw/
revise raised both the global MutationCache toast (leaking the raw server
message) and the section's own inline error, contradicting this ticket's
"no behavior change" goal versus main's try/catch-only handling.
Also fixes a related gap in checklist-section.tsx: updateItem.isError only
reflects the latest call on the shared mutation observer, so a failed toggle
whose item was not the most recent click showed no error at all. Tracks
failure locally instead, mirroring main's per-call error state.
Added regression coverage with the app's real query client (createAppQueryClient)
for comments, signoff, and uplift withdraw, asserting the inline message shows
and the global toast does not fire; plus a mixed pass/fail concurrent-toggle
case for the checklist.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TanStack Query v5 only runs a mutate() call's onSuccess for the latest
call on a shared observer, so checking two items before the first
request resolved silently dropped the first item's onItemUpdated call,
leaving the portal's checkbox out of sync with the server. Switch to
mutateAsync so each call's result is handled independently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A site saved before the contacts list existed maps with no contacts, only
the legacy contact/phone fields. The site-record sync read the contacts
list alone, so the dialog blanked the POC, blocked a notes-only Save until
the contact was retyped, and when the work order had no POC the legacy
autofill made the diff report a contact edit nobody made. The legacy
contact is now the site's main contact for display, baseline and request.
Save sends the values it had when clicked, so the dialog's fields are now
disabled while the site update is in flight instead of accepting edits
that neither save would include.
The site-update mutation already toasts its own failure, so it sets
meta.suppressErrorToast and the global mutation toast no longer repeats it.