Add Guests module: API integration + passport identification

- Migration 009: add passport_series, passport_number, rating to guests table
- Backend route /api/hotels/:slug/guests: list, get (with history), create/upsert by passport, update, delete
- Frontend GuestsPage: loads from API, passport tab in modal, add guest form, search by passport
- api.ts: add guests API methods and GuestApiType

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 14:03:04 +03:00
parent 3da2d5e0ab
commit de4fb2126b
5 changed files with 1041 additions and 492 deletions

View File

@@ -266,6 +266,24 @@ export const api = {
req<Hotel>('PATCH', `/api/hotels/${slug}`, toHotelPayload(data)),
},
// ── Guests ────────────────────────────────────────────────────────────────
guests: {
list: (slug: string, q?: string) =>
req<GuestApiType[]>('GET', `/api/hotels/${slug}/guests${q ? `?q=${encodeURIComponent(q)}` : ''}`),
get: (slug: string, id: string) =>
req<GuestApiType>('GET', `/api/hotels/${slug}/guests/${id}`),
create: (slug: string, data: GuestPayload) =>
req<GuestApiType>('POST', `/api/hotels/${slug}/guests`, data),
update: (slug: string, id: string, data: Partial<GuestPayload>) =>
req<GuestApiType>('PATCH', `/api/hotels/${slug}/guests/${id}`, data),
delete: (slug: string, id: string) =>
req<void>('DELETE', `/api/hotels/${slug}/guests/${id}`),
},
// ── NetUP IPTV ────────────────────────────────────────────────────────────
netup: {
getSettings: (slug: string) =>
@@ -373,6 +391,59 @@ export interface HotelPayload {
checkInTime?: string; checkOutTime?: string
}
export interface GuestApiType {
id: string
hotelId: string
firstName: string
lastName: string
email: string | null
phone: string | null
passport: string | null
passportSeries: string | null
passportNumber: string | null
birthDate: string | null
nationality: string | null
gender: string | null
city: string | null
notes: string
tags: string[]
loyaltyTier: string
loyaltyPoints: number
rating: number
totalStays: number
totalSpent: number
lastVisit: string | null
createdAt: string
updatedAt: string
history?: {
id: string
checkIn: string
checkOut: string
status: string
totalAmount: number | null
paidAmount: number
roomNumber: string | null
roomType: string | null
}[]
}
export interface GuestPayload {
first_name?: string
last_name?: string
email?: string
phone?: string
passport?: string
passport_series?: string
passport_number?: string
birth_date?: string
nationality?: string
gender?: string
city?: string
notes?: string
tags?: string[]
rating?: number
}
function toHotelPayload(h: HotelPayload): Record<string, unknown> {
const out: Record<string, unknown> = {}
if (h.name !== undefined) out.name = h.name