const AMENITY_KEYS = { has_ac: { icon: 'fa-snowflake', key: 'amenity.ac' }, has_tv: { icon: 'fa-tv', key: 'amenity.tv' }, has_fridge: { icon: 'fa-sink', key: 'amenity.fridge' }, has_wifi: { icon: 'fa-wifi', key: 'amenity.wifi' }, has_kettle: { icon: 'fa-mug-hot', key: 'amenity.kettle' }, has_hairdryer: { icon: 'fa-wind', key: 'amenity.hairdryer' }, has_shower: { icon: 'fa-shower', key: 'amenity.shower' }, has_sea_view: { icon: 'fa-water', key: 'amenity.sea_view' } }; function rt(key, fallback) { return (window.I18n && typeof window.I18n.t === 'function') ? window.I18n.t(key) : fallback; } let cachedRooms = []; async function loadRoomsPublic() { try { const res = await fetch('/api/rooms'); if (!res.ok) throw new Error('Failed to load rooms'); cachedRooms = await res.json(); renderRoomsPublic(cachedRooms); updateRoomPrices(cachedRooms); } catch (err) { console.error('Error loading rooms:', err); document.getElementById('roomsEmpty').style.display = 'block'; } } function renderRoomsPublic(rooms) { const grid = document.getElementById('roomsGrid'); const empty = document.getElementById('roomsEmpty'); if (!rooms || rooms.length === 0) { grid.innerHTML = ''; empty.style.display = 'block'; return; } empty.style.display = 'none'; grid.innerHTML = rooms.map((room, index) => { const amenities = Array.isArray(room.amenities) ? room.amenities : []; const floors = Array.isArray(room.floors) ? room.floors : []; const images = Array.isArray(room.images) ? room.images : []; const primaryImg = images.find(i => i.is_primary) || images[0]; let imageSrc = room.image_path || 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 250"%3E%3Crect fill="%23274151" width="400" height="250"/%3E%3Ctext fill="%2364748b" font-family="sans-serif" font-size="16" x="50%25" y="50%25" text-anchor="middle" dy=".3em"%3EФото%3C/text%3E%3C/svg%3E'; if (primaryImg && primaryImg.image_path) { imageSrc = primaryImg.image_path; } const fullImageSrc = imageSrc.startsWith('uploads') || imageSrc.startsWith('data') ? '/' + imageSrc : imageSrc; const allImages = images.length > 0 ? images : (room.image_path ? [{ image_path: room.image_path, is_primary: 1 }] : []); let imagesHtml = ''; if (allImages.length > 1) { const dotsHtml = allImages.map((img, i) => { const src = img.image_path.startsWith('uploads') || img.image_path.startsWith('data') ? '/' + img.image_path : img.image_path; return ``; }).join(''); imagesHtml = ``; } else { imagesHtml = `${room.name}`; } const amenitiesHtml = amenities.map(a => { const info = AMENITY_KEYS[a]; if (!info) return ''; return ` ${rt(info.key, a)}`; }).join(''); const floorsStr = floors.length > 0 ? rt('rooms.floors', 'Этажи') + ' ' + floors.join(', ') : ''; const extraBedsHtml = room.extra_beds > 0 ? `
${rt('rooms.extra_beds', 'Возможно').replace('{n}', room.extra_beds).replace('{price}', room.extra_bed_price)} +${room.extra_beds} ${rt('rooms.extra_beds_rest', 'доп. мест')} (${room.extra_bed_price} ₽)
` : ''; const featuredClass = room.type === 'Семейный' ? ' featured' : ''; return `
${imagesHtml}
${room.type}

${escapeHtml(room.name)}

${escapeHtml(room.description || '')}

${room.area_sqm || 20} м² ${floorsStr ? ` ${floorsStr}` : ''} ${amenities.includes('has_shower') ? ' ' + rt('amenity.shower', 'Душ в номере') + '' : ''}
${amenitiesHtml}
${extraBedsHtml}
`; }).join(''); initRoomBookingHandlers(); Object.values(roomCarouselTimers).forEach(t => clearInterval(t)); roomCarouselTimers = {}; rooms.forEach((room, i) => { const images = Array.isArray(room.images) ? room.images : []; if (images.length > 1) { roomCarouselTimers[i] = setInterval(() => roomCarouselNext(i), 10000); } }); } let roomCarouselState = {}; let roomCarouselTimers = {}; function roomImageClick(index) { const img = document.getElementById('roomCarouselImg' + index); const room = cachedRooms[index]; if (img && room) { openLightbox(img.src, room.name); } } function roomCarouselPrev(index) { if (!roomCarouselState[index]) roomCarouselState[index] = 0; const imgs = getRoomImagesForIndex(index); if (roomCarouselState[index] > 0) roomCarouselState[index]--; else roomCarouselState[index] = imgs.length - 1; applyCarouselFrame(index, imgs); } function roomCarouselNext(index) { if (!roomCarouselState[index]) roomCarouselState[index] = 0; const imgs = getRoomImagesForIndex(index); if (roomCarouselState[index] < imgs.length - 1) roomCarouselState[index]++; else roomCarouselState[index] = 0; applyCarouselFrame(index, imgs); } function getRoomImagesForIndex(index) { const room = cachedRooms[index]; if (!room) return []; const images = Array.isArray(room.images) && room.images.length > 0 ? room.images : (room.image_path ? [{ image_path: room.image_path, is_primary: 1 }] : []); return images; } function applyCarouselFrame(index, images) { const img = document.getElementById('roomCarouselImg' + index); const dots = document.querySelectorAll(`#roomCarouselImg${index} ~ .room-img-dots .room-img-dot`); if (!img || images.length === 0) return; const state = roomCarouselState[index]; const src = images[state].image_path.startsWith('uploads') || images[state].image_path.startsWith('data') ? '/' + images[state].image_path : images[state].image_path; img.src = src; dots.forEach((d, i) => d.classList.toggle('active', i === state)); } function updateRoomPrices(rooms) { const prices = {}; const maxGuests = {}; const extraBeds = {}; const extraBedPrices = {}; rooms.forEach(room => { prices[room.type] = room.price_per_night; maxGuests[room.type] = room.max_guests; extraBeds[room.type] = room.extra_beds || 0; extraBedPrices[room.type] = room.extra_bed_price || 0; }); if (window.updateRoomPricesData) { window.updateRoomPricesData(prices, maxGuests, extraBeds, extraBedPrices); } } function initRoomBookingHandlers() { document.querySelectorAll('.btn-book').forEach(btn => { btn.addEventListener('click', function() { const roomId = this.getAttribute('data-room-id'); const roomType = this.getAttribute('data-room-type'); const roomName = this.getAttribute('data-room-name'); const maxGuests = parseInt(this.getAttribute('data-max-guests')) || 4; const extraBeds = parseInt(this.getAttribute('data-extra-beds')) || 0; const extraBedPrice = parseInt(this.getAttribute('data-extra-bed-price')) || 0; document.getElementById('selectedRoom').value = roomType; document.getElementById('selectedRoomId').value = roomId; updateGuestOptionsDynamic(roomType, maxGuests, extraBeds, extraBedPrice); hidePriceInfo(); }); }); } function updateGuestOptionsDynamic(roomType, maxGuests) { const guestsSelect = document.querySelector('[name="guests"]'); if (!guestsSelect) return; let options = []; for (let i = 1; i <= maxGuests; i++) { const key = 'booking.guest_' + i; let text = rt(key, i === 1 ? '1 гость' : (i < 5 ? i + ' гостя' : i + ' гостей')); options.push(``); } guestsSelect.innerHTML = options.join(''); } async function checkRoomAvailability() { const checkin = document.querySelector('[name="checkin"]').value; const checkout = document.querySelector('[name="checkout"]').value; if (!checkin || !checkout) return; try { const res = await fetch(`/api/rooms/availability?checkin=${checkin}&checkout=${checkout}`); if (!res.ok) return; const data = await res.json(); cachedRooms.forEach((room, i) => { const avail = data.find(a => a.id === room.id); const el = document.getElementById('roomAvail' + i); if (el && avail) { el.style.display = 'block'; if (avail.available === 0) { el.textContent = rt('rooms.no_rooms', 'Нет мест'); el.style.background = 'rgba(239,68,68,0.85)'; } else if (avail.available <= 2) { el.textContent = rt('rooms.rooms_left', 'Мест:') + ' ' + avail.available; el.style.background = 'rgba(245,158,11,0.85)'; } else { el.textContent = rt('rooms.available', 'Свободно:') + ' ' + avail.available; el.style.background = 'rgba(22,163,74,0.85)'; } } }); } catch (e) {} } function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } document.addEventListener('DOMContentLoaded', () => { loadRoomsPublic(); });