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();
});