1let currentLang = 'es'; 2 3// --- DATA DEFINITIONS --- 4const mapLocations = [ 5 { name: 'Cultura Manor', coords: [-0.20489229704067102, -78.49774984800666], url: 'https://www.culturamanor.com/' }, 6 { name: 'Hacienda PinsaquÃ', coords: [0.27985415810021125, -78.23947004800858], url: 'https://www.haciendapinsaqui.com/' }, 7 { name: 'Hacienda Abraspungo', coords: [-1.6398183361810093, -78.64071554799848], url: 'https://haciendaabraspungo.com/' }, 8 { name: 'Hacienda La AlegrÃa', coords: [-0.4483615847880226, -78.58199848693125], url: 'https://haciendalaalegria.com/web/' }, 9 { name: 'Maquipucuna Reserve', coords: [0.12445494728705161, -78.62911537684317], url: 'https://www.maquipucuna.org/' }, 10 { name: 'Hamadryade Lodge', coords: [-1.0380095657414963, -77.70886836149693], url: 'https://hamadryade-lodge.com/' }, 11 { name: 'Iguana Crossing', coords: [-0.9555366373685902, -90.97136631873319], url: 'https://iguanacrossing.com.ec/' }, 12 { name: 'Hacienda La Ciénega', coords: [-0.77196802058979, -78.61955173450916], url: 'https://www.haciendalacienega.com/' } 13]; 14 15const tourData = { 16 en: [ 17 { nombre: 'QUITO HALF DAY EXPERIENCE', imagen: 'Tour 1.jpg', action: showQuitoHalfDayModal }, 18 { nombre: 'QUITO FULL DAY EXPERIENCE', imagen: 'Tour 3.jpg', action: showQuitoFullDayModal }, 19 { nombre: 'ANTISANA CONDORS EXPERIENCE', imagen: 'Tour 2.jpg', action: showAntisanaCondorsModal }, 20 { nombre: 'COTOPAXI VOLCANO EXPERIENCE', imagen: 'Tour 4.jpg', action: showCotopaxiVolcanoModal }, 21 { nombre: 'SURROUNDINGS OF QUITO EXPERIENCE', imagen: 'Tour 5.jpg', action: showSurroundingsOfQuitoModal }, 22 { nombre: 'OTAVALO MARKET EXPERIENCE', imagen: 'Tour 6.jpg', action: showOtavaloMarketModal }, 23 { nombre: 'HORSE RIDER EXPERIENCE', imagen: 'Tour 7.jpg', action: showHorseRiderModal }, 24 { nombre: 'SUMMITS OF THE SUN EXPERIENCE', imagen: 'Tour 8.jpg', action: showSummitsOfTheSunModal } 25 ], 26 es: [ 27 { nombre: 'EXPERIENCIA DE MEDIO DÃA EN QUITO', imagen: 'Tour 1.jpg', action: showQuitoHalfDayModal }, 28 { nombre: 'EXPERIENCIA DE DÃA COMPLETO EN QUITO', imagen: 'Tour 3.jpg', action: showQuitoFullDayModal }, 29 { nombre: 'EXPERIENCIA CÃNDORES ANTISANA', imagen: 'Tour 2.jpg', action: showAntisanaCondorsModal }, 30 { nombre: 'EXPERIENCIA VOLCÃN COTOPAXI', imagen: 'Tour 4.jpg', action: showCotopaxiVolcanoModal }, 31 { nombre: 'EXPERIENCIA ALREDEDORES DE QUITO', imagen: 'Tour 5.jpg', action: showSurroundingsOfQuitoModal }, 32 { nombre: 'EXPERIENCIA MERCADO DE OTAVALO', imagen: 'Tour 6.jpg', action: showOtavaloMarketModal }, 33 { nombre: 'EXPERIENCIA CABALGATA', imagen: 'Tour 7.jpg', action: showHorseRiderModal }, 34 { nombre: 'EXPERIENCIA CUMBRES DEL SOL', imagen: 'Tour 8.jpg', action: showSummitsOfTheSunModal } 35 ] 36}; 37 38const emergencyContacts = { 39 en: [ 40 { nombre: 'Police', numero: '911' }, 41 { nombre: 'Fire Department', numero: '102' } 42 ], 43 es: [ 44 { nombre: 'PolicÃa', numero: '911' }, 45 { nombre: 'Bomberos', numero: '102' } 46 ] 47}; 48 49const transportData = { 50 flights: { 51 en: [ 52 { nombre: 'Mariscal Sucre Airport', sitioWeb: 'https://www.quiport.com/contactenos/?lang=en', pbx: '+(593 2) 395-4200 / +(593 2) 395-4300', direccion: 'Quito-Tababela.' }, 53 { nombre: 'Exclusive Air Travel', sitioWeb: 'https://www.instagram.com/exclusiveairtravel?igsh=MTd6MnY0am5zaDV1dw%3D%3D&utm_source=qr' } 54 ], 55 es: [ 56 { nombre: 'Aeropuerto Mariscal Sucre', sitioWeb: 'https://www.quiport.com', pbx: '+(593 2) 395-4200 / +(593 2) 395-4300', direccion: 'Parroquia Tababela, vÃa a YaruquÃ.' }, 57 { nombre: 'Viajes Aéreos Exclusivos en Helicóptero', sitioWeb: 'https://www.instagram.com/rent4wd?igsh=MXRtM2lseDRxMDUxNw%3D%3D&utm_source=qr' }, 58 { nombre: 'Exclusive Air Travel', sitioWeb: 'https://www.instagram.com/exclusiveairtravel?igsh=MTd6MnY0am5zaDV1dw%3D%3D&utm_source=qr' } 59 ] 60 }, 61 carRental: { 62 en: [ 63 { nombre: 'Rent 4WD Safe hand at the Wheel.<br>CAR RENTAL SERVICE', sitioWeb: 'https://www.rent-4wd.com', backgroundImage: 'Rent 4WD.png' } 64 ], 65 es: [ 66 { nombre: 'AVIS Ret a Car', sitioWeb: 'https://www.avis.com/es/locations/ec/quito' } 67 ] 68 } 69}; 70 71const sitesData = { 72 'Cultura Manor': { 73 image: '1CMInt.jpg', 74 services: { 75 website: 'https://culturamanor.com/services/', 76 options: [ 77 { name: 'Romantic Getaway', icon: 'fa-heart', action: showRomanticModal }, 78 { name: 'Restaurant', icon: 'fa-utensils', action: showRestaurantModal }, 79 { name: 'Spa & Wellness', icon: 'fa-spa', action: showSpaModal }, 80 { name: 'Wedding Planner', icon: 'fa-ring', action: showWeddingPlannerModal }, 81 { name: 'Concierge Service', icon: 'fa-whatsapp', url: 'https://api.whatsapp.com/se
81nd/?phone=593994959502&text=Hello%2C+I+need+to+contact+reception&type=phone_number&app_absent=0' } 82 ] 83 } 84 }, 85 'Hacienda PinsaquÃ': { 86 image: '2HPInt.jpg', 87 services: { 88 options: [ 89 { name: 'Restaurant', icon: 'fa-utensils', action: showPinsaquiRestaurantModal }, 90 { name: 'Events & Wedding Planner', icon: 'fa-ring', action: showPinsaquiEventsModal }, 91 { name: 'Tours', icon: 'fa-route', action: showPinsaquiToursModal }, 92 { name: 'Activities', icon: 'fa-puzzle-piece', action: showPinsaquiActivitiesModal }, 93 { name: 'Spa', icon: 'fa-spa', action: showPinsaquiSpaModal }, 94 { name: 'Concierge Service', icon: 'fa-whatsapp', url: 'https://api.whatsapp.com/send/?phone=593999484819&text&type=phone_number&app_absent=0' } 95 ] 96 } 97 }, 98 'Hacienda Abraspungo': { 99 image: '3HAInt.jpg', 100 services: { 101 website: 'https://haciendaabraspungo.com/descubre-abraspungo/', 102 options: [ 103 { name: 'Restaurant', icon: 'fa-utensils', action: () => showGenericServiceModal('Restaurant', 'Empieza el dÃa con un variado desayuno, al medÃo dÃa sabores nacionales e internacionales para compartir en familia o amigos. Y al anochecer, una romántica cena junto a esa persona especial.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 104 { name: 'Eventos', icon: 'fa-calendar-alt', action: () => showGenericServiceModal('Eventos', 'Abraspungo es excelencia en eventos. Entendemos que los detalles marcan la diferencia. ¡Haremos ese dÃa inolvidable!', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 105 { name: 'Spa', icon: 'fa-spa', action: () => showGenericServiceModal('Spa', '<b>Masajes de Desbloqueo(60 min):</b> Desbloquea puntos emocionales y son liberados en una Sanación emocional.<br><b>Masaje relajante (60 min):</b> Sus beneficios son calmar dolores de espalda, favorece la circulación sanguÃnea, reduciendo el estrés.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 106 { name: 'Clases de Pintura', icon: 'fa-paint-brush', action: () => showGenericServiceModal('Clases de Pintura', 'Para grandes o pequeños, disfruta del contacto con la pintura y el lienzo cerca de la naturaleza en nuestros jardines. INCLUYE: Materiales.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 107 { name: 'Horseback Riding', icon: 'fa-horse', action: () => showGenericServiceModal('Horseback Riding', 'Paseo a caballo cubillines (4 horas) Aventura, increÃbles paisajes y las mejores fotografÃas en esta increÃble experiencia. VÃvela en familia o en pareja, una experiencia que no olvidarán.INCLUYE: Transporte y snacks.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 108 { name: 'Caballos para niños', icon: 'fa-child', action: () => showGenericServiceModal('Caballos para niños', 'Los niños y los caballos tienen una conexión especial, fomenta este vÃnculo desde pequeños con nuestros dóciles caballos dentro de la hacienda con un guÃa especilizado.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 109 { name: 'Trekking Cubillines', icon: 'fa-hiking', action: () => showGenericServiceModal('Trekking Cubillines', 'Tus sentidos en pleno contacto con la naturaleza, recorre increÃbles rutas llenas de aventura. Te aseguramos paisajes que no olvidarás. INCLUYE: Transporte y Snacks.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 110 { name: 'Romantic Getaway', icon: 'fa-heart', action: () => showGenericServiceModal('Romantic Getaway', 'Por aniversario, cumpleaños o esa ocasión especial; esta noche será inolvidable con una exquisita y romántica cena junto a la chimenea. INCLUYE: Cena de tres tiempos. Copa de Champagne. Copa de vino.', 'https://haciendaabraspungo.com/descubre-abraspungo/', true) }, 111 { name: 'Concierge Service', icon: 'fa-whatsapp', url: 'https://api.whatsapp.com/send?phone=593997683450&text=%20' } 112 ] 113 } 114 }, 115 'Hacienda La AlegrÃa': { 116 image: '5HLAInt.jpg', 117 services: { 118 options: [ 119 { name: 'Horseback Riding', icon: 'fa-horse', action: () => showGenericServiceModal('Horseback Riding', 'We offer day rides for those new to riding, travellers with limited time, or those who just want a brief taste of the fresh mountain air! We also run a program of more adventurous multi-day trips travelling on horseback through the mountains, forests and paramo (high grasslands) of the Andes. Details of some of our rides can be found on the menu on the right. For groups of two or more we are also happy to organize itineraries customized to your requirements.', 'https://haciendalaalegria.com/web/destination/horseback-riding/', true) }, 120 { name: 'Ecotourism', icon: 'fa-leaf', action: () => showGenericServiceModal('Ecotourism', 'Explore the various ecotourism options available at Hacienda La AlegrÃa.<br><br><a href="https://haciendalaalegria.com/web/destination/working-farm/" target="_blank" class="btn btn-primary mb-2 w-100">Working Farm</a><a href="https://haciendalaalegria.com/web/destination/things-to-do/" target="_blank" class="btn btn-primary w-100">Tours & Activities</a>', 'https://haciendalaalegria.com/web/destination/working-farm/', true) } 121 ] 122 } 123 }, 124 'Maquipucuna Reserve': { 125 image: '6MRLInt.jpg', 126 services: { 127 website: 'https://www.maquipucuna.org/activities', 128 options: [ 129 { name: 'EcoTours', icon: 'fa-leaf', action: () => showGenericServiceModal('EcoTours', 'Cloud Forest Discovery Tours, Cloud Forest Birding Tours, Cloud Forest Adventure & Community Tours. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 130 { name: 'ANDEAN SPECTACLED BEARS', icon: 'fa-paw', action: () => showGenericServiceModal('Andean Spectacled Bears', 'A unique opportunity to see the Andean Spectacled Bears. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 131 { name: 'BIRDING PARADISE', icon: 'fa-dove', action: () => showGenericServiceModal('Birding Paradise', 'Explore a paradise for birdwatchers. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 132 { name: 'ORCHIDS & BUTTERFLIES', icon: 'fa-fan', action: () => showGenericServiceModal('Orchids & Butterflies', 'Discover the vibrant world of orchids and butterflies. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 133 { name: 'NIGHT HIKES', icon: 'fa-moon', action: () => showGenericServiceModal('Night Hikes', 'Experience the cloud forest at night. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 134 { name: 'Swimming & Hiking', icon: 'fa-swimmer', action: () => showGenericServiceModal('Swimming & Hiking', 'Enjoy swimming and hiking through beautiful trails. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 135 { name: 'Mindo, Ziplines & Tulipe', icon: 'fa-tree', action: () => showGenericServiceModal('Mindo, Ziplines & Tulipe', 'Adventure awaits with ziplines and cultural visits. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) }, 136 { name: 'WATERFALLS, SWIMMINGHOLES & 32 KM OF TRAILS', icon: 'fa-water', action: () => showGenericServiceModal('Waterfalls & Trails', 'Explore waterfalls, swimming holes, and 32 km of trails. Visit the main activities page for more details.', 'https://www.maquipucuna.org/activities', true) } 137 ] 138 } 139 }, 140 'Hamadryade Lodge': { 141 image: '7HLInt.jpg', 142 services: { 143 website: 'https://hamadryade-lodge.com/actividades/', 144 options: [ 145 { name: 'Actividades Acuáticas', icon: 'fa-water', action: () => showGenericServiceModal('Actividades Acuáticas', 'Rafting class III, Rafting class IV, Kayak, Tubbing, Cascadas de las latas, Cascada de Umbini, Cascadas de Rumi Yacu.', 'https://hamadryade-lodge.com/actividades/', true) }, 146 { name: 'Actividades Culturales', icon: 'fa-landmark', action: () => showGenericServiceModal('Actividades Culturales', 'Pesca tradicional, Visita comunidad Kichwa, Taller chocolate, Taller cacao, Construye tu balsa, Taller de cerámica, Museo Kamak Maki, Visita comunidad Waorani.', 'https://hamadryade-lodge.com/actividades/', true) }, 147 { name: 'Actividades de Naturaleza', icon: 'fa-leaf', action: () => showGenericServiceModal('Actividades de Naturaleza', 'Amazónico rescue center, Avistamiento aves, Visita de la chacra, Laguna Paikawe o K
147awana, Visita Ceibo, Observación de saladero de loros, Tour del meliponario.', 'https://hamadryade-lodge.com/actividades/', true) }, 148 { name: 'Actividades de Aventura', icon: 'fa-hiking', action: () => showGenericServiceModal('Actividades de Aventura', 'Cavernas de Ponceloma, Caminata Machacu Yacu, Caminata Aitaka, Caminata nocturna, Caminata Jatun Sacha, Tour en avioneta, Rent a bike.', 'https://hamadryade-lodge.com/actividades/', true) }, 149 { name: 'Retiros', icon: 'fa-praying-hands', action: () => showGenericServiceModal('Retiros', 'Desde hace más de 13 años, Hamadryade Lodge & Retreat Center acoge retiros transformadores en el corazón de la AmazonÃa ecuatoriana. Nuestro centro, rodeado por 64 hectáreas de bosque protegido, es un santuario de reconexión profunda con la naturaleza, el cuerpo y el espÃritu. En Hamadryade, la naturaleza es maestra y medicina. Cada retiro está cuidadosamente diseñado para que vivas un proceso personal único, en un entorno de privacidad, belleza y contención espiritual.', 'https://hamadryade-lodge.com/retiros/', true) } 150 ] 151 } 152 }, 153 'Iguana Crossing': { 154 image: '9ICG.png', 155 services: { 156 website: 'https://iguanacrossing.com.ec/services/', 157 options: [ 158 { name: 'Restaurant', icon: 'fa-utensils', action: () => showGenericServiceModal('Restaurant', 'Among the chef\'s exquisite creations, featuring gourmet fusion dishes and our standout specialty: the lava stone barbecue. Experience true culinary artistry with delicacies paired perfectly with organic vegetables and vibrant greens from our highland farm in Isabela. Complement your meal with refreshing, freshly squeezed fruit juices that elevate every bite into a remarkable flavor journey.', 'https://iguanacrossing.com.ec/services/', true) }, 159 { name: 'Wedding Planner', icon: 'fa-ring', action: () => showGenericServiceModal('Wedding Planner', 'Celebrate love where nature itself bears witness. Iguana Crossing offers an intimate and breathtaking setting for your special day â from a romantic beachfront ceremony to personalized details that turn your wedding into a once-in-a-lifetime experience.', 'https://iguanacrossing.com.ec/services/', true) }, 160 { name: 'Concierge Service', icon: 'fa-whatsapp', url: 'https://wa.link/1swbam' } 161 ] 162 } 163 }, 164 'Hacienda La Ciénega': { 165 image: '10 HLc.jpg', 166 services: { 167 options: [ 168 { name: 'Rooms', icon: 'fa-bed', action: () => showGenericServiceModal('Rooms', 'Feel the spirit of the colonial era in the 21st century. With pieces from the 17th century, our rooms have a historical touch that recovers the memory of the hacienda, and combines with contemporary nuances to meet the highest standards of hospitality and elegance.', 'https://www.haciendalacienega.com/habitaciones/', true) }, 169 { name: 'Restaurant', icon: 'fa-utensils', action: () => showGenericServiceModal('Restaurant', 'Decorated hallways, the warmth of the fire, and old-fashioned dishes. The atmosphere of our dining room provides perfect settings to taste family recipes next to windows overlooking the garden; or if you prefer something more intimate, the lounges are ideal for afternoon gatherings with friends, surrounded by the golden light of a fireplace.', 'https://www.haciendalacienega.com/wp-content/uploads/2025/08/Carta-La-Cienega-2025.pdf', true) } 170 ] 171 } 172 } 173}; 174 175const cityWalkVideos = [ 176 { name: 'Hotel Cultura Manor - Quito', videoId: 'we8Ol0sqmnQ' }, 177 { name: 'Exclusive Hotels & Haciendas of Ecuador', videoId: 'mLzzcfzcWhg' }, 178 { name: 'Tunas y Cabras - Imbabura', videoId: 'cS_0b5lAFC8' } 179]; 180 181const sponsors = [ 182 { name: 'Cultura Manor', logo: 'LOGO_CM-.png', url: 'https://culturamanor.com/' }, 183 { name: 'EO Arte & Cultura', logo: 'LOgo Art.png', url: 'https://www.instagram.com/eo.artecultura/' } 184]; 185 186const bookingUrls = { 187 'Cultura Manor': 'https://culturamanor.com/book-now/', 188 'Hacienda PinsaquÃ': 'https://app.reservationcentric.com/hosteria-hacienda-pinsaqui/book-now#/choose-dates', 189 'Hacienda Abraspungo': 'https://hotels.cloudbeds.com/es/reservation/ekjvTe?widget=1&checkin=2023-03-20&checkout=2023-03-21¤cy=usd', 190 'Hacienda La AlegrÃa': 'https://haciendalaalegria.com/web/destination/reservation/', 191 'Maquipucuna Reserve': 'https://www.maquipucuna.org/cloudforest-all-inclusive-packages?lang=es', 192 'Hamadryade Lodge': 'https://hotels.cloudbeds.com/reservation/nTs36P', 193 'Iguana Crossing': 'https://iguanacrossing.com.ec/book-now/', 194 'Hacienda La Ciénega': 'https://www.haciendalacienega.com/' 195}; 196 197// --- GLOBAL FUNCTIONS --- 198function toggleLanguage() { 199 currentLang = currentLang === 'en' ? 'es' : 'en'; 200 document.getElementById('lang-text').textContent = currentLang === 'en' ? 'ES' : 'EN'; 201 202 const elements = document.querySelectorAll('[data-en], [data-es]');
203 elements.forEach(el => { 204 if (el.hasAttribute(`data-${currentLang}`)) { 205 el.innerHTML = el.getAttribute(`data-${currentLang}`); 206 } 207 }); 208 209 const inputs = document.querySelectorAll('[data-placeholder-en], [data-placeholder-es]'); 210 inputs.forEach(input => { 211 if (input.hasAttribute(`data-placeholder-${currentLang}`)) { 212 input.placeholder = input.getAttribute(`data-placeholder-${currentLang}`); 213 } 214 }); 215 216 // Reload dynamic content 217 cargarJourneys(); 218 cargarEmergencias(); 219 cargarServiciosTransporte(); 220 cargarServicios(); 221} 222 223function createModal(title, content, extraClasses = '') { 224 const modal = document.createElement('div'); 225 modal.classList.add('modal', 'show'); 226 if (extraClasses) modal.classList.add(extraClasses); 227 modal.style.display = 'block'; 228 modal.innerHTML = ` 229 <div class="modal-dialog modal-lg modal-dialog-scrollable"> 230 <div class="modal-content"> 231 <div class="modal-header"> 232 <h5 class="modal-title">${title}</h5> 233 <button type="button" class="btn-close btn-close-white close-modal"></button> 234 </div> 235 <div class="modal-body">${content}</div> 236 </div> 237 </div> 238 `; 239 const closeModalButton = modal.querySelector('.close-modal'); 240 closeModalButton.addEventListener('click', () => modal.remove()); 241 modal.addEventListener('click', (e) => { 242 if (e.target === modal) modal.remove(); 243 }); 244 return modal; 245} 246 247// Function to send email notification 248function sendEmailNotification(subject, messageData) { 249 const formspreeUrl = 'https://formspree.io/f/mdayobwe'; 250 const emailData = { 251 _subject: subject, 252 ...messageData 253 }; 254 255 console.log('Sending email notification:', subject, emailData); 256 fetch(formspreeUrl, { 257 method: 'POST', 258 headers: { 'Content-Type': 'application/json' }, 259 body: JSON.stringify(emailData) 260 }) 261 .then(res => { 262 console.log('Email response status:', res.status, res.statusText); 263 if (!res.ok) { 264 console.error('Email error - Status:', res.status, 'StatusText:', res.statusText); 265 return res.text().then(text => { 266 console.error('Email error body:', text); 267 throw new Error(`HTTP ${res.status}: ${text}`); 268 }); 269 } 270 return res.json(); 271 }) 272 .then(data => { 273 console.log('Email sent successfully:', data); 274 }) 275 .catch(err => { 276 console.error('Error sending email:', err); 277 alert('Error al enviar email: ' + err.message); 278 }); 279} 280 281// Test function to verify Telegram connection 282function testTelegramConnection() { 283 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 284 const chatId = '1853367208'; 285 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 286 method: 'POST', 287 headers: { 'Content-Type': 'application/json' }, 288 body: JSON.stringify({ 289 chat_id: chatId, 290 text: '𧪠Test message from Exclusive Ecuador App - Connection test successful!', 291 parse_mode: 'HTML' 292 }) 293 }) 294 .then(res => res.json()) 295 .then(data => { 296 if(data.ok) { 297 alert('â Test message sent successfully! Check your Telegram.'); 298 } else { 299 alert('â Telegram error: ' + (data.description || data.error_code || 'Unknown')); 300 } 301 }) 302 .catch(err => { 303 alert('â Connection error: ' + err.message); 304 }); 305} 306 307function handleFormSubmit(form, modal) { 308 const button = form.querySelector('button[type="submit"]'); 309 button.disabled = true; 310 button.textContent = 'Sending...'; 311 312 const formData = new FormData(form); 313 const subject = formData.get('_subject') || 'New Request'; 314 315 // Build message from form data 316 let message = `<b>${subject}</b>\n`; 317 for (let [key, value] of formData.entries()) { 318 if (!key.startsWith('_') && value) { 319 message += `<b>${key}:</b> ${value}\n`; 320 } 321 } 322 323 console.log('Mensaje a enviar:', message); 324 325 // Send to Telegram 326 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 327 const chatId = '1853367208'; 328 const telegramUrl = `https://api.telegram.org/bot${telegramToken}/sendMessage`; 329 console.log('URL de Telegram:', telegramUrl); 330 331 // Send email notification 332 const emailData = {}; 333 for (let [key, value] of formData.entries()) { 334 if (!key.startsWith('_') && value) { 335 emailData[key] = value; 336 } 337 } 338 emailData['_replyto'] = formData.get('Contact') || ''; 339 sendEmailNotification(subject, emailData); 340 341 fetch(telegramUrl, { 342 method: 'POST', 343 headers: { 'Content-Type': 'application/json' }, 344 body: JSON.stringify({ 345 chat_id: chatId, 346 text: message, 347 parse_mode: 'HTML' 348 }) 349 }) 350 .then(res => { 351 console.log('Respuesta de Telegram:', res); 352 return res.json(); 353 }) 354 .then(data => { 355 console.log('Datos de respuesta:', data); 356 if(data.ok) { 357 if(modal) modal.remove(); 358 document.body.appendChild(createModal('Request Sent', '<p>Thank you for your request. We will contact you shortly to confirm.</p>')); 359 } else { 360 console.error('Error de Telegram:', data); 361 button.disabled = false;
362 button.textContent = 'Send Request'; 363 alert('Error de Telegram: ' + (data.description || data.error_code || 'Unknown error')); 364 } 365 }) 366 .catch(err => { 367 console.error('Error en fetch:', err); 368 button.disabled = false; 369 button.textContent = 'Send Request'; 370 alert('Error de conexión: ' + err.message); 371 }); 372} 373 374// --- MODAL DISPLAY FUNCTIONS --- 375function showGenericServiceModal(title, content, url, useNotification = false) { 376 const buttonText = useNotification ? 'Book Now' : 'Learn More'; 377 const buttonClass = useNotification ? 'generic-service-book-btn' : 'generic-service-link'; 378 const modalContent = ` 379 <p>${content}</p> 380 ${url ? `<div class="text-center my-4"><button class="btn btn-primary ${buttonClass}" data-url="${url}">${buttonText}</button></div>` : ''} 381 `; 382 const modal = createModal(title, modalContent); 383 document.body.appendChild(modal); 384 385 // Add email notification when clicking button 386 if (url) { 387 const button = modal.querySelector(`.${buttonClass}`); 388 button.addEventListener('click', () => { 389 // Get IP, city and country 390 fetch('https://ipapi.co/json/') 391 .then(res => res.json()) 392 .then(data => { 393 const ip = data.ip || 'Unknown'; 394 const city = data.city || 'Unknown'; 395 const country = data.country_name || 'Unknown'; 396 397 if (useNotification) { 398 // Send both Telegram and Email notifications 399 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 400 const chatId = '1853367208'; 401 const telegramUrl = `https://api.telegram.org/bot${telegramToken}/sendMessage`; 402 const time = new Date().toLocaleString(); 403 const message = `ð¨ *Service Interest: ${title}*\n` + 404 `ð¤ Usuario interesado en: ${title}\n` + 405 `ð Fecha y hora: ${time}\n` + 406 `ð Ciudad: ${city}\n` + 407 `ð³ï¸ PaÃs: ${country}\n` + 408 `ð URL: ${url}`; 409 410 fetch(telegramUrl, { 411 method: 'POST', 412 headers: { 'Content-Type': 'application/json' }, 413 body: JSON.stringify({ 414 chat_id: chatId, 415 text: message, 416 parse_mode: 'Markdown' 417 }) 418 }) 419 .then(res => res.json()) 420 .then(data => { 421 sendEmailNotification(`Service Interest: ${title}`, { 422 'Service': title, 423 'URL': url, 424 'Fecha y hora': time, 425 'IP': ip, 426 'Ciudad': city, 427 'PaÃs': country 428 }); 429 window.open(url, '_blank'); 430 }) 431 .catch(err => { 432 console.error('Error sending Telegram notification:', err); 433 sendEmailNotification(`Service Interest: ${title}`, { 434 'Service': title, 435 'URL': url, 436 'Fecha y hora': time, 437 'IP': ip, 438 'Ciudad': city, 439 'PaÃs': country 440 }); 441 window.open(url, '_blank'); 442 }); 443 } else { 444 // Only send email notification 445 sendEmailNotification(`Service Interest: ${title}`, { 446 'Service': title, 447 'URL': url, 448 'Fecha y hora': new Date().toLocaleString(), 449 'IP': ip, 450 'Ciudad': city, 451 'PaÃs': country 452 }); 453 window.open(url, '_blank'); 454 } 455 }) 456 .catch(err => { 457 console.error('Error getting IP info:', err); 458 // Open URL even if IP fetch fails 459 window.open(url, '_blank'); 460 }); 461 }); 462 } 463} 464 465function showPinsaquiRestaurantModal() { 466 const title = 'Hacienda Pinsaquà Restaurants'; 467 const content = ` 468 <p>Discover our three exclusive restaurants:</p> 469 <div class="d-grid gap-3 mt-4"> 470 <button onclick="sendRestaurantClickNotification('Restaurante Pedro Freile', 'https://www.haciendapinsaqui.com/restaurante-pedro-freile.html')" class="btn btn-primary"> 471 <i class="fas fa-utensils me-2"></i> Restaurante Pedro Freile 472 </button> 473 <button onclick="sendRestaurantClickNotification('Restaurante Modesto Larrea', 'https://www.haciendapinsaqui.com/restaurante-modesto-larrea.html')" class="btn btn-primary"> 474 <i class="fas fa-utensils me-2"></i> Restaurante Modesto Larrea 475 </button> 476 <button onclick="sendRestaurantClickNotification('Restaurante Bar Ecuestre', 'https://www.haciendapinsaqui.com/bar-ecuestre.html')" class="btn btn-primary"> 477 <i class="fas fa-glass-cheers me-2"></i> Restaurante Bar Ecuestre 478 </button> 479 </div> 480 `; 481 const modal = createModal(title, content); 482 document.body.appendChild(modal); 483} 484 485function showPinsaquiEventsModal() { 486 const title = 'Events & Wedding Planner'; 487 const content = ` 488 <p>At Hacienda Pinsaquà we have ample spaces, ideal for that special event that should be unique and, with us, will be unforgettable. And if it comes to corporate events, here you will find the perfect/unique/appropriate environment that your firm has been looking for. Our staff is prepared to provide the exclusive service that great moments require.</p> 489 <div class="text-center my-4"> 490 <button onclick="sendEventsClickNotification('https://www.haciendapinsaqui.com/eventos.html')" class="btn btn-primary">Learn More</button> 491 </div> 492 `; 493 const modal = createModal(title, content); 494 document.body.appendChild(modal); 495} 496 497function sendEventsClickNotification(eventsUrl) { 498 const now = new Date();
499 const time = now.toLocaleString(); 500 // Get IP, city and country 501 fetch('https://ipapi.co/json/') 502 .then(res => res.json()) 503 .then(data => { 504 const ip = data.ip || 'Unknown'; 505 const city = data.city || 'Unknown'; 506 const country = data.country_name || 'Unknown'; 507 508 // Send to Telegram 509 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 510 const chatId = '1853367208'; 511 const message = `<b>ð Click en Events & Wedding Planner</b>\n` + 512 `<b>Fecha y hora:</b> ${time}\n` + 513 `<b>IP:</b> ${ip}\n` + 514 `<b>Ciudad:</b> ${city}\n` + 515 `<b>PaÃs:</b> ${country}`; 516 517 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 518 method: 'POST', 519 headers: { 'Content-Type': 'application/json' }, 520 body: JSON.stringify({ 521 chat_id: chatId, 522 text: message, 523 parse_mode: 'HTML' 524 }) 525 }) 526 .then(res => res.json()) 527 .then(data => { 528 // Send email notification 529 sendEmailNotification('Click en Events & Wedding Planner', { 530 'Fecha y hora': time, 531 'IP': ip, 532 'Ciudad': city, 533 'PaÃs': country, 534 'URL': eventsUrl 535 }); 536 // Close modal and open events URL after sending notification 537 document.querySelector('.modal').remove(); 538 window.open(eventsUrl, '_blank'); 539 }) 540 .catch(err => { 541 console.error('Error sending notification:', err); 542 // Close modal and open events URL even if notification fails 543 document.querySelector('.modal').remove(); 544 window.open(eventsUrl, '_blank'); 545 }); 546 }) 547 .catch(err => { 548 console.error('Error getting IP:', err); 549 // Close modal and open events URL even if IP fetch fails 550 document.querySelector('.modal').remove(); 551 window.open(eventsUrl, '_blank'); 552 }); 553} 554 555function showPinsaquiToursModal() { 556 const title = 'Tours'; 557 const content = ` 558 <p>Horseback riding, trekking, hot springs.</p> 559 <div class="text-center my-4"> 560 <button onclick="sendToursClickNotification('https://www.haciendapinsaqui.com/tours.html')" class="btn btn-primary">Learn More</button> 561 </div> 562 `; 563 const modal = createModal(title, content); 564 document.body.appendChild(modal); 565} 566 567function sendToursClickNotification(toursUrl) { 568 const now = new Date(); 569 const time = now.toLocaleString(); 570 // Get IP, city and country 571 fetch('https://ipapi.co/json/') 572 .then(res => res.json()) 573 .then(data => { 574 const ip = data.ip || 'Unknown'; 575 const city = data.city || 'Unknown'; 576 const country = data.country_name || 'Unknown'; 577 578 // Send to Telegram 579 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 580 const chatId = '1853367208'; 581 const message = `<b>ðï¸ Click en Tours</b>\n` + 582 `<b>Fecha y hora:</b> ${time}\n` + 583 `<b>IP:</b> ${ip}\n` + 584 `<b>Ciudad:</b> ${city}\n` + 585 `<b>PaÃs:</b> ${country}`; 586 587 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 588 method: 'POST', 589 headers: { 'Content-Type': 'application/json' }, 590 body: JSON.stringify({ 591 chat_id: chatId, 592 text: message, 593 parse_mode: 'HTML' 594 }) 595 }) 596 .then(res => res.json()) 597 .then(data => { 598 // Send email notification 599 sendEmailNotification('Click en Tours', { 600 'Fecha y hora': time, 601 'IP': ip, 602 'Ciudad': city, 603 'PaÃs': country, 604 'URL': toursUrl 605 }); 606 // Close modal and open tours URL after sending notification 607 document.querySelector('.modal').remove(); 608 window.open(toursUrl, '_blank'); 609 }) 610 .catch(err => { 611 console.error('Error sending notification:', err); 612 // Close modal and open tours URL even if notification fails 613 document.querySelector('.modal').remove(); 614 window.open(toursUrl, '_blank'); 615 }); 616 }) 617 .catch(err => { 618 console.error('Error getting IP:', err); 619 // Close modal and open tours URL even if IP fetch fails 620 document.querySelector('.modal').remove(); 621 window.open(toursUrl, '_blank'); 622 }); 623} 624 625function showPinsaquiActivitiesModal() { 626 const title = 'Activities'; 627 const content = ` 628 <p>Cooking classes, handicraft classes, music classes.</p> 629 <div class="text-center my-4"> 630 <button onclick="sendActivitiesClickNotification('https://www.haciendapinsaqui.com/actividades.html')" class="btn btn-primary">Learn More</button> 631 </div> 632 `; 633 const modal = createModal(title, content); 634 document.body.appendChild(modal); 635} 636 637function sendActivitiesClickNotification(activitiesUrl) { 638 const now = new Date();
639 const time = now.toLocaleString(); 640 // Get IP, city and country 641 fetch('https://ipapi.co/json/') 642 .then(res => res.json()) 643 .then(data => { 644 const ip = data.ip || 'Unknown'; 645 const city = data.city || 'Unknown'; 646 const country = data.country_name || 'Unknown'; 647 648 // Send to Telegram 649 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 650 const chatId = '1853367208'; 651 const message = `<b>ð§© Click en Activities</b>\n` + 652 `<b>Fecha y hora:</b> ${time}\n` + 653 `<b>IP:</b> ${ip}\n` + 654 `<b>Ciudad:</b> ${city}\n` + 655 `<b>PaÃs:</b> ${country}`; 656 657 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 658 method: 'POST', 659 headers: { 'Content-Type': 'application/json' }, 660 body: JSON.stringify({ 661 chat_id: chatId, 662 text: message, 663 parse_mode: 'HTML' 664 }) 665 }) 666 .then(res => res.json()) 667 .then(data => { 668 // Send email notification 669 sendEmailNotification('Click en Activities', { 670 'Fecha y hora': time, 671 'IP': ip, 672 'Ciudad': city, 673 'PaÃs': country, 674 'URL': activitiesUrl 675 }); 676 // Close modal and open activities URL after sending notification 677 document.querySelector('.modal').remove(); 678 window.open(activitiesUrl, '_blank'); 679 }) 680 .catch(err => { 681 console.error('Error sending notification:', err); 682 // Close modal and open activities URL even if notification fails 683 document.querySelector('.modal').remove(); 684 window.open(activitiesUrl, '_blank'); 685 }); 686 }) 687 .catch(err => { 688 console.error('Error getting IP:', err); 689 // Close modal and open activities URL even if IP fetch fails 690 document.querySelector('.modal').remove(); 691 window.open(activitiesUrl, '_blank'); 692 }); 693} 694 695function showPinsaquiSpaModal() { 696 const title = 'Spa'; 697 const content = ` 698 <p>Bathtub baths.</p> 699 <div class="text-center my-4"> 700 <button onclick="sendSpaClickNotification('https://www.haciendapinsaqui.com/spa.html')" class="btn btn-primary">Learn More</button> 701 </div> 702 `; 703 const modal = createModal(title, content); 704 document.body.appendChild(modal); 705} 706 707// Hacienda Abraspungo notification functions 708function sendAbraspungoServiceClickNotification(serviceName, serviceUrl) { 709 const now = new Date(); 710 const time = now.toLocaleString(); 711 // Get IP, city and country 712 fetch('https://ipapi.co/json/') 713 .then(res => res.json()) 714 .then(data => { 715 const ip = data.ip || 'Unknown'; 716 const city = data.city || 'Unknown'; 717 const country = data.country_name || 'Unknown'; 718 719 // Send to Telegram 720 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 721 const chatId = '1853367208'; 722 const telegramUrl = `https://api.telegram.org/bot${telegramToken}/sendMessage`; 723 const message = `ð¨ *Hacienda Abraspungo - ${serviceName}*\n` + 724 `ð¤ Usuario interesado en: ${serviceName}\n` + 725 `ð Fecha y hora: ${time}\n` + 726 `ð Ciudad: ${city}\n` + 727 `ð³ï¸ PaÃs: ${country}\n` + 728 `ð URL: ${serviceUrl}`; 729 730 fetch(telegramUrl, { 731 method: 'POST', 732 headers: { 'Content-Type': 'application/json' }, 733 body: JSON.stringify({ 734 chat_id: chatId, 735 text: message, 736 parse_mode: 'Markdown' 737 }) 738 }) 739 .then(res => res.json()) 740 .then(data => { 741 // Send email notification 742 sendEmailNotification(`Hacienda Abraspungo - ${serviceName}`, { 743 'Sitio': 'Hacienda Abraspungo', 744 'Servicio': serviceName, 745 'URL': serviceUrl, 746 'Fecha y hora': time, 747 'IP': ip, 748 'Ciudad': city, 749 'PaÃs': country 750 }); 751 // Open service URL 752 window.open(serviceUrl, '_blank'); 753 }) 754 .catch(err => { 755 console.error('Error sending notification:', err); 756 window.open(serviceUrl, '_blank'); 757 }); 758 }) 759 .catch(err => { 760 console.error('Error getting IP:', err); 761 window.open(serviceUrl, '_blank'); 762 }); 763} 764 765function sendSpaClickNotification(spaUrl) { 766 const now = new Date();
767 const time = now.toLocaleString(); 768 // Get IP, city and country 769 fetch('https://ipapi.co/json/') 770 .then(res => res.json()) 771 .then(data => { 772 const ip = data.ip || 'Unknown'; 773 const city = data.city || 'Unknown'; 774 const country = data.country_name || 'Unknown'; 775 776 // Send to Telegram 777 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 778 const chatId = '1853367208'; 779 const message = `<b>ð Click en Spa</b>\n` + 780 `<b>Fecha y hora:</b> ${time}\n` + 781 `<b>IP:</b> ${ip}\n` + 782 `<b>Ciudad:</b> ${city}\n` + 783 `<b>PaÃs:</b> ${country}`; 784 785 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 786 method: 'POST', 787 headers: { 'Content-Type': 'application/json' }, 788 body: JSON.stringify({ 789 chat_id: chatId, 790 text: message, 791 parse_mode: 'HTML' 792 }) 793 }) 794 .then(res => res.json()) 795 .then(data => { 796 // Send email notification 797 sendEmailNotification('Click en Spa', { 798 'Fecha y hora': time, 799 'IP': ip, 800 'Ciudad': city, 801 'PaÃs': country, 802 'URL': spaUrl 803 }); 804 // Close modal and open spa URL after sending notification 805 document.querySelector('.modal').remove(); 806 window.open(spaUrl, '_blank'); 807 }) 808 .catch(err => { 809 console.error('Error sending notification:', err); 810 // Close modal and open spa URL even if notification fails 811 document.querySelector('.modal').remove(); 812 window.open(spaUrl, '_blank'); 813 }); 814 }) 815 .catch(err => { 816 console.error('Error getting IP:', err); 817 // Close modal and open spa URL even if IP fetch fails 818 document.querySelector('.modal').remove(); 819 window.open(spaUrl, '_blank'); 820 }); 821} 822 823function sendRestaurantClickNotification(restaurantName, restaurantUrl) { 824 const now = new Date(); 825 const time = now.toLocaleString(); 826 // Get IP and country 827 fetch('https://ipapi.co/json/') 828 .then(res => res.json()) 829 .then(data => { 830 const ip = data.ip || 'Unknown'; 831 const country = data.country_name || 'Unknown'; 832 833 // Send to Telegram 834 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 835 const chatId = '1853367208'; 836 const message = `<b>ð½ï¸ Click en Restaurante</b>\n` + 837 `<b>Restaurante:</b> ${restaurantName}\n` + 838 `<b>Hora:</b> ${time}\n` + 839 `<b>IP:</b> ${ip}\n` + 840 `<b>PaÃs:</b> ${country}`; 841 842 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 843 method: 'POST', 844 headers: { 'Content-Type': 'application/json' }, 845 body: JSON.stringify({ 846 chat_id: chatId, 847 text: message, 848 parse_mode: 'HTML' 849 }) 850 }) 851 .then(res => res.json()) 852 .then(data => { 853 // Send email notification 854 sendEmailNotification('Click en Restaurante', { 855 'Restaurante': restaurantName, 856 'Hora': time, 857 'IP': ip, 858 'PaÃs': country, 859 'URL': restaurantUrl 860 }); 861 // Open restaurant URL after sending notification 862 window.open(restaurantUrl, '_blank'); 863 }) 864 .catch(err => { 865 console.error('Error sending notification:', err); 866 // Open restaurant URL even if notification fails 867 window.open(restaurantUrl, '_blank'); 868 }); 869 }) 870 .catch(err => { 871 console.error('Error getting IP:', err); 872 // Open restaurant URL even if IP fetch fails 873 window.open(restaurantUrl, '_blank'); 874 }); 875} 876 877function showRomanticModal() { 878 const title = 'Romantic Getaway'; 879 const content = ` 880 <p>A magical night in a historic Quito mansion, with every detail designed to celebrate love.</p> 881 <div class="card bg-dark text-white my-3"> 882 <div class="card-body"> 883 <h5 class="card-title">Special Package for Couples</h5> 884 <ul class="list-unstyled"> 885 <li><i class="fas fa-check text-primary me-2"></i>1 night in a romantic suite</li> 886 <li><i class="fas fa-check text-primary me-2"></i>Welcome cocktail</li> 887 <li><i class="fas fa-check text-primary me-2"></i>Intimate dinner for two</li> 888 <li><i class="fas fa-check text-primary me-2"></i>Special details in the room</li> 889 <li><i class="fas fa-check text-primary me-2"></i>Gourmet breakfast</li> 890 <li><i class="fas fa-check text-primary me-2"></i>Warm and exclusive atmosphere</li> 891 </ul> 892 </div> 893 </div> 894 <hr> 895 <h5 class="mb-3">Book Your Getaway</h5> 896 <form id="romantic-booking-form"> 897 <input type="hidden" name="_subject" value="New Romantic Getaway Reservation"> 898 <input type="hidden" name="Package" value="Romantic Getaway"> 899 <div class="row"> 900 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 901 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Contact (Phone or Email)</label><input type="text" name="Contact" class="form-control" required></div> 902 </div> 903 <div class="mb-3"><label class="form-label fw-bold">Desired Date</label><input type="date" name="Date" class="form-control" required></div> 904 <div class="mb-3"><label class="form-label fw-bold">Message (optional)</label><textarea name="Message" class="form-control" rows="3"></textarea></div> 905 <button type="submit" class="btn btn-primary w-100">Book Now</button> 906 </form> 907 `; 908 const modal = createModal(title, content); 909 document.body.appendChild(modal); 910 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 911 912 modal.querySelector('#romantic-booking-form').addEventListener('submit', e => { 913 e.preventDefault(); 914 const form = e.target; 915 const button = form.querySelector('button[type="submit"]'); 916 button.disabled = true; 917 button.textContent = 'Sending...'; 918 919 const formData = new FormData(form); 920 // Send to Telegram 921 const subject = formData.get('_subject') || 'New Romantic Getaway Reservation'; 922 let message = `<b>${subject}</b>\n`; 923 for (let [key, value] of formData.entries()) { 924 if (!key.startsWith('_') && value) { 925 message += `<b>${key}:</b> ${value}\n`; 926 } 927 } 928 929 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 930 const chatId = '1853367208'; 931 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 932 method: 'POST', 933 headers: { 'Content-Type': 'application/json' }, 934 body: JSON.stringify({ 935 chat_id: chatId, 936 text: message, 937 parse_mode: 'HTML' 938 }) 939 }) 940 .then(res => res.json()) 941 .then(data => { 942 // Send email notification 943 const emailData = {}; 944 for (let [key, value] of formData.entries()) { 945 if (!key.startsWith('_') && value) { 946 emailData[key] = value; 947 } 948 } 949 emailData['_replyto'] = formData.get('Contact') || ''; 950 sendEmailNotification('New Romantic Getaway Reservation', emailData); 951 952 // Open WhatsApp with form data 953 const fullName = formData.get('Full Name') || ''; 954 const contact = formData.get('Contact') || ''; 955 const date = formData.get('Date') || ''; 956 const messageText = formData.get('Message') || ''; 957 const package = formData.get('Package') || 'Romantic Getaway'; 958 const whatsappMessage = `Hola, me gustarÃa reservar el Romantic Getaway:\n` + 959 `Paquete: ${package}\n` + 960 `Nombre: ${fullName}\n` + 961 `Contacto: ${contact}\n` + 962 `Fecha: ${date}\n` + 963 `Mensaje: ${messageText}\n` + 964 `Por favor confirmen disponibilidad. Gracias!`; 965 window.open(`https://wa.me/593992010909?text=${encodeURIComponent(whatsappMessage)}`, '_blank'); 966 modal.remove(); 967 }) 968 .catch(err => { 969 alert('An error occurred while sending your request.'); 970 button.disabled = false;
971 button.textContent = 'Book Now'; 972 }); 973 }); 974} 975 976function showRestaurantModal() { 977 const title = 'Restaurant Reservations'; 978 const content = ` 979 <p>Savor Ecuadorâs rich flavors and international inspirations in our refined restaurant. From gourmet breakfasts to elegant dinners, every dish is crafted with fresh, seasonal ingredients and served in an intimate atmosphere.</p> 980 <div class="text-center my-4"> 981 <a href="https://drive.google.com/file/d/1GKfxANGjnOe1Xl8Srssu9albg3jsbvww/view?usp=sharing" target="_blank" class="btn btn-primary"><i class="fas fa-cloud-download-alt"></i> Download Menu</a> 982 </div> 983 <hr> 984 <h5 class="mb-3">Make a Reservation</h5> 985 <form id="restaurant-booking-form"> 986 <input type="hidden" name="_subject" value="New Restaurant Reservation Request"> 987 <div class="row"> 988 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 989 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Contact (Phone or Email)</label><input type="text" name="Contact" class="form-control" required></div> 990 </div> 991 <div class="row"> 992 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Date</label><input type="date" name="Date" class="form-control" required></div> 993 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Time</label><input type="time" name="Time" class="form-control" required></div> 994 </div> 995 <div class="mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 996 <div class="mb-3"><label class="form-label fw-bold">Special Requests</label><textarea name="Special Requests" class="form-control" rows="3"></textarea></div> 997 <button type="submit" class="btn btn-primary w-100">Send Request</button> 998 </form> 999 `; 1000 const modal = createModal(title, content); 1001 document.body.appendChild(modal); 1002 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1003 1004 modal.querySelector('#restaurant-booking-form').addEventListener('submit', e => { 1005 e.preventDefault(); 1006 const form = e.target; 1007 const button = form.querySelector('button[type="submit"]'); 1008 button.disabled = true; 1009 button.textContent = 'Sending...'; 1010 1011 const formData = new FormData(form); 1012 // Send to Telegram 1013 const subject = formData.get('_subject') || 'New Restaurant Reservation Request'; 1014 let message = `<b>${subject}</b>\n`; 1015 for (let [key, value] of formData.entries()) { 1016 if (!key.startsWith('_') && value) { 1017 message += `<b>${key}:</b> ${value}\n`; 1018 } 1019 } 1020 1021 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 1022 const chatId = '1853367208'; 1023 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 1024 method: 'POST', 1025 headers: { 'Content-Type': 'application/json' }, 1026 body: JSON.stringify({ 1027 chat_id: chatId, 1028 text: message, 1029 parse_mode: 'HTML' 1030 }) 1031 }) 1032 .then(res => res.json()) 1033 .then(data => { 1034 // Send email notification 1035 const emailData = {}; 1036 for (let [key, value] of formData.entries()) { 1037 if (!key.startsWith('_') && value) { 1038 emailData[key] = value; 1039 } 1040 } 1041 emailData['_replyto'] = formData.get('Contact') || ''; 1042 sendEmailNotification('New Restaurant Reservation Request', emailData); 1043 1044 // Open WhatsApp with form data 1045 const fullName = formData.get('Full Name') || ''; 1046 const contact = formData.get('Contact') || ''; 1047 const date = formData.get('Date') || ''; 1048 const time = formData.get('Time') || ''; 1049 const people = formData.get('Number of People') || ''; 1050 const specialRequests = formData.get('Special Requests') || ''; 1051 const whatsappMessage = `Hola, me gustarÃa hacer una reservación en el restaurante:\n` + 1052 `Nombre: ${fullName}\n` + 1053 `Contacto: ${contact}\n` + 1054 `Fecha: ${date}\n` + 1055 `Hora: ${time}\n` + 1056 `Personas: ${people}\n` + 1057 `Solicitudes especiales: ${specialRequests}\n` + 1058 `Por favor confirmen disponibilidad. Gracias!`; 1059 window.open(`https://wa.me/593992010909?text=${encodeURIComponent(whatsappMessage)}`, '_blank'); 1060 modal.remove(); 1061 }) 1062 .catch(err => { 1063 alert('An error occurred while sending your request.'); 1064 button.disabled = false;
1065 button.textContent = 'Send Request'; 1066 }); 1067 }); 1068} 1069 1070function showSpaModal() { 1071 const title = 'Spa & Wellness'; 1072 const content = ` 1073 <p>Book a moment of pure relaxation. Select the services you desire and send us a request.</p> 1074 <form id="spa-booking-form"> 1075 <input type="hidden" name="_subject" value="New Spa & Wellness Reservation"> 1076 <div class="row"> 1077 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1078 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Contact (Phone or Email)</label><input type="text" name="Contact" class="form-control" required></div> 1079 </div> 1080 <div class="row"> 1081 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Date</label><input type="date" name="Date" class="form-control" required></div> 1082 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Time</label><input type="time" name="Time" class="form-control" required></div> 1083 </div> 1084 <hr> 1085 <h6>Select Your Services:</h6> 1086 <div class="row"> 1087 <div class="col-md-6"> 1088 <strong>Spa & Wellness</strong> 1089 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Relaxing Massage (1hr)"><label class="form-check-label">Relaxing Massage (1hr)</label></div> 1090 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Bamboo Massage (1hr)"><label class="form-check-label">Bamboo Massage (1hr)</label></div> 1091 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Relieving Massage (1hr)"><label class="form-check-label">Relieving Massage (1hr)</label></div> 1092 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Hot Stone Massage"><label class="form-check-label">Hot Stone Massage</label></div> 1093 <strong class="mt-3 d-block">Facials</strong> 1094 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Organic Facial (60min)"><label class="form-check-label">Organic Facial (60min)</label></div> 1095 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Renewing Facial (60min)"><label class="form-check-label">Renewing Facial (60min)</label></div> 1096 </div> 1097 <div class="col-md-6"> 1098 <strong>Body Wraps</strong> 1099 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Chocolate Wrap (45min)"><label class="form-check-label">Chocolate Wrap (45min)</label></div> 1100 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Clay Wrap (45min)"><label class="form-check-label">Clay Wrap (45min)</label></div> 1101 <strong class="mt-3 d-block">Body Exfoliations</strong> 1102 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Coffee Exfoliation (45min)"><label class="form-check-label">Coffee Exfoliation (45min)</label></div> 1103 <div class="form-check"><input class="form-check-input" type="checkbox" name="Services[]" value="Passion Fruit Scrub (45min)"><label class="form-check-label">Passion Fruit Scrub (45min)</label></div> 1104 </div> 1105 </div> 1106 <hr> 1107 <button type="submit" class="btn btn-primary w-100">Send Request</button> 1108 </form> 1109 `; 1110 const modal = createModal(title, content); 1111 document.body.appendChild(modal); 1112 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1113 1114 modal.querySelector('#spa-booking-form').addEventListener('submit', e => { 1115 e.preventDefault(); 1116 const form = e.target; 1117 const button = form.querySelector('button[type="submit"]'); 1118 button.disabled = true; 1119 button.textContent = 'Sending...'; 1120 1121 const formData = new FormData(form); 1122 // Send to Telegram 1123 const subject = formData.get('_subject') || 'New Spa & Wellness Reservation'; 1124 let message = `<b>${subject}</b>\n`; 1125 for (let [key, value] of formData.entries()) { 1126 if (!key.startsWith('_') && value) { 1127 message += `<b>${key}:</b> ${value}\n`; 1128 } 1129 } 1130 1131 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 1132 const chatId = '1853367208'; 1133 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 1134 method: 'POST', 1135 headers: { 'Content-Type': 'application/json' }, 1136 body: JSON.stringify({ 1137 chat_id: chatId, 1138 text: message, 1139 parse_mode: 'HTML' 1140 }) 1141 }) 1142 .then(res => res.json()) 1143 .then(data => { 1144 // Send email notification 1145 const emailData = {}; 1146 for (let [key, value] of formData.entries()) { 1147 if (!key.startsWith('_') && value) { 1148 emailData[key] = value; 1149 } 1150 } 1151 // Get selected services 1152 const services = []; 1153 form.querySelectorAll('input[name="Services[]"]:checked').forEach(checkbox => { 1154 services.push(checkbox.value); 1155 }); 1156 emailData['Services'] = services.join(', ') || 'No seleccionados'; 1157 emailData['_replyto'] = formData.get('Contact') || ''; 1158 sendEmailNotification('New Spa & Wellness Reservation', emailData); 1159 1160 // Open WhatsApp with form data 1161 const fullName = formData.get('Full Name') || ''; 1162 const contact = formData.get('Contact') || ''; 1163 const date = formData.get('Date') || ''; 1164 const time = formData.get('Time') || ''; 1165 const whatsappMessage = `Hola, me gustarÃa reservar servicios de Spa & Wellness:\n` + 1166 `Nombre: ${fullName}\n` + 1167 `Contacto: ${contact}\n` + 1168 `Fecha: ${date}\n` + 1169 `Hora: ${time}\n` + 1170 `Servicios: ${services.join(', ') || 'No seleccionados'}\n` + 1171 `Por favor confirmen disponibilidad. Gracias!`; 1172 window.open(`https://wa.me/593992010909?text=${encodeURIComponent(whatsappMessage)}`, '_blank'); 1173 modal.remove(); 1174 }) 1175 .catch(err => { 1176 alert('An error occurred while sending your request.'); 1177 button.disabled = false;
1178 button.textContent = 'Send Request'; 1179 }); 1180 }); 1181} 1182 1183function showWeddingPlannerModal() { 1184 const title = 'Wedding Planner'; 1185 const content = ` 1186 <p>Cultura Manor Hotel has qualified staff, infrastructure, equipment, and contacts in churches to host first-class weddings in Quito, Ecuador. Especially, the beautiful Gothic-style Santa Teresita Church, located just one block away, is an ideal option for your ceremony. <a href="https://maps.app.goo.gl/nfzKm53ZZu28nEHP9" target="_blank">Santa Teresita Church</a></p> 1187 <hr> 1188 <h5 class="mb-3">Plan Your Dream Wedding</h5> 1189 <form id="wedding-planner-form"> 1190 <input type="hidden" name="_subject" value="New Wedding Planner Inquiry"> 1191 <input type="hidden" name="_cc" value="[email protected]"> 1192 <div class="row"> 1193 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1194 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Contact (Phone or Email)</label><input type="text" name="Contact" class="form-control" required></div> 1195 </div> 1196 <div class="row"> 1197 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Wedding Date</label><input type="date" name="Wedding Date" class="form-control" required></div> 1198 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Approximate Number of Guests</label><input type="number" name="Number of Guests" class="form-control" min="1"></div> 1199 </div> 1200 <div class="mb-3"> 1201 <label class="form-label fw-bold">Requirements:</label> 1202 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Catering"><label class="form-check-label">Catering</label></div> 1203 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Artistic Show"><label class="form-check-label">Artistic Show</label></div> 1204 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Live Music"><label class="form-check-label">Live Music</label></div> 1205 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Santa Teresa Church"><label class="form-check-label">Santa Teresa Church</label></div> 1206 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Reception"><label class="form-check-label">Reception</label></div> 1207 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Master of Ceremonies"><label class="form-check-label">Master of Ceremonies</label></div> 1208 <div class="form-check"><input class="form-check-input" type="checkbox" name="Requirements[]" value="Floral Arrangements"><label class="form-check-label">Floral Arrangements</label></div> 1209 </div> 1210 <div class="mb-3"><label class="form-label fw-bold">Additional Message</label><textarea name="Additional Message" class="form-control" rows="3"></textarea></div> 1211 <hr> 1212 <div class="d-flex justify-content-between align-items-center"> 1213 <button type="submit" class="btn btn-primary">Send Inquiry</button> 1214 <a href="https://api.whatsapp.com/send?phone=593994959503&text=Hello, I need information about wedding planning" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1215 </div> 1216 </form> 1217 `; 1218 const modal = createModal(title, content); 1219 document.body.appendChild(modal); 1220 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1221 1222 modal.querySelector('#wedding-planner-form').addEventListener('submit', e => { 1223 e.preventDefault(); 1224 const form = e.target; 1225 const button = form.querySelector('button[type="submit"]'); 1226 button.disabled = true; 1227 button.textContent = 'Sending...'; 1228 1229 const formData = new FormData(form); 1230 // Send to Telegram 1231 const subject = formData.get('_subject') || 'New Wedding Planner Inquiry'; 1232 let message = `<b>${subject}</b>\n`; 1233 for (let [key, value] of formData.entries()) { 1234 if (!key.startsWith('_') && value) { 1235 message += `<b>${key}:</b> ${value}\n`; 1236 } 1237 } 1238 1239 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 1240 const chatId = '1853367208'; 1241 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 1242 method: 'POST', 1243 headers: { 'Content-Type': 'application/json' }, 1244 body: JSON.stringify({ 1245 chat_id: chatId, 1246 text: message, 1247 parse_mode: 'HTML' 1248 }) 1249 }) 1250 .then(res => res.json()) 1251 .then(data => { 1252 // Send email notification 1253 const emailData = {}; 1254 for (let [key, value] of formData.entries()) { 1255 if (!key.startsWith('_') && value) { 1256 emailData[key] = value; 1257 } 1258 } 1259 // Get selected requirements 1260 const requirements = []; 1261 form.querySelectorAll('input[name="Requirements[]"]:checked').forEach(checkbox => { 1262 requirements.push(checkbox.value); 1263 }); 1264 emailData['Requirements'] = requirements.join(', ') || 'No seleccionados'; 1265 emailData['_replyto'] = formData.get('Contact') || ''; 1266 sendEmailNotification('New Wedding Planner Inquiry', emailData); 1267 1268 // Open WhatsApp with form data 1269 const fullName = formData.get('Full Name') || ''; 1270 const contact = formData.get('Contact') || ''; 1271 const weddingDate = formData.get('Wedding Date') || ''; 1272 const guests = formData.get('Number of Guests') || '';
1273 const additionalMessage = formData.get('Additional Message') || ''; 1274 const whatsappMessage = `Hola, me gustarÃa información sobre Wedding Planner:\n` + 1275 `Nombre: ${fullName}\n` + 1276 `Contacto: ${contact}\n` + 1277 `Fecha de boda: ${weddingDate}\n` + 1278 `Número de invitados: ${guests}\n` + 1279 `Requisitos: ${requirements.join(', ') || 'No seleccionados'}\n` + 1280 `Mensaje adicional: ${additionalMessage}\n` + 1281 `Por favor envÃenme información. Gracias!`; 1282 window.open(`https://wa.me/593992010909?text=${encodeURIComponent(whatsappMessage)}`, '_blank'); 1283 modal.remove(); 1284 }) 1285 .catch(err => { 1286 alert('An error occurred while sending your request.'); 1287 button.disabled = false; 1288 button.textContent = 'Send Inquiry'; 1289 }); 1290 }); 1291} 1292 1293function showQuitoHalfDayModal() { 1294 const title = 'Quito Half Day Tour Details'; 1295 const content = ` 1296 <h5>Rates per group:</h5> 1297 <ul> 1298 <li>1 - 2 people: $130</li> 1299 <li>3 people: $150</li> 1300 <li>4 people: $220</li> 1301 <li>5 people: $250</li> 1302 <li>6 people: $300</li> 1303 </ul> 1304 <hr> 1305 <h5 class="mb-3">Book Your Tour</h5> 1306 <form id="quito-half-day-form"> 1307 <input type="hidden" name="_subject" value="New Quito Half Day Tour Reservation"> 1308 <input type="hidden" name="_cc" value="[email protected]"> 1309 <div class="row"> 1310 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1311 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1312 </div> 1313 <div class="row"> 1314 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1315 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1316 </div> 1317 <div class="mb-3"> 1318 <label class="form-label fw-bold">Choose one option:</label> 1319 <select class="form-select" name="Tour Option" required> 1320 <option value="Option 1: Equator Line (Intiñan Museum) + Photos with llamas + Empanadas & ice cream.">Option 1: Equator Line (Intiñan Museum) + Photos with llamas + Empanadas & ice cream.</option> 1321 <option value="Option 2: Quito's Historic Center + Panecillo Viewpoint.">Option 2: Quito's Historic Center + Panecillo Viewpoint.</option> 1322 <option value="Option 3: Quito's Historic Center + Chocolate Tour.">Option 3: Quito's Historic Center + Chocolate Tour.</option> 1323 <option value="Option 4: Vegetable Market + Shamanic Cleansing + Gastronomic Tour.">Option 4: Vegetable Market + Shamanic Cleansing + Gastronomic Tour.</option> 1324 <option value="Option 5: Full Gastronomic Tour (breakfast, brunch, lunch, and dinner).">Option 5: Full Gastronomic Tour (breakfast, brunch, lunch, and dinner).</option> 1325 <option value="Option 6: Exclusive Galleries (Olga Fisch, Alpaca Collection) or Artisan Market.">Option 6: Exclusive Galleries (Olga Fisch, Alpaca Collection) or Artisan Market.</option> 1326 <option value="Option 7: GuayasamÃn Museum, National Museum, or Botanical Garden.">Option 7: GuayasamÃn Museum, National Museum, or Botanical Garden.</option> 1327 <option value="Option 8: Cable Car + Swing + Gastronomic Tour (Monday to Friday; weekends require 1-hour wait).">Option 8: Cable Car + Swing + Gastronomic Tour (Monday to Friday; weekends require 1-hour wait).</option> 1328 <option value="Option 9: Archaeological Site (Quitu-Cara Rumipamba Museum or Rumicucho Ruins).">Option 9: Archaeological Site (Quitu-Cara Rumipamba Museum or Rumicucho Ruins).</option> 1329 <option value="Option 10: Craft Beer Tour (visit to local microbreweries).">Option 10: Craft Beer Tour (visit to local microbreweries).</option> 1330 </select> 1331 </div> 1332 <div class="d-flex justify-content-between align-items-center"> 1333 <button type="submit" class="btn btn-primary">Send Request</button> 1334 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Quito Half Day Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1335 </div> 1336 </form> 1337 `; 1338 const modal = createModal(title, content); 1339 document.body.appendChild(modal); 1340 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1341 modal.querySelector('#quito-half-day-form').addEventListener('submit', e => { 1342 e.preventDefault(); 1343 handleFormSubmit(e.target, modal); 1344 }); 1345} 1346 1347function showQuitoFullDayModal() { 1348 const title = 'Quito Full Day Tour Details'; 1349 const content = ` 1350 <h5>Rates per group:</h5> 1351 <ul> 1352 <li>1 - 2 people: $210</li> 1353 <li>3 people: $250</li> 1354 <li>4 people: $300</li> 1355 <li>5 people: $350</li> 1356 <li>6 people: $400</li> 1357 </ul> 1358 <hr> 1359 <h5 class="mb-3">Book Your Tour</h5> 1360 <form id="quito-full-day-form"> 1361 <input type="hidden" name="_subject" value="New Quito Full Day Tour Reservation"> 1362 <input type="hidden" name="_cc" value="[email protected]"> 1363 <div class="row"> 1364 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1365 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1366 </div> 1367 <div class="row"> 1368 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1369 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1370 </div> 1371 <div class="mb-3"> 1372 <label class="form-label fw-bold">Choose 4 options:</label> 1373 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Dome Tour (2 hours)."><label class="form-check-label">Dome Tour (2 hours).</label></div> 1374 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Quito's Historic Center (3 hours)."><label class="form-check-label">Quito's Historic Center (3 hours).</label></div> 1375 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Panecillo Viewpoint (1 hour)."><label class="form-check-label">
1375Panecillo Viewpoint (1 hour).</label></div> 1376 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Cable Car + Swing (2h30 min)."><label class="form-check-label">Cable Car + Swing (2h30 min).</label></div> 1377 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Shamanic Cleansing (5 min per person)."><label class="form-check-label">Shamanic Cleansing (5 min per person).</label></div> 1378 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Vegetable Market (1 hour)."><label class="form-check-label">Vegetable Market (1 hour).</label></div> 1379 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Quito Gastronomic Tour (1 - 3 hours)."><label class="form-check-label">Quito Gastronomic Tour (1 - 3 hours).</label></div> 1380 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Street Food Tour (1 - 2 hours)."><label class="form-check-label">Street Food Tour (1 - 2 hours).</label></div> 1381 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Intiñan Museum + Photos with llamas."><label class="form-check-label">Intiñan Museum + Photos with llamas.</label></div> 1382 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Pululahua Crater (1 hour)."><label class="form-check-label">Pululahua Crater (1 hour).</label></div> 1383 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="GuayasamÃn Museum (3 hours)."><label class="form-check-label">GuayasamÃn Museum (3 hours).</label></div> 1384 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Artisan Market (1 - 2 hours)."><label class="form-check-label">Artisan Market (1 - 2 hours).</label></div> 1385 <div class="form-check"><input class="form-check-input" type="checkbox" name="Tour Options[]" value="Chocolate Tour (Pacari) (1 - 2 hours)."><label class="form-check-label">Chocolate Tour (Pacari) (1 - 2 hours).</label></div> 1386 </div> 1387 <div class="d-flex justify-content-between align-items-center"> 1388 <button type="submit" class="btn btn-primary">Send Request</button> 1389 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Quito Full Day Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1390 </div> 1391 </form> 1392 `; 1393 const modal = createModal(title, content); 1394 document.body.appendChild(modal); 1395 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1396 modal.querySelector('#quito-full-day-form').addEventListener('submit', e => { 1397 e.preventDefault(); 1398 handleFormSubmit(e.target, modal); 1399 }); 1400} 1401 1402function showAntisanaCondorsModal() { 1403 const title = 'Condors in Antisana Tour Details'; 1404 const content = ` 1405 <h5>Rates per group:</h5> 1406 <ul> 1407 <li>1 - 2 people: $250</li> 1408 <li>3 people: $300</li> 1409 <li>4 people: $350</li> 1410 <li>5 people: $400</li> 1411 <li>6 people: $500</li> 1412 </ul> 1413 <hr> 1414 <h5>Includes:</h5> 1415 <p>Andean wildlife observation: condors, hummingbirds, deer, hawks, eagles, and more.<br>Optional lunch: Fresh trout from the natural lake.</p> 1416 <hr> 1417 <h5 class="mb-3">Book Your Tour</h5> 1418 <form id="antisana-condors-form"> 1419 <input type="hidden" name="_subject" value="New Condors in Antisana Tour Reservation"> 1420 <input type="hidden" name="_cc" value="[email protected]"> 1421 <div class="row"> 1422 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1423 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1424 </div> 1425 <div class="row"> 1426 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1427 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1428 </div> 1429 <div class="mb-3"> 1430 <label class="form-label fw-bold">Optional Additional Activities:</label> 1431 <div class="form-check"> 1432 <input class="form-check-input" type="checkbox" name="Additional Activities[]" value="Mountain Biking (+$35 pp)"> 1433 <label class="form-check-label">Mountain Biking (+$35 pp)</label> 1434 </div> 1435 <div class="form-check"> 1436 <input class="form-check-input" type="checkbox" name="Additional Activities[]" value="Horseback Riding (+$35 pp)"> 1437 <label class="form-check-label">Horseback Riding (+$35 pp)</label> 1438 </div> 1439 </div> 1440 <div class="d-flex justify-content-between align-items-center"> 1441 <button type="submit" class="btn btn-primary">Send Request</button> 1442 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Condors in Antisana Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1443 </div> 1444 </form> 1445 `; 1446 const modal = createModal(title, content); 1447 document.body.appendChild(modal); 1448 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1449 modal.querySelector('#antisana-condors-form').addEventListener('submit', e => { 1450 e.preventDefault(); 1451 handleFormSubmit(e.target, modal); 1452 }); 1453} 1454 1455function showCotopaxiVolcanoModal() { 1456 const title = 'Cotopaxi Volcano Tour Details'; 1457 const content = ` 1458 <h5>Rates per group:</h5> 1459 <ul> 1460 <li>1 - 2 people: $270</li> 1461 <li>3 people: $350</li> 1462 <li>4 people: $400</li> 1463 <li>5 people: $500</li> 1464 <li>6 people: $600</li> 1465 </ul> 1466 <hr> 1467 <h5>Experience options:</h5> 1468 <ul> 1469 <li>
1469Cotopaxi National Park.</li> 1470 <li>Inca Hacienda (llama feeding and photos).</li> 1471 <li>Hot tea in a cozy fireplace lounge.</li> 1472 <li>Lunch at the hacienda (reservation required, $35 - $50 per person).</li> 1473 </ul> 1474 <hr> 1475 <h5 class="mb-3">Book Your Tour</h5> 1476 <form id="cotopaxi-volcano-form"> 1477 <input type="hidden" name="_subject" value="New Cotopaxi Volcano Tour Reservation"> 1478 <input type="hidden" name="_cc" value="[email protected]"> 1479 <div class="row"> 1480 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1481 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1482 </div> 1483 <div class="row"> 1484 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1485 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1486 </div> 1487 <div class="d-flex justify-content-between align-items-center"> 1488 <button type="submit" class="btn btn-primary">Send Request</button> 1489 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Cotopaxi Volcano Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1490 </div> 1491 </form> 1492 `; 1493 const modal = createModal(title, content); 1494 document.body.appendChild(modal); 1495 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1496 modal.querySelector('#cotopaxi-volcano-form').addEventListener('submit', e => { 1497 e.preventDefault(); 1498 handleFormSubmit(e.target, modal); 1499 }); 1500} 1501 1502function showSurroundingsOfQuitoModal() { 1503 const title = 'Outside of Quito (Full Day) Tour Details'; 1504 const content = ` 1505 <h5>Rates per group:</h5> 1506 <ul> 1507 <li>1 - 2 people: $250</li> 1508 <li>3 people: $300</li> 1509 <li>4 people: $350</li> 1510 <li>5 people: $400</li> 1511 <li>6 people: $500</li> 1512 </ul> 1513 <hr> 1514 <h5 class="mb-3">Book Your Tour</h5> 1515 <form id="surroundings-of-quito-form"> 1516 <input type="hidden" name="_subject" value="New Outside of Quito (Full Day) Tour Reservation"> 1517 <input type="hidden" name="_cc" value="[email protected]"> 1518 <div class="row"> 1519 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1520 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1521 </div> 1522 <div class="row"> 1523 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1524 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1525 </div> 1526 <div class="mb-3"> 1527 <label class="form-label fw-bold">Available Destinations:</label> 1528 <div class="form-check"> 1529 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Papallacta."> 1530 <label class="form-check-label">Papallacta.</label> 1531 </div> 1532 <div class="form-check"> 1533 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Otavalo Indigenous Market."> 1534 <label class="form-check-label">Otavalo Indigenous Market.</label> 1535 </div> 1536 <div class="form-check"> 1537 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Mindo Cloud Forest."> 1538 <label class="form-check-label">Mindo Cloud Forest.</label> 1539 </div> 1540 <div class="form-check"> 1541 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Horseback Riding Day (+$50 per horse)."> 1542 <label class="form-check-label">Horseback Riding Day (+$50 per horse).</label> 1543 </div> 1544 <div class="form-check"> 1545 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Mountain Biking (+$35 per bike and equipment)."> 1546 <label class="form-check-label">Mountain Biking (+$35 per bike and equipment).</label> 1547 </div> 1548 <div class="form-check"> 1549 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Cochasquà Pyramids + Photos with llamas."> 1550 <label class="form-check-label">Cochasquà Pyramids + Photos with llamas.</label> 1551 </div> 1552 <div class="form-check"> 1553 <input class="form-check-input" type="checkbox" name="Destinations[]" value="Rose Farm + Hacienda La Ciénega."> 1554 <label class="form-check-label">Rose Farm + Hacienda La Ciénega.</label> 1555 </div> 1556 </div> 1557 <div class="d-flex justify-content-between align-items-center"> 1558 <button type="submit" class="btn btn-primary">Send Request</button> 1559 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Outside of Quito (Full Day) Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1560 </div> 1561 </form> 1562 `; 1563 const modal = createModal(title, content); 1564 document.body.appendChild(modal); 1565 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1566 modal.querySelector('#surroundings-of-quito-form').addEventListener('submit', e => { 1567 e.preventDefault(); 1568 handleFormSubmit(e.target, modal); 1569 }); 1570} 1571 1572function showOtavaloMarketModal() { 1573 const title = 'Otavalo Market Tour Details'; 1574 const content = ` 1575 <p>Explore the Ecuadorian Andes with visits to emblematic sites and unique cultural experiences.</p> 1576 <hr> 1577 <h5 class="mb-3">Book Your Tour</h5> 1578 <form id="otavalo-market-form"> 1579 <input type="hidden" name="_subject" value="New Otavalo Market Tour Reservation"> 1580 <input type="hidden" name="_cc" value="[email protected]"> 1581 <div class="row"> 1582 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1583 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1584 </div> 1585 <div class="row"> 1586 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1587 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" id="otavalo-people" name="Number of People" class="form-control" min="1" required></div> 1588 </div> 1589 <div class="mb-3"> 1590 <label class="form-label fw-bold">Choose one food option (mandatory):</label> 1591 <div class="form-check"> 1592 <input class="form-check-input food-option" type="radio" name="Food Option" value="Lunch at Hacienda Pinsaquà ($25 - $35 per person)" data-cost-min="25" data-cost-max="35" required> 1593 <label class="form-check-label">Lunch at Hacienda Pinsaquà ($25 - $35 per person)</label> 1594 </div> 1595 <div class="form-check"> 1596 <input class="form-check-input food-option" type="radio" name="Food Option" value="Traditional food (roasted guinea pig, $22)" data-cost-min="22" data-cost-max="22"> 1597 <label class="form-check-label">Traditional food (roasted guinea pig, $22)</label> 1598 </div> 1599 <div class="form-check"> 1600 <input class="form-check-input food-option" type="radio" name="Food Option" value="Lunch at an Otavalo family home ($25 per person)" data-cost-min="25" data-cost-max="25"> 1601 <label class="form-check-label">Lunch at an Otavalo family home ($25 per person)</label> 1602 </div> 1603 </div> 1604 <div class="mb-3"> 1605 <label class="form-label fw-bold">Choose two additional options (mandatory, no extra cost):</label> 1606 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Quitsato (Middle of the World in Cayambe)."><label class="form-check-label">Quitsato (Middle of the World in Cayambe).</label></div> 1607 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Cochasquà Pyramids."><label class="form-check-label">Cochasquà Pyramids.</label></div> 1608 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Otavalo artisan market."><label class="form-check-label">Otavalo artisan market.</label></div> 1609 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Alpaca weaving workshops."><label class="form-check-label">Alpaca weaving workshops.</label></div> 1610 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Live Andean musicians."><label class="form-check-label">Live Andean musicians.</label></div> 1611 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Cotacachi (leather city)."><label class="form-check-label">Cotacachi (leather city).</label></div> 1612 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Peguche (waterfall and sacred tree viewpoint)."><label class="form-check-label">Peguche (waterfall and sacred tree viewpoint).</label></div> 1613 <div class="form-check"><input class="form-check-input additional-option" type="checkbox" name="Additional Options[]" value="Condor Park (raptor rescue)."><label class="form-check-label">Condor Park (raptor rescue).</label></div> 1614 </div> 1615 <div class="mb-3"> 1616 <label class="form-label fw-bold">Optional paid activities:</label> 1617 <div class="form-check"> 1618 <input class="form-check-input paid-option" type="checkbox" name="Paid Activities[]" value="Cuicocha Lagoon (optional walk, $15)" data-cost="15"> 1619 <label class="form-check-label">Cuicocha Lagoon (optional walk, $15)</label> 1620 </div> 1621 <div class="form-check"> 1622 <input class="form-check-input paid-option" type="checkbox" name="Paid Activities[]" value="Cockfights (Saturdays from 2 pm, $5)" data-cost="5"> 1623 <label class="form-check-label">Cockfights (Saturdays from 2 pm, $5)</label> 1624 </div> 1625 <div class="form-check"> 1626 <input class="form-check-input paid-option" type="checkbox" name="Paid Activities[]" value="Horseback riding at hacienda ($35 per person)" data-cost="35"> 1627 <label class="form-check-label">Horseback riding at hacienda ($35 per person)</label> 1628 </div> 1629 </div> 1630 <hr> 1631 <h5 class="mb-3">Total Estimated Cost: $<span id="otavalo-total-cost">0</span></h5> 1632 <div class="d-flex justify-content-between align-items-center"> 1633 <button type="submit" class="btn btn-primary">Send Request</button> 1634 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Otavalo Market Tour" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1635 </div> 1636 </form> 1637 `; 1638 const modal = createModal(title, content); 1639 document.body.appendChild(modal); 1640 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1641 1642 const form = modal.querySelector('#otavalo-market-form'); 1643 const totalCostEl = modal.querySelector('#otavalo-total-cost'); 1644 const peopleInput = modal.querySelector('#otavalo-people'); 1645 1646 function calculateTotal() { 1647 let total = 0; 1648 const numPeople = parseInt(peopleInput.value) || 0; 1649 const foodOption = form.querySelector('.food-option:checked'); 1650 if (foodOption) { 1651 const minCost = parseFloat(foodOption.dataset.costMin) || 0; 1652 // For simplicity, we'll use the minimum cost for the range 1653 total += minCost * numPeople; 1654 } 1655 form.querySelectorAll('.paid-option:checked').forEach(option => { 1656 const cost = parseFloat(option.dataset.cost) || 0; 1657 if (option.value.includes('per person')) { 1658 total += cost * numPeople; 1659 } else { 1660 total += cost; 1661 } 1662 }); 1663 totalCostEl.textContent = total.toFixed(2); 1664 } 1665 1666 form.addEventListener('change', calculateTotal); 1667 peopleInput.addEventListener('input', calculateTotal); 1668 1669 form.addEventListener('submit', e => { 1670 const additionalOptions = form.querySelectorAll('.additional-option:checked'); 1671 if (additionalOptions.length !== 2) { 1672 e.preventDefault(); 1673 alert('Please choose exactly two additional options.'); 1674 return; 1675 } 1676 e.preventDefault(); 1677 handleFormSubmit(e.target, modal); 1678 }); 1679} 1680 1681function showHorseRiderModal() { 1682 const title = 'Horse Rider Experience Details'; 1683 const content = ` 1684 <h5>Rates per group:</h5> 1685 <ul> 1686 <li>1 - 2 people: $280</li> 1687 <li>3 people: $350</li> 1688 <li>4 people: $450</li> 1689 <li>5 people: $550</li> 1690 <li>6 people: $650</li> 1691 </ul> 1692 <hr> 1693 <h5 class="mb-3">Book Your Tour</h5> 1694 <form id="horse-rider-form"> 1695 <input type="hidden" name="_subject" value="New Horse Rider Experience Reservation"> 1696 <div class="row"> 1697 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1698 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1699 </div> 1700 <div class="row"> 1701 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Tour Date</label><input type="date" name="Date" class="form-control" required></div> 1702 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" id="horse-rider-people" name="Number of People" class="form-control" min="1" required></div> 1703 </div> 1704 <div class="mb-3"> 1705 <label class="form-label fw-bold">Choose 2 activities (mandatory):</label> 1706 <div class="form-check"> 1707 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Rafting ($40, 3 hours)" data-cost="40"> 1708 <label class="form-check-label">Rafting ($40, 3 hours)</label> 1709 </div> 1710 <div class="form-check"> 1711 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Rappel ($40, 3 hours)" data-cost="40"> 1712 <label class="form-check-label">Rappel ($40, 3 hours)</label> 1713 </div> 1714 <div class="form-check"> 1715 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Casa del Ãrbol y columpio ($2)" data-cost="2"> 1716 <label class="form-check-label">Casa del Ãrbol y columpio ($2)</label> 1717 </div> 1718 <div class="form-check"> 1719 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Caminata (2 hours)" data-cost="0"> 1720 <label class="form-check-label">Caminata (2 hours)</label> 1721 </div> 1722 <div class="form-check"> 1723 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Ruta de las cascadas + Pailón del Diablo ($5)" data-cost="5"> 1724 <label class="form-check-label">Ruta de las cascadas + Pailón del Diablo ($5)</label> 1725 </div> 1726 <div class="form-check"> 1727 <input class="form-check-input activity-option" type="checkbox" name="Activities[]" value="Taller de tejidos nativos (1 hour)" data-cost="0"> 1728 <label class="form-check-label">Taller de tejidos nativos (1 hour)</label> 1729 </div> 1730 </div> 1731 <hr> 1732 <h5 class="mb-3">Total Activities Cost: $<span id="horse-rider-total-cost">0</span></h5> 1733 <div class="d-flex justify-content-between align-items-center"> 1734 <button type="submit" class="btn btn-primary">Send Re
1734quest</button> 1735 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Horse Rider Experience" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1736 </div> 1737 </form> 1738 `; 1739 const modal = createModal(title, content); 1740 document.body.appendChild(modal); 1741 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1742 1743 const form = modal.querySelector('#horse-rider-form'); 1744 const totalCostEl = modal.querySelector('#horse-rider-total-cost'); 1745 1746 function calculateTotal() { 1747 let total = 0; 1748 form.querySelectorAll('.activity-option:checked').forEach(option => { 1749 total += parseFloat(option.dataset.cost) || 0; 1750 }); 1751 totalCostEl.textContent = total.toFixed(2); 1752 } 1753 1754 form.addEventListener('change', calculateTotal); 1755 form.addEventListener('submit', e => { 1756 const activityOptions = form.querySelectorAll('.activity-option:checked'); 1757 if (activityOptions.length !== 2) { 1758 e.preventDefault(); 1759 alert('Please choose exactly two activities.'); 1760 return; 1761 } 1762 e.preventDefault(); 1763 handleFormSubmit(e.target, modal); 1764 }); 1765} 1766 1767function showSummitsOfTheSunModal() { 1768 const title = 'Summits of the Sun â Ecuador 10 Days'; 1769 const content = ` 1770 <p>This 10-day expedition in Ecuador is a meticulously planned journey that blends high-altitude mountaineering with luxurious stays in exclusive boutique hotels and historic haciendas. The itinerary is strategically designed for optimal acclimatization, allowing adventurers to safely tackle some of the Andes' most iconic peaks.</p> 1771 <p>The tour begins in Quito (2,850 m), where guests settle into the elegant Cultura Manor Boutique Hotel. The first two days are dedicated to acclimatization and cultural immersion. This includes exploring Quito's UNESCO World Heritage historic center, with visits to landmarks like the CompañÃa de Jesús Church and San Francisco Square, as well as a trip to the Middle of the World Monument to stand on the equatorial line.</p> 1772 <p>The physical challenge begins on Day 3 with an ascent of Rucu Pichincha (4,696 m), the volcano that overlooks Quito, offering breathtaking views and serving as a crucial acclimatization climb. This is followed on Day 4 by a more demanding trek in the Antisana Ecological Reserve, with the goal of hiking on the glaciers or summiting Antisana (5,704 m), depending on conditions.</p> 1773 <p>Next, the journey moves north. Guests will visit the vibrant indigenous handicraft market in Otavalo and take a scenic walk around the stunning turquoise crater lake, Cuicocha Lagoon. The accommodation for this leg is the historic Hacienda PinsaquÃ, an 18th-century colonial estate. From here, the team undertakes a midnight ascent of Cayambe (5,790 m), the only major volcano located directly on the equator, renowned for its spectacular glaciers.</p> 1774 <p>The expedition then travels south towards the "Avenue of the Volcanoes." After an acclimatization walk at Limpiopungo Lagoon at the foot of Cotopaxi, guests will check into Hacienda La Ciénega, one of Ecuador's oldest and most storied estates. The pinnacle of the trip is the pre-dawn climb of the legendary Cotopaxi (5,897 m), aiming to reach its perfectly symmetrical, snow-capped summit at sunrise for a once-in-a-lifetime experience.</p> 1775 <p>The final leg of the tour takes travelers to Riobamba, "the city of mountains." Here, they will visit the Chimborazo Wildlife Reserve, home to wild vicuñas, llamas, and alpacas, all while admiring the immense peak of Chimborazo. The journey concludes with a restful evening at Hacienda Abraspungo, blending traditional Andean architecture with modern comfort, before departure.</p> 1776 <hr> 1777 <h5 class="mb-3">Book Your Expedition</h5> 1778 <form id="summits-sun-form"> 1779 <input type="hidden" name="_subject" value="New Summits of the Sun Expedition Reservation"> 1780 <input type="hidden" name="_cc" value="[email protected]"> 1781 <div class="row"> 1782 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Full Name</label><input type="text" name="Full Name" class="form-control" required></div> 1783 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Phone or Email</label><input type="text" name="Contact" class="form-control" required></div> 1784 </div> 1785 <div class="row"> 1786 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Desired Start Date</label><input type="date" name="Date" class="form-control" required></div> 1787 <div class="col-md-6 mb-3"><label class="form-label fw-bold">Number of People</label><input type="number" name="Number of People" class="form-control" min="1" required></div> 1788 </div> 1789 <div class="mb-3"> 1790 <label class="form-label fw-bold">Experience Level</label> 1791 <select class="form-select" name="Experience Level" required> 1792 <option value="Beginner">Beginner</option> 1793 <option value="Intermediate">Intermediate</option> 1794 <option value="Advanced">Advanced</option> 1795 <option value="Expert">Expert</option> 1796 </select> 1797 </div> 1798 <div class="d-flex justify-content-between align-items-center"> 1799 <button type="submit" class="btn btn-primary">Send Request</button> 1800 <a href="https://wa.me/593992010909?text=Hola, me gustarÃa reservar el Summits of the Sun â Ecuador 10 Days" target="_blank" class="modal-whatsapp-btn"><i class="fab fa-whatsapp"></i></a> 1801 </div> 1802 </form> 1803 <hr> 1804 <div class="text-center my-4"> 1805 <a href="SUMMITS ECUADOR.pdf" target="_blank" class="btn btn-primary me-2"><i class="fas fa-download"></i> Detailed Itinerary</a> 1806 </div> 1807 `; 1808 const modal = createModal(title, content); 1809 document.body.appendChild(modal); 1810 modal.querySelector('input[type="date"]').value = new Date().toISOString().split('T')[0]; 1811 modal.querySelector('#summits-sun-form').addEventListener('submit', e => { 1812 e.preventDefault(); 1813 handleFormSubmit(e.target, modal); 1814 }); 1815} 1816 1817function showSiteServicesModal(siteName) { 1818 const site = sitesData[siteName]; 1819 if (!site) return; 1820 1821 let serviceButtonsHTML = ''; 1822 site.services.options.forEach((service, index) => { 1823 const uniqueId = `${siteName.replace(/\s+/g, '-')}-service-${index}`; 1824 if (service.url) { 1825 serviceButtonsHTML += ` 1826 <a href="${service.url}" target="_blank" class="list-group-item list-group-item-action service-url-link" data-service="${service.name}" data-url="${service.url}"> 1827 <i class="fab ${service.icon}"></i> ${service.name} 1828 </a>`; 1829 } else { 1830 serviceButtonsHTML += ` 1831 <button type="button" id="${uniqueId}" class="list-group-item list-group-item-action"> 1832 <i class="fas ${service.icon}"></i> ${service.name} 1833 </button>`; 1834 } 1835 }); 1836 1837 const content = ` 1838 ${site.services.website ? `<a href="${site.services.website}" target="_blank" class="services-website-link"><i class="fas fa-globe"></i> Visit our Services Page</a>` : ''} 1839 <div class="list-group service-modal-body"> 1840 ${serviceButtonsHTML} 1841 </div> 1842 `; 1843 1844 const modal = createModal(`${siteName} Services`, content, 'service-modal'); 1845 document.body.appendChild(modal); 1846 1847 // Add email notification for URL links 1848 modal.querySelectorAll('.service-url-link').forEach(link => { 1849 link.addEventListener('click', () => { 1850 const serviceName = link.dataset.service; 1851 const serviceUrl = link.dataset.url; 1852 // Get IP, city and country 1853 fetch('https://ipapi.co/json/') 1854 .then(res => res.json()) 1855 .then(data => { 1856 const ip = data.ip || 'Unknown'; 1857 const city = data.city || 'Unknown'; 1858 const country = data.country_name || 'Unknown'; 1859 sendEmailNotification(`Service Click: ${siteName} - ${serviceName}`, { 1860 'Site': siteName, 1861 'Service': serviceName, 1862 'URL': serviceUrl,
1863 'Fecha y hora': new Date().toLocaleString(), 1864 'IP': ip, 1865 'Ciudad': city, 1866 'PaÃs': country 1867 }); 1868 }) 1869 .catch(err => { 1870 console.error('Error getting IP info:', err); 1871 // Send email without IP info if fetch fails 1872 sendEmailNotification(`Service Click: ${siteName} - ${serviceName}`, { 1873 'Site': siteName, 1874 'Service': serviceName, 1875 'URL': serviceUrl, 1876 'Fecha y hora': new Date().toLocaleString(), 1877 'IP': 'Unknown', 1878 'Ciudad': 'Unknown', 1879 'PaÃs': 'Unknown' 1880 }); 1881 }); 1882 }); 1883 }); 1884 1885 site.services.options.forEach((service, index) => { 1886 if (service.action) { 1887 const uniqueId = `${siteName.replace(/\s+/g, '-')}-service-${index}`; 1888 modal.querySelector(`#${uniqueId}`).addEventListener('click', () => { 1889 modal.remove(); 1890 service.action(); 1891 }); 1892 } 1893 }); 1894} 1895 1896// --- DYNAMIC CONTENT LOADERS --- 1897function cargarServicios() { 1898 const container = document.getElementById('service-categories-container'); 1899 if (!container) return; 1900 container.innerHTML = ''; 1901
1902 Object.keys(sitesData).forEach(siteName => { 1903 const site = sitesData[siteName]; 1904 const card = document.createElement('div'); 1905 card.className = 'col-md-6 col-lg-4 mb-4'; 1906 let style = `background-image: url('${site.image}');`; 1907 if (siteName === 'Hacienda La AlegrÃa') { 1908 style += ' background-position: top;'; 1909 } else if (siteName === 'Maquipucuna Reserve') { 1910 style += ' background-position: center 30%;'; 1911 } 1912 card.innerHTML = ` 1913 <div class="site-service-card" style="${style}"> 1914 <h4>${siteName}</h4> 1915 </div> 1916 `; 1917 card.querySelector('.site-service-card').addEventListener('click', () => showSiteServicesModal(siteName)); 1918 container.appendChild(card); 1919 }); 1920} 1921 1922function cargarJourneys() { 1923 const container = document.getElementById('journey-options'); 1924 if (!container) return; 1925 container.innerHTML = ''; 1926 1927 tourData[currentLang].forEach(tour => { 1928 const item = document.createElement('div'); 1929 item.className = 'tour-item'; 1930 item.style.backgroundImage = `url('${tour.imagen}')`; 1931 item.innerHTML = `<div class="tour-item-overlay"></div><div class="tour-item-title">${tour.nombre}</div>`; 1932 if (tour.action) { 1933 item.addEventListener('click', tour.action); 1934 container.appendChild(item); 1935 } else { 1936 const link = document.createElement('a'); 1937 link.href = `https://api.whatsapp.com/send?phone=593994959503&text=Book ${tour.nombre}`; 1938 link.target = '_blank'; 1939 link.appendChild(item); 1940 container.appendChild(link); 1941 } 1942 }); 1943} 1944 1945function cargarEmergencias() { 1946 const list = document.getElementById('emergency-contacts'); 1947 if (!list) return; 1948 list.innerHTML = ''; 1949
1950 emergencyContacts[currentLang].forEach(c => { 1951 const item = document.createElement('li'); 1952 item.className = 'list-group-item d-flex justify-content-between align-items-center bg-transparent text-white'; 1953 item.innerHTML = `<span><strong>${c.nombre}</strong></span><span class="emergency-number">${c.numero}</span>`; 1954 list.appendChild(item); 1955 }); 1956} 1957 1958function cargarServiciosTransporte() { 1959 const container = document.getElementById('transport-info'); 1960 if (!container) return; 1961 container.innerHTML = ''; 1962 1963 const names = { 1964 en: { flights: 'Flights', taxi: 'Taxi Services', carRental: 'Car Rental' }, 1965 es: { flights: 'Vuelos', taxi: 'Servicios de Taxi', carRental: 'Alquiler de VehÃculos' } 1966 }; 1967 1968 Object.entries(transportData).forEach(([cat, data]) => { 1969 const categoryDiv = document.createElement('div'); 1970 categoryDiv.className = 'transport-category mb-4'; 1971 let itemsHTML = ''; 1972 1973 data[currentLang].forEach(srv => { 1974 const nameHTML = srv.sitioWeb ? `<h5><a href="${srv.sitioWeb}" target="_blank" class="transport-name-link">${srv.nombre}</a></h5>` : `<h5>${srv.nombre}</h5>`; 1975 let pbxHTML = srv.pbx ? `<p><i class="fas fa-phone"></i> ${srv.pbx}</p>` : ''; 1976 let direccionHTML = srv.direccion ? `<p><i class="fas fa-map-marker-alt"></i> ${srv.direccion}</p>` : ''; 1977 let style = ''; 1978 if (srv.backgroundImage) { 1979 style = `style="background-image: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url('${srv.backgroundImage}'); background-size: cover; background-position: center;"`; 1980 } 1981 itemsHTML += ` 1982 <div class="transport-item-new" ${style}> 1983 ${nameHTML} 1984 ${direccionHTML} 1985 ${pbxHTML} 1986 </div> 1987 `; 1988 }); 1989 1990 categoryDiv.innerHTML = ` 1991 <h4 class="transport-title">${names[currentLang][cat]}</h4> 1992 <div class="transport-grid"> 1993 ${itemsHTML} 1994 </div> 1995 `; 1996 container.appendChild(categoryDiv); 1997 }); 1998} 1999 2000function showCityWalkModal() { 2001 const menuItems = cityWalkVideos.map((video, index) => 2002 `<li class="list-group-item" data-video-id="${video.videoId}">${video.name}</li>` 2003 ).join(''); 2004 2005 const sponsorItems = sponsors.map(sponsor => 2006 `<a href="${sponsor.url}" target="_blank" class="sponsor-item"><img src="${sponsor.logo}" alt="${sponsor.name}"></a>` 2007 ).join(''); 2008 2009 const modalElement = document.createElement('div'); 2010 modalElement.className = 'city-walk-modal'; 2011 modalElement.innerHTML = ` 2012 <button class="city-walk-close"><i class="fas fa-arrow-left"></i></button> 2013 <div class="city-walk-video-container"> 2014 <iframe id="city-walk-iframe" src="https://www.youtube.com/embed/${cityWalkVideos[0].videoId}?autoplay=1&mute=1&controls=1&rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> 2015 </div> 2016 <div class="city-walk-bottom-bar"> 2017 <div class="menus-container"> 2018 <div class="city-walk-menu"> 2019 <ul class="list-group list-group-horizontal"> 2020 ${menuItems} 2021 </ul> 2022 </div> 2023 <div class="sponsors-menu"> 2024 ${sponsorItems} 2025 </div> 2026 </div> 2027 <div class="bottom-bar-buttons"> 2028 <button class="menu-toggle-btn"><i class="fas fa-bars"></i> Destinations</button> 2029 <button class="sponsors-toggle-btn"><i class="fas fa-star"></i> Sponsors</button> 2030 <a href="https://api.whatsapp.com/send?phone=593994959503&text=Hello, I need information about Cultura Manor services" target="_blank" class="contact-toggle-btn"><i class="fab fa-whatsapp"></i> Contact us</a> 2031 </div> 2032 </div> 2033 `; 2034 2035 document.body.appendChild(modalElement); 2036 document.body.style.overflow = 'hidden'; 2037 2038 const iframe = modalElement.querySelector('#city-walk-iframe'); 2039 const menuLinks = modalElement.querySelectorAll('.city-walk-menu .list-group-item'); 2040 const cityWalkMenu = modalElement.querySelector('.city-walk-menu'); 2041 const sponsorsMenu = modalElement.querySelector('.sponsors-menu'); 2042 2043 if (menuLinks.length > 0) menuLinks[0].classList.add('active'); 2044 2045 menuLinks.forEach(link => { 2046 link.addEventListener('click', function() { 2047 menuLinks.forEach(l => l.classList.remove('active')); 2048 this.classList.add('active'); 2049 const videoId = this.getAttribute('data-video-id'); 2050 iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1&mute=1&controls=1&rel=0`; 2051 }); 2052 }); 2053 2054 modalElement.querySelector('.menu-toggle-btn').addEventListener('click', () => { 2055 cityWalkMenu.classList.toggle('open'); 2056 sponsorsMenu.classList.remove('open'); 2057 }); 2058 2059 modalElement.querySelector('.sponsors-toggle-btn').addEventListener('click', () => { 2060 sponsorsMenu.classList.toggle('open'); 2061 cityWalkMenu.classList.remove('open'); 2062 }); 2063 2064 modalElement.querySelector('.city-walk-close').addEventListener('click', () => { 2065 modalElement.remove(); 2066 document.body.style.overflow = 'auto'; 2067 }); 2068} 2069 2070// --- DOMContentLoaded EVENT LISTENER --- 2071document.addEventListener('DOMContentLoaded', () => { 2072 // Initialize map
2073 const map = L.map('hotel-map', { minZoom: 6 }).setView([-1.0, -83.5], 6); 2074 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); 2075 2076 const smallIcon = L.icon({ 2077 iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', 2078 iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png', 2079 shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png', 2080 iconSize: [15, 25], iconAnchor: [7, 25], popupAnchor: [1, -20] 2081 }); 2082 2083 mapLocations.forEach(location => { 2084 L.marker(location.coords, { icon: smallIcon }).addTo(map) 2085 .bindPopup(`<strong>${location.name}</strong><br><a href="${location.url}" target="_blank">Visitar sitio web</a>`); 2086 }); 2087 2088 // Set initial date 2089 const today = new Date().toISOString().split('T')[0]; 2090 const checkDateInput = document.getElementById('check-date'); 2091 if (checkDateInput) checkDateInput.value = today; 2092 2093 document.getElementById('check-inout-form').addEventListener('submit', function(e) { 2094 e.preventDefault(); 2095 const form = e.target; 2096 const button = form.querySelector('button[type="submit"]'); 2097 button.disabled = true; 2098 button.textContent = 'Sending...'; 2099 2100 const formData = new FormData(form); 2101 // Send to Telegram 2102 const subject = formData.get('_subject') || 'New Booking Request'; 2103 let message = `<b>${subject}</b>\n`; 2104 for (let [key, value] of formData.entries()) { 2105 if (!key.startsWith('_') && value) { 2106 message += `<b>${key}:</b> ${value}\n`; 2107 } 2108 } 2109 2110 const telegramToken = '8641660748:AAEkXojnvRPk7ZXHmXEwEOBYsm2m2x2-7Y0'; 2111 const chatId = '1853367208'; 2112 fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, { 2113 method: 'POST', 2114 headers: { 'Content-Type': 'application/json' }, 2115 body: JSON.stringify({ 2116 chat_id: chatId, 2117 text: message, 2118 parse_mode: 'HTML' 2119 }) 2120 }) 2121 .then(res => res.json()) 2122 .then(data => { 2123 // Send email notification 2124 const emailData = {}; 2125 for (let [key, value] of formData.entries()) { 2126 if (!key.startsWith('_') && value) { 2127 emailData[key] = value; 2128 } 2129 } 2130 emailData['_replyto'] = formData.get('Contact') || ''; 2131 sendEmailNotification('New Booking Request', emailData); 2132 2133 // Open WhatsApp with form data 2134 const fullName = formData.get('Full Name') || ''; 2135 const contact = formData.get('Contact') || ''; 2136 const date = formData.get('Date') || ''; 2137 const adults = formData.get('Number of Adults') || ''; 2138 const children = formData.get('Number of Children') || '0'; 2139 const experience = formData.get('Selected Experience') || ''; 2140 const whatsappMessage = `Hola, me gustarÃa hacer una reserva:\n` + 2141 `Experiencia: ${experience}\n` + 2142 `Nombre: ${fullName}\n` + 2143 `Contacto: ${contact}\n` + 2144 `Fecha: ${date}\n` + 2145 `Adultos: ${adults}\n` + 2146 `Niños: ${children}\n` + 2147 `Por favor confirmen disponibilidad. Gracias!`; 2148 window.open(`https://wa.me/593992010909?text=${encodeURIComponent(whatsappMessage)}`, '_blank'); 2149 2150 button.disabled = false; 2151 button.textContent = 'Confirm'; 2152 form.reset(); 2153 }) 2154 .catch(err => { 2155 alert('An error occurred while sending your request.'); 2156 button.disabled = false; 2157 button.textContent = 'Confirm'; 2158 }); 2159 }); 2160 2161 // Members Only functionality 2162 document.getElementById('members-only-btn').addEventListener('click', showMemberLogin); 2163 2164 function showMemberLogin() { 2165 const loginContent = ` 2166 <div class="text-center"> 2167 <h5><i class="fas fa-lock"></i> Member Authentication</h5> 2168 <p class="mb-4">Please enter your email address to access your member dashboard</p> 2169 <form id="member-login-form"> 2170 <div class="mb-3"> 2171 <label for="member-email" class="form-label fw-bold">Email Address</label> 2172 <input type="email" class="form-control" id="member-email" placeholder="[email protected]" required> 2173 </div> 2174 <button type="submit" class="btn btn-primary w-100"> 2175 <i class="fas fa-sign-in-alt"></i> Access My Account 2176 </button> 2177 </form> 2178 </div> 2179 `; 2180 const modal = createModal('Member Login', loginContent); 2181 document.body.appendChild(modal); 2182 modal.querySelector('#member-login-form').addEventListener('submit', handleMemberLogin); 2183 } 2184 2185 async function handleMemberLogin(e) { 2186 e.preventDefault(); 2187 const email = e.target.querySelector('#member-email').value; 2188 const button = e.target.querySelector('button[type="submit"]'); 2189 const originalText = button.innerHTML; 2190 2191 button.disabled = true; 2192 button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Verifying...'; 2193 2194 try { 2195 // Fetch member data from Google Sheets 2196 const response = await fetch(`https://docs.google.com/spreadsheets/d/e/2PACX-1vQSFgiUWYtndIwhbahcYozYPgGLQDKUyvps0DEsbEjo4kwj3WD7m_rsNrKEn1X6PEc4UnvGI83RhbTW/pubhtml`); 2197 // Since we can't directly fetch from published HTML, we'll use a proxy service 2198 // For now, let's simulate the data structure 2199 const members = await fetchMemberData(); 2200 const member = members.find(m => m.email.toLowerCase() === email.toLowerCase()); 2201 2202 if (member) { 2203 showMemberDashboard(member); 2204 e.target.closest('.modal').remove(); 2205 } else { 2206 showNotRegisteredError(); 2207 } 2208 } catch (error) { 2209 console.error('Error fetching member data:', error); 2210 showNotRegisteredError(); 2211 } finally { 2212 button.disabled = false;
2213 button.innerHTML = originalText; 2214 } 2215 } 2216 2217 async function fetchMemberData() { 2218 try { 2219 // Try published CSV first 2220 const csvUrl = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQSFgiUWYtndIwhbahcYozYPgGLQDKUyvps0DEsbEjo4kwj3WD7m_rsNrKEn1X6PEc4UnvGI83RhbTW/pub?output=csv'; 2221 let response = await fetch(csvUrl); 2222 if (response.ok) { 2223 const csvText = await response.text(); 2224 return parseCSVData(csvText); 2225 } 2226 throw new Error('CSV fetch failed'); 2227 } catch (error) { 2228 console.error('Error fetching Google Sheet data:', error); 2229 console.log('Using fallback data - Google Sheets not accessible'); 2230 // Fallback data that works 2231 return [ 2232 { 2233 email: '[email protected]', 2234 name: 'Test Member', 2235 memberNumber: 'EHHEC-TEST-001', 2236 balance: '$1,000.00', 2237 estado: 'Active', 2238 qrCode: 'EHHEC-TEST-001-TESTMEMBER' 2239 }, 2240 { 2241 email: '[email protected]', 2242 name: 'Demo User', 2243 memberNumber: 'EHHEC-DEMO-002', 2244 balance: '$2,500.00', 2245 estado: 'Active', 2246 qrCode: 'EHHEC-DEMO-002-DEMOUSER' 2247 } 2248 ]; 2249 } 2250 } 2251 2252 function parseCSVData(csvText) { 2253 const rows = csvText.split('\n').filter(row => row.trim()); 2254 const members = []; 2255 console.log('CSV Data received:', csvText.substring(0, 400)); // Debug first 400 chars 2256 2257 for (let i = 1; i < rows.length; i++) { 2258 const columns = rows[i].split(',').map(col => col.trim().replace(/"/g, '')); 2259 console.log(`Row ${i}:`, columns); // Debug each row 2260 console.log(`Column count: ${columns.length}`); 2261 2262 // Expected columns: Email, Name, MemberNumber, Balance, Estado 2263 // Try to find Estado column by looking for common status values 2264 let estado = 'Active'; 2265 for (let j = 0; j < columns.length; j++) { 2266 const col = columns[j].toLowerCase(); 2267 if (col.includes('active') || col.includes('inactive') || col.includes('pending') || col.includes('suspended')) { 2268 estado = columns[j]; 2269 console.log(`Found Estado in column ${j}: ${estado}`); 2270 break; 2271 } 2272 } 2273 2274 if (columns.length >= 4 && columns[0]) { 2275 members.push({ 2276 email: columns[0], 2277 name: columns[1] || 'Unknown', 2278 memberNumber: columns[2] || 'N/A', 2279 balance: columns[3] || '$0.00', 2280 estado: estado, 2281 qrCode: `${columns[2] || 'UNKNOWN'}-${columns[1] || 'UNKNOWN'}`.replace(/\s+/g, '').toUpperCase() 2282 }); 2283 } 2284 } 2285 console.log('Parsed members:', members); // Debug final result 2286 return members; 2287 } 2288 2289 function showMemberDashboard(member) { 2290 // Generate timestamp for QR code 2291 const timestamp = new Date().toISOString(); 2292 // Create comprehensive QR data with card number 2293 const qrData = JSON.stringify({ 2294 name: member.name, 2295 cardNumber: member.memberNumber, 2296 timestamp: timestamp, 2297 type: 'EHHEC_MEMBER' 2298 }); 2299 2300 const dashboardContent = ` 2301 <div class="text-center member-dashboard"> 2302 <div class="member-header mb-3"> 2303 <h4 class="mb-2"><i class="fas fa-user-circle text-primary"></i> Exclusive Member</h4> 2304 <p class="text-muted mb-0">Welcome back, ${member.name}!</p> 2305 </div> 2306 <div class="member-info-grid mb-3"> 2307 <div class="info-card"> 2308 <div class="info-icon"> 2309 <i class="fas fa-user"></i> 2310 </div> 2311 <div class="info-content"> 2312 <small class="text-muted">Name</small> 2313 <p class="mb-0 fw-bold">${member.name}</p> 2314 </div> 2315 </div> 2316 <div class="info-card"> 2317 <div class="info-icon"> 2318 <i class="fas fa-credit-card"></i> 2319 </div> 2320 <div class="info-content"> 2321 <small class="text-muted">Card Number</small> 2322 <p class="mb-0 fw-bold">${member.memberNumber}</p> 2323 </div> 2324 </div> 2325 <div class="info-card" style="text-align: center;"> 2326 <div class="info-icon"> 2327 <i class="fas fa-info-circle"></i> 2328 </div> 2329 <div class="info-content"> 2330 <small class="text-muted">Status</small> 2331 <p class="mb-0 fw-bold ${member.estado === 'Active' ? 'text-success' : 'text-warning'}">${member.estado}</p> 2332 </div> 2333 </div> 2334 </div> 2335 <div class="qr-section text-center mb-3"> 2336 <div class="info-card qr-card-large"> 2337 <div class="info-icon"> 2338 <i class="fas fa-qrcode"></i> 2339 </div> 2340 <div class="info-content"> 2341 <div class="qr-code-wrapper-large"> 2342 <img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrData)}" 2343 alt="Member QR Code" 2344 class="qr-code-img-large"> 2345 </div> 2346 </div> 2347 </div> 2348 </div> 2349 <div class="alert alert-gold py-2 mb-3"> 2350 <small><i class="fas fa-qrcode"></i> QR code with your card number</small> 2351 </div> 2352 <div class="d-grid gap-2 mb-3"> 2353 <a href="https://ehhec.com/wp-content/uploads/2025/10/exclusive-travel-card.pdf" target="_blank" class="btn btn-outline-primary"> 2354 <i class="fas fa-file-pdf"></i> Member Information 2355 </a> 2356 </div> 2357 </div> 2358 `; 2359 const modal = createModal('Dashboard', dashboardContent, 'modal-md'); 2360 document.body.appendChild(modal); 2361 } 2362 2363 function refreshMemberDashboard(email) { 2364 const refreshBtn = document.querySelector('.btn-refresh-dashboard'); 2365 const originalIcon = refreshBtn.innerHTML; 2366 2367 // Show loading state 2368 refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>'; 2369 refreshBtn.disabled = true; 2370 2371 // Fetch fresh member data 2372 fetchMemberData().then(members => { 2373 const member = members.find(m => m.email.toLowerCase() === email.toLowerCase()); 2374 if (member) { 2375 // Close current modal and reopen with fresh data 2376 document.querySelector('.modal').remove(); 2377 showMemberDashboard(member); 2378 2379 // Show success feedback 2380 setTimeout(() => { 2381 const successToast = document.createElement('div'); 2382 successToast.className = 'position-fixed top-0 end-0 p-3'; 2383 successToast.style.zIndex = '9999'; 2384 successToast.innerHTML = ` 2385 <div class="toast show" role="alert"> 2386 <div class="toast-body"> 2387 <i class="fas fa-check-circle text-success me-2"></i> 2388 Data refreshed successfully 2389 </div> 2390 </div> 2391 `; 2392 document.body.appendChild(successToast); 2393 setTimeout(() => successToast.remove(), 3000); 2394 }, 100); 2395 } else { 2396 // Member not found anymore 2397 document.querySelector('.modal').remove(); 2398 showNotRegisteredError(); 2399 } 2400 }).catch(error => { 2401 console.error('Error refreshing data:', error); 2402 refreshBtn.innerHTML = originalIcon; 2403 refreshBtn.disabled = false;
2404 }); 2405 } 2406 2407 function showNotRegisteredError() { 2408 const errorContent = ` 2409 <div class="text-center"> 2410 <div class="alert alert-warning"> 2411 <i class="fas fa-exclamation-triangle fa-2x mb-3"></i> 2412 <h5>Member Not Found</h5> 2413 <p class="mb-3">We couldn't find your email in our member database.</p> 2414 <p class="mb-4">Please contact our technical support for assistance.</p> 2415 <a href="https://wa.link/6zal29" target="_blank" class="btn btn-success"> 2416 <i class="fab fa-whatsapp"></i> Contact Technical Support 2417 </a> 2418 </div> 2419 <button class="btn btn-secondary mt-3" onclick="this.closest('.modal').remove()"> 2420 <i class="fas fa-arrow-left"></i> Try Again 2421 </button> 2422 </div> 2423 `; 2424 const modal = createModal('Authentication Failed', errorContent); 2425 document.body.appendChild(modal); 2426 } 2427 2428 // Initial content load 2429 toggleLanguage(); // This will call all cargar... functions 2430});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.