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.
Site contacts carry only a name and a phone (LocationContact, and the
Sites page contact rows), so PATCH contact-info has nowhere to put an
additional contact's note. The Notes box on each additional contact card
let a dispatcher type one under copy saying contacts are saved to the
site record, but the note only reached this work order's copy and no
other work order at the site ever saw it.
The additional-contact Notes box is now hidden while the dialog edits the
site record. It stays where contacts are saved to the work order only:
inline create, the wizard, and an existing work order whose site record
failed to load.
Picking another site on an existing work order kept the old site's POC
name, phone and notes in the fields. When the new site's record failed
to load, the dialog never synced it, Save stayed enabled, and the work
order pointing at the new site was saved with the previous site's
contact and notes as its override.
A site switch now empties the POC name and phone for every work order,
and the notes for existing ones, so the fields hold either the new
site's record or what the dispatcher types for it.
The Site dialog loads extra contacts from the site record with their site
contact ids so the site request can update rows in place. buildSiteDialogPatch
copied those rows verbatim into the work-order patch, so onSave received
siteContactId (and half-filled rows) even though the type documents that the
id is never sent on work-order patches. The wire serializer already stripped
both, but the patch handed to the board did not.
Run the patch's extra contacts through normalizeAdditionalContacts, the same
helper the work-order serializer uses, so the patch carries only complete
name/phone/notes rows. An emptied list still clears the work order's copy.
The site request keeps its ids because it is built from the form fields.
The open work-order query keeps its cached result between dialog opens, so on
reopen the background refetch ran while isLoading was false and Delete was
enabled with the previous count. A work order created at the site since the
last open could be deleted past without a warning.
Treat an in-flight fetch as an unknown count: Delete and View open work orders
wait, the stale copy is hidden and the checking spinner shows until the fresh
count arrives.
Dismissing the edit dialog with Escape or the Close button while an update
is still in flight must not bring it back when the save lands. The per-call
mutate callbacks that reopen it in view mode are dropped by TanStack Query
once the dialog unmounts, so this pins that behaviour: the test fails if the
save is moved to mutateAsync().then(), which would reopen the dialog.
The delete dialog states the server's full open work-order count, but the
server returns at most 200 ids. "View open work orders" navigated with
that capped list, so a site with 240 open work orders showed 200, and an
empty list opened the unfiltered board.
The exact-id link is now used only when the ids cover the whole count.
Otherwise the link opens Work Orders filtered to the site and every open
status across all weeks, the ticket's "board filtered to that site". The
board drilldown now reads a `sites` param for this; `ids` still wins.
Switching to another site and back now loads the site record again, so its
extra contacts are not dropped on Save. The dialog also waits for the site
request to settle before syncing, so a record cached before an earlier save
is never shown or written back. Half-filled extra contacts stay off the site
record, as they already stay off the work order. The save path moves into
its own hook to keep the dialog state under the complexity limit.
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.
The contact lock only covered a pending load. After a failed getById the
fields unlock, and a later successful refetch (focus, invalidation) still
ran the apply effect and overwrote whatever the dispatcher had typed.
The apply effect now skips once the user has typed since the last reset or
site switch, so the dialog stays on the work order's values and saves them
to the work order only; the site's unseen notes and contacts are never
written back. Picking another site still clears the marker and adopts it.
SH-331 requires Street Address suggestions after 3 typed characters,
with a pick filling Street, City and State and unpicked typing kept as
free text. SH-272, which SH-331 consolidates, specifies those
suggestions as mocked, and the shared AddressAutocompleteField that
Vendors already uses (SH-271) was built for Sites to reuse. No Places
key is involved.
The Add Site modal and the detail panel's edit mode now render that
field. A pick writes all three values through their own controllers,
so the form marks them dirty and Save changes enables in edit mode.
The field gains an opt-in requiredMarker prop so Sites keeps its label
asterisk while Vendors stays without one.
- Delete sits in the view-mode footer for Admin and Scheduler only; the row delete icon is gone
- Confirmation states the open work-order count and links to exactly those work orders
- Sites without open work orders confirm with the plain permanent-removal copy
- A server refusal keeps the site and shows the error
The server honours only the id set when one is sent, so a facet applied during an ids drill-down showed as an active chip while doing nothing. Applying any facet now drops the selection; applying the panel unchanged keeps it. The ids param is also capped at the server limit whatever built the set.
- Sites page: Add Site modal with Site Code, Client, address, State/Area, optional Site Phone and Email, points of contact
- Duplicate Site Code shows inline under the field and keeps the modal open
- Row opens a non-modal detail panel; Street Address links to Google Maps; key-less map preview
- Edit switches the panel in place with Site Code read-only; Save needs a valid change, Cancel makes no call
- Site Phone is sent independently of the contacts; zip and title are carried through edits
- Retire the separate new/edit location pages
Use user-event without per-keystroke delays and a 15 s budget per test, as
the vendor create modal suite does, so the dialog tests don't time out on a
loaded runner.
Dispatchers see a one-line banner and a one-time toast while a Reactive/Emergency work order is past half of its SEV response window; the banner opens the board on exactly those work orders and dismisses for the session. Missed deadlines stay in the notification center until acknowledged, and Clear all skips them.
Adds a My Account page, reachable from the sidebar, with the signed-in
user's read-only details and a Change Password section. The new password
is checked live against the shared password rules (6+ characters, an
uppercase letter, a number and a special character), which the invite
registration flow reuses. The current password is still verified by the
server first, and its rejection is shown on the current password field.