import { useState, useRef } from 'react' import { Modal } from '../ui/Modal' import { cn } from '../../lib/utils' import type { Room, RoomStatus, HousekeepingStatus, BedType, BedItem, BedItemType, ExtraPlace, ChildPolicy } from '../../types' import { ImagePlus, X as XIcon, ChevronLeft, ChevronRight, Plus, Minus, Baby, Trash2 } from 'lucide-react' import { useAmenities } from '../../contexts/AmenitiesContext' const ROOM_TYPES = ['Стандарт', 'Делюкс', 'Полулюкс', 'Люкс', 'Пентхаус', 'Апартаменты', 'Другой'] const BED_TYPES: { value: BedType; label: string }[] = [ { value: 'single', label: 'Одна кровать' }, { value: 'double', label: 'Двуспальная' }, { value: 'queen', label: 'Queen' }, { value: 'king', label: 'King' }, { value: 'twin', label: 'Две кровати' }, ] const ROOM_STATUSES: { value: RoomStatus; label: string }[] = [ { value: 'available', label: 'Свободен' }, { value: 'occupied', label: 'Занят' }, { value: 'maintenance', label: 'Ремонт' }, { value: 'blocked', label: 'Закрыт' }, ] const HK_STATUSES: { value: HousekeepingStatus; label: string }[] = [ { value: 'clean', label: 'Чистый' }, { value: 'dirty', label: 'Грязный' }, { value: 'cleaning', label: 'Убирается' }, { value: 'inspect', label: 'Проверка' }, ] const BED_ITEM_TYPES: { value: BedItemType; label: string; icon: string }[] = [ { value: 'single', label: 'Кровать 1-сп.', icon: '🛏' }, { value: 'double', label: 'Кровать 2-сп.', icon: '🛏' }, { value: 'queen', label: 'Queen-кровать', icon: '🛏' }, { value: 'king', label: 'King-кровать', icon: '🛏' }, { value: 'twin', label: 'Две кровати', icon: '🛏' }, { value: 'sofa', label: 'Диван', icon: '🛋' }, { value: 'bunk', label: 'Двухъярусная', icon: '🛏' }, { value: 'cot', label: 'Раскладушка', icon: '🪑' }, ] // ── Tab type ─────────────────────────────────────────────────────────────────── type ModalTab = 'main' | 'places' | 'description' | 'photos' interface RoomModalProps { open: boolean room?: Room categories?: { id: string; name: string }[] onClose: () => void onSave: (room: Room) => void onDelete?: () => void error?: string | null } export function RoomModal({ open, room, categories = [], onClose, onSave, onDelete, error }: RoomModalProps) { const isEdit = !!room const [tab, setTab] = useState('main') const [form, setForm] = useState({ number: room?.number ?? '', name: room?.name ?? '', floor: room?.floor ?? 1, type: room?.type ?? (categories[0]?.name ?? 'Стандарт'), bedType: (room?.bedType ?? 'double') as BedType, maxGuests: room?.maxGuests ?? 2, baseRate: room?.baseRate ?? 5000, status: (room?.status ?? 'available') as RoomStatus, housekeepingStatus: (room?.housekeepingStatus ?? 'clean') as HousekeepingStatus, sortOrder: room?.sortOrder ?? 99, allowHourly: room?.allowHourly ?? false, hourlyRate: room?.hourlyRate ?? 1000, categoryId: room?.categoryId ?? (categories.find(c => c.name === room?.type)?.id ?? categories[0]?.id ?? ''), description: room?.description ?? '', earlyCheckinFee: room?.earlyCheckinFee ?? '', lateCheckoutFee: room?.lateCheckoutFee ?? '', }) const { amenities: allAmenities } = useAmenities() const [amenities, setAmenities] = useState(room?.amenities ?? ['Wi-Fi', 'TV', 'AC']) const [photos, setPhotos] = useState(room?.photos ?? []) const [photoIdx, setPhotoIdx] = useState(0) const fileInputRef = useRef(null) // ── Beds constructor ────────────────────────────────────────────────────── const [beds, setBeds] = useState( room?.beds ?? [{ type: 'double', count: 1 }] ) const [extraPlace, setExtraPlace] = useState( room?.extraPlace ?? { enabled: false, count: 1, price: 1500 } ) const [childPolicy, setChildPolicy] = useState( room?.childPolicy ?? { enabled: false, freeUnderAge: 12, chargeFromAge: 12 } ) const addBed = () => setBeds(prev => [...prev, { type: 'single', count: 1 }]) const removeBed = (i: number) => setBeds(prev => prev.filter((_, idx) => idx !== i)) const updateBed = (i: number, patch: Partial) => setBeds(prev => prev.map((b, idx) => idx === i ? { ...b, ...patch } : b)) const set = (k: K, v: typeof form[K]) => setForm(prev => ({ ...prev, [k]: v })) const toggleAmenity = (a: string) => setAmenities(prev => prev.includes(a) ? prev.filter(x => x !== a) : [...prev, a]) const handlePhotoUpload = (e: React.ChangeEvent) => { const files = Array.from(e.target.files ?? []) files.forEach(file => { const reader = new FileReader() reader.onload = ev => { const url = ev.target?.result as string setPhotos(prev => { const next = [...prev, url] setPhotoIdx(next.length - 1) return next }) } reader.readAsDataURL(file) }) e.target.value = '' } const removePhoto = (idx: number) => { setPhotos(prev => prev.filter((_, i) => i !== idx)) setPhotoIdx(p => Math.max(0, p - 1)) } const handleSave = () => { if (!form.number) return onSave({ id: room?.id ?? `r-${Date.now()}`, hotelId: room?.hotelId ?? 'hotel-1', number: form.number, name: form.name || undefined, floor: form.floor, type: form.type, bedType: form.bedType, maxGuests: form.maxGuests, baseRate: form.baseRate, status: form.status, housekeepingStatus: form.housekeepingStatus, amenities, sortOrder: form.sortOrder, allowHourly: form.allowHourly || undefined, hourlyRate: form.allowHourly ? form.hourlyRate : undefined, categoryId: form.categoryId || undefined, description: form.description || undefined, photos: photos.length > 0 ? photos : undefined, beds: beds.length > 0 ? beds : undefined, extraPlace: extraPlace.enabled ? extraPlace : undefined, childPolicy: childPolicy.enabled ? childPolicy : undefined, earlyCheckinFee: form.earlyCheckinFee !== '' ? Number(form.earlyCheckinFee) : undefined, lateCheckoutFee: form.lateCheckoutFee !== '' ? Number(form.lateCheckoutFee) : undefined, }) } const TABS: { key: ModalTab; label: string }[] = [ { key: 'main', label: 'Основное' }, { key: 'places', label: 'Места' }, { key: 'description', label: 'Описание' }, { key: 'photos', label: `Фото${photos.length > 0 ? ` (${photos.length})` : ''}` }, ] return ( {isEdit && onDelete && ( )} {error && (

{error}

)} } > {/* Tab bar */}
{TABS.map(t => ( ))}
{/* ── Main tab ── */} {tab === 'main' && (
set('number', e.target.value)} />
set('name', e.target.value)} />
set('floor', parseInt(e.target.value) || 1)} />
{categories.length > 0 ? ( ) : ( )}
set('maxGuests', parseInt(e.target.value) || 1)} />
set('baseRate', parseInt(e.target.value) || 0)} />
set('sortOrder', parseInt(e.target.value) || 1)} />
{/* Hourly section */}

Почасовая аренда

{form.allowHourly ? (
set('hourlyRate', parseInt(e.target.value) || 0)} />
) : (

Разрешить бронирование номера на несколько часов

)}
{/* Early check-in / late checkout fees */}

Тарифы за ранний/поздний заезд

Применяются если включено в настройках отеля

set('earlyCheckinFee', e.target.value)} />
set('lateCheckoutFee', e.target.value)} />
{/* Amenities */}
{allAmenities.map(a => { const selected = amenities.includes(a) return ( ) })}
)} {/* ── Places tab ── */} {tab === 'places' && (
{/* Beds constructor */}

Спальные места

Кровати, диваны и другая мебель для сна

{beds.map((bed, i) => (
{BED_ITEM_TYPES.find(b => b.value === bed.type)?.icon ?? '🛏'} {/* Count stepper */}
{bed.count}
))}
{/* Extra places */}

Дополнительные места

Раскладные кровати, кушетки за доп. плату

{extraPlace.enabled && (
{extraPlace.count}
setExtraPlace(p => ({ ...p, price: parseInt(e.target.value) || 0 }))} />
)}
{/* Child policy */}

Политика для детей

Возраст бесплатного проживания

{childPolicy.enabled ? (
🆓

Бесплатно до

до {childPolicy.freeUnderAge} лет
💰

Доп. место от

{extraPlace.enabled && (

{extraPlace.price} ₽/ночь

)} {!extraPlace.enabled && (

Включите «Доп. места» и укажите цену

)}
от {childPolicy.chargeFromAge} лет

Дети до {childPolicy.freeUnderAge} лет — бесплатно без доп. места. Дети от {childPolicy.chargeFromAge} лет — оплачивают дополнительное место.

) : (

Стандартные условия, все гости оплачиваются

)}
)} {/* ── Description tab ── */} {tab === 'description' && (

Будет отображаться в виджете онлайн-бронирования для гостей.