Three review findings on the additive add path:
- Create-mode reload refreshed selectedRoster (including rowVersion) but left
the form company fields on their pre-conflict values, so a retry diffed stale
fields against the reloaded roster and could overwrite the concurrent company
update that caused the 409. Reload now resets the form from the fresh roster,
matching the select path.
- mapVendorRosterAdditivePatchToBackend copied companyFields verbatim while the
reconcile write mapper canonicalizes companyPhone, so PATCH and PUT could send
different phone shapes for the same input.
- emptyRosterTechnician seeded preferredContact "Phone" even though that
control was removed from the form, so filling the first blank row submitted a
fabricated value. append already omitted it; both paths now agree.
An empty tradeOptions array meant two different things: "facets query still in
flight" and "vocabulary genuinely unavailable". Both enabled fail-open free
text and showed the outage warning, so a normal cold fetch looked like an
outage. Anything committed in that window was added to knownTradesRef and stayed
selectable after the canonical list arrived, permanently undermining the gate.
Expose the facets query isLoading as tradesLoading, thread it to the field as
tradeOptionsLoading, and keep the canonical gate active while loading. The
outage warning now shows only once the query has settled empty.
The pinned Unassigned section folded the queue query's pending state into
isFetching only, which the section never read. On first mount, once GET /board
resolved while the queue was still paging GET /board/search, the pin announced
"No unassigned work orders." instead of a loading state.
Expose the queue query's own isLoading as unassignedLoading, thread it through
the table data hook, and render an aria-busy loading row while it is true so the
loading and empty states are separately observable.
Adversarial review found a last-writer-wins window: after a stale 409 the
roster is refetched while the form keeps its pre-conflict values, so the
form-vs-roster diff resent our stale value for any company field another
user changed in that window, silently reverting their edit.
Transmit only fields the user actually edited, taken from react-hook-form
dirty state. Selecting a company calls reset(), so loaded values are never
dirty and only genuine edits are sent.
dirtyFields is resolved during render: formState is a Proxy that only tracks
properties read at render time, so reading it inside the submit callback
returned empty. The vendors E2E caught that; unit tests could not, since
they pass the flags in directly.
* fix(work-orders): allow blank WO number in wizard create path
[recover] remove malicious eslint payload (was d0d08508)
* fix(work-orders): emit severity before type in board scalar PATCH ops
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(work-orders): drop vendor collateral from SH-192 lane
* fix(work-orders): add severity to unscheduled highlight fixture after dev merge
* refactor(work-orders): extract inline type/severity cell under function line cap
* fix(work-orders): add severity to comp-doc and media fixtures after dev merge
PR #45 introduced completion-doc, media and table-mutation fixtures that predate the required severity field, so the integrated head failed tsc. Fill severity on those row builders (null for PM, SEV 1 for the Emergency fixture) and type the mutation BASE_ROW instead of asserting it.
* test(work-orders): align merge fixes with severity field and WO# placeholder
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Arthur Bassi <bassi-arthurr@users.noreply.github.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
* fix(work-orders): address wizard create review findings
[recover] remove malicious eslint payload (was 166c63e4)
* fix(work-orders): remove legacy completedDate EditWorkorder path
[recover] remove malicious eslint payload (was f4e6132b)
* fix(work-orders): use local calendar day for completedDate
Restore todayIso() after parent merge reintroduced UTC slice, and keep the wizard-date-utils regression test.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): send wizard status and clear POC notes on site change
Map draft.status to lifecycleStatus on board create, and reset pocNotes with POC fields when the wizard site changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(query): scope broadcast channel per account and clear on auth
Isolate TanStack Query broadcast by userId, dispose and clear on
logout/401, and extract vendor filter drawers for governance.
* feat(work-orders): board wizard create with service notes
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): enforce single-service wizard create per SH-118
Use single service selection in the wizard and omit extraServices from board create.
* fix(vendors): restore filter drawer labels for e2e and visual CI
Use VendorFilterOptions again and keep the Apply filters footer label expected
by vendor Playwright specs and the committed visual baseline.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(work-orders): board wizard create with service notes
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): enforce single-service wizard create per SH-118
Use single service selection in the wizard and omit extraServices from board create.
* refactor(work-orders): extract wizard controller for maintainability gate
Move duplicate-WO wiring and lookup hooks out of NewWoWizard so the
changed-file function line cap passes after merging dev.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh onto current dev and resolve exact-head review blockers:
- Remove duplicate WorkOrderBoardRowApi.completedDate after merge with dev
- Add SH-114 Technician closability gate alongside company/vendor check
- Update wo-closability regressions for missing technician
* docs(work-orders): add pending BE contract checklist for design parity gaps
* feat(work-orders): confirm dialog when duplicate wo number detected
* fix(work-orders): show duplicate wo number dialog on wizard create
* docs(work-orders): align pr description with jira and qa residuals
* style(work-orders): prettier wizard duplicate dialog files
* refactor(workorders): extract wizard state hook and list overlays
* fix(work-orders): check duplicate numbers globally
* fix(work-orders): exhaust fuzzy pages for exact WO number lookup
Prevent accepting duplicates that fall outside the first fuzzy search page.
* style(work-orders): format editable WO number cell for Prettier gate
* fix(work-orders): 0-based WO search page and stale duplicate guard
* fix(work-orders): sync draft WO# ref in effect for duplicate guard [SH-189]
* fix(work-orders): extract WO number editor under max-lines gate
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): harden duplicate WO number checks [SH-189]
* fix(work-orders): sync wizard refs in effects and exhaust duplicate WO lookup
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): extract hooks to satisfy max-lines governance gate
Co-authored-by: Cursor <cursoragent@cursor.com>
* style(work-orders): format wizard state hook for Prettier gate
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(work-orders): search one-character WO numbers for duplicates
---------
Co-authored-by: Cursor <cursoragent@cursor.com>