Initial state for GitNexus analysis

This commit is contained in:
Zaid Marzguioui
2026-03-29 19:44:57 +02:00
parent 9d7c7f8030
commit 61ff48cb73
155 changed files with 23483 additions and 1722 deletions

View File

@@ -0,0 +1,34 @@
{
"name": "@kit/booking-management",
"version": "0.1.0",
"private": true,
"typesVersions": {
"*": {
"*": [
"src/*"
]
}
},
"exports": {
"./api": "./src/server/api.ts",
"./schema/*": "./src/schema/*.ts",
"./components": "./src/components/index.ts"
},
"scripts": {
"clean": "git clean -xdf .turbo node_modules",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@kit/next": "workspace:*",
"@kit/shared": "workspace:*",
"@kit/supabase": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "catalog:",
"@types/react": "catalog:",
"next": "catalog:",
"next-safe-action": "catalog:",
"react": "catalog:",
"zod": "catalog:"
}
}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,40 @@
import { z } from 'zod';
export const BookingStatusEnum = z.enum(['pending', 'confirmed', 'checked_in', 'checked_out', 'cancelled', 'no_show']);
export const CreateRoomSchema = z.object({
accountId: z.string().uuid(),
roomNumber: z.string().min(1),
name: z.string().optional(),
roomType: z.string().default('standard'),
capacity: z.number().int().min(1).default(2),
floor: z.number().int().optional(),
pricePerNight: z.number().min(0),
description: z.string().optional(),
});
export const CreateBookingSchema = z.object({
accountId: z.string().uuid(),
roomId: z.string().uuid(),
guestId: z.string().uuid().optional(),
checkIn: z.string(),
checkOut: z.string(),
adults: z.number().int().min(1).default(1),
children: z.number().int().min(0).default(0),
status: BookingStatusEnum.default('confirmed'),
totalPrice: z.number().min(0).default(0),
notes: z.string().optional(),
});
export type CreateBookingInput = z.infer<typeof CreateBookingSchema>;
export const CreateGuestSchema = z.object({
accountId: z.string().uuid(),
firstName: z.string().min(1),
lastName: z.string().min(1),
email: z.string().email().optional().or(z.literal('')),
phone: z.string().optional(),
street: z.string().optional(),
postalCode: z.string().optional(),
city: z.string().optional(),
country: z.string().default('DE'),
});

View File

@@ -0,0 +1,88 @@
import type { Database } from '@kit/supabase/database';
import type { SupabaseClient } from '@supabase/supabase-js';
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;
},
};
}

View File

@@ -0,0 +1,6 @@
{
"extends": "@kit/tsconfig/base.json",
"compilerOptions": { "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" },
"include": ["*.ts", "*.tsx", "src"],
"exclude": ["node_modules"]
}