← All tasks

Slice K — Settings-CRUD refresh-in-place PROD · GATE 2

Shipped 2026-05-18. Tracked followup from Slice F (2026-05-15) — the Payment Methods admin test had to fall back to a hard page.goto() reload because the just-saved row didn't appear in the rendered list on prod cold cf-workers, even though revalidatePath + the client's router.refresh() both fired and the DB row was confirmed.

5/5 prod test (in-place visibility, NO page.goto fallback) · 51/51 regression green — 56/56 total. Applies Slice J's architecture (client owns list state, action returns mutated row, router.refresh() dropped) to three Settings tabs: payment-methods, payment-diff-reasons, modifiers. All three confirmed save → row visible in-place on get-coffee prod.

What shipped

3 actions refactored to return the mutated row: createPaymentMethodAction + updatePaymentMethodAction return { ok, row: PaymentMethodView }; createDiffReasonAction + setDiffReasonActiveAction return { ok, row: DiffReasonView }; createAttributeAction + createAttributeValueAction return { ok, row: AttributeView | AttributeValueView }. New exported view types project the full DAO row to the shape the client list renders.

Update + delete actions return { ok } — the client knows what it sent on update (no re-fetch needed) and what id it asked to delete. Simpler contract, fewer round-trips.

3 clients refactored to own list state: useState<X[]>(initialX) initialized from props; mutation callbacks splice/replace/filter local state directly. router.refresh() and useRouter removed throughout the affected components.

Modifiers (the gnarliest): nested attribute↔value relationship. Top-level ModifiersClient holds the canonical attributes: AttrRow[] state with six mutation helpers (addAttr/patchAttr/removeAttr/addValue/patchValue/removeValue) passed down to AttrCard/AttrModal/ValueModal. When a new attribute lands, it gets values: [] spread on so the nested shape stays consistent.

revalidatePath dropped from all CRUD actions on these three tabs. setProductAssignmentsAction kept its revalidatePath("/products") (different page, different list). The cashier-callable createDiffReasonForCloseShiftAction also dropped revalidatePath — the cashier is in the close-shift dialog, not the Settings list.

Mid-Gate-2 fix logged

Schema name typo in the prod test. First prod run was 4/5: the diff-reasons check failed because the test queried commerce.payment_diff_reasons but the table actually lives in cafe.payment_diff_reasons (the prefix commerce was guessed from the column-level test test-id commerce/diff-reason-… — there's no commerce table). Fixed the test SQL, re-ran 5/5. Worth checking: the commerce. prefix exists for shops and pin_identities; payment_diff_reasons always was a cafe. table.

Checks — 5/5 prod test

Raw: result.json

Regression sweep — 51/51 green

test-phase1-prod.mjs11/11
test-phase2-sso-outdoor-prod.mjs6/6
test-phase2-cafe-multishop-prod.mjs6/6
test-m1-prod.mjs10/10
test-r7-prod.mjs14/14
test-r8-prod.mjs4/4

Files (0 new + 6 modified, no migration, no new dep)

Modified: lib/actions/payment_methods.ts · lib/actions/payment_diff_reasons.ts · lib/actions/product_attributes.ts · app/(authed)/settings/payment-methods/client.tsx · app/(authed)/settings/payment-diff-reasons/diff-reasons-client.tsx · app/(authed)/settings/modifiers/modifiers-client.tsx

Commit: 403a743

Trade-offs accepted (logged for future)