<?php
/*
 * SISTEMA DE RESERVAS CHOFER MADRID
 * Autor: Generado para ChoferMadrid
 * Instalación: Pegar en functions.php
 * Uso: Insertar shortcode     
    
Calculando ruta...
ℹ️ Sin pago anticipado: Selecciona tu vehículo ahora. El pago se realiza directamente al conductor o vía enlace posterior.
WhatsApp ¿Tienes dudas? Escríbenos por WhatsApp

Finalizar Reserva

¡Solicitud Recibida!

⚠️ IMPORTANTE:
Esta solicitud NO es una confirmación definitiva.
  • Hemos recibido tus datos correctamente.
  • Verificaremos disponibilidad en breves minutos.
  • Nos pondremos en contacto contigo para confirmar.

Gracias por confiar en Chofer Madrid.

en la web */ function cm_premium_booking_shortcode() { ob_start(); ?> <div id="cm-booking-widget"> <div id="cm-step-search" class="cm-slide active"> <h3 class="cm-title">Reserva tu traslado privado</h3> <form id="cm-form-step1"> <div class="cm-grid-input"> <div class="cm-input-group"> <label>Punto de Recogida (Madrid)</label> <input type="text" id="cm-origin" placeholder="Aeropuerto, Hotel o Dirección..." required> </div> <div class="cm-input-group"> <label>Destino</label> <input type="text" id="cm-destination" placeholder="¿A dónde quieres ir?" required> </div> </div> <div class="cm-grid-input"> <div class="cm-input-group"> <label>Fecha</label> <input type="date" id="cm-date" required> </div> <div class="cm-input-group"> <label>Hora</label> <input type="time" id="cm-time" required> </div> <div class="cm-input-group btn-container"> <button type="button" id="cm-btn-calc" class="cm-btn-primary">VER PRECIOS</button> </div> </div> <div id="cm-msg-error" class="cm-error"></div> </form> </div> <div id="cm-step-vehicles" class="cm-slide"> <div class="cm-header-nav"> <button type="button" onclick="goToStep('search')" class="cm-back-link">← Volver a buscar</button> <span id="cm-trip-summary">Calculando ruta...</span> </div> <div id="cm-vehicle-list" class="cm-vehicle-grid"> </div> </div> <div id="cm-step-final" class="cm-slide"> <div class="cm-header-nav"> <button type="button" onclick="goToStep('vehicles')" class="cm-back-link">← Volver a vehículos</button> <h3>Finalizar Reserva</h3> </div> <div id="cm-selected-car-info"></div> <form id="cm-booking-form"> <input type="hidden" name="vehicle_id" id="f_vehicle"> <input type="hidden" name="price" id="f_price"> <input type="hidden" name="distance" id="f_distance"> <input type="hidden" name="origin" id="f_origin"> <input type="hidden" name="destination" id="f_dest"> <div class="cm-form-row"> <input type="text" name="fullname" placeholder="Nombre completo" required class="cm-input-clean"> </div> <div class="cm-form-row"> <input type="email" name="email" placeholder="Correo electrónico" required class="cm-input-clean"> <input type="tel" name="phone" placeholder="Teléfono móvil" required class="cm-input-clean"> </div> <div class="cm-form-row"> <textarea name="notes" placeholder="Notas adicionales (Número de vuelo, sillas de bebé, instrucciones de recogida...)" class="cm-input-clean"></textarea> </div> <button type="submit" class="cm-btn-primary full-width">CONFIRMAR RESERVA</button> </form> </div> <div id="cm-step-success" class="cm-slide" style="text-align:center; padding: 50px 20px;"> <div style="font-size: 60px; color: #28a745; margin-bottom: 20px;">✓</div> <h3 style="color:#111; margin-bottom:10px;">¡Solicitud Recibida!</h3> <p style="color:#666; font-size:16px; line-height:1.5;"> Hemos recibido los datos de tu reserva correctamente.<br> Nuestro equipo verificará la disponibilidad y te contactará en breve para confirmar el servicio. </p> <button type="button" onclick="location.reload()" class="cm-btn-secondary">Realizar otra reserva</button> </div> </div> <style> #cm-booking-widget { background: #ffffff; padding: 35px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); max-width: 950px; margin: 0 auto; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; position: relative; min-height: 400px; border: 1px solid #f0f0f0; } .cm-title { margin-top: 0; color: #111; font-weight: 700; font-size: 26px; margin-bottom: 25px; letter-spacing: -0.5px;} /* Inputs */ .cm-grid-input { display: flex; gap: 20px; margin-bottom: 20px; } .cm-input-group { flex: 1; display: flex; flex-direction: column; } .cm-input-group label { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #999; margin-bottom: 8px; font-weight: 600; } #cm-booking-widget input, #cm-booking-widget textarea { border: 1px solid #e1e1e1; background: #fdfdfd; padding: 16px; border-radius: 6px; font-size: 15px; width: 100%; box-sizing: border-box; font-family: inherit; color: #333; transition: all 0.3s; } #cm-booking-widget input:focus, #cm-booking-widget textarea:focus { border-color: #000; background: #fff; outline: none; box-shadow: 0 2px 10px rgba(0,0,0,0.05); } /* Botones */ .cm-btn-primary { background: #000; color: #fff; border: none; padding: 0 35px; border-radius: 6px; font-weight: 700; text-transform: uppercase; cursor: pointer; letter-spacing: 1px; height: 52px; margin-top: auto; width: 100%; font-size: 14px; transition: background 0.3s; } .cm-btn-primary:hover { background: #333; transform: translateY(-1px); } .cm-btn-secondary { background: #f0f0f0; color: #333; border: none; padding: 12px 25px; border-radius: 6px; cursor: pointer; margin-top: 25px; font-weight: 600; } .cm-btn-secondary:hover { background: #e0e0e0; } /* Animaciones */ .cm-slide { display: none; animation: cmFadeIn 0.5s ease; } .cm-slide.active { display: block; } @keyframes cmFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Listado de Coches */ .cm-vehicle-card { border: 1px solid #eee; border-radius: 8px; padding: 20px; display: flex; align-items: center; margin-bottom: 15px; transition: all 0.3s; background: #fff; } .cm-vehicle-card:hover { transform: translateY(-3px); box-shadow: 0 10px 25px rgba(0,0,0,0.08); border-color: #ddd;} .cm-v-img-box { width: 160px; height: 100px; margin-right: 25px; display: flex; align-items: center; justify-content: center; overflow: hidden; border-radius: 6px; background: #fcfcfc;} .cm-v-img-box img { width: 100%; height: 100%; object-fit: cover; } .cm-v-content { flex: 1; } .cm-v-title { font-size: 18px; font-weight: 700; color: #111; margin: 0 0 5px 0; } .cm-v-desc { font-size: 13px; color: #777; line-height: 1.5; } .cm-v-action { text-align: right; min-width: 110px; } .cm-price-display { display: block; font-size: 24px; font-weight: 700; color: #111; margin-bottom: 8px; } .cm-select-btn { background: transparent; border: 1px solid #111; color: #111; padding: 8px 18px; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 700; text-transform: uppercase; transition: all 0.2s; } .cm-select-btn:hover { background: #111; color: #fff; } /* Navegación y Extras */ .cm-header-nav { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #eee; padding-bottom: 15px; margin-bottom: 25px; } .cm-back-link { background: none; border: none; color: #666; cursor: pointer; font-size: 13px; text-decoration: underline; padding: 0;} .cm-form-row { display: flex; gap: 20px; margin-bottom: 20px; } /* Mobile */ @media (max-width: 768px) { #cm-booking-widget { padding: 20px; } .cm-grid-input, .cm-vehicle-card, .cm-form-row { flex-direction: column; gap: 15px; } .cm-v-img-box { width: 100%; height: 160px; margin-right: 0; margin-bottom: 15px; } .cm-v-action { text-align: left; margin-top: 15px; display: flex; justify-content: space-between; align-items: center; width: 100%; } .cm-btn-primary { width: 100%; } } </style> <script> document.addEventListener('DOMContentLoaded', function() { // --- TUS IMÁGENES REALES --- const imgE = 'https://www.chofermadrid.com/wp-content/uploads/2022/01/Mercedes-Benz-Clase-E-220.jpg'; const imgS = 'https://www.chofermadrid.com/wp-content/uploads/2022/01/Mercedes-Benz-Clase-S-350.jpg'; const imgV = 'https://www.chofermadrid.com/wp-content/uploads/2022/01/Mercedes-Benz-Clase-V-250.jpg'; const imgSprinter = 'https://www.chofermadrid.com/wp-content/uploads/2022/01/Mercedes-Benz-Sprinter.jpg'; const imgDefault = 'https://www.chofermadrid.com/wp-content/uploads/2022/01/Mercedes-Benz-Clase-E-AMG.jpg'; // --- CATALOGO DE PRECIOS Y VEHÍCULOS --- const vehicles = [ { id: 'std', name: 'Standard (4 Pax)', cap: '2 Maletas (23kg) + 2 Cabina', img: imgDefault, base_air: 65, base_std: 55, extra: 1.50 }, { id: 'std_van', name: 'Standard Van (6 Pax)', cap: '6 Maletas (23kg)', img: imgV, base_air: 95, base_std: 88, extra: 1.87 }, { id: 'merc_e', name: 'Mercedes Clase E', cap: '4 Pax · 2 Maletas (23kg)', img: imgE, base_air: 90, base_std: 77, extra: 1.87 }, { id: 'merc_v6', name: 'Mercedes Clase V (6 Pax)', cap: '6 Maletas (23kg)', img: imgV, base_air: 110, base_std: 95, extra: 2.20 }, { id: 'merc_v7', name: 'Mercedes Clase V (7 Pax)', cap: '7 Maletas (23kg) + 2 Cabina', img: imgV, base_air: 120, base_std: 105, extra: 2.20 }, { id: 'merc_s', name: 'Mercedes Clase S', cap: '3 Pax · 3 Maletas', img: imgS, base_air: 165, base_std: 150, extra: 3.08 }, { id: 'merc_eqs', name: 'Mercedes EQS SUV', cap: '4 Pax · 4 Maletas', img: imgDefault, base_air: 165, base_std: 150, extra: 3.08 }, { id: 'sprinter8', name: 'M. Sprinter (8 Pax)', cap: '8 Maletas', img: imgSprinter, base_air: 165, base_std: 150, extra: 3.08 }, { id: 'sprinter16', name: 'M. Sprinter (16 Pax)', cap: 'Consultar disponibilidad', img: imgSprinter, is_manual: true } ]; // 1. RESTRICCIÓN FECHA (HOY + 24H) const dateInput = document.getElementById('cm-date'); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); dateInput.min = tomorrow.toISOString().split('T')[0]; // 2. INICIALIZAR GOOGLE MAPS (SOLO MADRID) window.initWidgetMap = function() { // Coordenadas que cubren aprox la Comunidad de Madrid para priorizar búsquedas const madridBounds = new google.maps.LatLngBounds( new google.maps.LatLng(40.3, -3.8), new google.maps.LatLng(40.6, -3.5) ); const optionsOrigin = { bounds: madridBounds, componentRestrictions: { country: "es" }, // Restringe resultados a España fields: ["geometry", "formatted_address"], strictBounds: false // Permite buscar fuera si es necesario, pero prioriza Madrid }; const optionsDest = { componentRestrictions: { country: "es" }, fields: ["geometry", "formatted_address"] }; new google.maps.places.Autocomplete(document.getElementById("cm-origin"), optionsOrigin); new google.maps.places.Autocomplete(document.getElementById("cm-destination"), optionsDest); }; // 3. BOTÓN CALCULAR PRECIO document.getElementById('cm-btn-calc').addEventListener('click', function() { const org = document.getElementById('cm-origin').value; const dst = document.getElementById('cm-destination').value; const date = document.getElementById('cm-date').value; const time = document.getElementById('cm-time').value; // Validaciones if(!org || !dst || !date || !time) { return alert("Por favor completa todos los campos del formulario."); } // VALIDACIÓN ESTRICTA DE ORIGEN (MADRID) const originLower = org.toLowerCase(); const allowedKeywords = ['madrid', 'barajas', 'aeropuerto', 'alcobendas', 'pozuelo', 'las rozas', 'majadahonda', 'getafe', 'leganes', 'alcorcon', 'fuenlabrada', 'mostoles', 'torrejon', 'coslada', 'rivas']; const isMadrid = allowedKeywords.some(keyword => originLower.includes(keyword)); if(!isMadrid) { // Si no detectamos una palabra clave de Madrid, avisamos. // Nota: Esto es un filtro de texto simple. Google Maps API backend valida coordenadas, pero esto ahorra peticiones. return alert("El servicio de recogida está limitado a la Comunidad de Madrid y Aeropuerto."); } const btn = document.getElementById('cm-btn-calc'); const originalText = btn.innerText; btn.innerText = 'CALCULANDO...'; btn.disabled = true; // LLAMADA A DISTANCE MATRIX const service = new google.maps.DistanceMatrixService(); service.getDistanceMatrix({ origins: [org], destinations: [dst], travelMode: 'DRIVING', unitSystem: google.maps.UnitSystem.METRIC }, function(resp, status) { btn.innerText = originalText; btn.disabled = false; if(status !== 'OK') { return alert("Error conectando con Google Maps. Por favor verifica tu conexión."); } // Verificar resultados de ruta if(!resp.rows[0].elements[0] || resp.rows[0].elements[0].status !== "OK") { return alert("No se pudo calcular una ruta por carretera entre estos puntos. Verifica las direcciones."); } const element = resp.rows[0].elements[0]; const distKm = element.distance.value / 1000; // Detectar si es Aeropuerto (Buscamos palabras clave en Origen o Destino) const isAirport = (org.toLowerCase().includes('aeropuerto') || org.toLowerCase().includes('barajas') || dst.toLowerCase().includes('aeropuerto') || dst.toLowerCase().includes('barajas')); renderVehicles(distKm, isAirport); }); }); // 4. RENDERIZAR RESULTADOS function renderVehicles(km, isAirport) { const list = document.getElementById('cm-vehicle-list'); list.innerHTML = ''; // Resumen del trayecto const tarifaName = isAirport ? "Tarifa Aeropuerto" : "Tarifa Estándar"; document.getElementById('cm-trip-summary').innerHTML = `Trayecto: <strong>${km.toFixed(1)} km</strong> | ${tarifaName}`; vehicles.forEach(v => { let priceHtml = ''; let finalPrice = 0; if(v.is_manual) { priceHtml = '<span class="cm-price-display" style="font-size:16px; color:#666">Consultar</span>'; } else { // LÓGICA DE PRECIO: // 1. Elegir base (Aeropuerto vs Estandar) const base = isAirport ? v.base_air : v.base_std; // 2. Calcular exceso de km (si > 20km) if (km <= 20) { finalPrice = base; } else { const extraKm = km - 20; finalPrice = base + (extraKm * v.extra); } // Redondear a 2 decimales finalPrice = Math.round(finalPrice * 100) / 100; priceHtml = `<span class="cm-price-display">${finalPrice}€</span>`; } // Crear tarjeta HTML const item = document.createElement('div'); item.className = 'cm-vehicle-card'; item.innerHTML = ` <div class="cm-v-img-box"><img src="${v.img}" alt="${v.name}"></div> <div class="cm-v-content"> <h4 class="cm-v-title">${v.name}</h4> <div class="cm-v-desc">${v.cap}</div> </div> <div class="cm-v-action"> ${priceHtml} ${v.is_manual ? `<a href="mailto:reservas@chofermadrid.com?subject=Reserva Sprinter 16&body=Hola, quiero consultar precio..." class="cm-select-btn" style="text-decoration:none">Contactar</a>` : `<button class="cm-select-btn" onclick="selectCar('${v.name}', ${finalPrice})">RESERVAR</button>`} </div> `; list.appendChild(item); }); // Guardar datos globales para el paso final window.tripData = { km: km, origin: document.getElementById('cm-origin').value, dest: document.getElementById('cm-destination').value }; goToStep('vehicles'); } // FUNCIONES DE NAVEGACIÓN window.goToStep = function(stepName) { document.querySelectorAll('.cm-slide').forEach(s => s.classList.remove('active')); document.getElementById('cm-step-'+stepName).classList.add('active'); // Scroll arriba suave document.getElementById('cm-booking-widget').scrollIntoView({behavior: 'smooth'}); } window.selectCar = function(name, price) { // Rellenar campos ocultos del formulario final document.getElementById('f_vehicle').value = name; document.getElementById('f_price').value = price; document.getElementById('f_distance').value = window.tripData.km; document.getElementById('f_origin').value = window.tripData.origin; document.getElementById('f_dest').value = window.tripData.dest; // Mostrar resumen visual document.getElementById('cm-selected-car-info').innerHTML = `<div style="background:#f9f9f9; padding:15px; border-left: 4px solid #000; margin-bottom:20px; border-radius:4px;"> <h4 style="margin:0 0 5px 0">${name}</h4> <div style="font-size:14px; color:#555;">${window.tripData.origin} ➝ ${window.tripData.dest}</div> <div style="margin-top:5px; font-weight:bold;">Total Estimado: ${price}€</div> </div>`; goToStep('final'); } // ENVÍO DEL FORMULARIO AL SERVIDOR const form = document.getElementById('cm-booking-form'); form.addEventListener('submit', function(e){ e.preventDefault(); const btn = form.querySelector('button[type="submit"]'); const originalText = btn.innerText; btn.disabled = true; btn.innerText = "PROCESANDO..."; const formData = new FormData(form); formData.append('action', 'cm_send_booking'); // Acción PHP formData.append('date', document.getElementById('cm-date').value); formData.append('time', document.getElementById('cm-time').value); // Fetch AJAX a WordPress fetch('<?php echo admin_url('admin-ajax.php'); ?>', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if(data.success) { goToStep('success'); } else { alert("Lo sentimos, hubo un error técnico al enviar el correo. Por favor contáctanos por teléfono."); btn.disabled = false; btn.innerText = originalText; } }) .catch(err => { console.error(err); alert("Error de conexión. Verifica tu internet."); btn.disabled = false; btn.innerText = originalText; }); }); }); </script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyComcP3W3JmNQ4RMMrS2wvQ9TBWJXnQbmM&libraries=places&callback=initWidgetMap" async defer></script> <?php return ob_get_clean(); } // Registrar el Shortcode add_shortcode('formulario_reserva_premium', 'cm_premium_booking_shortcode'); /* * MANEJADOR DE CORREOS (BACKEND) * Se conecta automáticamente con tu plugin WP Mail SMTP */ function cm_send_booking_handler() { // Recoger datos $v = $_POST; // Email del Administrador (donde quieres recibir los avisos) $to = get_option('admin_email'); // Si prefieres un email fijo, descomenta la siguiente línea: // $to = 'reservas@chofermadrid.com'; $subject = "Nueva Reserva Web: " . sanitize_text_field($v['fullname']); // Cuerpo del mensaje para el ADMIN $msg = "Nueva solicitud de transporte recibida desde la web:\n\n"; $msg .= "====================================\n"; $msg .= " DETALLES DEL TRAYECTO\n"; $msg .= "====================================\n"; $msg .= "Fecha: " . sanitize_text_field($v['date']) . " a las " . sanitize_text_field($v['time']) . "\n"; $msg .= "Origen: " . sanitize_text_field($v['origin']) . "\n"; $msg .= "Destino: " . sanitize_text_field($v['destination']) . "\n"; $msg .= "Distancia: " . sanitize_text_field($v['distance']) . " km\n\n"; $msg .= "====================================\n"; $msg .= " VEHÍCULO Y CLIENTE\n"; $msg .= "====================================\n"; $msg .= "Vehículo: " . sanitize_text_field($v['vehicle_id']) . "\n"; $msg .= "Precio Est.: " . sanitize_text_field($v['price']) . " EUR\n"; $msg .= "Cliente: " . sanitize_text_field($v['fullname']) . "\n"; $msg .= "Teléfono: " . sanitize_text_field($v['phone']) . "\n"; $msg .= "Email: " . sanitize_email($v['email']) . "\n"; $msg .= "Notas: " . sanitize_textarea_field($v['notes']) . "\n"; // Enviar correo Admin $sent = wp_mail($to, $subject, $msg); // Enviar confirmación al Cliente if($sent && !empty($v['email'])) { $client_subject = "Reserva recibida - Chofer Madrid"; $client_msg = "Hola " . sanitize_text_field($v['fullname']) . ",\n\n"; $client_msg .= "Hemos recibido correctamente tu solicitud de reserva para el día " . sanitize_text_field($v['date']) . ".\n\n"; $client_msg .= "Detalles: " . sanitize_text_field($v['origin']) . " -> " . sanitize_text_field($v['destination']) . "\n"; $client_msg .= "Vehículo: " . sanitize_text_field($v['vehicle_id']) . " (Precio aprox: " . sanitize_text_field($v['price']) . "€)\n\n"; $client_msg .= "IMPORTANTE: Esta no es una confirmación definitiva. Nuestro equipo revisará la disponibilidad y te contactará en breve para confirmar el servicio.\n\n"; $client_msg .= "Gracias por confiar en Chofer Madrid."; wp_mail(sanitize_email($v['email']), $client_subject, $client_msg); } if($sent) { wp_send_json_success(); } else { wp_send_json_error(); } } // Hooks para permitir llamadas AJAX logueados y no logueados add_action('wp_ajax_cm_send_booking', 'cm_send_booking_handler'); add_action('wp_ajax_nopriv_cm_send_booking', 'cm_send_booking_handler'); ?>
Calculando ruta...
ℹ️ Sin pago anticipado: Selecciona tu vehículo ahora. El pago se realiza directamente al conductor o vía enlace posterior.
WhatsApp ¿Tienes dudas? Escríbenos por WhatsApp

Finalizar Reserva

¡Solicitud Recibida!

⚠️ IMPORTANTE:
Esta solicitud NO es una confirmación definitiva.
  • Hemos recibido tus datos correctamente.
  • Verificaremos disponibilidad en breves minutos.
  • Nos pondremos en contacto contigo para confirmar.

Gracias por confiar en Chofer Madrid.