import { useState, useEffect } from 'react' import { BedDouble, Users, Wifi, Plus, Search, Pencil, ChevronDown, ChevronUp, Check, Trash2, X as XIcon, Loader2 } from 'lucide-react' import { useAmenities } from '../contexts/AmenitiesContext' import { useAuth } from '../contexts/AuthContext' import { api } from '../lib/api' import { MOCK_CATEGORIES } from './RoomCategoriesPage' import type { Room } from '../types' import { cn, ROOM_STATUS_COLORS, ROOM_STATUS_LABELS, HK_STATUS_COLORS, HK_STATUS_LABELS, formatCurrency } from '../lib/utils' import { Badge } from '../components/ui/Badge' import { RoomModal } from '../components/rooms/RoomModal' export function RoomsPage() { const { user } = useAuth() const slug = user?.hotelSlug ?? '' const [rooms, setRooms] = useState([]) const [loading, setLoading] = useState(true) const [search, setSearch] = useState('') const [floorFilter, setFloorFilter] = useState('all') const [modalOpen, setModalOpen] = useState(false) const [editingRoom, setEditingRoom] = useState(undefined) const { amenities, addAmenity, removeAmenity, renameAmenity } = useAmenities() const [showAmenities, setShowAmenities] = useState(false) const [newAmenity, setNewAmenity] = useState('') const [editingAmenity, setEditingAmenity] = useState(null) const [editAmenityValue, setEditAmenityValue] = useState('') useEffect(() => { if (!slug) return api.rooms.list(slug) .then(setRooms) .catch(console.error) .finally(() => setLoading(false)) }, [slug]) const floors = [...new Set(rooms.map(r => r.floor))].sort() const filtered = rooms.filter(r => { const matchSearch = search === '' || r.number.includes(search) || r.type.toLowerCase().includes(search.toLowerCase()) || (r.name ?? '').toLowerCase().includes(search.toLowerCase()) const matchFloor = floorFilter === 'all' || r.floor === floorFilter return matchSearch && matchFloor }) const stats = { available: rooms.filter(r => r.status === 'available').length, occupied: rooms.filter(r => r.status === 'occupied').length, maintenance: rooms.filter(r => r.status === 'maintenance').length, dirty: rooms.filter(r => r.housekeepingStatus === 'dirty' || r.housekeepingStatus === 'cleaning').length, } const openCreate = () => { setEditingRoom(undefined) setModalOpen(true) } const openEdit = (room: Room) => { setEditingRoom(room) setModalOpen(true) } const [saveError, setSaveError] = useState(null) const handleDelete = async (roomId: string) => { try { await api.rooms.delete(slug, roomId) setRooms(prev => prev.filter(r => r.id !== roomId)) setModalOpen(false) } catch (err) { const msg = err instanceof Error ? err.message : 'Ошибка удаления' setSaveError(msg) } } const handleSave = async (room: Room) => { setSaveError(null) try { const isNew = !rooms.find(r => r.id === room.id) if (isNew) { const created = await api.rooms.create(slug, { number: room.number, type: room.type, floor: room.floor, maxGuests: room.maxGuests, baseRate: room.baseRate, status: room.status, amenities: room.amenities, name: room.name, categoryId: room.categoryId, bedType: room.bedType, beds: room.beds, housekeepingStatus: room.housekeepingStatus, sortOrder: room.sortOrder, allowHourly: room.allowHourly, hourlyRate: room.hourlyRate, extraPlace: room.extraPlace, childPolicy: room.childPolicy, description: room.description, photos: room.photos, }) setRooms(prev => [...prev, created]) } else { const updated = await api.rooms.update(slug, room.id, { number: room.number, type: room.type, floor: room.floor, maxGuests: room.maxGuests, baseRate: room.baseRate, status: room.status, amenities: room.amenities, name: room.name, categoryId: room.categoryId, bedType: room.bedType, beds: room.beds, housekeepingStatus: room.housekeepingStatus, sortOrder: room.sortOrder, allowHourly: room.allowHourly, hourlyRate: room.hourlyRate, extraPlace: room.extraPlace, childPolicy: room.childPolicy, description: room.description, photos: room.photos, }) setRooms(prev => prev.map(r => r.id === updated.id ? updated : r)) } setModalOpen(false) } catch (err) { const msg = err instanceof Error ? err.message : 'Ошибка сохранения' setSaveError( msg.includes('already exists') || msg.includes('duplicate') || msg.includes('unique') ? `Номер «${room.number}» уже существует` : msg, ) } } if (loading) { return (
) } return (
{/* Header */}

Номерной фонд

{rooms.length} номеров

{/* Stats */}
{[ { label: 'Свободных', value: stats.available, color: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-900/20' }, { label: 'Занятых', value: stats.occupied, color: 'text-brand-600 dark:text-brand-400', bg: 'bg-brand-50 dark:bg-brand-900/20' }, { label: 'На ремонте', value: stats.maintenance, color: 'text-orange-600 dark:text-orange-400', bg: 'bg-orange-50 dark:bg-orange-900/20' }, { label: 'Нужна уборка', value: stats.dirty, color: 'text-red-600 dark:text-red-400', bg: 'bg-red-50 dark:bg-red-900/20' }, ].map(s => (

{s.value}

{s.label}

))}
{/* Filters */}
setSearch(e.target.value)} />
{floors.map(f => ( ))}
{/* Room grid */} {filtered.length === 0 && !loading ? (
{rooms.length === 0 ? 'Нет номеров. Добавьте первый номер.' : 'Ничего не найдено.'}
) : (
{filtered.map(room => ( openEdit(room)} /> ))}
)} {/* Справочник удобств */}
{showAmenities && (
setNewAmenity(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); if (newAmenity.trim()) { addAmenity(newAmenity.trim()); setNewAmenity('') } } }} />
{amenities.map(a => (
{editingAmenity === a ? ( <> setEditAmenityValue(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { renameAmenity(a, editAmenityValue); setEditingAmenity(null) } if (e.key === 'Escape') setEditingAmenity(null) }} /> ) : ( <> {a}
)}
))}
)}
{/* Modal */} { setModalOpen(false); setSaveError(null) }} onSave={handleSave} categories={MOCK_CATEGORIES} error={saveError} />
) } function RoomCard({ room, onEdit }: { room: Room; onEdit: () => void }) { return (
{room.number} {room.name && ( {room.name} )}
{room.type} · Этаж {room.floor}
{ROOM_STATUS_LABELS[room.status]} {HK_STATUS_LABELS[room.housekeepingStatus]}
{room.bedType}
до {room.maxGuests}
{room.amenities.includes('Wi-Fi') && (
Wi-Fi
)}
{formatCurrency(room.baseRate)} /ночь{room.allowHourly ? ` · ${formatCurrency(room.hourlyRate ?? 0)}/ч` : ''}
{room.amenities.length > 0 && (
{room.amenities.slice(0, 3).map(a => ( {a} ))} {room.amenities.length > 3 && ( +{room.amenities.length - 3} )}
)}
) }