130 lines
5.8 KiB
JavaScript
130 lines
5.8 KiB
JavaScript
const AMENITY_ICONS = {
|
||
has_ac: { icon: 'fa-snowflake', label: 'Кондиционер' },
|
||
has_tv: { icon: 'fa-tv', label: 'ТВ' },
|
||
has_fridge: { icon: 'fa-sink', label: 'Холодильник' },
|
||
has_wifi: { icon: 'fa-wifi', label: 'WiFi' },
|
||
has_kettle: { icon: 'fa-mug-hot', label: 'Чайник' },
|
||
has_hairdryer: { icon: 'fa-wind', label: 'Фен' },
|
||
has_shower: { icon: 'fa-shower', label: 'Душ в номере' },
|
||
has_sea_view: { icon: 'fa-water', label: 'Вид на море' }
|
||
};
|
||
|
||
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 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';
|
||
const fullImageSrc = imageSrc.startsWith('uploads') ? '/' + imageSrc : imageSrc;
|
||
|
||
const amenitiesHtml = amenities.map(a => {
|
||
const info = AMENITY_ICONS[a];
|
||
if (!info) return '';
|
||
return `<span class="room-feature-tag"><i class="fas ${info.icon}"></i> ${info.label}</span>`;
|
||
}).join('');
|
||
|
||
const floorsStr = floors.length > 0 ? 'Этажи ' + floors.join(', ') : '';
|
||
|
||
const extraBedsHtml = room.extra_beds > 0
|
||
? `<div class="room-extra-hint">Возможно +${room.extra_beds} доп. мест (${room.extra_bed_price} ₽)</div>`
|
||
: '';
|
||
|
||
const featuredClass = room.type === 'Семейный' ? ' featured' : '';
|
||
|
||
return `
|
||
<div class="col-lg-4">
|
||
<div class="room-card${featuredClass}">
|
||
<div class="room-image">
|
||
<img src="${fullImageSrc}" alt="${room.name}"
|
||
onerror="this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 400 250%22%3E%3Crect fill=%22%23274151%22 width=%22400%22 height=%22250%22/%3E%3Ctext fill=%22%2364748b%22 font-family=%22sans-serif%22 font-size=%2216%22 x=%2250%25%22 y=%2250%25%22 text-anchor=%22middle%22 dy=%22.3em%22%3EФото%3C/text%3E%3C/svg%3E'">
|
||
<div class="room-category">${room.type}</div>
|
||
</div>
|
||
<div class="room-body">
|
||
<h3 class="room-name">${escapeHtml(room.name)}</h3>
|
||
<p class="room-desc">${escapeHtml(room.description || '')}</p>
|
||
<div class="room-meta">
|
||
<span><i class="fas fa-door-open"></i> ${room.area_sqm || 20} м²</span>
|
||
${floorsStr ? `<span><i class="fas fa-layer-group"></i> ${floorsStr}</span>` : ''}
|
||
${amenities.includes('has_shower') ? '<span><i class="fas fa-shower"></i> Душ в номере</span>' : ''}
|
||
</div>
|
||
<div class="room-features">
|
||
${amenitiesHtml}
|
||
</div>
|
||
${extraBedsHtml}
|
||
<div class="room-footer">
|
||
<div class="room-price">
|
||
<span class="amount">от ${room.price_per_night || 0} ₽</span>
|
||
<span class="period"> / ночь</span>
|
||
</div>
|
||
<button class="btn-book" data-bs-toggle="modal" data-bs-target="#bookingModal" data-room="${room.type}" data-max-guests="${room.max_guests}">Забронировать</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
initRoomBookingHandlers();
|
||
}
|
||
|
||
function initRoomBookingHandlers() {
|
||
document.querySelectorAll('.btn-book').forEach(btn => {
|
||
btn.addEventListener('click', function() {
|
||
const room = this.getAttribute('data-room');
|
||
const maxGuests = parseInt(this.getAttribute('data-max-guests')) || 4;
|
||
document.getElementById('selectedRoom').value = room;
|
||
updateGuestOptionsDynamic(room, maxGuests);
|
||
hidePriceInfo();
|
||
});
|
||
});
|
||
}
|
||
|
||
function updateGuestOptionsDynamic(roomType, maxGuests) {
|
||
const guestsSelect = document.querySelector('[name="guests"]');
|
||
if (!guestsSelect) return;
|
||
|
||
let options = [];
|
||
for (let i = 1; i <= maxGuests; i++) {
|
||
let text = i === 1 ? '1 гость' : (i < 5 ? `${i} гостя` : `${i} гостей`);
|
||
options.push(`<option value="${i}">${text}</option>`);
|
||
}
|
||
|
||
guestsSelect.innerHTML = options.join('');
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
if (!text) return '';
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
loadRoomsPublic();
|
||
}); |