A round of operator-facing fixes on the booth screen, shift views, and the
font-scale control. (Follows the font-scale feature in f706726, which used CSS
`zoom` — reverted here for the rem approach below.)
Font scaling (the A−/A+ control now actually works without breaking layout):
- The control scaled via CSS `zoom`, which also scaled viewport-locked containers
(h-screen frame, max-h-[90vh] modals) so at 130% modal headers/footers were
pushed off-screen. Reworked to scale TEXT only: converted every `text-[Npx]`
font utility to rem across the web app (~230 sites in 25 files + the
.label/.hint/.btn component classes + body in index.css; 16px root, so 100% is
visually identical), and applyFontScale now sets the ROOT font-size. vh/h-screen
layout stays put, so chrome never clips; tall content scrolls its own container.
Verified at 130%: text 12px→15.6px while the frame stayed viewport-height.
Live feed (event rows):
- Plate, badges and reason now flow inline after the identity and wrap only when
the row runs out of width — no more forced second line when there's empty space.
- Dropped the redundant TARGË via-badge (the plate chip already conveys it).
- Removed the Direction filter group (Hyrje/Dalje) — it duplicated the entry/exit
options already in the Type filter.
Active sessions:
- Rebuilt as a real table (Ticket/subscriber · Plate · Entry · Elapsed) so columns
align and long values (subscriber names, ticket ids) no longer truncate.
- Dropped the status column (an unpaid transient is normal; a subscriber shows ★ +
name; overstay keeps a row tint). Removed the now-redundant status filter; only
the Transient/Subscriber filter remains. Plate is now searchable (uses s.plate).
Shift report (close-shift modal + Shift History + printed Z-report slip):
- Removed the confusing `shitje` (subscription-sales) sub-line — Abonime is the
total; only the out-of-window part is broken out. subscriptionSalesMinor stays in
the signed payload (audit data), just not displayed/printed.
- Show the inherited opening cash ("Arka fillestare") above the expected drawer, so
opening + cash-taken = expected reads clearly. Money values no longer line-wrap.
Subscription edit modal:
- Fixed the 2-col grid alignment: a lone "only one version" cell was shifting every
following row by one column — it now emits a full label+value pair.
Removed orphaned i18n keys (fStatus*, fDir*, srcSubSales) from sq+en (parity kept).
Full workspace build/lint/test green.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Shift screen:
- The standalone ShiftControl block is gone from /shift. The open/CURRENT shift now
appears at the TOP of the shift list (CURRENT badge, live figures synthesized from
the X-report), unified with history. Selecting it shows its live activity log.
- Shift ACTIONS moved into the current shift's detail pane, each opening a MODAL:
End shift (confirm → signed Z-report result), drawer voucher (Mandat in/out),
takings-so-far (X-report). When no shift is open, a Start-shift button shows.
- The current shift's log auto-refreshes (5s); a closed shift is bounded by its
window. /setup/shifts stays read-only history (no manage props). Deleted the now-
orphaned ShiftControl.tsx.
Layout:
- Every screen is now full-width like /booth — stripped the per-screen
`mx-auto max-w-*` caps (Logs, Subscriptions, Plans, Tariff, Users, Roles, Setup
layout, Shifts). The shell <main> already provides padding.
Build+lint 12/12 (i18n parity). Verified a live open shift surfaces as the CURRENT
list entry.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Test rates "in time" (overnight windows, daily caps, overstay) in seconds against
any tariff version, instead of waiting hours/days. No real ledger writes.
- Extract priceSession() into @parking/shared: the grace/overstay wrapper over
computeFee (unpaid -> entry..now; within-grace -> settled 0; grace-expired ->
overstay, a fresh period from grace-expiry). PayStation.quote() now calls it so
the booth and the lab can never diverge.
- API (tariffs.ts, tariff:read, read-only): POST /api/tariff/simulate prices a
hypothetical session (active/any version/inline structure) and returns the
priceSession outcome + a 30m..3d duration curve (see where the daily cap flattens);
GET /api/tariff/simulate/session/:identity prefills from a real ledger session.
- UI TariffLab.tsx at Setup -> "Tariff Lab": version picker, entry/asOf times,
optional payment+grace, category, and load-a-real-ticket. Admin-gated, available
on-site (useful to quote a dispute).
- 4 new priceSession unit tests incl. the ticket-1245791632490 overstay-not-zero
regression (40 pass). i18n lab.* + nav.tariffLab (sq+en). Verified live via the UI.
Wiki: tariff (priceSession + Tariff Lab as-built), log.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V