feat: full Room Service backend integration
- Migrations 086-088: room_service_settings, menu_items, orders+order_items - Backend: /api/hotels/:slug/room-service/* routes (settings, menu CRUD, orders CRUD, public config, public order status) - push.ts: sendPushToHotel() — push to all hotel staff on new order - On new order: in-app notification + push to hotel staff - api.ts: RoomService types + roomService API methods - RoomServicePage: replaced mocked state with real API, 30s polling, add/edit/delete menu items modal, real advance/cancel order - GuestRoomServicePage: fetches real menu + config, uses hotel payment methods, submits real order, polls status every 15s Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
112
src/lib/api.ts
112
src/lib/api.ts
@@ -982,6 +982,118 @@ export const api = {
|
||||
return json as { bookingId: string; status: string }
|
||||
}),
|
||||
},
|
||||
|
||||
// ── Room Service ──────────────────────────────────────────────────────────
|
||||
roomService: {
|
||||
getPublicConfig: (slug: string) =>
|
||||
req<RoomServicePublicConfig>('GET', `/api/hotels/${slug}/room-service/public-config`),
|
||||
|
||||
getMenu: (slug: string) =>
|
||||
req<RoomServiceMenuItem[]>('GET', `/api/hotels/${slug}/room-service/menu`),
|
||||
|
||||
getMenuAll: (slug: string) =>
|
||||
req<RoomServiceMenuItem[]>('GET', `/api/hotels/${slug}/room-service/menu/all`),
|
||||
|
||||
createMenuItem: (slug: string, data: {
|
||||
name: string; category?: string; price: number; emoji?: string
|
||||
prepTimeMin?: number; isPopular?: boolean; sortOrder?: number
|
||||
}) =>
|
||||
req<RoomServiceMenuItem>('POST', `/api/hotels/${slug}/room-service/menu`, data),
|
||||
|
||||
updateMenuItem: (slug: string, itemId: string, data: Partial<{
|
||||
name: string; category: string; price: number; emoji: string
|
||||
prepTimeMin: number; isPopular: boolean; isStopList: boolean
|
||||
isActive: boolean; sortOrder: number
|
||||
}>) =>
|
||||
req<RoomServiceMenuItem>('PATCH', `/api/hotels/${slug}/room-service/menu/${itemId}`, data),
|
||||
|
||||
deleteMenuItem: (slug: string, itemId: string) =>
|
||||
req<void>('DELETE', `/api/hotels/${slug}/room-service/menu/${itemId}`),
|
||||
|
||||
getSettings: (slug: string) =>
|
||||
req<RoomServiceSettings>('GET', `/api/hotels/${slug}/room-service/settings`),
|
||||
|
||||
updateSettings: (slug: string, data: { isActive?: boolean; serviceChargePct?: number }) =>
|
||||
req<RoomServiceSettings>('PATCH', `/api/hotels/${slug}/room-service/settings`, data),
|
||||
|
||||
listOrders: (slug: string, params?: { status?: string; date?: string }) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params?.status) q.set('status', params.status)
|
||||
if (params?.date) q.set('date', params.date)
|
||||
const qs = q.toString() ? `?${q.toString()}` : ''
|
||||
return req<RoomServiceOrder[]>('GET', `/api/hotels/${slug}/room-service/orders${qs}`)
|
||||
},
|
||||
|
||||
createOrder: (slug: string, data: {
|
||||
roomNumber: string; guestName?: string; orderType?: 'room' | 'restaurant'
|
||||
scheduledTime?: string; paymentMethod?: string; notes?: string
|
||||
items: Array<{ menuItemId: string; quantity: number }>
|
||||
}) =>
|
||||
req<RoomServiceOrder>('POST', `/api/hotels/${slug}/room-service/orders`, data),
|
||||
|
||||
updateOrder: (slug: string, orderId: string, data: { status?: string; paymentMethod?: string }) =>
|
||||
req<RoomServiceOrder>('PATCH', `/api/hotels/${slug}/room-service/orders/${orderId}`, data),
|
||||
},
|
||||
}
|
||||
|
||||
// ── Room Service types ────────────────────────────────────────────────────────
|
||||
|
||||
export interface RoomServicePublicConfig {
|
||||
hotelName: string
|
||||
hotelSlug: string
|
||||
serviceChargePct: number
|
||||
paymentMethods: Array<{ id: string; name: string; type: string }>
|
||||
}
|
||||
|
||||
export interface RoomServiceMenuItem {
|
||||
id: string
|
||||
hotelId: string
|
||||
name: string
|
||||
category: string
|
||||
price: number
|
||||
emoji: string
|
||||
prepTimeMin: number
|
||||
isPopular: boolean
|
||||
isStopList: boolean
|
||||
isActive: boolean
|
||||
sortOrder: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface RoomServiceSettings {
|
||||
hotelId: string
|
||||
isActive: boolean
|
||||
serviceChargePct: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type RoomServiceOrderStatus = 'new' | 'preparing' | 'ready' | 'delivered' | 'cancelled'
|
||||
|
||||
export interface RoomServiceOrderItem {
|
||||
id: string
|
||||
menuItemId: string | null
|
||||
name: string
|
||||
emoji: string
|
||||
price: number
|
||||
quantity: number
|
||||
}
|
||||
|
||||
export interface RoomServiceOrder {
|
||||
id: string
|
||||
hotelId: string
|
||||
roomNumber: string
|
||||
guestName: string
|
||||
orderType: 'room' | 'restaurant'
|
||||
scheduledTime: string | null
|
||||
status: RoomServiceOrderStatus
|
||||
paymentMethod: string | null
|
||||
subtotal: number
|
||||
serviceCharge: number
|
||||
total: number
|
||||
notes: string | null
|
||||
items: RoomServiceOrderItem[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
// ── Schedule ─────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user