diff --git a/src/pages/BookingWidgetPage.tsx b/src/pages/BookingWidgetPage.tsx index 65df7b3..f1e0bca 100644 --- a/src/pages/BookingWidgetPage.tsx +++ b/src/pages/BookingWidgetPage.tsx @@ -1,9 +1,11 @@ import { useState } from 'react' import { - Code2, CreditCard, Globe, Palette, CalendarCheck2, Copy, CheckCheck, + Code2, CreditCard, Globe, CalendarCheck2, Copy, CheckCheck, ChevronLeft, ChevronRight, Star, Users, Dumbbell, Waves, Eye, Settings2, ArrowRight, Plus, Trash2, Check, X as XIcon, - BedDouble, Baby, ToggleLeft, ToggleRight, ChevronDown, + BedDouble, Baby, ToggleLeft, ToggleRight, ChevronDown, ChevronUp, + Maximize2, Wifi, Tv2, Wind, Coffee, Bath, Mountain, Shirt, Shield, + Scan, Image, } from 'lucide-react' import { cn } from '../lib/utils' import { useModules } from '../contexts/ModulesContext' @@ -61,13 +63,83 @@ const PAYMENT_PROVIDERS = [ { 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: '🌇' }, +interface MockRoom { + id: string + name: string + beds: number + guests: number + price: number + area: number // m² + description: string + photos: { bg: string; emoji: string; label: string }[] + amenities: string[] + has3dTour: boolean +} + +const MOCK_ROOMS: MockRoom[] = [ + { + id: 'r1', name: 'Стандарт', beds: 1, guests: 2, price: 4500, area: 22, + description: 'Уютный номер с двуспальной кроватью, современным ремонтом и всем необходимым для комфортного проживания. Вид во двор.', + photos: [ + { bg: 'from-slate-200 to-slate-300', emoji: '🛏️', label: 'Спальня' }, + { bg: 'from-sky-100 to-sky-200', emoji: '🚿', label: 'Ванная' }, + { bg: 'from-amber-100 to-amber-200', emoji: '🪟', label: 'Окно' }, + ], + amenities: ['Wi-Fi', 'TV', 'Кондиционер', 'Фен', 'Мини-холодильник', 'Сейф'], + has3dTour: false, + }, + { + id: 'r2', name: 'Комфорт', beds: 1, guests: 2, price: 6500, area: 30, + description: 'Просторный номер с зоной отдыха, большой кроватью и улучшенной ванной комнатой. Вид на парк.', + photos: [ + { bg: 'from-emerald-100 to-emerald-200', emoji: '🛏️', label: 'Спальня' }, + { bg: 'from-teal-100 to-teal-200', emoji: '🛁', label: 'Ванная' }, + { bg: 'from-green-100 to-green-200', emoji: '🌳', label: 'Вид из окна' }, + { bg: 'from-lime-100 to-lime-200', emoji: '🛋️', label: 'Зона отдыха' }, + ], + amenities: ['Wi-Fi', 'TV', 'Кондиционер', 'Мини-бар', 'Халат', 'Фен', 'Сейф', 'Кофемашина'], + has3dTour: false, + }, + { + id: 'r3', name: 'Делюкс', beds: 2, guests: 3, price: 8900, area: 45, + description: 'Роскошный двухкомнатный номер с панорамными окнами. Отдельная гостиная, большая ванная с джакузи. Вид на город.', + photos: [ + { bg: 'from-violet-100 to-violet-200', emoji: '🛏️', label: 'Спальня' }, + { bg: 'from-purple-100 to-purple-200', emoji: '🛁', label: 'Джакузи' }, + { bg: 'from-indigo-100 to-indigo-200', emoji: '🌆', label: 'Вид на город' }, + { bg: 'from-blue-100 to-blue-200', emoji: '🛋️', label: 'Гостиная' }, + { bg: 'from-cyan-100 to-cyan-200', emoji: '🍽️', label: 'Мини-кухня' }, + ], + amenities: ['Wi-Fi', 'Smart TV', 'Кондиционер', 'Джакузи', 'Мини-бар', 'Халат', 'Тапочки', 'Кофемашина', 'Сейф', 'Балкон'], + has3dTour: true, + }, + { + id: 'r4', name: 'Пентхаус', beds: 2, guests: 4, price: 15000, area: 80, + description: 'Эксклюзивный пентхаус на верхнем этаже с панорамной террасой и видом 360°. Собственная сауна, бар, гостиная класса люкс.', + photos: [ + { bg: 'from-amber-200 to-orange-200', emoji: '🌇', label: 'Панорама' }, + { bg: 'from-yellow-100 to-amber-200', emoji: '🛏️', label: 'Мастер-спальня' }, + { bg: 'from-orange-100 to-red-100', emoji: '🧖', label: 'Сауна' }, + { bg: 'from-rose-100 to-pink-100', emoji: '🛋️', label: 'Гостиная' }, + { bg: 'from-red-100 to-orange-100', emoji: '🌅', label: 'Терраса' }, + { bg: 'from-amber-100 to-yellow-100', emoji: '🍸', label: 'Бар' }, + ], + amenities: ['Wi-Fi 1Гбит', 'Smart TV 75"', 'Кондиционер', 'Сауна', 'Мини-бар', 'Джакузи', 'Терраса', 'Дворецкий', 'Кофемашина', 'Халат', 'Тапочки', 'Сейф'], + has3dTour: true, + }, ] +const AMENITY_ICONS: Record = { + 'Wi-Fi': Wifi, 'Wi-Fi 1Гбит': Wifi, + 'TV': Tv2, 'Smart TV': Tv2, 'Smart TV 75"': Tv2, + 'Кондиционер': Wind, + 'Кофемашина': Coffee, + 'Халат': Shirt, 'Тапочки': Shirt, + 'Джакузи': Bath, 'Ванная': Bath, + 'Балкон': Mountain, 'Терраса': Mountain, + 'Сейф': Shield, +} + const EXTRA_BED_PRICE = 1500 const DEFAULT_FORM_FIELDS: FormField[] = [ @@ -95,6 +167,8 @@ function WidgetPreview({ settings }: { settings: WidgetSettings }) { const [extraBeds, setExtraBeds] = useState(0) const [children, setChildren] = useState(0) const [selected, setSelected] = useState(null) + const [expandedRoom, setExpandedRoom] = useState(null) + const [photoIndex, setPhotoIndex] = useState>({}) const [step, setStep] = useState<'browse' | 'form' | 'payment' | 'success'>('browse') // Form state @@ -575,50 +649,208 @@ function WidgetPreview({ settings }: { settings: WidgetSettings }) { {/* Rooms list */} {previewTab === 'rooms' && ( -
+
{nights > 0 && (

{nights} {nights === 1 ? 'ночь' : nights < 5 ? 'ночи' : 'ночей'} · {guests} {settings.language === 'ru' ? 'гостей' : 'guests'} {children > 0 && ` · ${children} дет.`}

)} - {MOCK_ROOMS.map(room => ( -
setSelected(room.id === selected ? null : 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 - + {MOCK_ROOMS.map(room => { + const isExpanded = expandedRoom === room.id + const isSelected = selected === room.id + const curPhoto = photoIndex[room.id] ?? 0 + const photo = room.photos[curPhoto] + return ( +
+ {/* Card header — always visible */} +
setSelected(room.id === selected ? null : room.id)} + > + {/* Thumbnail */} +
+ {photo.emoji} +
+ +
+

{room.name}

+
+ + до {room.guests} + + + {room.beds} кровати + + {room.area} м² + + 4.8 + +
+
+ +
+

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

+

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

+
+ + {/* Expand toggle */} +
+ + {/* Expanded detail panel */} + {isExpanded && ( +
+ {/* Photo gallery */} +
+
+ {photo.emoji} + {photo.label} +
+ + {/* Photo nav */} + {room.photos.length > 1 && ( + <> + + + {/* Dots */} +
+ {room.photos.map((_, i) => ( +
+ + )} + + {/* Photo count */} +
+ + {curPhoto + 1}/{room.photos.length} +
+ + {/* 3D tour badge */} + {room.has3dTour && ( + + )} +
+ + {/* Thumbnail strip */} +
+ {room.photos.map((p, i) => ( + + ))} +
+ + {/* Description */} +
+

{room.description}

+
+ + {/* Amenities */} +
+

Удобства в номере

+
+ {room.amenities.map(a => { + const Icon = AMENITY_ICONS[a] + return ( + + {Icon ? : null} + {a} + + ) + })} +
+
+ + {/* 3D tour placeholder — future feature */} + {!room.has3dTour && ( +
+ +

3D-тур для этого номера появится в ближайшее время

+
+ )} + + {/* Book button in detail panel */} +
+ +
+
+ )}
-
-

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

-

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

-
-
- ))} + ) + })}
)}