import { FastifyPluginAsync } from 'fastify' import { db } from '../db' type SlugParam = { Params: { slug: string } } type SlugIdParam = { Params: { slug: string; id: string } } const rental: 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 rental objects ───────────────────────────────────────────────────── fastify.get( '/api/hotels/:slug/rental-objects', { 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 { rows } = await db.query( 'SELECT * FROM rental_objects WHERE hotel_id = $1 ORDER BY sort_order, created_at', [hotelId], ) return rows }, ) // ── POST rental object ───────────────────────────────────────────────────── fastify.post( '/api/hotels/:slug/rental-objects', { 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 b = request.body as Record const { rows: [obj] } = await db.query( `INSERT INTO rental_objects (hotel_id, name, icon, color, text_color, price_per_hour, price_per_day, open_hour, close_hour, max_hours_per_slot, buffer_minutes, sort_order) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12) RETURNING *`, [ hotelId, b.name ?? '', b.icon ?? '🏨', b.color ?? 'bg-slate-500', b.text_color ?? 'text-slate-700 dark:text-slate-400', b.price_per_hour ?? 0, b.price_per_day ?? 0, b.open_hour ?? 8, b.close_hour ?? 22, b.max_hours_per_slot ?? null, b.buffer_minutes ?? 0, b.sort_order ?? 0, ], ) return reply.code(201).send(obj) }, ) // ── PATCH rental object ──────────────────────────────────────────────────── fastify.patch( '/api/hotels/:slug/rental-objects/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) const b = request.body as Record const allowed = ['name','icon','color','text_color','price_per_hour','price_per_day', 'open_hour','close_hour','max_hours_per_slot','buffer_minutes','sort_order','is_active'] const sets: string[] = [] const vals: unknown[] = [] for (const key of allowed) { if (b[key] !== undefined) { sets.push(`${key} = $${vals.length + 1}`) vals.push(b[key]) } } if (sets.length === 0) return reply.code(400).send({ error: 'Nothing to update' }) vals.push(id) const { rows: [obj] } = await db.query( `UPDATE rental_objects SET ${sets.join(', ')} WHERE id = $${vals.length} RETURNING *`, vals, ) if (!obj) return reply.code(404).send({ error: 'Not found' }) return obj }, ) // ── DELETE rental object ─────────────────────────────────────────────────── fastify.delete( '/api/hotels/:slug/rental-objects/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) await db.query('DELETE FROM rental_objects WHERE id = $1', [id]) return reply.code(204).send() }, ) // ── GET rental bookings ──────────────────────────────────────────────────── fastify.get( '/api/hotels/:slug/rental-bookings', { 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 as Record let query = 'SELECT * FROM rental_bookings WHERE hotel_id = $1' const vals: unknown[] = [hotelId] if (from) { vals.push(from); query += ` AND date >= $${vals.length}` } if (to) { vals.push(to); query += ` AND date <= $${vals.length}` } query += ' ORDER BY date, start_hour' const { rows } = await db.query(query, vals) return rows }, ) // ── POST rental booking ──────────────────────────────────────────────────── fastify.post( '/api/hotels/:slug/rental-bookings', { 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 b = request.body as Record const { rows: [booking] } = await db.query( `INSERT INTO rental_bookings (hotel_id, object_id, date, is_full_day, start_hour, end_hour, guest_name, guest_phone, linked_room_id, total_amount, paid_amount, status, notes) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING *`, [ hotelId, b.object_id, b.date, b.is_full_day ?? false, b.start_hour ?? 0, b.end_hour ?? 0, b.guest_name ?? '', b.guest_phone ?? '', b.linked_room_id ?? null, b.total_amount ?? 0, b.paid_amount ?? 0, b.status ?? 'confirmed', b.notes ?? null, ], ) return reply.code(201).send(booking) }, ) // ── PATCH rental booking ─────────────────────────────────────────────────── fastify.patch( '/api/hotels/:slug/rental-bookings/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) const b = request.body as Record const allowed = ['date','is_full_day','start_hour','end_hour','guest_name','guest_phone', 'linked_room_id','total_amount','paid_amount','status','notes'] const sets: string[] = [] const vals: unknown[] = [] for (const key of allowed) { if (b[key] !== undefined) { sets.push(`${key} = $${vals.length + 1}`) vals.push(b[key]) } } if (sets.length === 0) return reply.code(400).send({ error: 'Nothing to update' }) vals.push(id) const { rows: [booking] } = await db.query( `UPDATE rental_bookings SET ${sets.join(', ')} WHERE id = $${vals.length} RETURNING *`, vals, ) if (!booking) return reply.code(404).send({ error: 'Not found' }) return booking }, ) // ── DELETE rental booking ────────────────────────────────────────────────── fastify.delete( '/api/hotels/:slug/rental-bookings/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' }) await db.query('DELETE FROM rental_bookings WHERE id = $1', [id]) return reply.code(204).send() }, ) } export default rental