diff --git a/backend/src/routes/rooms.js b/backend/src/routes/rooms.js index f10b541..1477329 100644 --- a/backend/src/routes/rooms.js +++ b/backend/src/routes/rooms.js @@ -47,6 +47,83 @@ function filterBookingData(booking, canSeeGuest, canSeeRate, canSeeAllNotes, vis export async function roomRoutes(app) { app.addHook('preHandler', requireAuth) + // GET /api/rooms/debug-room?room=101&date=YYYY-MM-DD + // Returns raw NewBook data for a single room — use to diagnose bracket/booking issues. + app.get('/api/rooms/debug-room', async (req, reply) => { + if (!hasCap(req, 'settings')) return reply.status(403).send({ error: 'Forbidden' }) + + const viewDate = req.query.date || new Date().toISOString().slice(0, 10) + const roomName = req.query.room + const yesterday = dateOffset(viewDate, -1) + const tomorrow = dateOffset(viewDate, +1) + const dayAfterTomorrow = dateOffset(viewDate, +2) + + const [sites, bookings] = await Promise.all([ + fetchSites(), + fetchBookings(yesterday, dayAfterTomorrow), + ]) + + if (!roomName) { + const sample = bookings[0] || {} + return { + hint: 'Pass ?room= to inspect a specific room', + view_date: viewDate, + total_sites: sites.length, + total_bookings: bookings.length, + booking_field_names: Object.keys(sample), + site_field_names: sites.length ? Object.keys(sites[0]) : [], + site_list: sites.map(s => ({ id: s.site_id, name: s.site_name })), + } + } + + const site = sites.find(s => s.site_name === roomName) + if (!site) return { error: `Room "${roomName}" not found`, available: sites.map(s => s.site_name) } + + const siteId = String(site.site_id) + const idFields = bookings.length + ? Object.keys(bookings[0]).filter(k => + k.toLowerCase().includes('site') || k.toLowerCase().includes('room') || + k.toLowerCase().includes('unit') || k.toLowerCase().includes('location') + ) + : [] + + const matchFn = b => + idFields.some(f => String(b[f] || '') === siteId) || + String(b.site_id || '') === siteId || + String(b.booking_site_id || '') === siteId || + (b.site_name && b.site_name === roomName) + + const siteBookings = bookings.filter(matchFn) + + const classified = classifyRoom(site, bookings, viewDate, yesterday, tomorrow) + + return { + view_date: viewDate, yesterday, tomorrow, day_after_tomorrow: dayAfterTomorrow, + site: { id: siteId, name: site.site_name, fields: Object.keys(site) }, + booking_id_fields: idFields, + total_bookings_fetched: bookings.length, + matching_bookings: siteBookings.map(b => { + const out = {} + for (const f of ['booking_id', 'booking_status', 'booking_arrival', 'booking_departure', ...idFields]) { + out[f] = b[f] + } + return out + }), + classified: { + flow_type: classified.flow_type, + spans_previous: classified.spans_previous, + spans_next: classified.spans_next, + previous_status: classified.previous_status, + next_status: classified.next_status, + booking_id: classified.booking?.booking_id, + booking_arrival: classified.booking?.booking_arrival, + booking_departure: classified.booking?.booking_departure, + next_booking_id: classified.next_booking?.booking_id, + next_booking_arrival: classified.next_booking?.booking_arrival, + }, + } + }) + // GET /api/rooms?date=YYYY-MM-DD app.get('/api/rooms', async (req, reply) => { if (!hasCap(req, 'view')) return reply.status(403).send({ error: 'Missing capability: view' }) diff --git a/frontend/src/components/RoomCard.tsx b/frontend/src/components/RoomCard.tsx index f5a6008..74acb71 100644 --- a/frontend/src/components/RoomCard.tsx +++ b/frontend/src/components/RoomCard.tsx @@ -104,8 +104,8 @@ function RoomCard({ room, viewDate, config, onClick }: Props) { } if (room.spans_next) { dataAttrs['data-spans-next'] = 'true' - } else if (room.next_status) { - dataAttrs['data-next-status'] = room.next_status.toLowerCase() + } else if (room.next_status != null) { + dataAttrs['data-next-status'] = room.next_status || 'unknown' } // Task status pill