Rental: connect to API, improve cell UI; remove price from room labels
Backend: - Migration 015_rental.sql: rental_objects + rental_bookings tables, seed demo data - New routes: GET/POST/PATCH/DELETE rental-objects and rental-bookings per hotel - Register rentalRoutes in app.ts Frontend: - api.ts: add rental.listObjects, listBookings, createBooking, updateBooking, deleteBooking - CalendarPage: fetch rental objects + bookings from API instead of mock data - BookingCalendar: rental cell UI redesigned — colored squares + working hours + time slots (matches design spec) - BookingCalendar: remove base rate price from desktop room label column (irrelevant with dynamic pricing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -312,6 +312,29 @@ export const api = {
|
||||
req<void>('DELETE', `/api/hotels/${slug}/guests/${id}`),
|
||||
},
|
||||
|
||||
// ── Rental ────────────────────────────────────────────────────────────────
|
||||
rental: {
|
||||
listObjects: (slug: string) =>
|
||||
req<RentalObjectApi[]>('GET', `/api/hotels/${slug}/rental-objects`),
|
||||
|
||||
listBookings: (slug: string, from?: string, to?: string) => {
|
||||
const qs = new URLSearchParams()
|
||||
if (from) qs.set('from', from)
|
||||
if (to) qs.set('to', to)
|
||||
const q = qs.toString()
|
||||
return req<RentalBookingApi[]>('GET', `/api/hotels/${slug}/rental-bookings${q ? `?${q}` : ''}`)
|
||||
},
|
||||
|
||||
createBooking: (slug: string, data: RentalBookingPayload) =>
|
||||
req<RentalBookingApi>('POST', `/api/hotels/${slug}/rental-bookings`, data),
|
||||
|
||||
updateBooking: (slug: string, id: string, data: Partial<RentalBookingPayload>) =>
|
||||
req<RentalBookingApi>('PATCH', `/api/hotels/${slug}/rental-bookings/${id}`, data),
|
||||
|
||||
deleteBooking: (slug: string, id: string) =>
|
||||
req<void>('DELETE', `/api/hotels/${slug}/rental-bookings/${id}`),
|
||||
},
|
||||
|
||||
// ── NetUP IPTV ────────────────────────────────────────────────────────────
|
||||
netup: {
|
||||
getSettings: (slug: string) =>
|
||||
@@ -515,6 +538,56 @@ export interface GuestPayload {
|
||||
rating?: number
|
||||
}
|
||||
|
||||
export interface RentalObjectApi {
|
||||
id: string
|
||||
hotelId: string
|
||||
name: string
|
||||
icon: string
|
||||
color: string
|
||||
textColor: string
|
||||
pricePerHour: number
|
||||
pricePerDay: number
|
||||
openHour: number
|
||||
closeHour: number
|
||||
maxHoursPerSlot: number | null
|
||||
bufferMinutes: number
|
||||
sortOrder: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface RentalBookingApi {
|
||||
id: string
|
||||
hotelId: string
|
||||
objectId: string
|
||||
date: string
|
||||
isFullDay: boolean
|
||||
startHour: number
|
||||
endHour: number
|
||||
guestName: string
|
||||
guestPhone: string
|
||||
linkedRoomId: string | null
|
||||
totalAmount: number
|
||||
paidAmount: number
|
||||
status: 'confirmed' | 'cancelled'
|
||||
notes: string | null
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface RentalBookingPayload {
|
||||
object_id: string
|
||||
date: string
|
||||
is_full_day?: boolean
|
||||
start_hour?: number
|
||||
end_hour?: number
|
||||
guest_name: string
|
||||
guest_phone?: string
|
||||
linked_room_id?: string
|
||||
total_amount?: number
|
||||
paid_amount?: number
|
||||
status?: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
function toHotelPayload(h: HotelPayload): Record<string, unknown> {
|
||||
const out: Record<string, unknown> = {}
|
||||
if (h.name !== undefined) out.name = h.name
|
||||
|
||||
Reference in New Issue
Block a user