feat: housekeeping automation — auto-task on checkout + room status on done

Backend:
- Migration 023: housekeeping_settings table (checkout_auto, checkout_priority, inspection_after_clean)
- New route: GET/PATCH /api/hotels/:slug/housekeeping-settings
- bookings.ts: on checked_out → auto-create turnover task + mark room dirty + broadcast WS
- housekeeping.ts: on task done → update room status (inspect|clean per setting) + broadcast WS
- ws.ts: export broadcast() for use in other routes

Frontend:
- HousekeepingPage: load/save settings to API, live Loader on save button
- useHotelSocket: add housekeeping WS message types
- HousekeepingPage: subscribe to WS — new tasks appear instantly without refresh
- BookingModal: show total section when room + nights selected (not just when total > 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-23 22:27:18 +03:00
parent ce20c6545d
commit 9af11df1b0
10 changed files with 205 additions and 12 deletions

View File

@@ -1,5 +1,7 @@
import { FastifyPluginAsync } from 'fastify'
import { db } from '../db'
import { getHkSettings } from './housekeeping-settings'
import { broadcast } from './ws'
type SlugParam = { Params: { slug: string } }
type SlugIdParam = { Params: { slug: string; id: string } }
@@ -118,7 +120,21 @@ const housekeeping: FastifyPluginAsync = async (fastify) => {
values,
)
if (!rows[0]) return reply.code(404).send({ error: 'Task not found' })
return rows[0]
const task = rows[0]
// When task is marked done → update room housekeeping status
if (request.body.status === 'done' && task.room_id) {
const settings = await getHkSettings(hotelId).catch(() => null)
const newRoomStatus = settings?.inspection_after_clean ? 'inspect' : 'clean'
await db.query(
`UPDATE rooms SET housekeeping_status = $1 WHERE id = $2`,
[newRoomStatus, task.room_id],
)
broadcast(slug, { type: 'housekeeping_done', taskId: task.id, roomId: task.room_id, roomStatus: newRoomStatus })
}
broadcast(slug, { type: 'housekeeping_updated', task })
return task
},
)