diff --git a/src/pages/BookingWidgetPage.tsx b/src/pages/BookingWidgetPage.tsx index b9d3ac1..1a182ed 100644 --- a/src/pages/BookingWidgetPage.tsx +++ b/src/pages/BookingWidgetPage.tsx @@ -1034,9 +1034,13 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, realR {/* Expand toggle */} @@ -1375,6 +1379,10 @@ export function BookingWidgetPage() { }) const [copied, setCopied] = useState(false) const [saving, setSaving] = useState(false) + const [codeMode, setCodeMode] = useState<'iframe' | 'floating' | 'trigger'>('iframe') + const [widgetMaxWidth, setWidgetMaxWidth] = useState('500px') + const [triggerClass, setTriggerClass] = useState('hotelsync-book') + const [floatBtnText, setFloatBtnText] = useState('Забронировать') const [saved, setSaved] = useState(false) const [settingsReady, setSettingsReady] = useState(false) const [pageTab, setPageTab] = useState<'constructor' | 'code' | 'stats'>('constructor') @@ -1489,18 +1497,48 @@ export function BookingWidgetPage() { const widgetUrl = `https://app.hotelsync.ru/${slug || 'grand-palace'}/book?color=${encodeURIComponent(settings.primaryColor)}&lang=${settings.language}&mode=${settings.roomDisplayMode}&min-nights=${settings.minNights}&rental=${settings.showRental}&promo=${settings.showPromo}&extra-beds=${settings.allowExtraBeds}&children=${settings.allowChildren}` - const snippet = `` + const iframeSnippet = widgetMaxWidth === '100%' + ? `` + : `
\n \n
` + + const modalScript = `(function(){ + var W='${widgetUrl}'; + var m=document.createElement('div'); + m.id='hs-modal'; + m.style.cssText='display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99998;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;'; + m.innerHTML='
' + +'' + +'' + +'
'; + m.addEventListener('click',function(e){if(e.target===m)m.style.display='none';}); + document.body.appendChild(m); + window.hsOpenWidget=function(){m.style.display='flex';}; +})()` + + const floatingSnippet = `` + + const triggerSnippet = `` + + const activeSnippet = codeMode === 'iframe' ? iframeSnippet : codeMode === 'floating' ? floatingSnippet : triggerSnippet const copyCode = () => { - navigator.clipboard.writeText(snippet).catch(() => {}) + navigator.clipboard.writeText(activeSnippet).catch(() => {}) setCopied(true) setTimeout(() => setCopied(false), 2000) } @@ -1854,11 +1892,124 @@ export function BookingWidgetPage() { {/* ── Code ── */} {pageTab === 'code' && (
+ + {/* Install mode selector */} +
+

Способ установки

+
+ {([ + { key: 'iframe', icon: Code2, title: 'Встроенный', desc: 'Вставить блоком на страницу' }, + { key: 'floating', icon: Zap, title: 'Плавающая кнопка', desc: 'Кнопка в углу сайта' }, + { key: 'trigger', icon: Scan, title: 'По классу', desc: 'Клик на любой элемент сайта' }, + ] as const).map(m => ( + + ))} +
+
+ + {/* Options per mode */} + {codeMode === 'iframe' && ( +
+

Ширина виджета

+
+ {[ + { label: 'Узкий (400px)', value: '400px' }, + { label: 'Стандарт (500px)', value: '500px' }, + { label: 'Широкий (700px)', value: '700px' }, + { label: 'Во всю ширину', value: '100%' }, + ].map(opt => ( + + ))} + setWidgetMaxWidth(e.target.value)} + className="w-24 text-xs border border-slate-200 dark:border-slate-600 rounded-lg px-2 py-1.5 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300" + placeholder="700px" + /> +
+

Виджет автоматически отцентрируется на странице

+
+ )} + + {codeMode === 'floating' && ( +
+

Настройки кнопки

+
+
+ + setFloatBtnText(e.target.value)} + className="w-full text-sm border border-slate-200 dark:border-slate-600 rounded-lg px-3 py-1.5 bg-white dark:bg-slate-800" + placeholder="Забронировать" + /> +
+
+ +
+
+

Кнопка появится в правом нижнем углу сайта. Цвет берётся из настроек виджета.

+
+ )} + + {codeMode === 'trigger' && ( +
+

CSS-класс триггера

+
+ . + setTriggerClass(e.target.value.replace(/[^a-z0-9-_]/gi, ''))} + className="flex-1 text-sm border border-slate-200 dark:border-slate-600 rounded-lg px-3 py-1.5 bg-white dark:bg-slate-800 font-mono" + placeholder="hotelsync-book" + /> +
+

+ Добавьте этот класс любой кнопке или ссылке на сайте — при клике откроется виджет: +

+
{``}
+
+ )} + + {/* Code block */}
-

Код для вставки на сайт

+

+ {codeMode === 'iframe' ? 'Код для вставки' : codeMode === 'floating' ? 'Скрипт плавающей кнопки' : 'Скрипт триггера по классу'} +

-
-              {snippet}
+            
+              {activeSnippet}
             
-
- -
-

Инструкция по установке

- {[ - { step: '1', text: 'Скопируйте код выше' }, - { step: '2', text: 'Вставьте перед закрывающим тегом на страницах, где хотите показать виджет' }, - { step: '3', text: 'Виджет автоматически добавит кнопку "Забронировать" или встроится в нужный блок' }, - { step: '4', text: 'Настройте цвета и параметры в конструкторе — изменения применяются сразу' }, - ].map(s => ( -
- {s.step} -

{s.text}

-
- ))} +

+ {codeMode === 'iframe' + ? 'Вставьте в любое место страницы где должен отображаться виджет.' + : 'Вставьте перед закрывающим тегом .'} +

{[ - { icon: CalendarCheck2, title: 'Мгновенное подтверждение', desc: 'Email + SMS гостю' }, + { icon: CalendarCheck2, title: 'Мгновенное подтверждение', desc: 'Email гостю' }, { icon: CreditCard, title: 'Онлайн-оплата', desc: 'ЮKassa, Тинькофф, СБП' }, - { icon: Globe, title: 'Без комиссии OTA', desc: 'Только платёжная комиссия ~1.5%' }, + { icon: Globe, title: 'Без комиссии OTA', desc: 'Только ~1.5% платёжной комиссии' }, ].map(f => (