feat(drawer): drawer hub — balance now, this-shift figure, daily activity, shift history; busy spinners
Build desktop / desktop (push) Successful in 4m12s
Build & push images / images (push) Successful in 2m53s
CI / check (push) Successful in 41s

/drawer was record + review only: no current balance, no sight of the open
shift's incomings, no daily activity, no shift history. Rebuilt as a hub:

- Drawer now: the till's running balance (new GET /api/drawer/balance,
  shift:read — exposes the service's existing drawerBalance(); the drawer
  is one site-wide till, same exposure the X-report already had) with the
  open shift's X-report breakdown alongside (float + takings + vouchers =
  expected = balance) and a "This shift: ±X" figure (expected − opening
  float — the shift's own contribution vs what it inherited).
- Today's cash activity: every cash payment + voucher since local
  midnight from the signed chain, live, with day totals (card never
  enters the till).
- Record + movements/review: the 2026-07-01 flow, unchanged.
- Closed shifts: drawer-focused history via the scope-aware /api/shifts
  (float → takings ± vouchers → expected per shift).

Also: every shift open/close button (header, /shifts, pay modal, end-
shift confirm) now shows an animated spinner + dims while busy — the old
label-swap-only feedback read as a dead click when a shift open ran slow.
The slowness itself (drawer/shift reads fold the WHOLE chain, O(chain))
is recorded as an open item in wiki/concepts/shift.md with the fix
sketch: fold from the last z-report's signed expectedDrawerMinor forward.

No new ledger surface — one read-only endpoint; RBAC test added.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-07-05 15:56:30 +02:00
parent d5ff2097bd
commit c5ed3f1308
13 changed files with 489 additions and 72 deletions
+10
View File
@@ -66,6 +66,16 @@ export const en: Catalog = {
profile: "Profile",
},
drawer: {
stateTitle: "Drawer now",
openShift: "Open shift: {{operator}}",
noShiftOpen: "No shift open — the drawer carries the last shift's closing balance.",
thisShift: "This shift:",
todayTitle: "Today's cash activity",
todayPayments: "{{count}} payments",
payment: "Payment",
noActivity: "No cash activity today.",
historyTitle: "Closed shifts",
noShifts: "No closed shifts yet.",
recordTitle: "Record a cash movement",
amount: "amount",
reasonPlaceholder: "reason (e.g. supplier payment, bank drop)",
+14 -4
View File
@@ -68,6 +68,16 @@ export const sq = {
profile: "Profili",
},
drawer: {
stateTitle: "Arka tani",
openShift: "Turn i hapur: {{operator}}",
noShiftOpen: "Asnjë turn i hapur — arka mban gjendjen e mbylljes së turnit të fundit.",
thisShift: "Ky turn:",
todayTitle: "Aktiviteti i arkës sot",
todayPayments: "{{count}} pagesa",
payment: "Pagesë",
noActivity: "Pa lëvizje arke sot.",
historyTitle: "Turne të mbyllura",
noShifts: "Ende pa turne të mbyllura.",
recordTitle: "Regjistro një lëvizje arke",
amount: "shuma",
reasonPlaceholder: "arsyeja (p.sh. pagesë furnitori, depozitë banke)",
@@ -499,10 +509,10 @@ export const sq = {
// Anashkalimi i portës së pranisë (radar/kamera me defekt) — admini heq një sinjal si kusht.
presenceGateTitle: "Porta e pranisë në hyrje",
presenceGateHint:
"Butoni i hyrjes normalisht kërkon edhe radarin/lakun edhe një zbulim nga kamera për të konfirmuar një automjet real. Nëse një pajisje ka defekt, anashkaloje që kalimtarët të mund të hyjnë derisa ta rregullojë ekipi i mbështetjes. Çdo ndryshim regjistrohet në ledger, dhe biletat e lëshuara gjatë anashkalimit shënohen.",
presenceBypassRadar: "Anashkalo radarin / lakun (sensor prania me defekt)",
presenceBypassCamera: "Anashkalo kamerën (zbulim automjeti me defekt)",
presenceBypassActive: "Anashkalimi i pranisë aktiv — porta e hyrjes është dobësuar. Fike sapo pajisja të rregullohet.",
"Butoni i hyrjes normalisht kërkon edhe radarin edhe një event nga kamera për të konfirmuar një automjet në hyrje. Nëse një pajisje ka defekt, anashkaloje që kalimtarët të mund të hyjnë derisa ta rregullohet/ndërrohet. Çdo ndryshim regjistrohet në ledger, dhe biletat e lëshuara gjatë anashkalimit shënohen.",
presenceBypassRadar: "Anashkalo radarin (radari me defekt)",
presenceBypassCamera: "Anashkalo kamerën (kamera me defekt)",
presenceBypassActive: "Anashkalimi i pranisë aktiv — siguria e hyrjes është dobësuar.",
// Reveal/hide toggle for a secret field (e.g. the device web password).
revealSecret: "Shfaq fjalëkalimin",
hideSecret: "Fshih fjalëkalimin",