fix: dates timezone, guest lookup fallback to bookings, regex fix

- localDate() uses local timezone instead of UTC (prevents showing yesterday
  after midnight in UTC+3)
- Guest lookup: fallback to bookings table by guest_email/guest_phone when
  no guest profile found in CRM (guests table)
- Fix REGEXP_REPLACE pattern from '\D' (PCRE) to '[^0-9]' (PostgreSQL POSIX)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-08 02:41:38 +03:00
parent e1d74ff63d
commit b0bbb11c87
2 changed files with 43 additions and 8 deletions

View File

@@ -115,7 +115,7 @@ const publicWidget: FastifyPluginAsync = async (fastify) => {
if (phone) { if (phone) {
const cleanPhone = phone.replace(/\D/g, '') const cleanPhone = phone.replace(/\D/g, '')
params.push(cleanPhone) params.push(cleanPhone)
conditions.push(`REGEXP_REPLACE(g.phone, '\\D', '', 'g') = $${params.length}`) conditions.push(`REGEXP_REPLACE(g.phone, '[^0-9]', '', 'g') = $${params.length}`)
} }
const { rows } = await db.query( const { rows } = await db.query(
@@ -127,8 +127,41 @@ const publicWidget: FastifyPluginAsync = async (fastify) => {
params, params,
) )
if (!rows[0]) return reply.code(404).send({ error: 'Guest not found' }) if (rows[0]) return rows[0]
return rows[0]
// Fallback: search in past bookings by guest_email / guest_phone
const bConditions: string[] = []
const bParams: unknown[] = [hotel.id]
if (email) {
bParams.push(email.toLowerCase().trim())
bConditions.push(`LOWER(b.guest_email) = $${bParams.length}`)
}
if (phone) {
const cleanPhone = phone.replace(/\D/g, '')
bParams.push(cleanPhone)
bConditions.push(`REGEXP_REPLACE(b.guest_phone, '[^0-9]', '', 'g') = $${bParams.length}`)
}
const { rows: bRows } = await db.query(
`SELECT b.guest_name, b.guest_email, b.guest_phone
FROM bookings b
WHERE b.hotel_id = $1 AND (${bConditions.join(' OR ')})
ORDER BY b.created_at DESC
LIMIT 1`,
bParams,
)
if (!bRows[0]) return reply.code(404).send({ error: 'Guest not found' })
// Parse guest_name into first/last name (format: "Фамилия Имя Отчество" or "Имя Фамилия")
const nameParts = (bRows[0].guest_name as string || '').trim().split(/\s+/)
return {
first_name: nameParts[1] ?? nameParts[0] ?? '',
last_name: nameParts[0] ?? '',
middle_name: nameParts[2] ?? null,
email: bRows[0].guest_email ?? null,
phone: bRows[0].guest_phone ?? null,
is_blacklisted: false,
}
}, },
) )
@@ -289,7 +322,7 @@ const publicWidget: FastifyPluginAsync = async (fastify) => {
if (guestPhone) { if (guestPhone) {
const cleanPhone = guestPhone.replace(/\D/g, '') const cleanPhone = guestPhone.replace(/\D/g, '')
blParams.push(cleanPhone) blParams.push(cleanPhone)
blConditions.push(`REGEXP_REPLACE(phone, '\\D', '', 'g') = $${blParams.length}`) blConditions.push(`REGEXP_REPLACE(phone, '[^0-9]', '', 'g') = $${blParams.length}`)
} }
const { rows: blRows } = await db.query( const { rows: blRows } = await db.query(
`SELECT id FROM guests WHERE hotel_id = $1 AND is_blacklisted = TRUE AND (${blConditions.join(' OR ')}) LIMIT 1`, `SELECT id FROM guests WHERE hotel_id = $1 AND is_blacklisted = TRUE AND (${blConditions.join(' OR ')}) LIMIT 1`,

View File

@@ -195,10 +195,12 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
paymentEnabled?: boolean paymentEnabled?: boolean
}) { }) {
const [previewTab, setPreviewTab] = useState<'rooms' | 'rental'>('rooms') const [previewTab, setPreviewTab] = useState<'rooms' | 'rental'>('rooms')
const todayStr = new Date().toISOString().slice(0, 10) const localDate = (offsetDays = 0) => {
const tomorrowStr = new Date(Date.now() + 86400000).toISOString().slice(0, 10) const d = new Date(); d.setDate(d.getDate() + offsetDays)
const [checkIn, setCheckIn] = useState(todayStr) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
const [checkOut, setCheckOut] = useState(tomorrowStr) }
const [checkIn, setCheckIn] = useState(() => localDate(0))
const [checkOut, setCheckOut] = useState(() => localDate(1))
const [guests, setGuests] = useState(2) const [guests, setGuests] = useState(2)
const [extraBeds, setExtraBeds] = useState(0) const [extraBeds, setExtraBeds] = useState(0)
const [children, setChildren] = useState(0) const [children, setChildren] = useState(0)