<?php
/*
* SISTEMA DE RESERVAS CHOFER MADRID
* Autor: Generado para ChoferMadrid
* Instalación: Pegar en functions.php
* Uso: Insertar shortcode
Reserva tu traslado privado
Calculando ruta...
ℹ️ Sin pago anticipado: Selecciona tu vehículo ahora. El pago se realiza directamente al conductor o vía enlace posterior.
¿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');
?>
Gracias por confiar en Chofer Madrid.