// Preloader window.addEventListener('load', () => { setTimeout(() => { document.getElementById('preloader').classList.add('hidden'); }, 1500); }); // Navbar scroll effect const navbar = document.querySelector('.navbar'); window.addEventListener('scroll', () => { if (window.scrollY > 80) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }); // Scroll animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animated'); } }); }, observerOptions); document.querySelectorAll('.animate-on-scroll').forEach(el => { observer.observe(el); }); // Counter animation const counterObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const counters = entry.target.querySelectorAll('.counter'); counters.forEach(counter => { const target = parseInt(counter.getAttribute('data-target')); const duration = 2000; const step = target / (duration / 16); let current = 0; const timer = setInterval(() => { current += step; if (current >= target) { counter.textContent = target; clearInterval(timer); } else { counter.textContent = Math.floor(current); } }, 16); }); counterObserver.unobserve(entry.target); } }); }, { threshold: 0.5 }); const statsSection = document.querySelector('.hero-stats'); if (statsSection) counterObserver.observe(statsSection); // Set min date for checkin to today const today = new Date().toISOString().split('T')[0]; document.querySelector('[name="checkin"]').min = today; document.querySelector('[name="checkout"]').min = today; // Booking modal - set room name document.querySelectorAll('.btn-book').forEach(btn => { btn.addEventListener('click', function() { const room = this.getAttribute('data-room'); document.getElementById('selectedRoom').value = room; const maxGuests = parseInt(this.getAttribute('data-max-guests')) || ROOM_MAX_GUESTS[room] || 4; updateGuestOptionsFallback(room); 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(``); } guestsSelect.innerHTML = options.join(''); } window.updateRoomPricesData = function(prices, maxGuests) { Object.assign(ROOM_PRICES, prices); Object.assign(ROOM_MAX_GUESTS, maxGuests); }; function updateGuestOptions(room) { const guestsSelect = document.querySelector('[name="guests"]'); if (!guestsSelect) return; const maxGuests = ROOM_MAX_GUESTS[room] || 4; let options = []; for (let i = 1; i <= maxGuests; i++) { let text = i === 1 ? '1 гость' : (i < 5 ? `${i} гостя` : `${i} гостей`); options.push(``); } guestsSelect.innerHTML = options.join(''); } function updateGuestOptionsFallback(room) { const guestsSelect = document.querySelector('[name="guests"]'); if (!guestsSelect) return; const options2x = [ { value: 1, text: '1 гость' }, { value: 2, text: '2 гостя' } ]; const options3x = [ { value: 1, text: '1 гость' }, { value: 2, text: '2 гостя' }, { value: 3, text: '3 гостя' } ]; const optionsDefault = [ { value: 1, text: '1 гость' }, { value: 2, text: '2 гостя' }, { value: 3, text: '3 гостя' }, { value: 4, text: '4 гостя' } ]; let options = optionsDefault; if (room === '2x-местный') options = options2x; else if (room === '3х-местный') options = options3x; guestsSelect.innerHTML = options.map(o => ``).join(''); } function hideGuestOptionsUpdate() {} // Price calculation const ROOM_PRICES = { '2x-местный': 1500, '3х-местный': 2000, 'Семейный': 3000, 'Люкс': 4500 }; const ROOM_MAX_GUESTS = { '2x-местный': 2, '3х-местный': 3, 'Семейный': 4, 'Люкс': 4 }; let currentPromocodeData = null; function calculateNights(checkin, checkout) { const ci = new Date(checkin); const co = new Date(checkout); return Math.ceil((co - ci) / (1000 * 60 * 60 * 24)); } function validateBookingDates() { const checkinInput = document.querySelector('[name="checkin"]'); const checkoutInput = document.querySelector('[name="checkout"]'); const today = new Date(); today.setHours(0, 0, 0, 0); if (checkinInput.value) { const checkinDate = new Date(checkinInput.value); if (checkinDate < today) { checkinInput.setCustomValidity('Дата заезда не может быть в прошлом'); } else { checkinInput.setCustomValidity(''); } } if (checkinInput.value && checkoutInput.value) { const checkinDate = new Date(checkinInput.value); const checkoutDate = new Date(checkoutInput.value); if (checkoutDate <= checkinDate) { checkoutInput.setCustomValidity('Дата выезда должна быть позже даты заезда'); } else { checkoutInput.setCustomValidity(''); } } return checkinInput.checkValidity() && checkoutInput.checkValidity(); } document.querySelector('[name="checkin"]').addEventListener('change', validateBookingDates); document.querySelector('[name="checkout"]').addEventListener('change', validateBookingDates); function updatePriceDisplay(basePrice, discountPercent, discountAmount, totalPrice) { document.getElementById('basePriceDisplay').textContent = basePrice + ' ₽'; document.getElementById('discountPercentDisplay').textContent = discountPercent; document.getElementById('discountAmountDisplay').textContent = '-' + discountAmount + ' ₽'; document.getElementById('totalPriceDisplay').textContent = totalPrice + ' ₽'; document.getElementById('priceInfo').style.display = 'block'; } function hidePriceInfo() { document.getElementById('priceInfo').style.display = 'none'; currentPromocodeData = null; } function getFormData() { return { room: document.getElementById('selectedRoom').value, checkin: document.querySelector('[name="checkin"]').value, checkout: document.querySelector('[name="checkout"]').value, promocode: document.getElementById('promocodeInput').value.trim() }; } async function checkPromocode() { const { room, checkin, checkout, promocode } = getFormData(); if (!room || !checkin || !checkout) { hidePriceInfo(); return; } const basePrice = ROOM_PRICES[room] ? ROOM_PRICES[room] * calculateNights(checkin, checkout) : 0; if (!promocode) { updatePriceDisplay(basePrice, 0, 0, basePrice); currentPromocodeData = null; return; } try { const response = await fetch('/api/promocodes/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: promocode, room_type: room, checkin, checkout }) }); const data = await response.json(); if (data.valid) { currentPromocodeData = data; updatePriceDisplay(data.base_price, data.discount_percent, data.discount_amount, data.total_price); } else { hidePriceInfo(); currentPromocodeData = null; } } catch (error) { console.error('Promocode validation error:', error); hidePriceInfo(); } } document.getElementById('checkPromocodeBtn').addEventListener('click', checkPromocode); document.getElementById('promocodeInput').addEventListener('blur', checkPromocode); document.querySelector('[name="checkin"]').addEventListener('change', checkPromocode); document.querySelector('[name="checkout"]').addEventListener('change', checkPromocode); document.querySelectorAll('.btn-book').forEach(btn => { btn.addEventListener('click', function() { setTimeout(checkPromocode, 100); }); }); // Form submission document.getElementById('bookingForm').addEventListener('submit', async function(e) { e.preventDefault(); const btn = this.querySelector('.btn-submit-booking'); const originalText = btn.innerHTML; btn.innerHTML = 'Отправка...'; btn.disabled = true; const formData = { name: this.querySelector('[name="name"]').value, phone: this.querySelector('[name="phone"]').value, adults: parseInt(this.querySelector('[name="guests"]').value), children: 0, checkin: this.querySelector('[name="checkin"]').value, checkout: this.querySelector('[name="checkout"]').value, wishes: this.querySelector('[name="wishes"]').value, room: document.getElementById('selectedRoom').value, promocode: document.getElementById('promocodeInput').value.trim() || null }; try { const response = await fetch('/api/bookings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); if (response.ok) { btn.innerHTML = 'Заявка отправлена Рауфу Алексеевичу!'; btn.style.background = '#25d366'; this.reset(); hidePriceInfo(); setTimeout(() => { btn.innerHTML = originalText; btn.style.background = ''; btn.disabled = false; bootstrap.Modal.getInstance(document.getElementById('bookingModal')).hide(); }, 2500); } else { const errorData = await response.json(); throw new Error(errorData.error || 'Ошибка сервера'); } } catch (error) { console.error('Booking error:', error); btn.innerHTML = 'Ошибка отправки'; btn.style.background = '#c9302c'; setTimeout(() => { btn.innerHTML = originalText; btn.style.background = ''; btn.disabled = false; }, 3000); } }); // Smooth scroll for nav links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Close mobile menu const navCollapse = document.querySelector('.navbar-collapse'); if (navCollapse.classList.contains('show')) { bootstrap.Collapse.getInstance(navCollapse).hide(); } } }); }); // Parallax effect on hero window.addEventListener('scroll', () => { const hero = document.querySelector('.hero-bg'); if (hero) { const scrolled = window.scrollY; hero.style.transform = `scale(${1 + scrolled * 0.0001}) translateY(${scrolled * 0.3}px)`; } });