// Initialize Google Maps services let map, placesService, distanceMatrixService; let pickupAutocomplete, destinationAutocomplete; let currentFormStep = 1; let bookingHistory = []; // Uber NYC rates (in USD) const uberRates = { uberX: { base: 2.50, perMile: 1.75, perMinute: 0.35, name: “UberX” }, uberXL: { base: 5.00, perMile: 2.85, perMinute: 0.50, name: “UberXL” }, black: { base: 8.00, perMile: 4.10, perMinute: 0.65, name: “Uber Black” } }; // Peak hours (7-9am and 4-7pm) const isPeakHour = (dateTime) => { const hour = new Date(dateTime).getHours(); return (hour >= 7 && hour < 9) || (hour >= 16 && hour < 19); }; // Initialize form when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Generate CSRF token document.getElementById('csrfToken').value = generateCsrfToken(); // Load booking history from session loadBookingHistory(); // Initialize form validation initFormValidation(); // Set up event listeners setupEventListeners(); // Set minimum datetime for pickup (current time + 30 minutes) const now = new Date(); now.setMinutes(now.getMinutes() + 30); document.getElementById('pickupTime').min = now.toISOString().slice(0, 16); }); // Initialize Google Maps Autocomplete function initAutocomplete() { distanceMatrixService = new google.maps.DistanceMatrixService(); placesService = new google.maps.places.PlacesService(document.createElement('div')); // Initialize autocomplete for pickup and destination pickupAutocomplete = new google.maps.places.Autocomplete( document.getElementById('pickup'), { types: ['geocode'] } ); destinationAutocomplete = new google.maps.places.Autocomplete( document.getElementById('destination'), { types: ['geocode'] } ); // Add listeners for place changes pickupAutocomplete.addListener('place_changed', calculatePrice); destinationAutocomplete.addListener('place_changed', calculatePrice); } // Generate CSRF token function generateCsrfToken() { return 'csrf-' + Math.random().toString(36).substr(2, 16) + '-' + Date.now().toString(36); } // Initialize form validation function initFormValidation() { const validateName = (value) => value.length >= 2; const validatePhone = (value) => /^[\d\s\-()+]{7,}$/.test(value); const validateEmail = (value) => value === ” || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); const validateRequired = (value) => value.trim() !== ”; // Add input event listeners for validation document.getElementById(‘name’).addEventListener(‘input’, function() { validateField(this, validateName, ‘Please enter a valid name’); }); document.getElementById(‘phone’).addEventListener(‘input’, function() { validateField(this, validatePhone, ‘Please enter a valid phone number’); }); document.getElementById(’email’).addEventListener(‘input’, function() { validateField(this, validateEmail, ‘Please enter a valid email’); }); document.getElementById(‘pickup’).addEventListener(‘input’, function() { validateField(this, validateRequired, ‘Pickup location is required’); }); document.getElementById(‘destination’).addEventListener(‘input’, function() { validateField(this, validateRequired, ‘Destination is required’); }); document.getElementById(‘pickupTime’).addEventListener(‘change’, function() { validateField(this, validateRequired, ‘Pickup time is required’); }); } // Validate a form field function validateField(field, validator, errorMessage) { const isValid = validator(field.value); const validationMsg = field.parentElement.querySelector(‘.validation-message’); if (isValid) { field.classList.remove(‘error’); field.classList.add(‘valid’); validationMsg.textContent = ”; } else { field.classList.remove(‘valid’); field.classList.add(‘error’); validationMsg.textContent = errorMessage; } return isValid; } // Set up event listeners function setupEventListeners() { // Navigation buttons document.querySelectorAll(‘.next-btn’).forEach(btn => { btn.addEventListener(‘click’, goToNextStep); }); document.querySelectorAll(‘.back-btn’).forEach(btn => { btn.addEventListener(‘click’, goToPrevStep); }); // Return trip toggle document.getElementById(‘returnTrip’).addEventListener(‘change’, function() { const returnFields = document.getElementById(‘returnFields’); returnFields.style.display = this.checked ? ‘block’ : ‘none’; calculatePrice(); }); // Vehicle type change document.getElementById(‘vehicleType’).addEventListener(‘change’, calculatePrice); // Tip buttons document.querySelectorAll(‘.tip-btn’).forEach(btn => { btn.addEventListener(‘click’, function() { document.querySelectorAll(‘.tip-btn’).forEach(b => b.classList.remove(‘active’)); this.classList.add(‘active’); document.getElementById(‘selectedTip’).value = this.dataset.tip; calculatePrice(); }); }); // Custom tip input document.getElementById(‘customTip’).addEventListener(‘input’, function() { document.querySelectorAll(‘.tip-btn’).forEach(b => b.classList.remove(‘active’)); document.getElementById(‘selectedTip’).value = this.value; calculatePrice(); }); // Current location button document.getElementById(‘currentLocation’).addEventListener(‘click’, getCurrentLocation); // Theme toggle document.getElementById(‘themeToggle’).addEventListener(‘click’, toggleDarkMode); // New booking button document.getElementById(‘newBooking’).addEventListener(‘click’, resetForm); // Form submission document.getElementById(‘taxiForm’).addEventListener(‘submit’, submitForm); } // Calculate price using Distance Matrix API function calculatePrice() { const pickup = document.getElementById(‘pickup’).value; const destination = document.getElementById(‘destination’).value; const vehicleType = document.getElementById(‘vehicleType’).value; const returnTrip = document.getElementById(‘returnTrip’).checked; const pickupTime = document.getElementById(‘pickupTime’).value; const tipPercentage = parseFloat(document.getElementById(‘selectedTip’).value) || 0; if (!pickup || !destination || !pickupTime) return; const pickupPlace = pickupAutocomplete.getPlace(); const destinationPlace = destinationAutocomplete.getPlace(); if (!pickupPlace || !destinationPlace || !pickupPlace.geometry || !destinationPlace.geometry) { return; } const rate = uberRates[vehicleType]; const origin = pickupPlace.geometry.location; const destinationLoc = destinationPlace.geometry.location; // Show loading state const priceDisplay = document.getElementById(‘estimatedPrice’); priceDisplay.innerHTML = ‘ Calculating…’; distanceMatrixService.getDistanceMatrix({ origins: [origin], destinations: [destinationLoc], travelMode: ‘DRIVING’, unitSystem: google.maps.UnitSystem.IMPERIAL }, function(response, status) { if (status === ‘OK’) { const element = response.rows[0].elements[0]; if (element.status === ‘OK’) { const distanceMiles = element.distance.value / 1609.34; // meters to miles const durationMinutes = element.duration.value / 60; // seconds to minutes // Check if peak hour const isPeak = isPeakHour(pickupTime); const peakMultiplier = isPeak ? 1.2 : 1; // Calculate fare components const baseFare = rate.base; const distanceFare = distanceMiles * rate.perMile; const timeFare = durationMinutes * rate.perMinute; // Calculate one-way price let price = (baseFare + distanceFare + timeFare) * peakMultiplier; // Double the price for return trip if (returnTrip) { price *= 2; } // Add tip if specified let tipAmount = 0; if (tipPercentage > 0) { tipAmount = price * (tipPercentage / 100); price += tipAmount; } // Update fare breakdown document.getElementById(‘baseFare’).textContent = `$${baseFare.toFixed(2)}`; document.getElementById(‘distanceFare’).textContent = `$${distanceFare.toFixed(2)}`; document.getElementById(‘timeFare’).textContent = `$${timeFare.toFixed(2)}`; // Show/hide peak fee const peakFeeElement = document.querySelector(‘.peak-fee’); if (isPeak) { const peakFee = (baseFare + distanceFare + timeFare) * 0.2; document.getElementById(‘peakFee’).textContent = `$${peakFee.toFixed(2)}`; peakFeeElement.classList.remove(‘hidden’); } else { peakFeeElement.classList.add(‘hidden’); } // Show/hide tip const tipElement = document.querySelector(‘.tip-row’); if (tipAmount > 0) { document.getElementById(‘tipAmount’).textContent = `$${tipAmount.toFixed(2)}`; tipElement.classList.remove(‘hidden’); } else { tipElement.classList.add(‘hidden’); } // Update distance info document.getElementById(‘distanceInfo’).style.display = ‘block’; document.getElementById(‘distanceInfo’).innerHTML = ` ${distanceMiles.toFixed(1)} miles • ${Math.ceil(durationMinutes)} minutes ${isPeak ? ‘• Peak hours apply’ : ”} `; // Display the price document.getElementById(‘estimatedPrice’).textContent = `$${price.toFixed(2)}`; // Save to session storage saveFormProgress(); } } }); } // Get current location function getCurrentLocation() { const pickupField = document.getElementById(‘pickup’); pickupField.disabled = true; pickupField.value = ‘Detecting your location…’; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const geocoder = new google.maps.Geocoder(); const latlng = { lat: position.coords.latitude, lng: position.coords.longitude }; geocoder.geocode({ location: latlng }, (results, status) => { if (status === ‘OK’ && results[0]) { pickupField.value = results[0].formatted_address; pickupField.dispatchEvent(new Event(‘input’)); calculatePrice(); } else { pickupField.value = ”; alert(‘Could not determine your address. Please enter it manually.’); } pickupField.disabled = false; }); }, (error) => { pickupField.value = ”; pickupField.disabled = false; alert(‘Geolocation error: ‘ + error.message); } ); } else { pickupField.value = ”; pickupField.disabled = false; alert(‘Geolocation is not supported by your browser.’); } } // Navigate to next step function goToNextStep(e) { const currentStep = currentFormStep; const nextStep = parseInt(e.target.dataset.next); // Validate current step before proceeding if (currentStep === 1) { const nameValid = validateField(document.getElementById(‘name’), (v) => v.length >= 2, ‘Please enter a valid name’); const phoneValid = validateField(document.getElementById(‘phone’), (v) => /^[\d\s\-()+]{7,}$/.test(v), ‘Please enter a valid phone number’); const emailValid = validateField(document.getElementById(’email’), (v) => v === ” || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v), ‘Please enter a valid email’); if (!nameValid || !phoneValid || !emailValid) return; } else if (currentStep === 2) { const pickupValid = validateField(document.getElementById(‘pickup’), (v) => v.trim() !== ”, ‘Pickup location is required’); const destValid = validateField(document.getElementById(‘destination’), (v) => v.trim() !== ”, ‘Destination is required’); const timeValid = validateField(document.getElementById(‘pickupTime’), (v) => v.trim() !== ”, ‘Pickup time is required’); if (!pickupValid || !destValid || !timeValid) return; // Calculate price when moving to confirmation step calculatePrice(); } // Switch steps document.querySelector(`.form-step[data-step=”${currentStep}”]`).classList.remove(‘active’); document.querySelector(`.form-step[data-step=”${nextStep}”]`).classList.add(‘active’); document.querySelector(`.progress-steps .step[data-step=”${currentStep}”]`).classList.remove(‘active’); document.querySelector(`.progress-steps .step[data-step=”${nextStep}”]`).classList.add(‘active’); currentFormStep = nextStep; saveFormProgress(); } // Navigate to previous step function goToPrevStep(e) { const currentStep = currentFormStep; const prevStep = parseInt(e.target.dataset.prev); document.querySelector(`.form-step[data-step=”${currentStep}”]`).classList.remove(‘active’); document.querySelector(`.form-step[data-step=”${prevStep}”]`).classList.add(‘active’); document.querySelector(`.progress-steps .step[data-step=”${currentStep}”]`).classList.remove(‘active’); document.querySelector(`.progress-steps .step[data-step=”${prevStep}”]`).classList.add(‘active’); currentFormStep = prevStep; } // Toggle dark mode function toggleDarkMode() { document.body.classList.toggle(‘dark-mode’); localStorage.setItem(‘darkMode’, document.body.classList.contains(‘dark-mode’)); } // Save form progress to session storage function saveFormProgress() { const formData = { step: currentFormStep, name: document.getElementById(‘name’).value, phone: document.getElementById(‘phone’).value, email: document.getElementById(’email’).value, pickup: document.getElementById(‘pickup’).value, destination: document.getElementById(‘destination’).value, pickupTime: document.getElementById(‘pickupTime’).value, returnTrip: document.getElementById(‘returnTrip’).checked, returnTime: document.getElementById(‘returnTime’).value, vehicleType: document.getElementById(‘vehicleType’).value, additionalInfo: document.getElementById(‘additionalInfo’).value, tip: document.getElementById(‘selectedTip’).value }; sessionStorage.setItem(‘taxiBookingForm’, JSON.stringify(formData)); } // Load form progress from session storage function loadFormProgress() { const savedData = sessionStorage.getItem(‘taxiBookingForm’); if (savedData)