← All tests

POS Section Rework — Slice 5 (local)

2026-05-14 Gate 1 local. Final slice of the POS Section Rework arc (Narong spec items 8 & 9): the Registers relocation + section permission gating. The registers admin moves fully under the Point of Sale section, and the nav rail becomes permission-aware. No migration.

9/9 local test PASS. nix-cafe typechecks clean. No migration. 8 files (1 moved, 2 new-content, 5 edited).

visiblePosNav — the permission filter (unit-tested directly)

visiblePosNav(["nix_cafe.pos.view"])                    → ["Dashboard"]
visiblePosNav([])                                       → ["Dashboard"]   (no 'requires')
visiblePosNav(["…pos.view", "…reports.view"])           → ["Dashboard", "Sessions"]
visiblePosNav(["…pos.view", "…orders.view"])            → ["Dashboard", "Orders"]
visiblePosNav([all 4 perms])                            → ["Dashboard", "Registers", "Sessions", "Orders"]

PosShell: visiblePosNav(perms).length > 1 ? master-detail grid + rail
                                          : header + content, no rail

Local test — 9/9

✓ visiblePosNav pure-function probe ran clean
✓ visiblePosNav — a pure cashier (pos.view only) sees just Dashboard
✓ visiblePosNav — each manager permission unlocks exactly its rail item
✓ Registers admin relocated — /pos/registers is the real page
✓ /settings/registers is now a redirect stub; old client file removed
✓ Settings nav drops the Registers row; revalidatePath retargeted
✓ PosNav is permission-aware — requires codes + visiblePosNav export + permissions prop
✓ PosShell takes permissions + drops the rail when only Dashboard is visible
✓ pos/layout.tsx resolves + passes the user's permissions to PosShell

visiblePosNav is a pure exported function — unit-tested directly. The
relocation is verified structurally against the source tree (the moved
client exists at its new home, the old one is gone, /settings/registers
is a redirect, the Settings nav row is dropped, revalidatePath retargeted).
Gate 2 does the behavioural pass on prod — rail filtering per role, the
redirect stub, registers admin working at its new home.

Files — 8 touched, no migration

moved (git mv, history preserved):
  app/(authed)/settings/registers/registers-client.tsx
    → app/(authed)/pos/registers/registers-client.tsx
new content:
  app/(authed)/pos/registers/page.tsx          re-export → the real registers page
  app/(authed)/settings/registers/page.tsx     real page → redirect stub
edited:
  app/(authed)/settings/_components/settings-nav.tsx   dropped the Registers row
  lib/actions/registers.ts                     revalidatePath ×4 → /pos/registers
  app/(authed)/pos/_components/pos-nav.tsx      + requires gates + visiblePosNav export
  app/(authed)/pos/_components/pos-shell.tsx    + permissions prop, rail-hide logic
  app/(authed)/pos/layout.tsx                   resolves + passes permissions

Gate 2 plan + arc close-out

Gate 2 (prod, get-coffee + lumiere):
  • /cafe/pos rail renders the right items for the owner (all 4)
  • /cafe/settings/registers redirects to /cafe/pos/registers
  • the registers admin works at /cafe/pos/registers (shop picker, etc.)
  • the Settings nav no longer lists Registers
  • 59/59 regression sweep

This closes the POS Section Rework arc — Slices 1–5 all shipped:
  1 ✅ session-scoped data layer
  2 ✅ landing card UI rework
  3 ✅ master-detail submenu shell
  4 ✅ Sessions history page
  5 ✅ Registers relocation + permission gating  ← this

Out of scope (noted, not done): the top-level "Orders" sidebar item
stays in both places (memo Open Q3 default = "keep both for now").