document.addEventListener("DOMContentLoaded", async function() { // Configuration object for better maintainability const CONFIG = { API_URL: 'https://sereners.beveillard.workers.dev', SELECTORS: { loader: '#top-loader', nonDisponible: '.non-disponible-template', infoBookingWrapper: '[data-booking-wrapper]', dataWrapper: '#data-wrapper', dateRange: '#dateRange', apartmentName: '#apartment-name', priceElement: '.price-cms-txt' }, DEFAULTS: { adults: 2, children: 0, babies: 0, maxGuests: 10 }, ERROR_MESSAGES: { noGuestyId: 'Identifiant d\'appartement manquant.', fetchError: 'Erreur lors de la récupération des données de l\'appartement.', initError: 'Une erreur est survenue lors du chargement.', capacityError: 'Cet appartement ne peut accueillir que {capacity} personnes.', availabilityError: 'Une erreur est survenue lors de la vérification de disponibilité.', notAvailable: 'Pas disponible pour ces dates', notFound: 'Appartement non trouvé dans la réponse de disponibilité.', missingData: 'Impossible de procéder à la réservation. Données d\'appartement manquantes.', selectDates: 'Veuillez sélectionner des dates et le nombre d\'invités.', selectStayDates: 'Veuillez sélectionner les dates de séjour' }, TAX_RATE: 2.6 // City tax rate per guest per night }; // Get URL parameters and path const urlParams = new URLSearchParams(window.location.search); const pathSegments = window.location.pathname.split('/'); const guestyId = urlParams.get('guesty_id') || pathSegments[pathSegments.length - 1]; // Get initial values from URL parameters const initialCheckinDate = urlParams.get('checkin_date') || ''; const initialCheckoutDate = urlParams.get('checkout_date') || ''; const initialAdults = parseInt(urlParams.get('adults')) || CONFIG.DEFAULTS.adults; const initialChildren = parseInt(urlParams.get('children')) || CONFIG.DEFAULTS.children; const initialBabies = parseInt(urlParams.get('babies')) || CONFIG.DEFAULTS.babies; const isLongterm = urlParams.get('is_longterm') === 'true'; console.log('Initial URL parameters:', { initialCheckinDate, initialCheckoutDate, initialAdults, initialChildren, initialBabies, urlParams: urlParams.toString() }); // Initialize state variables let currentPricing = null; let checkinDate = initialCheckinDate; let checkoutDate = initialCheckoutDate; let adults = initialAdults; let children = initialChildren; let babies = initialBabies; let apartmentData = null; // DOM elements using configuration const loader = document.querySelector(CONFIG.SELECTORS.loader); const nonDisponible = document.querySelector(CONFIG.SELECTORS.nonDisponible); const infoBookingWrapper = document.querySelector(CONFIG.SELECTORS.infoBookingWrapper); const bathroomsElement = document.querySelectorAll('.appart-info_text-cms')[3]; const priceElement = document.querySelector(CONFIG.SELECTORS.priceElement); // Helper functions function showLoader() { console.log('Attempting to show loader...'); if (loader) { loader.style.display = 'flex'; loader.style.visibility = 'visible'; loader.style.opacity = '1'; console.log('Loader shown successfully'); } else { console.warn('Loader element not found - creating fallback'); createFallbackLoader(); } // Hide the data-wrapper while loading const dataWrapper = document.querySelector(CONFIG.SELECTORS.dataWrapper); if (dataWrapper) { dataWrapper.style.display = 'none'; console.log('Hiding data-wrapper while loading'); } } function hideLoader() { console.log('Hiding loader...'); if (loader) { loader.style.display = 'none'; loader.style.visibility = 'hidden'; loader.style.opacity = '0'; } // Hide fallback loader if it exists const fallbackLoader = document.querySelector('.fallback-loader'); if (fallbackLoader) { fallbackLoader.remove(); } // Show the data-wrapper after loading is complete const dataWrapper = document.querySelector(CONFIG.SELECTORS.dataWrapper); if (dataWrapper) { dataWrapper.style.display = 'block'; console.log('Showing data-wrapper after loading'); } } function createFallbackLoader() { // Remove existing fallback if any const existingFallback = document.querySelector('.fallback-loader'); if (existingFallback) { existingFallback.remove(); } // Create a simple fallback loader const fallbackLoader = document.createElement('div'); fallbackLoader.className = 'fallback-loader'; fallbackLoader.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.8); display: flex; justify-content: center; align-items: center; z-index: 9999; font-size: 18px; color: #333; `; fallbackLoader.innerHTML = '
Chargement des données...
'; document.body.appendChild(fallbackLoader); console.log('Fallback loader created'); } function formatPrice(price, currency = 'EUR') { if (typeof price !== 'number') { price = parseFloat(price) || 0; } return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: currency, minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(price); } function calculateNights(startDate, endDate) { if (!startDate || !endDate) return 0; const diffTime = Math.abs(new Date(endDate) - new Date(startDate)); return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); } // Utility function for consistent date formatting function formatDateForDisplay(dateStr) { if (!dateStr) return ''; return new Date(dateStr).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' }); } // Utility function for API date formatting function formatDateForAPI(date) { return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0'); } // Track if availability has been explicitly checked let availabilityChecked = false; let isCurrentlyAvailable = false; // Helper function to check if booking requirements are met function areBookingRequirementsMet() { const hasValidDates = checkinDate && checkoutDate && checkinDate !== checkoutDate; const hasValidGuests = (parseInt(adults) + parseInt(children) + parseInt(babies)) > 0; // Now also require that availability has been checked and confirmed return hasValidDates && hasValidGuests && availabilityChecked && isCurrentlyAvailable; } // Helper function to reset availability when dates/guests change function resetAvailability() { availabilityChecked = false; isCurrentlyAvailable = false; console.log('Availability reset - user needs to check availability again'); } // Helper function to show/hide booking wrapper based on requirements function updateBookingWrapperVisibility() { if (!infoBookingWrapper) return; const requirementsMet = areBookingRequirementsMet(); console.log('Booking requirements met:', requirementsMet, { checkinDate, checkoutDate, adults, children, babies, availabilityChecked, isCurrentlyAvailable }); if (requirementsMet) { infoBookingWrapper.style.display = 'flex'; } else { infoBookingWrapper.style.display = 'none'; } } // Helper function to show unavailability message function showUnavailabilityMessage(message) { if (nonDisponible) { nonDisponible.style.display = 'flex'; const messageElement = nonDisponible.querySelector('p'); if (messageElement) { messageElement.textContent = message; } } if (infoBookingWrapper) infoBookingWrapper.style.display = 'none'; } function updateDateAndGuestInfo() { const dateBookingElement = document.getElementById('date-booking'); const guestBookingElement = document.getElementById('guest-booking'); if (dateBookingElement && checkinDate && checkoutDate) { const formattedCheckin = formatDateForDisplay(checkinDate); const formattedCheckout = formatDateForDisplay(checkoutDate); dateBookingElement.textContent = `${formattedCheckin} - ${formattedCheckout}`; console.log('Updated date booking:', dateBookingElement.textContent); // Also update the dateRange input display if it exists const dateRangeInput = document.getElementById('dateRange'); if (dateRangeInput) { // Set a formatted value for display purposes dateRangeInput.value = `${formattedCheckin} - ${formattedCheckout}`; console.log('Updated dateRange input display:', dateRangeInput.value); } } // Update guest count display const totalGuests = parseInt(adults) + parseInt(children) + parseInt(babies); const guestText = totalGuests === 1 ? '1 voyageur' : `${totalGuests} voyageurs`; if (guestBookingElement) { guestBookingElement.textContent = guestText; console.log('Updated guest booking:', guestText); } // Also update any other elements that might display guest count const numberPeopleElement = document.getElementById('number-people'); if (numberPeopleElement) { numberPeopleElement.textContent = `${totalGuests} invités`; console.log('Updated number-people:', `${totalGuests} invités`); } // Update number of nights in the pricing section if (checkinDate && checkoutDate) { const numberOfNights = calculateNights(checkinDate, checkoutDate); const numberOfNightsElement = document.querySelector('.number_of_night'); if (numberOfNightsElement) { numberOfNightsElement.textContent = numberOfNights; console.log('Updated number of nights:', numberOfNights); } // If we have apartment data, update the pricing information if (apartmentData) { updatePricingInformation(apartmentData); } } } function updatePriceDisplay(basePrice, totalNights) { if (!basePrice || !totalNights) return; const totalAmount = basePrice * totalNights; const cleaningFee = apartmentData?.prices?.cleaningFee || 0; const taxRate = 0.10; // Assuming 10% tax rate const taxAmount = totalAmount * taxRate; document.querySelectorAll('.price_per_night').forEach(element => { element.textContent = formatPrice(basePrice); }); document.querySelectorAll('.total_price').forEach(element => { element.textContent = formatPrice(totalAmount + cleaningFee + taxAmount); }); document.querySelectorAll('.number_of_night').forEach(element => { element.textContent = totalNights; }); document.querySelectorAll('.clean_price').forEach(element => { element.textContent = formatPrice(cleaningFee); }); document.querySelectorAll('.taxes_price').forEach(element => { element.textContent = formatPrice(taxAmount); }); // Update any other pricing elements const priceElement = document.querySelector('.price-cms-txt'); if (priceElement) { priceElement.textContent = formatPrice(basePrice); } } // Function to update apartment details in the UI function updateApartmentDetails(data) { if (!data) { console.error('No apartment data available'); return; } console.log('Updating apartment details with data:', data); // Update price if (priceElement) { const basePrice = data.prices?.basePrice || 0; priceElement.textContent = formatPrice(basePrice); console.log('Updated price:', priceElement.textContent); } if (bathroomsElement) { bathroomsElement.textContent = data.bathrooms || '1'; console.log('Updated bathrooms:', bathroomsElement.textContent); } // Update form elements with pricing information updatePricingInformation(data); } // Function to update pricing information in the form function updatePricingInformation(data) { // Get form elements const pricePerNightElement = document.querySelector('.price_per_night'); const numberOfNightsElement = document.querySelector('.number_of_night'); const totalPriceElements = document.querySelectorAll('.total_price'); const totalPriceNightElement = document.querySelector('.total_price-night'); const cleanPriceElement = document.querySelector('.clean_price'); const taxesPriceElement = document.querySelector('.taxes_price'); // Get pricing data const basePrice = data.prices?.basePrice || 0; const cleaningFee = data.prices?.cleaningFee || 0; // Calculate number of nights let numberOfNights = 0; if (checkinDate && checkoutDate) { const checkin = new Date(checkinDate); const checkout = new Date(checkoutDate); numberOfNights = Math.ceil((checkout - checkin) / (1000 * 60 * 60 * 24)); } // If no nights are selected, default to 1 night for display purposes numberOfNights = numberOfNights || 1; // Calculate taxes based on accountTaxes data let taxesAmount = 0; // Calculate total guests (excluding babies as per standard practice for city tax) const totalGuests = parseInt(adults) + parseInt(children); // Apply fixed city tax rate from CONFIG per guest per night taxesAmount = CONFIG.TAX_RATE * totalGuests * numberOfNights; console.log(`Calculated city tax: ${CONFIG.TAX_RATE}€ * ${totalGuests} guests * ${numberOfNights} nights = ${taxesAmount}€`); // Note: We're using the fixed rate instead of API data as requested // If API data is needed in the future, uncomment the code below: /* if (data.accountTaxes && data.accountTaxes.length > 0) { // Process each tax data.accountTaxes.forEach(tax => { if (tax.type === 'CITY_TAX' && tax.quantifier === 'PER_GUEST_PER_NIGHT') { // Calculate tax based on amount * guests * nights const taxAmount = tax.amount * totalGuests * numberOfNights; taxesAmount += taxAmount; console.log(`Calculated ${tax.name}: ${tax.amount} * ${totalGuests} guests * ${numberOfNights} nights = ${taxAmount}`); } else { console.log(`Skipping tax ${tax.name} with type ${tax.type} and quantifier ${tax.quantifier}`); } }); } else { console.log('No accountTaxes data available'); } */ // Calculate total price const subtotal = basePrice * numberOfNights; const total = subtotal + cleaningFee + taxesAmount; // Update UI elements if (pricePerNightElement) { pricePerNightElement.textContent = formatPrice(basePrice); console.log('Updated price per night:', pricePerNightElement.textContent); } if (numberOfNightsElement) { numberOfNightsElement.textContent = numberOfNights; console.log('Updated number of nights:', numberOfNightsElement.textContent); } // Update the total price for just the nights (basePrice * numberOfNights) if (totalPriceNightElement) { totalPriceNightElement.textContent = formatPrice(subtotal); console.log('Updated total price for nights:', totalPriceNightElement.textContent); } if (cleanPriceElement) { cleanPriceElement.textContent = formatPrice(cleaningFee); console.log('Updated cleaning fee:', cleanPriceElement.textContent); } if (taxesPriceElement) { taxesPriceElement.textContent = formatPrice(taxesAmount); console.log('Updated taxes:', taxesPriceElement.textContent); } // Update all total price elements totalPriceElements.forEach(element => { element.textContent = formatPrice(total); }); console.log('Updated total price:', formatPrice(total)); // Update cancellation policy information updateCancellationPolicy(data); } // Function to update cancellation policy information function updateCancellationPolicy(data) { const infoNonRemboursableElement = document.getElementById('info-non-remboursable'); const infoRemboursableElement = document.getElementById('info-remboursable'); // Calculate dates for cancellation policy const today = new Date(); // Get check-in date const checkin = checkinDate ? new Date(checkinDate) : null; let freeCancellationDate = new Date(today); freeCancellationDate.setDate(today.getDate() + 14); // Free cancellation within 14 days // If check-in date exists and is valid if (checkin && checkin > today) { // If check-in is less than 14 days away, set free cancellation to day before check-in const dayBeforeCheckin = new Date(checkin); dayBeforeCheckin.setDate(checkin.getDate() - 1); // Use the earlier of the two dates: 14 days from today or day before check-in if (checkin < freeCancellationDate || dayBeforeCheckin < freeCancellationDate) { freeCancellationDate = dayBeforeCheckin; } } const partialRefundDate = new Date(today); partialRefundDate.setDate(today.getDate() + 30); // Partial refund within 30 days // Format dates const freeCancellationDateFormatted = freeCancellationDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long' }); const partialRefundDateFormatted = partialRefundDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long' }); // Update cancellation policy text const nonRemboursableText = `Annulation gratuite avant le ${freeCancellationDateFormatted}. Si vous annulez avant le ${partialRefundDateFormatted}, vous aurez droit à un remboursement partiel.`; // const remboursableText = `Annulation gratuite avant le ${freeCancellationDateFormatted}. Si vous annulez avant le ${partialRefundDateFormatted}, vous aurez droit à un remboursement complet.`; const remboursableText = `Annulation gratuite avant le ${freeCancellationDateFormatted}.`; if (infoNonRemboursableElement) { infoNonRemboursableElement.textContent = nonRemboursableText; console.log('Updated non-remboursable info:', nonRemboursableText); } if (infoRemboursableElement) { infoRemboursableElement.textContent = remboursableText; console.log('Updated remboursable info:', remboursableText); } } // Function to check apartment availability async function checkAvailability(checkin, checkout, adultCount, childrenCount, babiesCount) { console.log('🏨 checkAvailability function called with:', { checkin, checkout, adultCount, childrenCount, babiesCount, apartmentDataExists: !!apartmentData, guestyId }); if (!checkin || !checkout) { console.error('❌ Missing dates for availability check'); alert('Veuillez sélectionner les dates de séjour.'); return false; } if (!apartmentData) { console.error('❌ Apartment data not loaded yet'); alert('Chargement des données de l\'appartement en cours...'); return false; } console.log('✅ checkAvailability validation passed, proceeding with API call'); showLoader(); try { console.log(`Checking availability for apartment ${guestyId} from ${checkin} to ${checkout} for ${adultCount} adults, ${childrenCount} children, ${babiesCount} babies`); // First check capacity locally const totalGuests = parseInt(adultCount) + parseInt(childrenCount) + parseInt(babiesCount); console.log(`Total guests: ${totalGuests}, Apartment capacity: ${apartmentData.accommodates}`); if (totalGuests > apartmentData.accommodates) { console.log(`Apartment cannot accommodate ${totalGuests} guests (max: ${apartmentData.accommodates})`); // Show unavailability message due to capacity showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.capacityError.replace('{capacity}', apartmentData.accommodates)); return false; } // Make API call to check actual availability console.log('Making API call to check availability...'); const response = await fetch(`${CONFIG.API_URL}/${guestyId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ checkin_date: checkin, checkout_date: checkout, guestyId: guestyId }) }); if (!response.ok) { throw new Error(`Availability check failed with status ${response.status}`); } const availabilityData = await response.json(); console.log('Availability API response:', availabilityData); // Find the specific apartment in the rentals array let currentApartment = null; if (availabilityData.rentals && Array.isArray(availabilityData.rentals)) { currentApartment = availabilityData.rentals.find(rental => rental.guesty_id === guestyId); console.log('Found apartment in response:', currentApartment); } // Check if the apartment is available based on API response const isAvailable = currentApartment && currentApartment.is_available === true; if (isAvailable) { console.log('Apartment is available for the selected dates'); // Hide unavailability message if (nonDisponible) nonDisponible.style.display = 'none'; // Update pricing information based on the selected dates updatePricingInformation(apartmentData); return true; } else { // Determine why it's not available if (!currentApartment) { console.log('Apartment not found in availability response'); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.notFound); } else if (currentApartment.is_available === false) { console.log('Apartment is not available for the selected dates'); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.notAvailable); } else { console.log('Unexpected availability status:', currentApartment); showUnavailabilityMessage('Statut de disponibilité inattendu.'); } return false; } } catch (error) { console.error('Error checking availability:', error); // Show error message showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.availabilityError); return false; } finally { hideLoader(); } } // Main function to fetch apartment data from Guesty API async function fetchApartmentData(id) { showLoader(); try { // Use the correct endpoint that works - same as availability check const response = await fetch(`${CONFIG.API_URL}/${id}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ checkin_date: checkinDate || new Date().toISOString().split('T')[0], checkout_date: checkoutDate || new Date(Date.now() + 24*60*60*1000).toISOString().split('T')[0], guestyId: id }) }); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } const data = await response.json(); console.log('Apartment API response:', data); console.log('🔍 Looking for apartment with guestyId:', id); // Extract the specific apartment from the rentals array let apartmentInfo = null; if (data.rentals && Array.isArray(data.rentals)) { console.log('📋 Available apartments in response:'); data.rentals.forEach((rental, index) => { console.log(` ${index}: guesty_id="${rental.guesty_id}", apartment_name="${rental.apartment_name}"`); }); apartmentInfo = data.rentals.find(rental => rental.guesty_id === id); console.log('Found apartment in response:', apartmentInfo); if (!apartmentInfo) { console.log('🔍 Apartment not found by guesty_id, trying to match by apartment_name or other fields...'); // Try to find by apartment name or other matching criteria apartmentInfo = data.rentals.find(rental => rental.apartment_name && rental.apartment_name.toLowerCase().includes(id.toLowerCase()) || rental.nickname && rental.nickname.toLowerCase().includes(id.toLowerCase()) ); console.log('Found apartment by name/nickname:', apartmentInfo); } } if (!apartmentInfo) { throw new Error('Apartment not found in API response'); } // Transform the data to match the expected format const transformedData = { guestyId: apartmentInfo.guesty_id, apartment_name: apartmentInfo.apartment_name, accommodates: apartmentInfo.accommodates, bathrooms: apartmentInfo.bathrooms || 1, nickname: apartmentInfo.apartment_name, prices: { basePrice: apartmentInfo.pricing && apartmentInfo.pricing.length > 0 ? apartmentInfo.pricing[0].avg_per_night : 0, cleaningFee: apartmentInfo.pricing && apartmentInfo.pricing.length > 0 ? apartmentInfo.pricing[0].cleaning_fee || 0 : 0 }, is_available: apartmentInfo.is_available, // Add any other fields needed accountTaxes: apartmentInfo.accountTaxes || [] }; console.log('Transformed apartment data:', transformedData); return transformedData; } catch (error) { console.error('Error fetching apartment data:', error); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.fetchError); return null; } finally { hideLoader(); } } // Initialize date picker function initializeDatePicker() { flatpickr("#dateRange", { mode: "range", dateFormat: "j M", // Format to match "22 avr." style minDate: "today", defaultDate: [checkinDate, checkoutDate], locale: 'fr', // Use French locale for month names onChange: function(selectedDates) { if (selectedDates.length !== 2) return; console.log('Date range selected:', selectedDates); // Use utility function for consistent API date formatting const newCheckinDate = formatDateForAPI(selectedDates[0]); const newCheckoutDate = formatDateForAPI(selectedDates[1]); console.log('New check-in date:', newCheckinDate); console.log('New check-out date:', newCheckoutDate); // Update global variables checkinDate = newCheckinDate; checkoutDate = newCheckoutDate; // Update URL parameters const params = new URLSearchParams(window.location.search); params.set('checkin_date', checkinDate); params.set('checkout_date', checkoutDate); const newUrl = `${window.location.pathname}?${params.toString()}`; window.history.replaceState(null, '', newUrl); // Reset availability since dates changed resetAvailability(); // Update UI updateDateAndGuestInfo(); // Update booking wrapper visibility (will be hidden since availability was reset) updateBookingWrapperVisibility(); } }); } // Initialize guest counter function initializeGuestCounter() { // Get the maximum guests from data-max-guests attribute const maxGuestsElement = document.querySelector('[data-max-guests]'); const maxGuestsFromAttribute = maxGuestsElement ? parseInt(maxGuestsElement.getAttribute('data-max-guests')) : null; // Use the data-max-guests value if available, otherwise fall back to apartmentData.accommodates or CONFIG default const maxGuests = maxGuestsFromAttribute || (apartmentData ? apartmentData.accommodates : CONFIG.DEFAULTS.maxGuests); console.log('Maximum guests from data-max-guests attribute:', maxGuestsFromAttribute); console.log('Using maximum guests:', maxGuests); // Log the maximum guests value for debugging console.log('Maximum guests set to:', maxGuests, 'for guest counter limits'); // Get guest counter elements const guestDropdown = document.querySelector('.dropdown-list_appart-guest'); const guestToggle = document.querySelector('.dropdown-toggle_appart-guest'); // Get counter elements const adultNumber = document.getElementById('adultNumber'); const childrenNumber = document.getElementById('childrenNumber'); const babiesNumber = document.getElementById('babiesNumber'); // Set initial values based on URL parameters if (adultNumber) adultNumber.textContent = adults; if (childrenNumber) childrenNumber.textContent = children; if (babiesNumber) babiesNumber.textContent = babies; // Get plus/minus buttons const adultPlusBtn = document.getElementById('adultPlusBtn'); const adultMinusBtn = document.getElementById('adultMinusBtn'); const childrenPlusBtn = document.getElementById('childrenPlusBtn'); const childrenMinusBtn = document.getElementById('childrenMinusBtn'); const babiesPlusBtn = document.getElementById('babiesPlusBtn'); const babiesMinusBtn = document.getElementById('babiesMinusBtn'); // Toggle dropdown on click if (guestToggle) { guestToggle.addEventListener('click', () => { if (guestDropdown) { guestDropdown.classList.toggle('w--open'); } }); } // Close dropdown when clicking outside document.addEventListener('click', (event) => { if (guestDropdown && guestToggle && !guestToggle.contains(event.target) && !guestDropdown.contains(event.target)) { guestDropdown.classList.remove('w--open'); } }); // Add event listeners for plus/minus buttons if (adultPlusBtn) { adultPlusBtn.addEventListener('click', () => { if (adultNumber) { const currentValue = parseInt(adultNumber.textContent); const totalGuests = currentValue + parseInt(children) + parseInt(babies); if (totalGuests < maxGuests) { adults = currentValue + 1; adultNumber.textContent = adults; updateGuestCount(); } } }); } if (adultMinusBtn) { adultMinusBtn.addEventListener('click', () => { if (adultNumber) { const currentValue = parseInt(adultNumber.textContent); if (currentValue > 1) { adults = currentValue - 1; adultNumber.textContent = adults; updateGuestCount(); } } }); } if (childrenPlusBtn) { childrenPlusBtn.addEventListener('click', () => { if (childrenNumber) { const currentValue = parseInt(childrenNumber.textContent); const adultValue = parseInt(adultNumber.textContent); const babiesValue = parseInt(babiesNumber.textContent); const totalGuests = adultValue + currentValue + babiesValue; if (totalGuests < maxGuests) { children = currentValue + 1; childrenNumber.textContent = children; updateGuestCount(); } } }); } if (childrenMinusBtn) { childrenMinusBtn.addEventListener('click', () => { if (childrenNumber) { const currentValue = parseInt(childrenNumber.textContent); if (currentValue > 0) { children = currentValue - 1; childrenNumber.textContent = children; updateGuestCount(); } } }); } if (babiesPlusBtn) { babiesPlusBtn.addEventListener('click', () => { if (babiesNumber) { const currentValue = parseInt(babiesNumber.textContent); const adultValue = parseInt(adultNumber.textContent); const childrenValue = parseInt(childrenNumber.textContent); const totalGuests = adultValue + childrenValue + currentValue; if (totalGuests < maxGuests) { babies = currentValue + 1; babiesNumber.textContent = babies; updateGuestCount(); } } }); } if (babiesMinusBtn) { babiesMinusBtn.addEventListener('click', () => { if (babiesNumber) { const currentValue = parseInt(babiesNumber.textContent); if (currentValue > 0) { babies = currentValue - 1; babiesNumber.textContent = babies; updateGuestCount(); } } }); } // Initial update without resetting availability (will be handled later in initialization) updateDateAndGuestInfo(); // Update URL parameters without resetting availability const url = new URL(window.location.href); url.searchParams.set('adults', adults); url.searchParams.set('children', children); url.searchParams.set('babies', babies); window.history.replaceState({}, '', url.toString()); console.log(`Initial guest count setup: ${adults} adults, ${children} children, ${babies} babies`); } // Update guest count and check availability function updateGuestCount() { // Reset availability since guest count changed resetAvailability(); // Update the guest count in the UI updateDateAndGuestInfo(); // Update booking wrapper visibility (will be hidden since availability was reset) updateBookingWrapperVisibility(); // Update URL parameters const url = new URL(window.location.href); url.searchParams.set('adults', adults); url.searchParams.set('children', children); url.searchParams.set('babies', babies); // Update browser history without reloading the page window.history.replaceState({}, '', url.toString()); console.log(`Updated guest count: ${adults} adults, ${children} children, ${babies} babies`); } // Initialize submit button function initializeSubmitButton() { const submitBtn = document.getElementById('submit'); console.log('🔍 Initializing submit button, found element:', !!submitBtn); if (submitBtn) { console.log('✅ Submit button found, adding event listener'); console.log('Submit button element:', submitBtn); console.log('Submit button ID:', submitBtn.id); console.log('Submit button classes:', submitBtn.className); submitBtn.addEventListener('click', function(event) { console.log('🚀 SUBMIT BUTTON CLICKED - Starting availability check process'); event.preventDefault(); // Debug current values console.log('📊 Current state when submit clicked:', { checkinDate, checkoutDate, adults, children, babies, availabilityChecked, isCurrentlyAvailable, apartmentDataLoaded: !!apartmentData, guestyId }); // Check basic requirements without availability check first const hasValidDates = checkinDate && checkoutDate && checkinDate !== checkoutDate; const hasValidGuests = (parseInt(adults) + parseInt(children) + parseInt(babies)) > 0; console.log('🔍 Basic requirements check:', { hasValidDates, hasValidGuests, checkinDate, checkoutDate, totalGuests: parseInt(adults) + parseInt(children) + parseInt(babies), datesNotEqual: checkinDate !== checkoutDate }); if (!hasValidDates) { console.log('❌ Invalid dates - blocking submission'); alert(CONFIG.ERROR_MESSAGES.selectDates); return; } if (!hasValidGuests) { console.log('❌ Invalid guest count - blocking submission'); alert(CONFIG.ERROR_MESSAGES.selectDates); return; } console.log('✅ Basic validation passed, proceeding to apartment data check'); // Ensure apartment data is loaded before checking availability if (!apartmentData) { console.log('⚠️ Apartment data not loaded, fetching now...'); console.log('🔄 Starting apartment data fetch for guestyId:', guestyId); // Try to fetch apartment data first fetchApartmentData(guestyId).then(data => { console.log('📦 Apartment data fetch completed, result:', !!data); if (data) { console.log('✅ Apartment data received, updating global variable and UI'); apartmentData = data; updateApartmentDetails(apartmentData); console.log('🔍 Now checking availability with fetched data'); // Now check availability return checkAvailability(checkinDate, checkoutDate, adults, children, babies); } else { console.log('❌ Failed to fetch apartment data'); alert('Impossible de charger les données de l\'appartement.'); return false; } }).then(isAvailable => { console.log('🎯 Availability check result:', isAvailable); if (isAvailable) { console.log('✅ Apartment available - showing booking wrapper'); availabilityChecked = true; isCurrentlyAvailable = true; updateBookingWrapperVisibility(); } else { console.log('❌ Apartment not available - keeping booking wrapper hidden'); availabilityChecked = true; isCurrentlyAvailable = false; updateBookingWrapperVisibility(); } }).catch(error => { console.error('💥 Error during apartment data fetch or availability check:', error); alert('Une erreur est survenue lors de la vérification.'); }); console.log('🔄 Apartment data fetch initiated, returning early'); return; } console.log('✅ Apartment data already loaded, proceeding directly to availability check'); // Check availability and update booking wrapper visibility accordingly console.log('🔍 Starting direct availability check (apartment data already loaded)'); checkAvailability(checkinDate, checkoutDate, adults, children, babies).then(isAvailable => { console.log('🎯 Direct availability check result:', isAvailable); if (isAvailable) { console.log('✅ Apartment available - showing booking wrapper'); availabilityChecked = true; isCurrentlyAvailable = true; updateBookingWrapperVisibility(); } else { console.log('❌ Apartment not available - keeping booking wrapper hidden'); availabilityChecked = true; isCurrentlyAvailable = false; updateBookingWrapperVisibility(); } }).catch(error => { console.error('💥 Error in direct availability check:', error); }); }); } else { console.error('❌ Submit button not found! Element with ID "submit" does not exist.'); console.log('🔍 Available buttons on page:'); const buttons = document.querySelectorAll('button, input[type="submit"], input[type="button"]'); buttons.forEach((btn, index) => { console.log(` ${index}: id="${btn.id}", class="${btn.className}", text="${btn.textContent?.trim()}", type="${btn.type}"`); }); // Also check for elements with common search-related IDs or classes const searchElements = document.querySelectorAll('[id*="search"], [class*="search"], [id*="submit"], [class*="submit"], [data-action*="search"]'); console.log('🔍 Elements with search/submit related attributes:'); searchElements.forEach((elem, index) => { console.log(` ${index}: tag="${elem.tagName}", id="${elem.id}", class="${elem.className}", text="${elem.textContent?.trim()}"`); }); } } // Initialize booking functionality function initializeBookingFunctionality() { const nonRemboursableBtn = document.getElementById('non-remboursable'); const remboursableBtn = document.getElementById('remboursable'); if (remboursableBtn) { remboursableBtn.classList.add('active'); } // if (nonRemboursableBtn) { // nonRemboursableBtn.classList.add('active'); // nonRemboursableBtn.addEventListener('click', function() { // if (remboursableBtn) remboursableBtn.classList.remove('active'); // this.classList.add('active'); // // Update pricing if needed // }); // } if (remboursableBtn) { remboursableBtn.addEventListener('click', function() { if (nonRemboursableBtn) nonRemboursableBtn.classList.remove('active'); this.classList.add('active'); // Update pricing if needed }); } // Payment button handler const paymentBtn = document.getElementById('btn-payment'); console.log('Payment button found:', !!paymentBtn, paymentBtn); if (paymentBtn) { console.log('Adding click listener to payment button'); paymentBtn.addEventListener('click', function(event) { console.log('Payment button clicked!'); event.preventDefault(); // Prevent any default behavior // Check if dates are selected console.log('Checking dates:', {checkinDate, checkoutDate}); if (!checkinDate || !checkoutDate) { alert(CONFIG.ERROR_MESSAGES.selectStayDates); console.log('Payment blocked: No dates selected'); return; } // Check if we have apartment data console.log('Checking apartment data:', !!apartmentData); if (!apartmentData) { alert(CONFIG.ERROR_MESSAGES.missingData); console.log('Payment blocked: No apartment data'); return; } console.log('All validation passed, creating checkout URL...'); // Create URL for checkout page with all necessary parameters const checkoutUrl = new URL('/checkout', window.location.origin); // Add apartment details checkoutUrl.searchParams.set('guesty_id', apartmentData.guestyId); // Get apartment name from the DOM element const apartmentNameElement = document.querySelector(CONFIG.SELECTORS.apartmentName); const apartmentName = apartmentNameElement ? apartmentNameElement.textContent.trim() : apartmentData.nickname || ''; checkoutUrl.searchParams.set('apartment_name', apartmentName); console.log('Setting apartment name:', apartmentName); checkoutUrl.searchParams.set('accommodates', apartmentData.accommodates); // Add dates checkoutUrl.searchParams.set('checkin_date', checkinDate); checkoutUrl.searchParams.set('checkout_date', checkoutDate); // Add guest information checkoutUrl.searchParams.set('adults', adults); checkoutUrl.searchParams.set('children', children); checkoutUrl.searchParams.set('babies', babies); // Add pricing information const pricePerNightElement = document.querySelector('.price_per_night'); const totalPriceElement = document.querySelector('.total_price'); const cleaningFeeElement = document.querySelector('.clean_price'); const taxesPriceElement = document.querySelector('.taxes_price'); console.log('Price elements found:', { pricePerNight: !!pricePerNightElement, totalPrice: !!totalPriceElement, cleaningFee: !!cleaningFeeElement, taxes: !!taxesPriceElement }); if (pricePerNightElement) { const pricePerNight = pricePerNightElement.dataset.value || pricePerNightElement.textContent.replace(/[^\d]/g, '') || '0'; checkoutUrl.searchParams.set('price_per_night', pricePerNight); console.log('Setting price per night:', pricePerNight); } if (totalPriceElement) { const totalPrice = totalPriceElement.dataset.value || totalPriceElement.textContent.replace(/[^\d]/g, '') || '0'; checkoutUrl.searchParams.set('total_price', totalPrice); console.log('Setting total price:', totalPrice); } if (cleaningFeeElement) { const cleaningFee = cleaningFeeElement.dataset.value || cleaningFeeElement.textContent.replace(/[^\d]/g, '') || '0'; checkoutUrl.searchParams.set('cleaning_fee', cleaningFee); console.log('Setting cleaning fee:', cleaningFee); } else { console.warn('Cleaning fee element not found with selector .clean_price'); } if (taxesPriceElement) { const taxes = taxesPriceElement.dataset.value || taxesPriceElement.textContent.replace(/[^\d]/g, '') || '0'; checkoutUrl.searchParams.set('taxes', taxes); console.log('Setting taxes:', taxes); } // Add number of nights const numberOfNights = calculateNights(checkinDate, checkoutDate); checkoutUrl.searchParams.set('nights', numberOfNights); // Add cancellation policy type (remboursable or non-remboursable) const remboursableElement = document.getElementById('remboursable'); const isRemboursable = remboursableElement ? remboursableElement.classList.contains('active') : true; // default to true checkoutUrl.searchParams.set('refundable', isRemboursable ? 'true' : 'false'); console.log('Refundable policy:', isRemboursable); // Redirect to checkout page console.log('Redirecting to checkout:', checkoutUrl.toString()); window.location.href = checkoutUrl.toString(); }); } // Payment button handler // const paymentBtn = document.getElementById('btn-payment'); // if (paymentBtn) { // paymentBtn.addEventListener('click', function() { // alert('Booking functionality will be implemented in the next phase'); // // Implement booking functionality here // }); // } } // Main initialization async function initialize() { console.log('Initializing apartment template with Guesty ID:', guestyId); // Ensure guest count variables are numbers using CONFIG defaults adults = parseInt(adults) || CONFIG.DEFAULTS.adults; children = parseInt(children) || CONFIG.DEFAULTS.children; babies = parseInt(babies) || CONFIG.DEFAULTS.babies; console.log(`Initialized values:`, { adults, children, babies, checkinDate, checkoutDate, guestyId }); if (!guestyId) { console.error('No Guesty ID found in URL'); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.noGuestyId); return; } showLoader(); // Initialize components that don't require apartment data first initializeDatePicker(); console.log('🔧 Initializing UI components (independent of apartment data)...'); initializeGuestCounter(); // This now handles updateDateAndGuestInfo() console.log('🔧 Initializing submit button...'); initializeSubmitButton(); console.log('🔧 Initializing booking functionality...'); initializeBookingFunctionality(); // Fetch apartment data try { apartmentData = await fetchApartmentData(guestyId); if (apartmentData) { console.log('Successfully fetched apartment data:', apartmentData); // Update UI with apartment details updateApartmentDetails(apartmentData); // Check if we have valid dates and guests from URL parameters const hasValidDatesFromURL = checkinDate && checkoutDate && checkinDate !== checkoutDate; const hasValidGuestsFromURL = (parseInt(adults) + parseInt(children) + parseInt(babies)) > 0; if (hasValidDatesFromURL && hasValidGuestsFromURL) { console.log('Valid dates and guests found in URL - automatically checking availability'); // Automatically check availability for users coming from listing page const isAvailable = await checkAvailability(checkinDate, checkoutDate, adults, children, babies); if (isAvailable) { console.log('Automatic availability check successful - setting flags'); availabilityChecked = true; isCurrentlyAvailable = true; updateBookingWrapperVisibility(); console.log('Booking wrapper visibility updated after automatic check'); } else { console.log('Automatic availability check failed - apartment not available'); } } else { console.log('No valid dates/guests in URL - user must check availability manually'); // Reset availability on initialization - user must check availability first resetAvailability(); // Update booking wrapper visibility (will be hidden since availability needs to be checked) updateBookingWrapperVisibility(); } } else { console.error('Failed to fetch apartment data - but UI components are still functional'); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.fetchError); } } catch (error) { console.error('Error during apartment data fetch:', error); showUnavailabilityMessage(CONFIG.ERROR_MESSAGES.fetchError); console.log('UI components should still be functional even without apartment data'); } finally { hideLoader(); } } // Start initialization initialize(); });