import { FastifyPluginAsync } from 'fastify' import { db } from '../db' type SlugParam = { Params: { slug: string } } const scheduleRoutes: FastifyPluginAsync = async (fastify) => { const getHotelId = async (slug: string): Promise => { const { rows } = await db.query('SELECT id FROM hotels WHERE slug = $1', [slug]) return rows[0]?.id ?? null } const canAccess = (userSlug: string | null, role: string, slug: string) => role === 'super_admin' || userSlug === slug // GET /api/hotels/:slug/schedule?from=YYYY-MM-DD&to=YYYY-MM-DD fastify.get( '/api/hotels/:slug/schedule', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) const { from, to } = request.query let where = 'WHERE ss.hotel_id = $1' const params: unknown[] = [hotelId] if (from) { params.push(from); where += ` AND ss.date >= $${params.length}` } if (to) { params.push(to); where += ` AND ss.date <= $${params.length}` } const { rows } = await db.query( `SELECT ss.id, ss.user_id, ss.date, ss.shift_start, ss.shift_end, ss.is_day_off, ss.notes, u.name AS user_name, u.role AS user_role, u.position AS user_position FROM staff_schedules ss JOIN users u ON u.id = ss.user_id ${where} ORDER BY ss.date, u.name`, params, ) return rows }, ) // PUT /api/hotels/:slug/schedule — upsert one entry fastify.put( '/api/hotels/:slug/schedule', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!['manager', 'hotel_admin', 'super_admin'].includes(request.user.role)) return reply.code(403).send({ error: 'Forbidden' }) if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) const { user_id, date, shift_start, shift_end, is_day_off, notes } = request.body const { rows } = await db.query( `INSERT INTO staff_schedules (hotel_id, user_id, date, shift_start, shift_end, is_day_off, notes) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (hotel_id, user_id, date) DO UPDATE SET shift_start = EXCLUDED.shift_start, shift_end = EXCLUDED.shift_end, is_day_off = EXCLUDED.is_day_off, notes = COALESCE(EXCLUDED.notes, staff_schedules.notes), updated_at = NOW() RETURNING *`, [hotelId, user_id, date, shift_start ?? null, shift_end ?? null, is_day_off ?? false, notes ?? null], ) return rows[0] }, ) // DELETE /api/hotels/:slug/schedule/:userId/:date fastify.delete<{ Params: { slug: string; userId: string; date: string } }>( '/api/hotels/:slug/schedule/:userId/:date', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, userId, date } = request.params if (!['manager', 'hotel_admin', 'super_admin'].includes(request.user.role)) return reply.code(403).send({ error: 'Forbidden' }) if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) await db.query( 'DELETE FROM staff_schedules WHERE hotel_id = $1 AND user_id = $2 AND date = $3', [hotelId, userId, date], ) return reply.code(204).send() }, ) } export default scheduleRoutes