Adds GET /api/staff/arrivals (NewBook bookings_list, same list_type: 'staying'
pattern room-planner/hvac/maintenance already use) and a popup section that
lists each arrival with its own Push button, falling back to a per-row device
picker on busy/offline/no-default — a second route to the same push flow,
separate from the content-script's injected NewBook page buttons.
- Injected buttons now show state via the tablet icon itself — spinner
while sending, check on success, warning triangle on busy/offline/error —
with detail in a hover tooltip, rather than a persistent inline text block
- Replaced the in-page dropdown panel (block-inserted for row/banner
buttons, fixed-position floating for menu items) with a single centred
modal overlay used by all three injection shapes — simpler than
maintaining per-variant positioning logic, and looks the same everywhere
- Picker overlay now opens automatically when the default tablet is busy
(409) or configured but not actually connected (delivered: false), not
just when no default is set at all — mirrored in popup.js's own fallback
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A push can succeed server-side (session created) with nowhere to actually
deliver to — the backend's pushNavigate() already distinguishes this via a
delivered:false return (see device-stream.js), but nothing in the extension
read it: every surface just said "Sent" regardless. Confirmed live on the
dev stack 2026-10-01 — the registered dev tablet hadn't heartbeated in a
month, so a push looked successful but nothing happened. Now every send
path (injected buttons' fast path + manual picker, popup's fast path +
manual picker) shows an amber "tablet isn't connected right now" warning
when delivered is false.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Scope the new bookings_checkin banner to that page's own tablist only —
bookings_view has a ui-tabs-nav tablist too, so it was duplicating
alongside the existing arrival-reminder banner there
- options.js: persist whatever the default-tablet <select> ends up showing,
not just on manual change — a <select> auto-selects its first <option>
without firing 'change', so a single-tablet setup (or anyone happy with
the first option) never actually got a default saved despite the dropdown
looking selected, which sent every injected button to the manual picker
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Inject Send to Tablet into the bookings_checkin page's top/bottom context
menus (left of Confirm Check-In) and a dedicated banner above the tabs
- Popup now auto-pushes to the default tablet instead of just pre-selecting
it, with a terminal-key-authed reset route so reception can clear a
stuck-busy tablet from the extension popup
- Replace the extension icon with a person+checkmark glyph matching the
app's own UserCheck icon and accent colour
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>
- Options page gains a 'Default tablet for this machine' picker,
populated from the device list once base URL + terminal key are
configured, auto-saved on change (small enough setting not to warrant
its own Save button).
- Popup pre-selects the default in its device dropdown but still shows
the full picker — opening the popup is already a deliberate action, so
staff can still override it there.
- Injected buttons (row + banner) skip the dropdown entirely when a
default is configured — one click pushes straight to it. Only falls
back to the full picker if that specific push comes back busy (409),
with a note explaining why it's asking despite a default being set.
background.js's pushBooking() now returns the HTTP status alongside
ok/error so this can be detected reliably rather than string-matching
the error message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New injection point on bookings_view pages — NewBook's own 'This Booking
is due to Arrive today. Do you want to go to Check-In?' banner
(.newbook_message.plain), confirmed from a real sample 2026-09-01. Shares
the row-injection's button/panel logic rather than duplicating it;
makeSendButton/togglePanel now take a variant ('block' for the list row's
table cell, 'inline' for this banner's flowing text context). Booking ID
extraction differs per shape: the list row has a checkbox value, this
banner's Check In button only carries it inside its onclick attribute's
URL, no separate structured attribute.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Backend CORS is disabled by default (origin: false), and an unpacked
extension's ID isn't fixed/predictable per install anyway, so allowlisting
it server-side would be awkward. options.js now calls
chrome.permissions.request() for the entered base URL on Save — once
granted, the extension's own fetches bypass CORS entirely for that origin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>