174 lines
4.8 KiB
TypeScript
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;
|
|
},
|
|
};
|
|
}
|