Implement the complete collection confirm feature set: - Add new `/collection-confirm` operational page for reviewing and confirming collected order items - Add sidebar navigation entries for Collection Confirm and Invoicing - Add `order_items` to the file proxy API whitelist for attachment access - Install required testing dependencies and configure Vitest test setup - Add full design documentation, test suite, and page overview screenshot - Update package.json and build configuration files
5.5 KiB
Collection Confirm Selection And Return Design
Date: 2026-07-07
Area: src/app/(app)/collection-confirm/page.tsx, src/app/(app)/collection-confirm/view-model.ts
Status: Drafted for review
Goal
Extend the /collection-confirm page so the PO Confirm this PO action only signs off the rows the operator has selected via per-row checkboxes, and add a per-row Return to pool action that sends a single item back to the collection pool with a reason note.
User Need
Operators can already confirm an entire PO at once with one shared invoice, but today the confirm applies to every row in the PO group automatically. Operators also need to send a single row back to the collection pool when a confirmation needs to wait for more information.
Approved Direction
- Add a checkbox to every row. The page tracks the set of selected row ids.
- The PO group header keeps the existing
Confirm this POaction but now signs off only the selected rows in that group. The button label shows how many rows will be confirmed (e.g.Confirm 3 of 5). - Add a per-row
Return to poolbutton that opens a small modal, requires a reason note, and flips the row back tocollection_poolwhile appending a notes entry.
Scope
In scope:
- per-row checkbox state and selection UI,
- updating the PO group's
Confirm this POaction to operate on the selected rows in the group, - adding the
Return to poolmodal and per-row button, - clearing the assignment lifecycle fields when a row is returned.
Out of scope:
- changing the existing confirm modal or its audit note format,
- adding a per-group
Select allcheckbox or any select-all-in-page affordance, - changing attachment or invoice upload behavior,
- touching the collection-pool page.
Selection Behavior
- Selection lives in a single
Set<string>at the page level. - Each row carries an
aria-label="Select item"checkbox on the left side of its card. - The checkbox state is controlled by the page-level selection state.
- Selecting or deselecting a row never triggers a refetch.
- Selection is cleared after a successful confirm or after
Return to poolcompletes.
Confirm This PO Update
- The PO header button label is computed from the group's selection count:
- 0 selected:
Confirm this PO, disabled with a small inline hint. - 1+ selected:
Confirm <N> of <group size>, enabled.
- 0 selected:
- The bulk confirm handler iterates over only the selected rows in that group.
- Selection clears after the bulk confirm completes.
- Problem-flagged rows without a confirming note are skipped from bulk confirm and surface a small inline message in the group.
Return to Pool
Modal
- Title:
Return to pool. - Fields:
Reasontextarea, required.
- The modal uses the existing
ModalShell.Dialogpattern.
Behavior
- On submit:
- flip
item_statustocollection_pool, - clear
drv_id,drv_status,assigned_at, - clear
invoice_statustopendingif present, - append a
noteentry to the unified notes feed with bodyReturned to pool. <reason>.
- flip
- The row disappears from the collection-confirm list once the page reloads.
- Failures keep the modal open with a toast error and leave the row in place.
- The button is disabled while the row is busy.
Implementation Shape
src/app/(app)/collection-confirm/view-model.ts:- add
buildReturnPatch({ row, actor, reason, ts })that returns the patch and notes string, - export the new helper for tests.
- add
src/app/(app)/collection-confirm/page.tsx:- add
selectedRowIds: Set<string>state, - add a checkbox UI inside
ConfirmCard, - update the PO group header to compute its action label from the selected subset,
- update
bulkConfirmto iterate over only the selected rows, - add a
Return to poolbutton on each row, - add a
ReturnToPoolDialogcomponent, - wire the return handler to call the view-model helper.
- add
Error Handling
- Failed return writes keep the modal open with a toast error.
- The selection set is never partially mutated by a failed write.
- Empty selection on
Confirm this POis a no-op (the button is disabled).
Validation Plan
After implementation, validate that:
- Every row has a checkbox, and toggling it updates the page-level selection.
Confirm this POis disabled when nothing in the group is selected and shows the liveConfirm N of Mlabel otherwise.Confirm this POsigns off only the selected rows in the group, clearing the selection on success.Return to poolopens a modal that requires a reason and writes the row back tocollection_poolwith the reason in the notes feed.- Returning a row clears the assignment lifecycle fields (
drv_id,drv_status,assigned_at,invoice_status). - Failed writes keep the modal open and surface toast errors.
Risks And Guardrails
Primary risk: accidentally confirming an unselected row when selection state is stale.
Guardrails:
bulkConfirmalways re-derives the list of rows from the current selection before iterating,- selection is cleared after a successful confirm,
- failed writes do not mutate the selection state.
Acceptance Criteria
This enhancement is complete when:
- every row has a checkbox and selection lives in the page state,
Confirm this POoperates only on the selected rows in the group,- the PO header action label reflects the live selection count,
Return to poolflips a row back tocollection_poolwith a reason note,- returning a row clears the assignment lifecycle fields,
- selection clears after a successful confirm or return,
- no schema changes are introduced.