Commit graph

8 commits

Author SHA1 Message Date
5180adf8d2 Kiosk: live-force reset to welcome screen, configurable inactivity timeout
Kiosk's SSE connection now stays open for the whole session instead of
closing the moment a booking is pushed, so a staff-triggered Reset (from
the extension/Terminals popup or the Devices admin page) now actually
forces the tablet back to the welcome screen immediately, discarding
whatever was mid-entry — not just flipping its status in the DB with
nothing listening tablet-side. Removed the two now-redundant/harmful
pushIdle() calls in routes/checkin.js's own submit success/failure paths,
which would otherwise race the confirmation/error screen now that the
stream stays open. Added a confirm() prompt before Reset on both trigger
surfaces since it's now a real interruption, not just an unstick.

Also made the tablet's inactivity auto-cancel timeout configurable from
Settings (checkin_inactivity_timeout_minutes, default 5, same as the old
hardcoded value) instead of a fixed constant, read via the existing
token-gated GET /api/checkin/:token response.
2026-10-09 12:19:53 +00:00
790431c446 Extension: show installed/latest version for drift tracking
Popup footer shows its own installed version (from manifest.json, via
chrome.runtime.getManifest()); Terminals page shows the latest bundled
version next to the download link (new GET /api/extension/version, reads
the same manifest.json). Bumped manifest.json to 0.2.0 for the Today's
Arrivals change, and documented in extension-src/README.md that every
future change to this folder must bump the version too — nothing else
keeps it in sync automatically.
2026-10-09 12:12:32 +00:00
2b83f47556 Kiosk redesign: logo, tablet sizing, 3-step flow, better email/car-reg logic
- Uploadable reception logo (Settings, PNG/JPEG, public GET so the kiosk
  can show it without a staff login) — shown top-center on the kiosk
  login and idle screens, Welcome copy centered underneath.
- Tablet-sized styling pass on all kiosk views for a ~10" tablet (bigger
  touch targets, base text, spacing) rather than the general app's
  desktop-first sizing.
- New booking-confirm step (name/arrive/nights/depart/rate name(s)/rate
  total) replaces the old partial review step.
- Contact details, car registration and marketing merged into one step
  (was 3 separate steps) — 5 steps down to 3 overall.
- Email handling now matches the legacy system's confirmed behaviour:
  excluded-domain (OTA/agent relay) addresses are filtered out of what's
  shown as 'on file' entirely, not just flagged, and a new
  excluded_email_domains Settings field (CSV) replaces the old hardcoded
  domain list so any OTA/agent domain can be added without a code change.
- Single email field design: only pre-filled when there's exactly one
  valid existing email, left blank (not guessed) when there are several —
  marketing opt-in only ever touches whatever's in that one field, never
  blanket-subscribes multiple pre-existing emails (project_checkin memory,
  2026-09-01 'stay in touch' note).
- Car registration now allows multiple additions — add/remove list UI,
  buildContactDetails appends each new one not already on file rather
  than the old single-slot overwrite.
- Signature now required to actually complete (tracked live via
  SignatureCanvas onEnd, not just caught on submit) alongside accepted
  terms — Complete Check-In stays disabled until both are true.
- T&C block is height-capped with its own internal scroll (keeps the step
  on one non-scrolling screen) and now renders basic HTML (<b>, <br>,
  <p>, <ul> etc) instead of plain text — content is admin-authored
  (settings capability required), same trust boundary as any other
  setting. PDF generation strips tags back to plain text since pdfkit
  can't render HTML.
- PDF and booking-confirm screen both surface tariff name(s) (deduped
  across tariffs_quoted in case the rate changed mid-stay) and nights,
  not just the total.
- Also fixed: injected extension buttons could hang forever on 'Loading
  tablets…' with no feedback if the extension was reloaded without also
  refreshing the NewBook tab (stale content script) — now surfaces a
  clear 'Extension was reloaded — refresh this page' message instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 21:21:30 +00:00
67def8ac19 Move extension download from Settings to Terminals page
Makes more sense there — creating a terminal key and installing the
extension are the same setup workflow, not two unrelated things on
separate pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 20:35:54 +00:00
82f926d736 Kiosk: self-heal a busy device on fresh page load
A browser refresh mid-checkin bypasses the Cancel button entirely (no
React unmount cleanup runs), so a device stayed locked 'busy' with no
route back except a manual admin reset — hit for real 2026-09-01.
registerAndGoIdle() only runs on a genuine fresh mount, so if the device
comes back 'busy' at exactly that moment, nothing on this browser is
actually mid-checkin (state was just initialized) — self-correct via the
same reset the Cancel button already uses, rather than trusting stale
server-side status. Uses kioskReset (krequest, throws KioskSessionError
on 401) not resetDevice (request, redirects to the staff /login page on
401) — the latter would be wrong on a kiosk tablet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:20:10 +00:00
f4625e0860 Fix nginx trailing-slash mixed-content redirect, add self-healing device reclaim
- location /tablet-checkin/api/devices/ (trailing slash, proxy_pass) made
  nginx auto-redirect the bare /api/devices list endpoint, and since this
  inner nginx sits behind NPM's TLS termination it built that redirect as
  http:// even over an https:// request — browsers block that as mixed
  content, surfacing as a bare 'Failed to fetch' on the Devices admin page
  with no other clue. Dropped the trailing slash on both the location
  pattern and proxy_pass target; still correctly prefix-matches every
  /devices/:id/... sub-route, no more auto-redirect at all.
- pushNavigate/send() now only reports delivered:true if a write
  demonstrably succeeded, not just because a stream entry exists for the
  device — a stale-but-not-formally-closed connection can silently accept
  a write that never reaches the far end.
- pushBookingToDevice can now self-heal a device stuck 'busy' with no live
  session behind it (session already expired unused) rather than requiring
  an admin to manually click Reset first. Real failure mode hit testing on
  a physical tablet 2026-09-01: two pushes worked end-to-end, a third's
  navigate event never reached the tablet (SSE connection silently died
  during an idle gap), leaving the device stuck with no way to recover
  except a manual admin reset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 19:05:53 +00:00
4d3774ef0d Fix NewBook write mechanism, bundle push extension, remove vestigial hotel_id
- setBookingCustomFields: use bookings_update's own custom_fields array
  (confirmed from the real legacy plugin code) instead of
  instance_custom_fields_set, which was only ever proven for a different,
  unrelated plugin
- online_checkin_complete is now a configurable per-hotel custom field
  (online_checkin_complete_custom_field_id setting), not a bogus top-level
  bookings_update field NewBook would silently ignore
- Fix field-name mismatches vs a real live NewBook booking response:
  booking_arrival/departure/length, guest address (street/city/state/
  postcode/country, not a single field), phone/mobile (in contact_details,
  not top-level), and pickPrimaryGuest now checks primary_client (was
  checking fields that don't exist in the real API)
- Bundle the standalone push extension into backend/extension-src/, add
  GET /api/extension/download (zips on request via archiver) and a
  download button on the Settings page
- Remove newbook_hotel_id — vestigial from the legacy system's multi-hotel
  routing model, never actually reached NewBook's API, this stack is
  single-hotel-per-deployment so there's nothing to route between

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 18:11:35 +00:00
3b2e25df3f Initial scaffold: tablet-checkin app
Staff-push guest check-in tablet. Fastify backend + React/Vite frontend,
three-tier auth (staff JWT, terminal API key, kiosk cookie), NewBook
integration via bookings_get/guests_update/bookings_update, pdfkit-based
regcard generation, marketing opt-in with Sendy/Mailchimp adapters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 17:35:39 +00:00