/** * 123VTC — vtc-distance.js * ───────────────────────────────────────────────────────────────────────────── * Calcule automatiquement la distance (km) entre le point de départ et * l'arrivée via l'API Google Maps, puis estime le prix selon la grille * tarifaire. Met à jour le champ caché `distance_km` avant la soumission. * * INSTALLATION * ──────────── * 1. Charger le script dans functions.php : * * add_action('wp_enqueue_scripts', function () { * // Remplacez YOUR_GOOGLE_MAPS_API_KEY par votre clé * wp_enqueue_script( * 'google-maps', * 'https://maps.googleapis.com/maps/api/js' * . '?key=YOUR_GOOGLE_MAPS_API_KEY' * . '&libraries=places' * . '&callback=vtcInitMaps', * [], * null, * true // chargé dans le footer * ); * wp_enqueue_script( * 'vtc-distance', * get_template_directory_uri() . '/js/vtc-distance.js', * ['google-maps'], * '1.0.0', * true * ); * }); * * 2. Ajouter ces champs dans votre formulaire HTML : * * * * * * * * *
* * ───────────────────────────────────────────────────────────────────────────── */ /* ─── Grille tarifaire (doit correspondre à functions.php) ─────────────────── */ var VTC_RATES = { matin: 1.80, // 06h–10h journee: 1.50, // 10h–16h soir: 1.80, // 16h–22h nuit: 1.90, // 22h–06h special: 1.90, // Dimanche & jours fériés minimum: 20.00 }; /* ─── Jours fériés français ───────────────────────────────────────────────── Recalculés côté JS pour l'aperçu uniquement. Le calcul officiel (et binding) reste dans functions.php côté serveur. */ function vtcGetEaster(year) { var a = year % 19, b = Math.floor(year / 100), c = year % 100; var d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25); var g = Math.floor((b - f + 1) / 3); var h = (19 * a + b - d - g + 15) % 30; var i = Math.floor(c / 4), k = c % 4; var l = (32 + 2 * e + 2 * i - h - k) % 7; var m = Math.floor((a + 11 * h + 22 * l) / 451); var month = Math.floor((h + l - 7 * m + 114) / 31) - 1; // 0-based var day = ((h + l - 7 * m + 114) % 31) + 1; return new Date(year, month, day); } function vtcIsPublicHoliday(date) { var y = date.getFullYear(); var easter = vtcGetEaster(y); var d = function (m, day) { return new Date(y, m - 1, day); }; var add = function (base, days) { var r = new Date(base); r.setDate(r.getDate() + days); return r; }; var fmt = function (dt) { return dt.getFullYear() + '-' + String(dt.getMonth() + 1).padStart(2, '0') + '-' + String(dt.getDate()).padStart(2, '0'); }; var target = fmt(date); var holidays = [ d(1,1), add(easter,1), d(5,1), d(5,8), add(easter,39), add(easter,49), d(7,14), d(8,15), d(11,1), d(11,11), d(12,25) ]; return holidays.some(function (h) { return fmt(h) === target; }); } /* ─── Calcul du tarif ────────────────────────────────────────────────────── */ function vtcGetRate(dateStr, timeStr) { if (!dateStr || !timeStr) return null; var date = new Date(dateStr); var hour = parseInt(timeStr.split(':')[0], 10); var isSun = date.getDay() === 0; var isFer = vtcIsPublicHoliday(date); if (isSun || isFer) return { rate: VTC_RATES.special, label: isSun ? 'Dimanche' : 'Jour férié' }; if (hour >= 6 && hour < 10) return { rate: VTC_RATES.matin, label: 'Matin (6h–10h)' }; if (hour >= 10 && hour < 16) return { rate: VTC_RATES.journee, label: 'Journée (10h–16h)' }; if (hour >= 16 && hour < 22) return { rate: VTC_RATES.soir, label: 'Soir (16h–22h)' }; return { rate: VTC_RATES.nuit, label: 'Nuit (22h–6h)' }; } function vtcCalculatePrice(distanceKm, dateStr, timeStr) { var info = vtcGetRate(dateStr, timeStr); if (!info) return null; var price = Math.round(distanceKm * info.rate * 100) / 100; return { price: Math.max(VTC_RATES.minimum, price), rate: info.rate, label: info.label, isMinimum: price < VTC_RATES.minimum }; } /* ─── Mise à jour de l'aperçu du prix ────────────────────────────────────── */ function vtcUpdatePreview(distanceKm) { var preview = document.getElementById('vtc_price_preview'); if (!preview) return; var dateStr = (document.getElementById('vtc_date') || {}).value || ''; var timeStr = (document.getElementById('vtc_time') || {}).value || ''; var result = vtcCalculatePrice(distanceKm, dateStr, timeStr); if (!result) { preview.innerHTML = ''; return; } var priceStr = result.price.toFixed(2).replace('.', ',') + '\u00a0€'; var minNote = result.isMinimum ? '(tarif minimum appliqué)' : '' + distanceKm.toFixed(1).replace('.', ',') + '\u00a0km × ' + result.rate.toFixed(2).replace('.', ',') + '\u00a0€/km'; preview.innerHTML = '
' + '
' + result.label + '
' + '
' + priceStr + '
' + minNote + '
'; } /* ─── Google Maps : Autocomplete + Distance Matrix ───────────────────────── */ var vtcAutocompletePickup = null; var vtcAutocompleteDropoff = null; var vtcDistanceService = null; var vtcLastDistanceKm = 0; window.vtcInitMaps = function () { var fieldPickup = document.getElementById('vtc_pickup'); var fieldDropoff = document.getElementById('vtc_dropoff'); if (!fieldPickup || !fieldDropoff) return; var options = { componentRestrictions: { country: 'fr' }, fields: ['formatted_address', 'geometry'] }; vtcAutocompletePickup = new google.maps.places.Autocomplete(fieldPickup, options); vtcAutocompleteDropoff = new google.maps.places.Autocomplete(fieldDropoff, options); vtcDistanceService = new google.maps.DistanceMatrixService(); vtcAutocompletePickup.addListener('place_changed', vtcComputeDistance); vtcAutocompleteDropoff.addListener('place_changed', vtcComputeDistance); }; function vtcComputeDistance() { var pickup = document.getElementById('vtc_pickup'); var dropoff = document.getElementById('vtc_dropoff'); var hidden = document.getElementById('vtc_distance_km'); var preview = document.getElementById('vtc_price_preview'); if (!pickup || !dropoff || !hidden) return; if (!pickup.value.trim() || !dropoff.value.trim()) return; // Indicateur de chargement if (preview) { preview.innerHTML = '
Calcul en cours…
'; } vtcDistanceService.getDistanceMatrix( { origins: [pickup.value], destinations: [dropoff.value], travelMode: google.maps.TravelMode.DRIVING, unitSystem: google.maps.UnitSystem.METRIC, region: 'fr' }, function (response, status) { if (status !== 'OK') { if (preview) { preview.innerHTML = '
Impossible de calculer la distance. Vérifiez les adresses.
'; } hidden.value = '0'; return; } var element = response.rows[0].elements[0]; if (element.status !== 'OK') { if (preview) { preview.innerHTML = '
Trajet introuvable entre ces deux adresses.
'; } hidden.value = '0'; return; } // Distance en km (arrondie à 1 décimale) var meters = element.distance.value; var distanceKm = Math.round(meters / 100) / 10; // ex: 12340m → 12.3km vtcLastDistanceKm = distanceKm; hidden.value = distanceKm; vtcUpdatePreview(distanceKm); } ); } /* ─── Recalcul du prix si date/heure changent ────────────────────────────── */ function vtcOnDateTimeChange() { if (vtcLastDistanceKm > 0) { vtcUpdatePreview(vtcLastDistanceKm); } } /* ─── Vérification avant soumission ─────────────────────────────────────── */ function vtcValidateBeforeSubmit(e) { var hidden = document.getElementById('vtc_distance_km'); if (!hidden || parseFloat(hidden.value) <= 0) { e.preventDefault(); alert('Veuillez sélectionner le départ et l\'arrivée dans les suggestions pour calculer la distance.'); return false; } } /* ─── Initialisation des écouteurs ────────────────────────────────────────── */ document.addEventListener('DOMContentLoaded', function () { var dateField = document.getElementById('vtc_date'); var timeField = document.getElementById('vtc_time'); var form = document.querySelector('form[action*="admin-post"]'); if (dateField) dateField.addEventListener('change', vtcOnDateTimeChange); if (timeField) timeField.addEventListener('change', vtcOnDateTimeChange); if (form) form.addEventListener('submit', vtcValidateBeforeSubmit); }); https://www.123vtc.com/wp-sitemap-posts-post-1.xmlhttps://www.123vtc.com/wp-sitemap-posts-page-1.xmlhttps://www.123vtc.com/wp-sitemap-taxonomies-category-1.xml