feat(booth): rework Active Sessions + pay/exit modal around barrier re-open

Move the audited barrier re-open out of the inline Active-Sessions row button
and into the modal, and turn the modal's dead-ends into useful views.

- Remove the inline per-row "Open barrier" button. Clicking a row opens the
  modal, which carries the action.
- Modal recognizes a closed-within-grace transient (found && !open &&
  withinGrace) and shows the session view + Open barrier instead of dead-ending
  on "already closed" — the exact case (paid, barrier unconfirmed) that needs a
  re-pulse. Server reopenBarrier guard unchanged.
- Active-Sessions rows show a live grace-remaining countdown badge
  (exited - M:SS, 1s tick off graceExpiresAt) via new formatCountdown helper.
- Settled sessions show the ACTUAL sum paid (new SessionLookup.paidMinor,
  summed across payment events) instead of a flat "PAID" badge.
- A fully-closed (grace-expired) session's modal is no longer a dead-end: it
  shows a read-only review view (figures + paid amount + entry/exit snapshot
  strip) for dispute/audit review, with no pay/exit/open controls.

i18n sq+en parity kept; web build/lint/test green.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-30 17:58:24 +02:00
parent cfac14e09e
commit 61de1fe772
7 changed files with 194 additions and 105 deletions
+8
View File
@@ -160,6 +160,10 @@ export const en: Catalog = {
fEvtVoid: "Void",
fEvtAnomaly: "Anomaly",
openPayExit: "Open pay / exit",
openReopenBarrier: "Open — paid, awaiting barrier",
exitedGrace: "exited · grace",
exitedGraceLeft: "exited · {{time}}",
exitedGraceTitle: "Paid and exited — barrier not confirmed; waiting out the grace period.",
openBarrier: "Open barrier",
openBarrierTitle: "Human-intervention barrier open (audited)",
barrierOpened: "barrier opened",
@@ -874,14 +878,18 @@ export const en: Catalog = {
ticket: "Ticket",
entry: "Entry",
now: "Now",
exit: "Exit",
duration: "Duration",
statusLabel: "Status",
paid: "PAID",
unpaid: "UNPAID",
overstay: "OVERSTAY",
overstayHint: "Earlier session paid. The customer failed to exit during the grace period. Payment for the new period is required. The total below is the new period's fee.",
closedWithinGrace: "EXITED · GRACE",
closedWithinGraceHint: "Paid and exit recorded — the barrier didn't confirm yet. The car stays listed until the grace period ends. Open the barrier manually if it's still waiting.",
topUp: "New period due",
total: "Total",
paidAmount: "Paid",
noTariff: "no tariff",
tender: "Tender",
cash: "Cash",