feat: single ФИО field with autocomplete replaces 3 separate name inputs
- BookingModal (room tab): replace Фамилия/Имя/Отчество grid with single 'ФИО *' input; parseFullName() splits by spaces into last/first/middle; DB search via api.guests.list (debounced 300ms); dropdown shows current bookings + DB guests with phone/email autofill - BookingDetailPanel: merge Фамилия (with autocomplete) + Имя + Отчество into single ФИО * input; existing acQuery/acResults infrastructure reused; parseFio() updates all 3 bgForm fields on every keystroke; openEditForm initializes ФИО from full name - RentalBookingModal + BookingModal rental tab: include middleName when displaying DB guest results and when setting guestName on selection; update type to include middleName field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,10 +146,16 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
setEarlyOpen(false)
|
||||
}, [booking.id])
|
||||
|
||||
// Parse ФИО string into form fields
|
||||
const parseFio = (val: string) => {
|
||||
const parts = val.trim().split(/\s+/)
|
||||
return { last_name: parts[0] ?? '', first_name: parts[1] ?? '', middle_name: parts.slice(2).join(' ') || undefined }
|
||||
}
|
||||
|
||||
// Debounced guest autocomplete
|
||||
const handleAcInput = (val: string) => {
|
||||
setBgForm(f => ({ ...f, last_name: val }))
|
||||
setAcQuery(val)
|
||||
setBgForm(f => ({ ...f, ...parseFio(val) }))
|
||||
if (acTimerRef.current) clearTimeout(acTimerRef.current)
|
||||
if (val.length < 2 || !slug) { setAcResults([]); return }
|
||||
acTimerRef.current = setTimeout(() => {
|
||||
@@ -162,10 +168,11 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
}
|
||||
|
||||
const selectAcGuest = (g: GuestApiType) => {
|
||||
const fullName = [g.lastName, g.firstName, g.middleName].filter(Boolean).join(' ')
|
||||
setBgForm(f => ({
|
||||
...f,
|
||||
first_name: g.firstName,
|
||||
last_name: g.lastName,
|
||||
first_name: g.firstName,
|
||||
last_name: g.lastName,
|
||||
middle_name: g.middleName ?? undefined,
|
||||
birth_date: g.birthDate ?? undefined,
|
||||
passport_series: g.passportSeries ?? undefined,
|
||||
@@ -174,7 +181,7 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
passport_issue_date: g.passportIssueDate ?? undefined,
|
||||
nationality: g.nationality ?? undefined,
|
||||
}))
|
||||
setAcQuery(g.lastName)
|
||||
setAcQuery(fullName)
|
||||
setAcResults([])
|
||||
}
|
||||
|
||||
@@ -219,7 +226,7 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
passport_issue_date: bg.passportIssueDate?.slice(0, 10) ?? undefined,
|
||||
nationality: bg.nationality ?? undefined,
|
||||
})
|
||||
setAcQuery('')
|
||||
setAcQuery([bg.lastName, bg.firstName, bg.middleName].filter(Boolean).join(' '))
|
||||
setAcResults([])
|
||||
setShowGuestForm(true)
|
||||
}
|
||||
@@ -938,15 +945,15 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
{editingBg ? 'Редактировать гостя' : 'Добавить гостя'}
|
||||
</p>
|
||||
|
||||
{/* Autocomplete last name */}
|
||||
{/* ФИО with autocomplete */}
|
||||
<div ref={acWrapRef} className="relative">
|
||||
<label className={lbl}>Фамилия *</label>
|
||||
<label className={lbl}>ФИО *</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
className={fld}
|
||||
value={bgForm.last_name ?? ''}
|
||||
value={acQuery}
|
||||
onChange={e => handleAcInput(e.target.value)}
|
||||
placeholder="Иванов"
|
||||
placeholder="Иванов Иван Иванович"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{acLoading && (
|
||||
@@ -966,7 +973,7 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
<Search size={11} className="text-slate-400 shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-900 dark:text-slate-100">
|
||||
{g.lastName} {g.firstName}
|
||||
{[g.lastName, g.firstName, g.middleName].filter(Boolean).join(' ')}
|
||||
</p>
|
||||
{(g.passportSeries || g.passportNumber) && (
|
||||
<p className="text-xs text-slate-400 font-mono">
|
||||
@@ -981,18 +988,6 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, slug, on
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* First name + middle name */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label className={lbl}>Имя *</label>
|
||||
<input className={fld} value={bgForm.first_name ?? ''} onChange={e => setBgForm(f => ({ ...f, first_name: e.target.value }))} placeholder="Иван" />
|
||||
</div>
|
||||
<div>
|
||||
<label className={lbl}>Отчество</label>
|
||||
<input className={fld} value={bgForm.middle_name ?? ''} onChange={e => setBgForm(f => ({ ...f, middle_name: e.target.value }))} placeholder="Иванович" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Birth date + flags */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user