- 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
A request raised from the work order stores the increase, so the vendor card
read a +90 request on a 600 NTE as "Requested $90.00 (was $600.00)". The card
now shows the new total, $690.00, for those requests; vendor requests are
unchanged.
Picking a video as uplift evidence reads its movie header before the file is stored in form state. A submit that landed inside that window validated the previous (usually empty) evidence, showed the missing-evidence error and dropped the request, forcing a second click. The submit button is now disabled and reads "Checking evidence…" while the latest pick is being checked, and the submit handler ignores submissions in that window.
The queue Amount column, the detail modal and the reject/revoke dialogs
showed requestedNTE (the resulting NTE total) while the decision toasts
showed delta (the increase), so a work order with an existing NTE named
two different figures for one request. Route every surface through one
upliftAmount helper that returns the increase, matching the prototype's
single uplift amount, and show it on both Approve buttons.
The evidence picker filtered to PDF/JPG/PNG and its label and missing-file
messages said the same, while the pre-check (and the server, which applies
the SH-116 allowlist and work-order counts to UpliftEvidence uploads)
accepts HEIC, MP4 and MOV. On a phone the picker hid exactly the photos and
clips the validator would pass.
The evidence input now uses the same accept filter as the completion
upload, exported once from document-validation, and the label and both
missing-evidence messages name all six types.
The vendor portal hint listed the photo and video limits but not the PDF one, so a vendor rejected for an 11 MB PDF saw a limit that was never stated. PDFs are the only accepted kind whose cap was missing from the copy; a test now pins the full hint.
The changed-file maintainability gate caps functions at 150 lines;
VendorPortalDocuments reached 156. Move the per-document row into
VendorPortalDocumentRow with no behavior change.
A work order with a zero granted total rendered $0.00 in the Pending
table's Approved on WO column and in the detail modal's Total line.
Both now share isGrantedAmount, so zero or unknown totals render a dash
and the column always matches the modal Total.
Persisted HEIC files and local files with an empty or octet-stream type
were classified as "other", so they did not count toward the 10 photo /
3 video limit. Both classifiers now recognise them by extension.
The Photos & Videos uploader and Extra Docs ignore a new selection while
the previous one is still being screened, and the vendor portal drops an
earlier pick whose video check finishes after a newer pick.
Also removes ticket keys from source comments.
The tile link sent the board API token __unassigned__ as the dispatcher.
The drill-down parser stores that as an Advanced Filters dispatcher id,
while the filter UI only knows __unassigned, so the banner showed a raw
__unassigned__ chip, Assigned to did not tick Unassigned, and the option
could not clear it. Send ASSIGNEE_FILTER_UNASSIGNED, as the notification
queue link already does; toBoardSearchQueryParams still maps it to
__unassigned__ for /board/search, so the rows are unchanged.
The review contract bars Jira keys in source comments and test names;
ticket identity lives in the PR, commit and branch. The previous commit
cleaned the source files; this strips the six (SH-391) suffixes the
branch added to test names and a test comment, keeping the behavioural
wording.
The vendor completion upload and uplift evidence checked type, size and
duration but not the 10-photo / 3-video work-order limit. Use the counts the
dispatch detail now reports (shoc-backend#173), on the same basis as the
server: a new completion version does not count the document it replaces.
When the backend does not report counts yet, the server check still applies.
The browser pre-check decoded the picked file through a video element and
an object URL, which CodeQL flags as DOM text reinterpreted as HTML. Parse
the moov/mvhd movie header from file slices instead, the same way the
server enforces the 90-second limit, so both sides read one duration.
Unreadable headers still never block an upload.
Extra Docs advertised the 90-second limit but only checked type and size.
Both dispatcher surfaces now share one screening step (type, size, count,
duration), and the Completion Doc media tab and Extra Docs count the whole
work order's photos and videos rather than only their own tab's share.
Failed local uploads no longer count toward the limit.
Shows open work orders with no dispatcher for the selected period and
drills into the same list on the board (dispatcher Unassigned, open
statuses, same dates; All time includes undated work orders).
With no range selected, or with Unscheduled, the board search adds only
open undated work orders; completed and canceled ones without a date are
never unscheduled. The board page kept them. It now applies the same rule,
so both return the same rows.
A foreign declared type (e.g. video/3gpp on a .jpg) gave the file the video
size allowance in the browser while the server sizes it as a photo. Use the
same rule as the server: an allowlisted type decides, otherwise the extension.
A week-only row that still carries a scheduled date outside the range
passed the board search (its target week overlaps) but failed the
on-page filter, which judged it by the date alone. Both now accept a row
dated in the range or a week-only row whose target week overlaps it.
Any image/* or video/* type passed the client check, so GIF, WebP and WebM
were only rejected after upload. Resolve the kind from the same allowlist
the server uses: an allowlisted browser type, otherwise the extension.