Show Arrival today alongside Departing for back-to-back turnovers

fetchRoomOccupancy() was dropping a site from `arriving` as soon as it
was also `departing`, on the assumption departing always wins. But a
departing room can have a same-day incoming booking already confirmed —
worth flagging on its own, not silently swallowed. Site now stays in
both sets when that's the case; the Rooms Accessible View card renders
both the purple "Departing" and amber "Arrival today" badges together.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-09-03 09:38:26 +00:00
parent 8ddb99d2d7
commit e3e7e60594
2 changed files with 12 additions and 6 deletions

View file

@ -78,10 +78,12 @@ async function fetchRoomOccupancy() {
arriving.add(id) arriving.add(id)
} }
} }
// A site can have more than one booking on a turnover day (e.g. a departing guest // A site can have more than one booking on a turnover day. Physically-present-right-
// plus a same-day incoming one) — physically-present-right-now always wins over a // now always wins over "merely confirmed to arrive later", so departing beats
// booking that's merely confirmed to arrive later. // occupied. But a departing site that ALSO has a same-day incoming booking is a
for (const id of departing) { occupied.delete(id); arriving.delete(id) } // genuine back-to-back turnover worth flagging — leave it in `arriving` too rather
// than dropping it, so the UI can show both "Departing" and "Arrival today".
for (const id of departing) occupied.delete(id)
for (const id of occupied) arriving.delete(id) for (const id of occupied) arriving.delete(id)
return { occupied, departing, arriving } return { occupied, departing, arriving }
} }
@ -353,7 +355,9 @@ export async function taskRoutes(app) {
// GET /api/occupancy — room state from NewBook for the Rooms Accessible View // GET /api/occupancy — room state from NewBook for the Rooms Accessible View
// Returns three lists: occupied (staying, guest in room), departing (guest in room // Returns three lists: occupied (staying, guest in room), departing (guest in room
// but checking out today — accessible later) and arriving (booked today, not yet checked in) // but checking out today — accessible later) and arriving (booked today, not yet
// checked in). A site can appear in both departing and arriving — a back-to-back
// turnover, today's guest not gone yet but the next one already booked in.
app.get('/api/occupancy', { preHandler: requireCap('view') }, async (req, reply) => { app.get('/api/occupancy', { preHandler: requireCap('view') }, async (req, reply) => {
try { try {
const { occupied, departing, arriving } = await fetchRoomOccupancy() const { occupied, departing, arriving } = await fetchRoomOccupancy()

View file

@ -258,7 +258,9 @@ export default function Summary() {
const sid = t.newbook_site_id ? String(t.newbook_site_id) : null const sid = t.newbook_site_id ? String(t.newbook_site_id) : null
const isOccupied = roomsView && !!sid && occupiedSiteIds.has(sid) const isOccupied = roomsView && !!sid && occupiedSiteIds.has(sid)
const isDeparting = roomsView && !!sid && !isOccupied && departingSiteIds.has(sid) const isDeparting = roomsView && !!sid && !isOccupied && departingSiteIds.has(sid)
const isArriving = roomsView && !!sid && !isOccupied && !isDeparting && arrivingSiteIds.has(sid) // Not exclusive with isDeparting — a departing room can also have a same-day
// incoming booking (back-to-back turnover), and both badges should show.
const isArriving = roomsView && !!sid && !isOccupied && arrivingSiteIds.has(sid)
return ( return (
<div <div
key={t.id} key={t.id}