Add collection confirm rewrite spec
This commit is contained in:
parent
579acb300f
commit
683b6c8d0a
1 changed files with 120 additions and 0 deletions
|
|
@ -0,0 +1,120 @@
|
|||
# Collection Confirm — layout rewrite + actor hydration fix
|
||||
|
||||
**Goal:** Two distinct fixes on the Collection Confirm page:
|
||||
|
||||
1. **Rewrite the row layout.** The current 3-column grid crams every field, action, attachment, invoice chip, and problem chip into a single row. Stacked vertical sections per row (header / details / actions / notes) read better, give the actions more room, and let the notes feed breathe.
|
||||
2. **Fix the "actor id is empty" error.** The actor state hydrates from `localStorage.wfdash.actor`, which is never written. The real identity lives in the `wfdash_dev_email` cookie. Hydrate from the cookie via `getClientIdentity()` instead.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- Rewrite the `ConfirmCard` JSX in `src/app/(app)/collection-confirm/page.tsx` from the current 3-column grid to a stacked vertical layout. Sections: header (supplier + description + order/customer links) → details (ordered/collected/problem/invoice/attachments, two-column on `md:`) → actions (full-width buttons in a 2x3 grid on `sm:`, single column on `xs:`) → notes feed (existing).
|
||||
- Replace the `localStorage.wfdash.actor` hydration with `getClientIdentity()` from `@/lib/dev-identity`. `actor.id = email`, `actor.name = email.split("@")[0]`.
|
||||
- Add a tiny "Signed in as X" indicator in the page header (so the operator can see at a glance which identity is active, mirroring the dev-identity-switcher widget in the sprint page).
|
||||
- Keep all existing behaviour: search, group by PO, bulk confirm, return to pool, all four dialogs (Quantity, Note, Attach, Confirm Invoice).
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Changes to the dev-identity switcher, dev-identity helpers, or the `wfdash_dev_email` cookie lifecycle.
|
||||
- Changes to the `view-model.ts` (the page's existing helpers all stay).
|
||||
- The `dev-identity-switcher` widget itself — it lives in the sprint page. We just borrow its style (a tiny header indicator) for consistency.
|
||||
- Server routes — the actor flows entirely client-side via the cookie.
|
||||
|
||||
## Current State
|
||||
|
||||
- `ConfirmCard` (lines 504–752) uses `grid md:grid-cols-[minmax(0,1.2fr)_minmax(0,0.9fr)_auto]` to lay out header / details / actions side-by-side. The third column is a `flex flex-col` with five stacked buttons. On a typical 1440px screen the action column has ~12rem of width for five buttons + text — buttons truncate or wrap awkwardly.
|
||||
- The `actor` state is hydrated from `localStorage.wfdash.actor` (lines 151–163). The dev-identity switcher writes to the `wfdash_dev_email` cookie via `setClientIdentity`, never to localStorage. The hydration finds nothing, `actor` stays empty, the submit handlers trip the "actor id is empty" toast.
|
||||
- The sprint page (`src/app/(app)/sprint/page.tsx`) renders the `DevIdentitySwitcher` widget in its header. The collection-confirm page does not — so the operator has no way to see or change the active identity from this page.
|
||||
|
||||
## User Confirmed Rule
|
||||
|
||||
- Pick the best layout direction. (Option (b): stacked vertical sections per row, in a single card.)
|
||||
- Actor id and name can be any shape derived from the cookie. (Use `id = email`, `name = local-part`.)
|
||||
|
||||
## UX
|
||||
|
||||
### Row card layout
|
||||
|
||||
```
|
||||
┌─ Card ─────────────────────────────────────────────────────────┐
|
||||
│ [✓] Supplier name PO-2026-07-009 │
|
||||
│ Description text │
|
||||
│ 👤 Customer · 🏢 Company · 🔗 Order link │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ ORDERED 12 │
|
||||
│ COLLECTED 10 [Problem: short] [Invoice INV-1 · ...] │
|
||||
│ ATTACHMENTS 📎 proof.pdf 📎 photo.jpg │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ [ Adjust Quantity ] [ Add Note ] [ Attach ] │
|
||||
│ [ Confirm for invoicing ] [ Return to pool ] │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ Notes │
|
||||
│ • 2026-07-09 · david.els1 · QUANTITY │
|
||||
│ Adjusted qty 10 → 12. Operator error. │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- The checkbox moves to the absolute top-left of the card, beside the supplier name.
|
||||
- The actions grid is `grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2` — five buttons distribute cleanly. The primary "Confirm for invoicing" spans 2 columns on `sm:` to give it visual weight, the rest are 1 column each.
|
||||
- The notes feed stays at the bottom, unchanged.
|
||||
- The PO group header (the existing `<header>` above each group of rows) gets a `Selected: N` chip on the right and the "Confirm N of M" button. Unchanged otherwise.
|
||||
|
||||
### Identity indicator in the page header
|
||||
|
||||
A small, monospaced pill next to the "Refresh" button, mirroring the sprint page's identity switcher:
|
||||
|
||||
```
|
||||
[ Refresh ] [ 👤 david.els1 ]
|
||||
```
|
||||
|
||||
No edit affordance on this page — operators can change identity from the sprint page if needed. The pill is read-only here.
|
||||
|
||||
## Behavior
|
||||
|
||||
### Actor hydration
|
||||
|
||||
Replace the localStorage effect with a call to `getClientIdentity()`:
|
||||
|
||||
```ts
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
const email = getClientIdentity();
|
||||
if (!email) return;
|
||||
setActor({ id: email, name: email.split("@")[0] });
|
||||
}, []);
|
||||
```
|
||||
|
||||
The shape is unchanged: `actor.id` is the email (so `buildConfirmPatch`'s `setBy`/`setByName` fields stay valid), `actor.name` is the local-part (so badges and notes display the right operator name).
|
||||
|
||||
If the cookie is unset, `getClientIdentity()` returns the `DEFAULT_DEV_EMAIL` (`david.els1@outlook.com`). The actor hydrates to that default, and the "actor id is empty" toast never fires.
|
||||
|
||||
### Identity indicator
|
||||
|
||||
A `useEffect` reads `getClientIdentity()` on mount and renders the local-part as a small pill. Updates only on full page reload (cookie is set elsewhere); no live updates needed.
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit / component tests
|
||||
|
||||
- `getClientIdentity()` returns the cookie value when set; falls back to `DEFAULT_DEV_EMAIL` otherwise.
|
||||
- The page reads the actor from the cookie, not from localStorage.
|
||||
- The "actor id is empty" toast does not fire when the cookie is set.
|
||||
|
||||
The page itself has no test file today. The simplest test target is the hydration logic if we extract it into a helper, but that may be over-engineering. **Recommendation: add a tiny unit test for the new hydration effect via `@testing-library/react` for the `actor` state derivation.**
|
||||
|
||||
If adding a test file is too heavy for this single change, leave it and document that the change is verified by manual smoke (open the page after a sprint-page identity switch; the cookie persists, the page hydrates, the confirm button works).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- The row card reads top-to-bottom: header → details → actions → notes. No three-column cram.
|
||||
- The Confirm for invoicing button is visually the primary action in the actions grid.
|
||||
- The page header shows a "Signed in as X" pill that reflects the active dev identity.
|
||||
- The "actor id is empty" toast never fires for a user who is signed in via the dev-identity switcher.
|
||||
- All existing tests (if any) still pass; full suite non-regressive.
|
||||
|
||||
## Risk / Rollback
|
||||
|
||||
- **Layout risk:** every row's JSX changes. Visual regressions are likely in places I can't anticipate. The good news: there's no test on the page layout, so manual smoke is the only verification path.
|
||||
- **Hydration risk:** low. The new effect reads the same cookie the rest of the app already uses. No new persistence surface.
|
||||
- **Rollback:** revert the file. The actor hydration and layout changes are scoped to `page.tsx`. No shared component is touched.
|
||||
Loading…
Reference in a new issue