Commit graph wutzcalc/client/src/tablet
Author SHA1 Message Date
iris
3c9fd3d55c client: long-press picker also offers quick-remove when already in cart
Fixes the rest of #70 - mara's answer: "when there are already drinks
in cart, i want to be able to remove them - so show negative numbers
there too."

Second row of -1..-5 buttons appears in the count picker only when the
long-pressed drink already has cart quantity (an empty-cart drink keeps
the original add-only picker - nothing to remove). Routed through
incLine (the qty-stepper's own clamp-at-zero-and-drop-the-line logic)
rather than addDrink's raw qty+n, so over-removing (e.g. -5 on a qty-3
line) drops the line cleanly instead of going negative - verified this
specific clamping behavior with a standalone reproduction of the logic
before trusting it. Remove buttons get the existing button.danger
styling (red), matching the app's established negative/removal
convention (Pfand-return tile, cart-line.return).

pnpm --filter client typecheck and build both clean.
2026-08-03 17:26:13 +02:00
iris
0ae25ecab7 client: fall back to touch events for long-press (Pointer Events not on old WebKit)
Fixes half of #70 - the long-tap/count-picker interaction.

Pointer Events (onPointerDown/Up/Cancel) only shipped in Safari 13/iOS
13. The same class of old-enough-WebKit kiosk device #66/#68 are
already about doesn't fire them at all, so the long-press timer never
starts there even though plain clicks (universally supported) keep
working - which matches the report exactly: add-one-by-tapping works
on the tablet, the long-press count picker doesn't, and both work fine
on a laptop's modern browser.

Added onTouchStart/onTouchEnd/onTouchCancel alongside the existing
pointer handlers, wired to the same start/cancel functions - safe on
browsers that fire both pointer and touch events for one touch, since
starting/cancelling the timer is already idempotent.

Not touching the second half of the report (negative amounts for
cart-only drinks) - genuinely ambiguous relayed text with several
plausible readings that imply different UI, asked mara for
clarification on the issue instead of guessing.

pnpm --filter client typecheck and build both clean.
2026-08-03 16:35:26 +02:00
iris
09e1bec9bb client: show build version + browser diagnostics for bug triage
Fixes #68.

- Bar switcher and stats page get a low-contrast "wutzcalc <git-sha>"
  footer at the bottom (VersionFooter component, shared).
- Admin panel gets a fuller Diagnose section: version, user agent,
  screen resolution + device pixel ratio, viewport size, browser
  language.

Version is the short git commit sha, baked in at build time via
vite.config.ts define (falls back to "dev" if .git is unavailable at
build time, e.g. a tarball/CI-artifact deploy) - matches how deploys
actually run (pnpm build from a git checkout, per
deploy/wutzcalc.service), but stays defensive rather than failing the
build.

pnpm --filter client typecheck and build both clean.
2026-08-03 15:33:17 +02:00
iris
7a58a4a1bb client: fix reset-error handling, undersized qty buttons, chart resize,
dedupe j()/errText(), stale-fetch guard, empty bar-picker state

Six small, mostly-unrelated items from the #47 review round (#57):

1. 'Statistik zurücksetzen' now checks res.ok and shows the server's
   error instead of silently re-rendering the un-reset data on a 401/500
   with no indication anything went wrong.
2. DayChart now tracks its container width via ResizeObserver and calls
   uPlot's setSize() on change — previously read once at plot-creation
   time, so a resize/orientation-change (more visible on the phone/
   tablet path into /stats) left the chart the wrong width.
3. .qty-btn (cart +/- buttons) grown from 28x28px to 44x44px, the
   conventional minimum touch target — was the one conspicuously small
   interactive control in the money path, at a fast-moving bar counter.
4. Tablet's bar-config fetch (App.tsx) now guards against an
   out-of-order resolution if barId changes again while a previous
   api.config() call is still in flight. Currently unreachable (nothing
   triggers a second barId change mid-fetch today) but closes the gap
   before the next 'add a cancel button to the loading screen' change
   could make it live.
5. j()/errText() were duplicated byte-for-byte between Admin.tsx and
   Stats.tsx (Admin.tsx's own comment noted this exact duplication had
   already caused drift once before) — centralized in api.ts, both files
   now import instead of redeclaring.
6. Tablet's bar picker now shows an explicit empty-state message when
   zero bars exist (fresh install, or every bar deleted), previously
   indistinguishable from 'still loading'.

Closes #57. Verified: tsc --noEmit and vite build both clean.
2026-07-31 12:52:01 +02:00
iris
cb316d16d1 tablet: fix cart scroll — flex-end + overflow-y:auto doesn't scroll in WebKit
Follow-up to the #39 fix in d274275, which mara reports still doesn't
scroll in practice.

The bug: overflow-y: auto and justify-content: flex-end on the *same* flex
column is a known WebKit/Safari interop gap — the flex-end-justified
overflow gets clipped at the container bounds instead of becoming a
scrollable region, so nothing actually changed from the user's side despite
the CSS being 'correct' per spec. This app targets iOS Safari 12+
(vite.config.ts legacy targets), squarely in the affected range.

Fix: split the concerns onto two elements. .cart-items is now a plain
overflow-y: auto scroll container with no alignment property. The new
inner .cart-items-inner wrapper carries display:flex/flex-direction:column/
justify-content:flex-end, with min-height: 100% (a floor, not a fixed
height) so it still bottom-anchors a short list but grows past 100% and
scrolls normally in the outer container once content overflows it.

Build clean.
2026-07-30 12:17:15 +02:00
iris
058c77524a tablet error screen can recover from a stale bar_id; admin drink reorder gets a non-drag fallback
- App.tsx: the error screen (shown when api.config(barId) fails, e.g.
  an admin deleted/renamed the bar while this tablet was offline)
  only offered 'Neu laden', which re-reads the same stale localStorage
  bar_id and fails again — a permanent stuck loop with no in-app fix.
  Added a 'Bar wechseln' button reusing the existing changeBar()
  logic, which now also clears the error state.
- Admin.tsx BarDrinkEditor: the drag-and-drop reorder is a
  mouse-oriented API that doesn't fire on touch-only input and has no
  keyboard equivalent. Added ▲/▼ buttons alongside the drag handle so
  reordering works regardless of input method.
2026-07-29 20:33:57 +02:00
iris
e52a0469c8 fix retried submissions double-booking a sale
The server dedupes on client_uuid, but the tablet minted a fresh uuid
on every confirm() call — including retries after a timeout/dropped
connection, exactly the case the idempotency key exists to guard
against. The dedup check never fired on a real retry, so a flaky-Wi-Fi
resend could book the same sale twice.

Client: generate one uuid per pending cart (a ref, lazily created),
reuse it across retries of the same submission, reset it only when
the cart is cleared (success or cancel) so the next cart gets its own
id.

Server: the existing-row dedup check and the insert straddled the
db.transaction() boundary, so a genuine UNIQUE-violation race would
have surfaced as a raw 500 instead of the idempotent response. Catch
that specific violation and fall back to re-reading the row.
2026-07-29 20:16:39 +02:00
iris
b5735c270f tablet: +/- buttons on cart lines, long-press drink for 1-5 picker
Cart entries (drink lines and Pfand-zurück) now have +/- buttons next
to the quantity instead of only accumulating via repeated taps;
decrementing to 0 removes the line. Long-pressing a drink tile opens
a small overlay with buttons 1-5 to add that many at once — a plain
tap still adds one. The long-press timer is cancelled on pointerup/
leave/cancel, and the click that follows a fired long-press is
swallowed so it doesn't also add a plain 1x.
2026-07-29 18:33:51 +02:00
müde
6a0d30c8b5 add svg favicon + toolbar logo; set theme before paint
- favicon.svg (beer mug), linked from both entry points and shown in
  the tablet topbar next to the bar name
- inline data-theme in <head> kills the flash of dark on light-mode load
- hide the "Pfand zurück" tile when the bar has no Pfand
2026-06-14 22:55:15 +02:00
müde
2a43b59947 tablet: split price/pfand, light mode, bigger bold text
- show drink price and Pfand separately on tiles and in cart
- add light/dark toggle on the bar-picker page (persisted)
- theme via CSS variables, applied on load for tablet + admin
- larger, bolder text throughout; much bigger total sum
2026-06-14 21:57:52 +02:00
müde
e0898bea22 scaffold festival drink tracker (pnpm workspace, Fastify + SQLite, Preact tablet UI, admin) 2026-05-19 18:12:01 +02:00