From 42f15b6c5b49e3d3177b8488dfb13d88ff9f3ea6 Mon Sep 17 00:00:00 2001 From: HotelSync Date: Wed, 11 Mar 2026 22:28:43 +0300 Subject: [PATCH] Add passport data tab to booking modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two tabs in the booking modal: «Бронирование» (existing) and «Документы гостя» (new). Passport tab includes: - Document type selector: Паспорт РФ / Загранпаспорт / Иной документ - Full name: Фамилия / Имя / Отчество - Date of birth, place of birth, nationality - Document details: series + number (RF passport), or just number (other); issued by, issue date, division code (RF passport) - Registration address - Green dot indicator on tab when data is entered Co-Authored-By: Claude Sonnet 4.6 --- src/components/bookings/BookingModal.tsx | 214 ++++++++++++++++++++++- 1 file changed, 213 insertions(+), 1 deletion(-) diff --git a/src/components/bookings/BookingModal.tsx b/src/components/bookings/BookingModal.tsx index 060e59e..bb6158f 100644 --- a/src/components/bookings/BookingModal.tsx +++ b/src/components/bookings/BookingModal.tsx @@ -1,11 +1,43 @@ import { useState } from 'react' import { format } from 'date-fns' -import { Star, Banknote, CreditCard, AlertCircle, Map, Plus, Trash2 } from 'lucide-react' +import { Star, Banknote, CreditCard, AlertCircle, Map, Plus, Trash2, IdCard, CalendarDays } from 'lucide-react' import { Modal } from '../ui/Modal' import { cn, BOOKING_STATUS_LABELS } from '../../lib/utils' import type { Booking, DraftBooking, Room, BookingStatus } from '../../types' import { FloorMapModal } from '../floormap/FloorMapModal' +type DocType = 'rf_passport' | 'foreign_passport' | 'other' + +interface PassportData { + docType: DocType + lastName: string + firstName: string + patronymic: string + birthDate: string + birthPlace: string + nationality: string + series: string + number: string + issuedBy: string + issuedDate: string + divisionCode: string + registrationAddress: string +} + +const EMPTY_PASSPORT: PassportData = { + docType: 'rf_passport', + lastName: '', firstName: '', patronymic: '', + birthDate: '', birthPlace: '', nationality: 'Россия', + series: '', number: '', issuedBy: '', issuedDate: '', + divisionCode: '', registrationAddress: '', +} + +const DOC_TYPES: { id: DocType; label: string }[] = [ + { id: 'rf_passport', label: 'Паспорт РФ' }, + { id: 'foreign_passport', label: 'Загранпаспорт' }, + { id: 'other', label: 'Иной документ' }, +] + const GUEST_TAGS = ['VIP', 'Постоянный гость', 'Корпоративный', 'Медовый месяц', 'День рождения', 'Особые пожелания'] const ADDITIONAL_SERVICES = [ @@ -48,6 +80,13 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: const [showFloorMap, setShowFloorMap] = useState(false) const [addedServices, setAddedServices] = useState([]) const [selectedServiceId, setSelectedServiceId] = useState(ADDITIONAL_SERVICES[0].id) + const [activeTab, setActiveTab] = useState<'booking' | 'passport'>('booking') + const [passport, setPassport] = useState(EMPTY_PASSPORT) + + const setP = (k: K, v: PassportData[K]) => + setPassport(prev => ({ ...prev, [k]: v })) + + const passportFilled = !!(passport.lastName || passport.number) // Hourly booking state const [isHourly, setIsHourly] = useState(false) @@ -137,6 +176,35 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: } > + {/* ── Tabs ── */} +
+ {([ + { id: 'booking' as const, label: 'Бронирование', icon: CalendarDays }, + { id: 'passport' as const, label: 'Документы гостя', icon: IdCard, + badge: passportFilled }, + ]).map(t => ( + + ))} +
+ + {/* ── BOOKING TAB ── */} + {activeTab === 'booking' && (
{/* ── LEFT COLUMN ── */} @@ -534,6 +602,150 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: )}
+ )} {/* end booking tab */} + + {/* ── PASSPORT TAB ── */} + {activeTab === 'passport' && ( +
+ {/* Document type */} +
+ +
+ {DOC_TYPES.map(dt => ( + + ))} +
+
+ + {/* Name fields */} +
+
+ + setP('lastName', e.target.value)} /> +
+
+ + setP('firstName', e.target.value)} /> +
+
+ + setP('patronymic', e.target.value)} /> +
+
+ + {/* Birth + nationality */} +
+
+ + setP('birthDate', e.target.value)} /> +
+
+ + setP('birthPlace', e.target.value)} /> +
+
+ + setP('nationality', e.target.value)} /> +
+
+ + {/* Document fields */} +
+
+

Реквизиты документа

+
+
+ {passport.docType === 'rf_passport' && ( +
+
+ + setP('series', e.target.value)} + /> +
+
+ + setP('number', e.target.value)} + /> +
+
+ )} + {passport.docType !== 'rf_passport' && ( +
+ + setP('number', e.target.value)} + /> +
+ )} + +
+ + setP('issuedBy', e.target.value)} + /> +
+ +
+
+ + setP('issuedDate', e.target.value)} /> +
+ {passport.docType === 'rf_passport' && ( +
+ + setP('divisionCode', e.target.value)} + /> +
+ )} +
+
+
+ + {/* Registration address */} +
+ + setP('registrationAddress', e.target.value)} + /> +
+ + {/* Hint */} +

+ Данные используются для формирования регистрационной карты и уведомления УМВД (при необходимости) +

+
+ )} {/* end passport tab */} + {showFloorMap && (