feat(booth): open the pay/exit modal on a hardware scan regardless of focus
A barcode/QR scanner is an HID "keyboard wedge" — it types the id + Enter into whatever holds focus. Previously that only worked while the ticket <input> was focused; a scan with focus elsewhere (or nowhere) went nowhere. New useScanner hook (apps/web/src/lib/use-scanner.ts): a document-level keydown listener that detects the scanner's FAST keystroke burst ended by Enter and opens the pay/exit modal via setActiveTicket — regardless of focus. A gap > 50ms resets the buffer, so human-paced typing with nothing focused never registers as a scan (min length 3 guards stray Enters). Keystrokes into an input/textarea/select/ contenteditable are ignored, so the manual ticket field still works by hand. The hook is paused while a modal is already open — a scan must not abandon an in-progress payment; the operator finishes/closes, then scans the next car. Verified at runtime (Playwright): a fast burst with focus on BODY opens the modal; a second scan while the modal is open is ignored; slow (120ms) human typing does NOT open it; the manual input submit still opens it. build+lint 14/14. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -68,6 +68,14 @@ a right column with the **live event ticker**. Submitting/clicking a ticket open
|
||||
modal** (entry/duration/total, tender, voucher checkbox, entry/exit snapshots). All live-refreshed via
|
||||
the WS.
|
||||
|
||||
> **Focus-independent scan capture (2026-06-21).** A scan now opens the pay/exit modal **regardless of
|
||||
> focus** — the operator needn't click the ticket field first. A document-level listener (`useScanner`,
|
||||
> `apps/web/src/lib/use-scanner.ts`) detects the HID scanner's fast keystroke BURST ended by Enter (a
|
||||
> gap > 50ms resets the buffer, so human-paced typing with nothing focused never triggers it) and fires
|
||||
> the same `setActiveTicket`. It ignores keystrokes into an `<input>/<textarea>/select/contenteditable`
|
||||
> so the manual ticket field still works by hand. It is **paused while a modal is already open** — a
|
||||
> scan must not abandon an in-progress payment; the operator finishes/closes, then scans the next car.
|
||||
|
||||
### Explainable activity log (2026-06-19)
|
||||
The ticker used to flag an **anomaly** with no explanation — a red row with just an id, "nobody knows
|
||||
what happened." Now every event is **self-describing and clickable**:
|
||||
|
||||
Reference in New Issue
Block a user