import { useState } from 'react' import { Code2, CreditCard, Globe, Palette, CalendarCheck2, Copy, CheckCheck, ChevronLeft, ChevronRight, Star, Users, Dumbbell, Waves, Eye, Settings2, ArrowRight, } from 'lucide-react' import { cn } from '../lib/utils' import { useModules } from '../contexts/ModulesContext' // ── Widget settings type ─────────────────────────────────────────────────────── interface WidgetSettings { hotelName: string primaryColor: string language: 'ru' | 'en' showRooms: boolean showRental: boolean minNights: number paymentProvider: 'yukassa' | 'tinkoff' | 'cloudpayments' | 'none' showPromo: boolean } const COLORS = [ { label: 'Индиго', value: '#4F46E5' }, { label: 'Изумруд', value: '#059669' }, { label: 'Синий', value: '#2563EB' }, { label: 'Фиолетовый',value: '#7C3AED' }, { label: 'Красный', value: '#DC2626' }, { label: 'Янтарный', value: '#D97706' }, { label: 'Розовый', value: '#DB2777' }, { label: 'Серый', value: '#475569' }, ] const PAYMENT_PROVIDERS = [ { value: 'yukassa', label: 'ЮKassa' }, { value: 'tinkoff', label: 'Тинькофф Pay' }, { value: 'cloudpayments',label: 'CloudPayments' }, { value: 'none', label: 'Без оплаты' }, ] const MOCK_ROOMS = [ { id: 'r1', name: 'Стандарт', beds: 1, guests: 2, price: 4500, img: '🛏️' }, { id: 'r2', name: 'Комфорт', beds: 1, guests: 2, price: 6500, img: '🛏️' }, { id: 'r3', name: 'Делюкс', beds: 2, guests: 3, price: 8900, img: '🛏️' }, { id: 'r4', name: 'Пентхаус', beds: 2, guests: 4, price: 15000, img: '🌇' }, ] const MOCK_RENTAL = [ { id: 'o1', name: 'Теннисный корт', icon: '🎾', price: 1200, unit: 'ч' }, { id: 'o2', name: 'Сауна', icon: '🧖', price: 2500, unit: 'ч' }, { id: 'o3', name: 'Банкетный зал', icon: '🎪', price: 15000, unit: 'день' }, ] // ── Widget Preview Component ─────────────────────────────────────────────────── function WidgetPreview({ settings }: { settings: WidgetSettings }) { const [previewTab, setPreviewTab] = useState<'rooms' | 'rental'>('rooms') const [checkIn, setCheckIn] = useState('2026-03-20') const [checkOut, setCheckOut] = useState('2026-03-22') const [guests, setGuests] = useState(2) const [selected, setSelected] = useState(null) const nights = checkIn && checkOut ? Math.max(0, (new Date(checkOut).getTime() - new Date(checkIn).getTime()) / 86400000) : 0 return (
{/* Widget header */}

{settings.hotelName || 'Название отеля'}

{settings.language === 'ru' ? 'Бронирование онлайн · Без комиссии' : 'Book directly · No fees'}

{/* Tabs */} {settings.showRooms && settings.showRental && (
{[ ['rooms', settings.language === 'ru' ? 'Номера' : 'Rooms'], ['rental', settings.language === 'ru' ? 'Аренда' : 'Rental'], ].map(([key, label]) => ( ))}
)} {/* Search bar */} {previewTab === 'rooms' && (
setCheckIn(e.target.value)} className="w-full text-sm border border-slate-200 rounded-lg px-2.5 py-1.5 bg-white focus:outline-none" style={{ '--focus-color': settings.primaryColor } as any} />
setCheckOut(e.target.value)} className="w-full text-sm border border-slate-200 rounded-lg px-2.5 py-1.5 bg-white focus:outline-none" />
{guests}
)} {/* Rooms list */} {previewTab === 'rooms' && (
{nights > 0 && (

{nights} {nights === 1 ? 'ночь' : nights < 5 ? 'ночи' : 'ночей'} · {guests} {settings.language === 'ru' ? 'гостя' : 'guests'}

)} {MOCK_ROOMS.map(room => (
setSelected(room.id)} className={cn( 'flex items-center gap-4 p-3 rounded-xl border-2 cursor-pointer transition-all', selected === room.id ? 'border-2' : 'border-slate-200 hover:border-slate-300', )} style={selected === room.id ? { borderColor: settings.primaryColor, background: settings.primaryColor + '08' } : {}} >
{room.img}

{room.name}

{room.guests} 4.8

{nights > 0 ? (room.price * nights).toLocaleString('ru-RU') : room.price.toLocaleString('ru-RU') } ₽

{nights > 0 ? `за ${nights} ноч.` : 'за ночь'}

))}
)} {/* Rental list */} {previewTab === 'rental' && (

Выберите объект и удобное время

{MOCK_RENTAL.map(obj => (
setSelected(obj.id)} className={cn( 'flex items-center gap-4 p-3 rounded-xl border-2 cursor-pointer transition-all', selected === obj.id ? '' : 'border-slate-200 hover:border-slate-300', )} style={selected === obj.id ? { borderColor: settings.primaryColor, background: settings.primaryColor + '08' } : {}} >
{obj.icon}

{obj.name}

{obj.price.toLocaleString('ru-RU')} ₽

/{obj.unit}

))}
)} {/* CTA */}
{settings.showPromo && (

{settings.language === 'ru' ? 'Лучшая цена гарантирована' : 'Best rate guaranteed'}

)}
) } // ── Main Page ────────────────────────────────────────────────────────────────── export function BookingWidgetPage() { const { statuses } = useModules() const rentalActive = statuses['rental'] === 'active' || statuses['rental'] === 'trial' const [settings, setSettings] = useState({ hotelName: 'Grand Palace Hotel', primaryColor: '#4F46E5', language: 'ru', showRooms: true, showRental: rentalActive, minNights: 1, paymentProvider: 'yukassa', showPromo: true, }) const [copied, setCopied] = useState(false) const [pageTab, setPageTab] = useState<'constructor' | 'code' | 'stats'>('constructor') const set = (k: K, v: WidgetSettings[K]) => setSettings(prev => ({ ...prev, [k]: v })) const snippet = `` const copyCode = () => { navigator.clipboard.writeText(snippet).catch(() => {}) setCopied(true) setTimeout(() => setCopied(false), 2000) } return (
{/* Header */}

Виджет онлайн-бронирования

Конструктор виджета для размещения на сайте отеля

{/* Tabs */}
{([ ['constructor', , 'Конструктор'], ['code', , 'Код установки'], ['stats', , 'Статистика'], ] as const).map(([key, icon, label]) => ( ))}
{/* ── Constructor ── */} {pageTab === 'constructor' && (
{/* Settings panel */}

Внешний вид

set('hotelName', e.target.value)} placeholder="Grand Palace Hotel" />
{COLORS.map(c => (
{([['ru', '🇷🇺 Русский'], ['en', '🇬🇧 English']] as const).map(([k, l]) => ( ))}

Что показывать

{rentalActive && ( )}
set('minNights', Math.max(1, parseInt(e.target.value) || 1))} />

Онлайн-оплата

{PAYMENT_PROVIDERS.map(p => ( ))}
{settings.paymentProvider !== 'none' && (

Настройте ключи API в разделе Настройки → Оплата

)}
{/* Live preview */}

Предпросмотр виджета

Так виджет будет выглядеть на сайте отеля

)} {/* ── Code ── */} {pageTab === 'code' && (

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

              {snippet}
            

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

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

{s.text}

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

{f.title}

{f.desc}

))}
)} {/* ── Stats ── */} {pageTab === 'stats' && (
{[ { label: 'Бронирований через виджет', value: '—', sub: 'нет данных' }, { label: 'Конверсия посетителей', value: '—', sub: 'нет данных' }, { label: 'Прямая выручка', value: '—', sub: 'нет данных' }, ].map(s => (

{s.value}

{s.label}

))}

Статистика появится после установки виджета

Разместите виджет на сайте и подключите к нему первые бронирования

)}
) }