Commit graph

44 commits

Author SHA1 Message Date
52af5f86bc Add iOS install hint + Apple PWA meta tags
iOS Safari has no beforeinstallprompt API, so the portal's Install
button (?install=1) silently did nothing there. IosInstallHint now
shows Share -> Add to Home Screen steps on Safari, or a prompt to
switch to Safari first if opened in another iOS browser/in-app
webview (those can't install PWAs on iOS at all).

Also added apple-mobile-web-app-title + apple-touch-icon so the
home screen icon isn't a page screenshot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 14:12:12 +00:00
16c70f5ba4 Fix past-departure room cards incorrectly merging with prior night
A past-departure record only marks that a checkout happened on the
viewed date — it never occupied the night before, so it must not
merge with the previous card the way a booking still actively
occupying viewDate would.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 14:11:40 +00:00
db38b586d5 Add update-available banner + versioned /health endpoint
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 14:11:32 +00:00
331d15ac88 Scope PWA manifest to app's own path instead of origin root
Every installed PWA claimed scope "/", so Android link-capturing routed
navigation for any app on the shared origin into whichever PWA had it.
2026-07-29 13:47:18 +00:00
2dbd490c1c Fix session-expiry redirect breaking standalone PWA out of its shell
Same fix as maintenance/reports: manifest scope was the app's own
base path instead of "/", and AuthGate unconditionally hard-navigated
window.top to the central /login on session expiry even when not
embedded in the portal iframe — together these dropped an installed/
standalone room-planner PWA into the portal's framed browser view
instead of staying in its own window.

AuthGate now only bounces to central login when actually embedded
(passing ?from= so it returns here afterwards); standalone or
directly-opened tabs get an in-app login form and never navigate
away. Also wired up the previously-dead inactivity auto-logout timer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 17:07:02 +00:00
24c6fa1f7d Add name, email and sign out button to sidebar footer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 11:10:07 +00:00
3000e227a3 Configurable inactivity timeout — disabled automatically in PWA/standalone mode 2026-07-13 12:28:59 +00:00
2bc587900b Add shared device inactivity timeout to AuthGate 2026-07-13 11:53:05 +00:00
9f3550a0a6 Replace letter icons with Lucide PWA icons 2026-07-11 20:30:08 +00:00
e49fa14856 Make room-planner installable as a PWA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 11:27:00 +00:00
35a3f0044e Fix bracket gap and right-side alignment for overdue checkout
- Trim bracket to 42px (from 46px) to restore 4px gap between bracket and card
- Remove margin-right override so spans-next/normal rules handle the right edge
  correctly (was causing a gap on cards with data-spans-next="true")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 12:32:57 +00:00
d5492a0152 Fix overdue checkout bracket position: shift card right not just content
Increase margin-left to 46px so the card's left edge moves 23px rightward,
giving the bracket clean space to its left rather than overlapping the card.
Restore margin-right to 23px — the extra left margin is the compensation.
Reposition bracket and sliver-depart to left: -46px to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 12:26:41 +00:00
7e908d3447 Fix overdue checkout bracket: narrow card right, centre and bold checkout time
- Increase margin-right to 46px so the card pulls in rightward by the same 23px
  the bracket intrudes leftward — net card width stays consistent with other rooms
- Widen .card-sliver-depart to match bracket width (46px) so the time and clock
  icon centre horizontally within the bracket rather than pinning to the far left
- Bump font-weight to 700 and font-size to 10px for clearer readability
- Also correct ::after right position when a next-status bracket is present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 12:16:51 +00:00
d14670b060 Widen left bracket sliver 3× for overdue checkouts on today's view
When viewing today and the departing booking is still 'arrived' in NewBook
(not yet checked out), the blue left-bracket sliver now extends ~3× wider
into the arriving card slot with a light blue fill, making the uncleared
checkout visually prominent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 12:10:24 +00:00
8606bbbf6d Fix expired session showing EmbeddedFallback instead of login page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 11:43:59 +00:00
a0874484a3 Today/future depart-only rooms show as vacant, not depart
'Depart' flow is now exclusively a past-date historical record. On today
and future dates a room with only a morning checkout and no new arrival
classifies as vacant — the room IS free for tonight regardless of the
11am checkout. The departed booking's status still drives the left bracket
colour and the checkout time still appears in the sliver for late checkouts.

B2B rooms (depart + new arrival on the same day) are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 13:59:05 +00:00
c7ec19b74e Fix: vacant card after a departure no longer spans left past the checkout
bookingsForSiteOnDate uses strict `departure > viewDate`, so a booking
that departed yesterday (departure_date = yesterday) doesn't appear as
prevBooking when computing today's card. prevVacant was therefore true,
causing the vacant card to span left into yesterday's margin — visually
indistinguishable from an ongoing stay.

Fix: check departingBookingForSite(yesterday) as prevDepartedBooking.
Include it in prevVacant guard (prevVacant = !prevBooking && !prevDepartedBooking)
and use its status for previous_status so the correct coloured bracket
appears on the left without the card spanning into yesterday.

Result: day after a checkout shows vacant card with coloured left bracket
(blue = still-arrived, purple = departed in NewBook) but full left border;
consecutive vacant days from the second night onward still span left into
each other correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 13:50:52 +00:00
663eda34af Future dates: morning-only departure classifies as vacant, not depart
A day-card represents that night. 'Depart' is a live operational state
(checkout tracking) that only makes sense for today: on a future date the
room's last occupied night is the previous day (shown as Stay), and the
departure morning itself leaves the night vacant. classifyRoom now takes
opts.future — when set, a departing-only room stays 'vacant' (or arrive/B2B
if someone arrives), with the departing booking's coloured sliver on the
left and the late-checkout time still shown in the bracket.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:55:31 +00:00
7fb9e245d9 Vacancy participates in the Gantt bracket system
- A vacant room spans into adjacent vacant days (left/right border removed, card extends into the margin) exactly like multi-night bookings
- An occupied room adjacent to a vacant day shows a grey 'vacant' sliver bracket on that side (previously no bracket rendered at all when the neighbouring day was empty)
- previous_status falls back to 'vacant' when nothing occupied yesterday; next_status falls back to 'vacant' when nothing occupies tomorrow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:43:59 +00:00
b528624be0 Cards: date-aware display — late checkouts only on future days, hide current-state info off-today
- Future dates: departure time sliver only shows for late checkouts (after default_checkout_time, default 11:00) — on future days every in-house booking is still 'arrived' so the time was showing on every card
- Sliver bracket widens to 19px when a time is shown and the time renders vertically (writing-mode) to fit the narrow space
- Clean/dirty site status pill only shows when viewing today — it reflects current state, meaningless on other dates
- NewBook task pill, task dots, and category header task/dirty badges hidden on future dates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:33:07 +00:00
2ec629cb5b Controls: separate Categories filter toggle, standalone buttons, full-width category headers
- FilterBar split into CategoryFilterBar / StatesFilterBar / StatFilterBar — each independently toggleable from the controls bar (Categories Filter / States Filter / Stats Filter), visibility persisted to localStorage
- Control buttons are now individual rounded buttons with a 6px gap (removed the merged/segmented styling that left a missing border)
- Category group headers span the full width with top/bottom borders only; 37px side padding keeps the header text aligned with room card content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:23:24 +00:00
a4c1f9bce5 Room planner: controls bar redesign + semantic filters + flat list view
- Cog button in date bar toggles controls bar visibility (persisted to localStorage)
- Controls bar: [Flat List][Categories] view toggle | [States Filter][Stats Filter] filter visibility toggles | [Reset View]
- Flat list view renders all visible rooms sorted numerically by name, no category headers
- States/stats filter bars independently show/hide based on controls bar toggles
- All view preferences (controlsVisible, viewMode, statesFilterVisible, statsFilterVisible) persist across page refreshes
- Semantic flow filters: 'arriving'/'departing' each match back-to-back rooms too; 'back-to-back' as exact match
- Filter chip counts use semantic union logic to match filter behaviour
- Guest name extraction tries guests[0].firstname+lastname (NewBook field names)
- booking_locked normalised: '0'/'1' strings handled explicitly (JS truthy fix)
- Category sort order pulled from settings global_config via internal HTTP endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:41:39 +00:00
5f3e52d8a6 Debug: expose full raw booking in debug-room; fix guest name extraction
- debug-room now returns full raw NewBook booking objects so all
  field names are visible for diagnosis
- guest_name extraction also tries guests[0].firstname+lastname
  (original PHP plugin pattern), not just guests[0].guest_name

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:24:24 +00:00
d0e80c2484 Fix settings URL path: include /settings prefix
SETTINGS_URL is the base host only; routes are prefixed /settings/api.
Was calling /api/internal/... which returned nothing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:21:57 +00:00
030c46a846 Rename app to Day Planner
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:19:41 +00:00
536bcd3340 Fix 500: fetch category order from settings service, not direct DB query
global_config lives in settings_db, not room_planner_db. Replace the
broken cross-DB pool query with an HTTP call to the new internal
settings endpoint. Falls back to NewBook order gracefully if settings
service is unavailable or rooms haven't been synced yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:13:13 +00:00
fbe913c01c Room planner: semantic flow filters, forward arrow, padlock + guest name fixes
- Flow filter chips are now semantic: Departing = depart|B2B,
  Arriving = arrive|B2B, so B2B rooms appear in both chips. Adds
  missing Arriving filter.
- Forward → button added to date bar (right of datepicker)
- booking_locked normalised to real boolean — NewBook returns '0'/'1'
  strings which were all truthy, causing padlock on every room
- guest_name fallback: also try booking.guest_name (flat field) before
  account_for_name in case NewBook omits the guests[] array

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:07:55 +00:00
1ab864f4e3 Room planner: pull category sort order from settings global_config
Category ordering on the default view now uses the admin-configured
sort_order from global_config.newbook.rooms rather than the raw
NewBook site_category_order. Falls back to NewBook order if the
settings row is absent. Also overrides category_order on each room.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 12:53:20 +00:00
9ef047900b Room planner: fix settings scroll, checkout time to left-bracket sliver
- Settings page: add overflow-y:auto + flex:1 so content scrolls within
  page-content (which has overflow:hidden)

- Checkout time: move departure time out of B2B card stats row and into
  the left-bracket sliver, matching original plugin behaviour:
  - Only shown while prev booking status is still 'arrived' (not yet
    checked out); collapses to normal bracket once NewBook flips to
    'departed'
  - Positioned absolutely over the ::before left bracket area
  - Remove the forced 'departed' override on prev_status — let NewBook's
    real status drive bracket colour so the sliver shows correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 12:30:27 +00:00
89ad3811da Room planner: fix bookings_list to use list_type=staying
The original WordPress plugin (class-hhdl-ajax.php line 409) uses
list_type='staying', which returns bookings whose stay *overlaps* the
date window (arrival <= period_to AND departure >= period_from).

Using list_type='all' was returning bookings filtered by booking-placed
date (or a flat dump), completely missing tomorrow-arriving bookings
that are needed for right-bracket display. Switching to 'staying' with
the original yesterday→tomorrow window gives 50 bookings vs 89 before,
and correctly includes adjacent-day arrivals.

Verified on dev: room 101 now shows confirmed right-bracket for July 5
arrival, room 102 now correctly classifies as back-to-back.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 12:18:26 +00:00
3c4bdd9a1c Room planner: fix category exclusion field name (category_id not site_category_id)
NewBook API returns category_id on site objects; exclusion check was using
the non-existent site_category_id field so excluded categories config never worked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 12:09:16 +00:00
0d14473daf Room planner: add debug-room endpoint, fix next_status null check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:54:32 +00:00
916dc50390 Room planner: fix next-bracket visibility and vacant room misdetection
Extend booking fetch window to day-after-tomorrow so tomorrow-arriving
bookings appear in nextBooking lookups (some NewBook regions exclude
arrivals exactly on period_to date, causing right brackets to not render).

Add booking_site_id fallback in site-ID matching so bookings are found
correctly even when NewBook returns booking_site_id instead of site_id in
the booking object (was causing some rooms to show as vacant).

Remove category debug logging (no longer needed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:40:46 +00:00
cbfec7d591 Room planner: fix mobile layout — move top-bar inside page-content
top-bar was a sibling of page-content in the flex-row app-shell, so on
mobile it sat in the left column instead of spanning the top. Moving it
inside page-content (a flex column) makes it stack above the content on
mobile while remaining hidden on desktop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:34:30 +00:00
b66b3a7854 Room planner: fix spans_next, night counter, and B2B booking priority
spans_next was using departure > viewDate (today), which made any 1-night
booking extend the card to the right. Fixed to departure > tomorrow so the
card only extends when it's also active tomorrow.

Night counter used Date.parse() on NewBook datetime strings, which browsers
parse as local time and shift off by hours in BST. Replaced with utcDay()
which slices the YYYY-MM-DD prefix and calls Date.UTC() — always correct.

When multiple bookings overlap today, prefer the one arriving today so an
arriving guest takes priority over a stale stopover. Also treat a co-active
non-arriving booking as the B2B departing context.

Force previous-day bracket to show as "departed" (purple) when that booking
departs today, without waiting for NewBook to update its status field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:31:29 +00:00
663ef5d107 Room planner: flexible category field resolution + debug logging
Resolve NewBook category fields from site_category_id, category_id, or
category.id (whichever is present) so categories work regardless of which
naming convention the NewBook region uses. Log the first site's keys on
each /api/rooms request to confirm field names in docker compose logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:13:33 +00:00
00e5e684fa Room planner: rewrite card layout to match original Gantt design
Replace CSS grid with the original plugin's vertical list + 23px margin
bracket system. Each card has a 2px border coloured by booking status;
data-previous-status / data-next-status drive C-bracket pseudo-elements in
the 23px gutter; data-spans-previous/next remove the corresponding border
and collapse the margin to extend the card across days.

Card interior is restructured: header row (room number, flow badge, lock,
nights counter, site-status pill), guest info row, and stats row (time
pill, bed-type pill, task-status bordered pill). Category headers now match
the original's sticky bordered style with edge-extending pseudo-elements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 10:02:44 +00:00
3c0eb5ae73 fix: pass task_type=[-1] to tasks_list — required param, -1 means all types
NewBook returns 412 if task_type is absent. The original dailylist plugin
always passes [-1] as a fallback when no specific types are configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 09:43:34 +00:00
a7cc79a9b1 fix: serve frontend via root + slug subdir, not alias — assets 404/fallback
nginx alias + try_files resolves paths incorrectly (nginx trac #97), so
asset requests fell through to the SPA fallback and index.html was served
as the JS bundle — the app never booted. Match the proven twin-optimiser
pattern: copy dist to html/room-planner and use root with try_files.
Also node:20 → node:22 per stack convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 16:32:45 +00:00
5a2dab637c fix: AuthGate stuck on Loading — verify returns flat user object
The auth /verify endpoint returns { user_id, name, email, is_admin, caps }
directly, not wrapped in a user key, so data.user was undefined and the
gate never rendered. Also: caps come back as bare slugs when ?app= is
passed, so the prefixed room-planner:cap checks always failed — replaced
with the standard can(user, cap) helper honouring is_admin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 16:25:20 +00:00
2993dfa216 room-planner: fix nginx — add missing central auth proxy block
Without /room-planner/api/auth/ → 10.10.10.101, auth verify hits the
app backend instead of the central auth service, causing infinite loading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:29:24 +00:00
63b3e42a12 fix: correct index.html script src — Vite prepends base path, don't include it manually
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:15:30 +00:00
7983865f79 fix: remove NEWBOOK_LOCATION_ID env var — credentials come from settings service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:11:01 +00:00
1e658b6a48 feat: add room-planner app — 3-day HK room view with NewBook integration
NewBook-connected daily housekeeping planner. Replaces the hotelhubmodule-housekeeping-dailylist
WordPress plugin. LXC 120 · 10.10.10.120:3080 · slug: room-planner.

- 3-day booking window (yesterday/today/tomorrow) fetched live from NewBook
- Task completion ticks back to NewBook; room status patches NewBook directly
- 23px border sliver CSS system for adjacent-day booking status
- 3-state filter cycling (off→inclusive→exclusive) for categories and flow types
- Stat filters for outstanding tasks and clean/dirty status
- Rolling 48h activity log with checkout/checkin/status/tasks events
- newbook_pings event bus for future NewBook poller integration
- Room modal with permission-gated guest/rate/notes, task checkboxes, status buttons
- Placeholder sections for future linen-count and routine-tasks modules
- Settings page: task type colours, twin/extra-bed detection, category exclusions
- Mobile-first layout (sidebar desktop, compact top bar mobile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:07:00 +00:00