Commit graph

460 commits

Author SHA1 Message Date
Alexandre Brandizzi
5bcc0f7081 feat(sites): delete a site from the detail panel with an open work-order warning
- 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
2026-09-25 11:59:08 -03:00
Alexandre Brandizzi
ffc4a9da68 Merge remote-tracking branch 'origin/feat/ab/wo-ids-filter' into feat/ab/sh-334-site-delete 2026-09-25 11:47:31 -03:00
Alexandre Brandizzi
551f6af208 refactor(sites): read optional site fields through one helper 2026-09-25 11:45:31 -03:00
Alexandre Brandizzi
a75b9c8be6 feat(sites): Add Site modal and site detail panel with in-place edit
- 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
2026-09-25 11:43:30 -03:00
Alexandre Brandizzi
e052dcb37a feat(workorders): filter board to an exact work-order id set
/workorders?ids=101,205,318 opens the board on exactly those work orders
through board/search's ids filter. The id set replaces every other
drill-down filter, a chip reads "Showing N selected work orders" and
clearing it returns to the weekly board. buildWorkOrderIdsHref builds
the link for alerts and linked-work-order lists.
2026-09-25 11:06:09 -03:00
Alexandre Brandizzi
358ec3e890
Merge pull request #261 from Sea-Haven-Industries/feat/ab/sh-392-dashboard-unassigned
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
SH-392: Unassigned KPI tile on the Dashboard
2026-09-25 06:52:00 +00:00
Alexandre Brandizzi
2084cfa453
Merge pull request #266 from Sea-Haven-Industries/fix/ab/sh-400-vendor-readonly-uplifts
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
Show work-order uplift requests read-only in the Vendor Portal
2026-09-25 06:20:35 +00:00
Alexandre Brandizzi
9f46a91cb9
Merge pull request #260 from Sea-Haven-Industries/fix/ab/sh-383-media-contract
fix(media): apply the SH-116 media contract to every upload surface
2026-09-25 06:06:59 +00:00
Alexandre Brandizzi
b91682f53c Merge remote-tracking branch 'origin/main' into lane/pr261 2026-09-25 03:02:25 -03:00
Alexandre Brandizzi
70f8b303b5 Show work-order uplift requests read-only in the Vendor Portal
Revise and Withdraw now appear only on uplift requests the vendor raised.
Requests raised from the work order stay visible without either control.
2026-09-25 02:55:04 -03:00
Alexandre Brandizzi
52cbb87126
Merge pull request #259 from Sea-Haven-Industries/fix/ab/sh-391-adv-filter-date-range
fix(workorders): make the Advanced Filters date range narrow results (SH-391)
2026-09-25 05:53:39 +00:00
Alexandre Brandizzi
28e30f8edb fix(uplifts): show the requested increase as the one uplift amount
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.
2026-09-25 01:49:24 -03:00
Alexandre Brandizzi
39ee98e09b fix(vendor-portal): advertise phone photo and video types for uplift evidence
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.
2026-09-25 01:43:34 -03:00
Alexandre Brandizzi
6012ebabe7 fix(media): count HEIC and untyped phone files, guard overlapping picks
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.
2026-09-24 22:51:17 -03:00
Alexandre Brandizzi
8bb4c0c512 fix(dashboard): open the Unassigned tile as the Unassigned filter option
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.
2026-09-24 22:45:10 -03:00
Alexandre Brandizzi
b316168930 fix(vendor-portal): pre-check the per-work-order photo/video limit
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.
2026-09-24 22:29:20 -03:00
Alexandre Brandizzi
f64447f52c chore(dashboard): keep ticket keys out of source comments 2026-09-24 22:06:24 -03:00
Alexandre Brandizzi
7984640a5f Merge remote-tracking branch 'origin/fix/ab/sh-391-adv-filter-date-range' into feat/ab/sh-392-dashboard-unassigned 2026-09-24 22:05:33 -03:00
Alexandre Brandizzi
792dc6917b Merge remote-tracking branch 'origin/main' into feat/ab/sh-392-dashboard-unassigned
# Conflicts:
#	e2e/__screenshots__/dashboard/dashboard.visual.spec.ts/dashboard-admin.png
#	e2e/__screenshots__/dashboard/dashboard.visual.spec.ts/dashboard-dispatcher.png
2026-09-24 22:05:10 -03:00
Alexandre Brandizzi
c1fd8f6d88 chore(workorders): keep ticket keys out of source comments 2026-09-24 22:03:23 -03:00
Alexandre Brandizzi
c99e81d0d7 fix(media): pre-check duration and per-work-order counts on Extra Docs
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.
2026-09-24 22:01:22 -03:00
Alexandre Brandizzi
11a753a2e9 docs(media): note the server now enforces the 90-second video limit 2026-09-24 21:39:07 -03:00
Alexandre Brandizzi
ed931ea2eb SH-392: Unassigned KPI tile on the Dashboard
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).
2026-09-24 21:34:35 -03:00
Alexandre Brandizzi
51ca244524 fix(workorders): keep closed undated WOs out of the board page like the search (SH-391)
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.
2026-09-24 21:34:24 -03:00
Alexandre Brandizzi
b5da537c93 fix(media): classify client uploads by the server's type rule
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.
2026-09-24 21:31:03 -03:00
Alexandre Brandizzi
f69c8a04eb fix(workorders): match a range on the board the same way the search does (SH-391)
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.
2026-09-24 21:10:40 -03:00
Alexandre Brandizzi
f65a48077d fix(vendor-portal): validate uploads against the server allowlist
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.
2026-09-24 21:01:36 -03:00
Alexandre Brandizzi
44e96cf2a3 fix(media): apply the SH-116 media contract to every upload surface
Photos up to 10 MB (JPG/PNG/HEIC), videos up to 100 MB and 90 s (MP4/MOV),
at most 10 photos and 3 videos per work order, pre-validated with stable
generic messages on the Photos & Videos modal, the Completion Doc media tab,
Extra Docs and the vendor portal. Video duration is read from metadata when
the browser can; unreadable metadata never blocks. Mobile MIME variants
(empty type, octet-stream with a video extension, QuickTime) stay accepted.
The signed completion PDF keeps its 50 MB cap.
2026-09-24 20:59:26 -03:00
Alexandre Brandizzi
0d6af0da59 fix(workorders): make the Advanced Filters date range narrow results (SH-391)
The Advanced Filters default was This week, and that value doubled as no
range, so an explicit This week searched every week. Other ranges passed
every undated WO through on the server. No range is now its own state
(nothing selected, like the prototype; clicking a selected range clears it).
It searches every week with undated rows, via includeDateless. Any selected
range narrows strictly. The pinned Unassigned queue and the WO# duplicate
lookup keep spanning every week.
2026-09-24 20:58:47 -03:00
Alexandre Brandizzi
80640642cc fix: allowlist internal uplift denial copy 2026-09-22 23:41:17 -03:00
Alexandre Brandizzi
7b30895aca fix: map protected uplift create denial safely 2026-09-22 23:35:44 -03:00
npalseahaven
4965b8a076
Merge pull request #224 from Sea-Haven-Industries/refactor/dispatch-detail-modal-split-v2
Some checks failed
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Has been cancelled
Deploy Web / Deploy SPA to (push) Has been cancelled
refactor(vendor-portal,settings): move state and fetch logic into hook
2026-09-22 14:17:57 +00:00
Alexandre Brandizzi
e769b5fcc2
Merge pull request #227 from Sea-Haven-Industries/feat/ab/sh-288-event-notifications
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
feat(notifications): open assignment, comment, mention and uplift items where they happened (SH-288, SH-289, SH-290, SH-215)
2026-09-21 20:58:57 +00:00
cb6fd48916
Merge branch 'main' into refactor/dispatch-detail-modal-split-v2 2026-09-21 16:54:40 -04:00
Alexandre Brandizzi
93f68f84e4 fix(workorders): cap completion documents at 50 MB 2026-09-21 15:39:47 -03:00
Alexandre Brandizzi
ca2b3b5134 fix(work-orders): raise media upload limit to 200 MB 2026-09-21 15:09:51 -03:00
Cursor Agent
647accc737
Merge remote-tracking branch 'origin/main' into feat/ab/sh-288-event-notifications
Co-authored-by: adam <adam@seahavenind.com>
2026-09-20 21:53:34 +00:00
Alexandre Brandizzi
3e5fba4d2b
Merge pull request #225 from Sea-Haven-Industries/feat/ab/sh-292-notification-center
Some checks are pending
Frontend checks / Build and test (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
SH-292: Notification Center panel and feed
2026-09-18 23:33:59 +00:00
Alexandre Brandizzi
cbfed3dde8
Merge pull request #226 from Sea-Haven-Industries/feat/ab/sh-209-uplift-detail-modal
SH-209: Align the Uplift Detail modal with the spec
2026-09-18 23:33:55 +00:00
Alexandre Brandizzi
f25a3dd628
Merge pull request #228 from Sea-Haven-Industries/fix/ab/sh-379-poc-persist
Persist manual POC edits via dedicated POC endpoint (SH-379)
2026-09-18 23:33:52 +00:00
Alexandre Brandizzi
7fdf85fc47
Merge branch 'main' into feat/ab/sh-304-services-registry-ui 2026-09-18 19:21:21 -03:00
Alexandre Brandizzi
7c1243eec3
Merge branch 'main' into feat/ab/sh-304-services-registry-ui 2026-09-18 19:09:49 -03:00
Alexandre Brandizzi
744d7af7bb
Merge branch 'main' into feat/ab/sh-382-extra-doc-view 2026-09-18 19:09:47 -03:00
Alexandre Brandizzi
1a7a4ba58f fix(work-orders): open extra documents without credentialed CORS (SH-382)
Viewing a work-order extra document called the authorized content endpoint
with `credentials: "include"`. The API replies `Access-Control-Allow-Origin: *`,
and in credentialed mode the browser rejects a wildcard origin outright, so the
fetch threw, `openExtraDocContent` fell into its catch, closed the tab and
toasted "Unable to open this document." — QA CT-03.

The endpoint authenticates with the bearer token the ky beforeRequest hook
attaches, not cookies, so credentials mode was dead weight. List, upload and
delete never set it, which is why only viewing failed. Drop the option and
guard the request shape in a test (JSDOM cannot enforce CORS).
2026-09-18 16:35:01 -03:00
Codex Review Integration
84568dd737 refactor(workorders): extract board patch api module
work-orders-api.ts exceeded the 500-line godfile cap after adding
updatePoc. Move patchBoardField/updatePoc and their shared response
handling into work-order-board-patch-api.ts and re-export through
workOrdersApi, mirroring workOrderBoardDocumentsApi.
2026-09-18 15:03:59 -03:00
Codex Review Integration
3b1da21abb fix(workorders): preserve partial POC edits 2026-09-18 15:00:01 -03:00
Codex Review Integration
5b0c758a9f fix(workorders): persist manual POC edits via dedicated POC endpoint (SH-379)
The UI dropped manual POC edits before they reached the API: the patch
mapper listed pocName/pocPhone/pocNotes as local-only keys and the
slide-over draft excluded them from Save, so the optimistic edit vanished
on refetch and the completion freeze captured the Site contact instead.

- Emit one composite POC op from table patches and route it through a new
  workOrdersApi.updatePoc (PATCH workorders/{id}/poc), reusing the board
  patch row/error contract (409 conflict with currentState, 422 validation).
- Include POC scalars in slide-over edit keys so dirty state and Save carry
  them; site dialog and slide-over now both persist POC edits.
2026-09-18 14:44:06 -03:00
Codex Review Integration
75c2c1c1fa Merge remote-tracking branch 'origin/feat/ab/sh-292-notification-center' into HEAD
# Conflicts:
#	src/app/(protected)/workorders/_components/detail/use-slide-over-edit-state.ts
#	src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts
#	src/app/(protected)/workorders/_components/detail/work-order-slide-over.tsx
#	src/app/(protected)/workorders/_components/list/work-orders-list-page-panels.tsx
#	src/app/(protected)/workorders/_hooks/use-work-orders-list-page.ts
2026-09-18 14:35:17 -03:00
Codex Review Integration
062a1e26c8 fix: resolve notification review findings 2026-09-18 14:23:43 -03:00
albrand
74e7c526e8 fix(notifications): re-open tab and match API mentions for event highlights
Re-apply the requested slide-over tab on every open via an open counter, so a
second comment/mention notification for the work order already on screen still
switches to Comments and re-scrolls to the highlighted comment. Treat a board
comment's mentions id array (plain @Name text) as a mention alongside encoded
@[id:name] tokens so mention items highlight and comment items exclude them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-18 14:01:55 -03:00