- Standalone page: ?width=full removes max-w-lg container constraint - iframeSnippet: passes &width=full to URL when 100% width selected - public/widget-loader.js: hosted script, reads data-* attrs, no inline code needed - data-mode="floating" → styled FAB button bottom-right - data-mode="trigger" → listens for .hotelsync-book class clicks - Proper modal with close button and backdrop click - Code tab snippets for floating/trigger are now single <script src> lines Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
94 lines
4.2 KiB
TypeScript
94 lines
4.2 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { useParams, useSearchParams } from 'react-router-dom'
|
|
import { api, type WidgetRoom, type WidgetCategory, type WidgetRentalObject } from '../lib/api'
|
|
import { WidgetPreview, DEFAULT_FORM_FIELDS, DEFAULT_SERVICES } from './BookingWidgetPage'
|
|
import type { WidgetSettings } from './BookingWidgetPage'
|
|
|
|
export function BookingWidgetStandalonePage() {
|
|
const { slug } = useParams<{ slug: string }>()
|
|
const [searchParams] = useSearchParams()
|
|
|
|
const [realRooms, setRealRooms] = useState<WidgetRoom[]>([])
|
|
const [realCategories, setRealCategories] = useState<WidgetCategory[]>([])
|
|
const [realRentalObjects, setRealRentalObjects] = useState<WidgetRentalObject[]>([])
|
|
const [paymentEnabled, setPaymentEnabled] = useState<boolean | undefined>(undefined)
|
|
const [settings, setSettings] = useState<WidgetSettings>({
|
|
hotelName: '',
|
|
primaryColor: searchParams.get('color') ?? '#4F46E5',
|
|
language: (searchParams.get('lang') ?? 'ru') as 'ru' | 'en',
|
|
showRooms: true,
|
|
showRental: searchParams.get('rental') === 'true',
|
|
roomDisplayMode: (searchParams.get('mode') ?? 'rooms') as 'rooms' | 'categories',
|
|
minNights: parseInt(searchParams.get('min-nights') ?? '1') || 1,
|
|
paymentTimeout: 15,
|
|
paymentProvider: 'yukassa',
|
|
showPromo: searchParams.get('promo') !== 'false',
|
|
allowExtraBeds: searchParams.get('extra-beds') !== 'false',
|
|
allowChildren: searchParams.get('children') !== 'false',
|
|
formFields: DEFAULT_FORM_FIELDS,
|
|
additionalServices: DEFAULT_SERVICES,
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (!slug) return
|
|
api.widget.getConfig(slug)
|
|
.then(config => {
|
|
setRealRooms(config.rooms ?? [])
|
|
setRealCategories(config.categories ?? [])
|
|
setRealRentalObjects(config.rentalObjects ?? [])
|
|
setPaymentEnabled(config.paymentEnabled)
|
|
const rentalObjs = config.rentalObjects ?? []
|
|
const deriveServices = (savedServices: { id: string; enabled: boolean }[] | null | undefined) =>
|
|
rentalObjs.map(o => ({
|
|
id: o.id,
|
|
name: o.name,
|
|
icon: o.icon,
|
|
price: o.pricePerHour,
|
|
unit: 'ч',
|
|
enabled: savedServices ? (savedServices.find(s => s.id === o.id)?.enabled ?? true) : true,
|
|
}))
|
|
|
|
if (config.widgetSettings) {
|
|
const ws = config.widgetSettings
|
|
setSettings(prev => ({
|
|
...prev,
|
|
hotelName: ws.hotelName ?? config.hotelName ?? prev.hotelName,
|
|
primaryColor: ws.primaryColor ?? prev.primaryColor,
|
|
language: (ws.language ?? prev.language) as 'ru' | 'en',
|
|
roomDisplayMode: (ws.roomDisplayMode ?? prev.roomDisplayMode) as 'rooms' | 'categories',
|
|
minNights: ws.minNights ?? prev.minNights,
|
|
showRental: ws.showRental ?? prev.showRental,
|
|
showPromo: ws.showPromo ?? prev.showPromo,
|
|
allowExtraBeds: ws.allowExtraBeds ?? prev.allowExtraBeds,
|
|
allowChildren: ws.allowChildren ?? prev.allowChildren,
|
|
additionalServices: rentalObjs.length > 0 ? deriveServices(ws.services) : prev.additionalServices,
|
|
}))
|
|
} else {
|
|
setSettings(prev => ({
|
|
...prev,
|
|
hotelName: config.hotelName ?? prev.hotelName,
|
|
additionalServices: rentalObjs.length > 0 ? deriveServices(null) : prev.additionalServices,
|
|
}))
|
|
}
|
|
})
|
|
.catch(() => {})
|
|
}, [slug])
|
|
|
|
const isFullWidth = searchParams.get('width') === 'full'
|
|
|
|
return (
|
|
<div className={isFullWidth ? 'min-h-screen bg-slate-50' : 'min-h-screen bg-slate-50 flex items-start justify-center py-6 px-3'}>
|
|
<div className={isFullWidth ? 'w-full' : 'w-full max-w-lg'}>
|
|
<WidgetPreview
|
|
settings={settings}
|
|
slug={slug}
|
|
realRooms={realRooms.length > 0 ? realRooms : undefined}
|
|
realCategories={realCategories.length > 0 ? realCategories : undefined}
|
|
realRentalObjects={realRentalObjects.length > 0 ? realRentalObjects : undefined}
|
|
paymentEnabled={paymentEnabled}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|