fix(web): booth UI/UX pass — readable font scaling + booth layout/report clarity
Build desktop / desktop (push) Successful in 4m12s
Build & push images / images (push) Successful in 2m42s
CI / check (push) Successful in 37s

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
This commit is contained in:
2026-06-28 15:15:09 +02:00
parent f706726eeb
commit cce99aadfd
31 changed files with 410 additions and 401 deletions
+8 -8
View File
@@ -82,7 +82,7 @@ export function Profile({
<h2 className="text-sm uppercase tracking-wider text-term-muted">
{t("profile.accountSection")}
</h2>
<div className="grid grid-cols-2 gap-3 text-[11px] text-term-muted">
<div className="grid grid-cols-2 gap-3 text-[0.6875rem] text-term-muted">
<div>
<span className="block">{t("profile.username")}</span>
<span className="text-sm text-term-text">{user.username}</span>
@@ -92,7 +92,7 @@ export function Profile({
<span className="text-sm text-term-text">{user.roleName}</span>
</div>
</div>
<label className="flex flex-col gap-1 text-[11px] text-term-muted">
<label className="flex flex-col gap-1 text-[0.6875rem] text-term-muted">
{t("profile.fullName")}
<input
className="input"
@@ -101,7 +101,7 @@ export function Profile({
onChange={(e) => setFullName(e.target.value)}
/>
</label>
<label className="flex flex-col gap-1 text-[11px] text-term-muted">
<label className="flex flex-col gap-1 text-[0.6875rem] text-term-muted">
{t("profile.email")}
<input
className="input"
@@ -115,7 +115,7 @@ export function Profile({
<button type="button" className="btn btn-primary btn-sm" onClick={saveAccount} disabled={savingAccount}>
{t("profile.saveProfile")}
</button>
{accountMsg && <span className="text-[11px] text-term-muted">{accountMsg}</span>}
{accountMsg && <span className="text-[0.6875rem] text-term-muted">{accountMsg}</span>}
</div>
</section>
@@ -124,7 +124,7 @@ export function Profile({
<h2 className="text-sm uppercase tracking-wider text-term-muted">
{t("profile.passwordSection")}
</h2>
<label className="flex flex-col gap-1 text-[11px] text-term-muted">
<label className="flex flex-col gap-1 text-[0.6875rem] text-term-muted">
{t("profile.currentPassword")}
<input
className="input"
@@ -134,7 +134,7 @@ export function Profile({
onChange={(e) => setCurrent(e.target.value)}
/>
</label>
<label className="flex flex-col gap-1 text-[11px] text-term-muted">
<label className="flex flex-col gap-1 text-[0.6875rem] text-term-muted">
{t("profile.newPassword")}
<input
className="input"
@@ -144,7 +144,7 @@ export function Profile({
onChange={(e) => setNext(e.target.value)}
/>
</label>
<label className="flex flex-col gap-1 text-[11px] text-term-muted">
<label className="flex flex-col gap-1 text-[0.6875rem] text-term-muted">
{t("profile.confirmPassword")}
<input
className="input"
@@ -163,7 +163,7 @@ export function Profile({
>
{t("profile.changePassword")}
</button>
{pwMsg && <span className="text-[11px] text-term-muted">{pwMsg}</span>}
{pwMsg && <span className="text-[0.6875rem] text-term-muted">{pwMsg}</span>}
</div>
</section>
</div>