Files
myeasycms-v2/packages/features/booking-management/src/server/api.ts

174 lines
4.8 KiB
TypeScript

import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from '@kit/supabase/database';
import type { CreateBookingInput } from '../schema/booking.schema';
/* eslint-disable @typescript-eslint/no-explicit-any */
export function createBookingManagementApi(client: SupabaseClient<Database>) {
const db = client;
return {
// --- Rooms ---
async listRooms(accountId: string) {
const { data, error } = await client
.from('rooms')
.select('*')
.eq('account_id', accountId)
.eq('is_active', true)
.order('room_number');
if (error) throw error;
return data ?? [];
},
async getRoom(roomId: string) {
const { data, error } = await client
.from('rooms')
.select('*')
.eq('id', roomId)
.single();
if (error) throw error;
return data;
},
// --- Availability ---
async checkAvailability(roomId: string, checkIn: string, checkOut: string) {
const { count, error } = await client
.from('bookings')
.select('*', { count: 'exact', head: true })
.eq('room_id', roomId)
.not('status', 'in', '("cancelled","no_show")')
.lt('check_in', checkOut)
.gt('check_out', checkIn);
if (error) throw error;
return (count ?? 0) === 0;
},
// --- Bookings ---
async listBookings(
accountId: string,
opts?: { status?: string; from?: string; to?: string; page?: number },
) {
let query = client
.from('bookings')
.select('*', { count: 'exact' })
.eq('account_id', accountId)
.order('check_in', { ascending: false });
if (opts?.status) query = query.eq('status', opts.status);
if (opts?.from) query = query.gte('check_in', opts.from);
if (opts?.to) query = query.lte('check_out', opts.to);
const page = opts?.page ?? 1;
query = query.range((page - 1) * 25, page * 25 - 1);
const { data, error, count } = await query;
if (error) throw error;
return { data: data ?? [], total: count ?? 0 };
},
async createBooking(input: CreateBookingInput) {
const available = await this.checkAvailability(
input.roomId,
input.checkIn,
input.checkOut,
);
if (!available)
throw new Error('Room is not available for the selected dates');
const { data, error } = await client
.from('bookings')
.insert({
account_id: input.accountId,
room_id: input.roomId,
guest_id: input.guestId,
check_in: input.checkIn,
check_out: input.checkOut,
adults: input.adults,
children: input.children,
status: input.status,
total_price: input.totalPrice,
notes: input.notes,
})
.select()
.single();
if (error) throw error;
return data;
},
async updateBookingStatus(bookingId: string, status: string) {
const { error } = await client
.from('bookings')
.update({ status })
.eq('id', bookingId);
if (error) throw error;
},
// --- Guests ---
async listGuests(accountId: string, search?: string) {
let query = client
.from('guests')
.select('*')
.eq('account_id', accountId)
.order('last_name');
if (search)
query = query.or(
`last_name.ilike.%${search}%,first_name.ilike.%${search}%,email.ilike.%${search}%`,
);
const { data, error } = await query;
if (error) throw error;
return data ?? [];
},
async createGuest(input: {
accountId: string;
firstName: string;
lastName: string;
email?: string;
phone?: string;
city?: string;
}) {
const { data, error } = await client
.from('guests')
.insert({
account_id: input.accountId,
first_name: input.firstName,
last_name: input.lastName,
email: input.email,
phone: input.phone,
city: input.city,
})
.select()
.single();
if (error) throw error;
return data;
},
async createRoom(input: {
accountId: string;
roomNumber: string;
name?: string;
roomType?: string;
capacity?: number;
floor?: number;
pricePerNight: number;
description?: string;
}) {
const { data, error } = await client
.from('rooms')
.insert({
account_id: input.accountId,
room_number: input.roomNumber,
name: input.name,
room_type: input.roomType ?? 'standard',
capacity: input.capacity ?? 2,
floor: input.floor,
price_per_night: input.pricePerNight,
description: input.description,
})
.select()
.single();
if (error) throw error;
return data;
},
};
}