diff --git a/modules/holidays/holidays.css b/modules/holidays/holidays.css index f4c78f2..a794f3c 100644 --- a/modules/holidays/holidays.css +++ b/modules/holidays/holidays.css @@ -788,7 +788,7 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover { .hol-cp-header { background: var(--bg-page); - padding: 12px 15px; + padding: 6px 8px; display: flex; justify-content: space-between; align-items: center; @@ -1485,3 +1485,157 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover { justify-content: center; } } + +/* --- EN-TÊTE DES ÉTAPES ROADTRIP --- */ +.hol-step-header { + padding: 6px 8px; + border-bottom: 1px solid var(--border-light); + display: flex; + flex-direction: column; + gap: 8px; +} + +/* Ligne 1 : Icône + Titre + Poignée */ +.hol-step-row-top { + display: flex; + align-items: flex-start; + gap: 10px; + width: 100%; +} + +.hol-step-icon { + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1rem; + flex-shrink: 0; + margin-top: 2px; +} + +.hol-step-title { + flex: 1; + font-size: 1.05rem; + font-weight: 700; + color: var(--text-main); + line-height: 1.3; + word-break: break-word; + cursor: pointer; + padding-top: 4px; +} + +.hol-step-controls { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; +} + +.hol-drag-handle { + color: #cbd5e1; + font-size: 1.4rem; + cursor: grab; + user-select: none; + line-height: 1; +} + +.hol-step-arrows { + display: flex; + flex-direction: column; + gap: 0; +} + +.hol-step-arrows button { + background: transparent; + border: none; + color: #94a3b8; + font-size: 0.75rem; + padding: 4px 8px; + cursor: pointer; + line-height: 1; +} + +/* Ligne 2 : Tags/Dates + Prix/Actions */ +.hol-step-row-bottom { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 5px; + width: 100%; +} + +.hol-step-meta { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; +} + +.hol-tag-return { + background: #fff7ed; + color: #ea580c; + padding: 2px 6px; + border-radius: 4px; + font-size: 0.75rem; + font-weight: 700; + border: 1px solid #ffedd5; + line-height: 1.2; +} + +.hol-step-date { + font-size: 0.8rem; + color: #64748b; + background: var(--bg-page); + padding: 2px 6px; + border-radius: 4px; + border: 1px solid var(--border-light); + line-height: 1.2; +} + +.hol-step-actions { + display: flex; + align-items: center; + gap: 8px; + margin-left: auto; /* Pousse le prix et les boutons tout à droite */ +} + +.hol-step-price { + font-weight: 800; + color: var(--primary); + white-space: nowrap; +} + +/* Optimisation Mobile */ +@media (max-width: 768px) { + .hol-step-row-bottom { + padding-left: 0; /* Sur mobile, on prend toute la largeur pour éviter d'écraser les boutons */ + } +} + +/* --- LIGNES DE DÉPENSES --- */ +.hol-expense-main { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 8px; +} + +.hol-expense-name { + flex: 1; + min-width: 0; + word-break: break-word; +} + +.hol-expense-price-group { + display: inline-flex; + align-items: center; + gap: 6px; + white-space: nowrap; /* Interdit le passage à la ligne */ + flex-shrink: 0; /* Empêche le bloc d'être écrasé */ + margin-top: 2px; +} + +.hol-expense-price-group .status-paid, +.hol-expense-price-group .status-pending { + line-height: 1; +} diff --git a/modules/holidays/holidays.js b/modules/holidays/holidays.js index 35df442..51f2d37 100644 --- a/modules/holidays/holidays.js +++ b/modules/holidays/holidays.js @@ -257,10 +257,11 @@ function initDetailMap() { Promise.all(routePromises).then((results) => { results.sort((a, b) => a.index - b.index); - // Compteur global de distance + // 🔥 NOUVEAU : Compteurs et HTML de la modale let totalTripDistance = 0; + let totalTripDuration = 0; // en secondes + let transitDetailsHtml = ""; - // Détection de l'étape de retour (Ligne Orange) let returnStartIndex = latlngs.length - 2; if ( typeof window.GLOBAL_RETURN_STEP_ID !== "undefined" && @@ -269,22 +270,21 @@ function initDetailMap() { const customReturnStep = MAP_POINTS.findIndex( (p) => p.sort_order == window.GLOBAL_RETURN_STEP_ID, ); - if (customReturnStep > 0) { - returnStartIndex = customReturnStep; - } + if (customReturnStep > 0) returnStartIndex = customReturnStep; } results.forEach((res) => { const i = res.index; - let routeColor = "#3b82f6"; - let routeWeight = window.innerWidth < 768 ? 4 : 6; - let routeDash = null; - - if (i >= returnStartIndex) { - routeColor = "#f97316"; - routeWeight = window.innerWidth < 768 ? 3 : 4; - routeDash = "10, 10"; - } + let routeColor = i >= returnStartIndex ? "#f97316" : "#3b82f6"; + let routeWeight = + window.innerWidth < 768 + ? i >= returnStartIndex + ? 3 + : 4 + : i >= returnStartIndex + ? 4 + : 6; + let routeDash = i >= returnStartIndex ? "10, 10" : null; if (res.data && res.data.code === "Ok" && res.data.routes.length > 0) { const routeCoords = res.data.routes[0].geometry.coordinates.map( @@ -299,30 +299,54 @@ function initDetailMap() { lineJoin: "round", }).addTo(detailMap); - // CALCUL AUTO DU COUT ET KILOMÈTRES + // CALCULS const distanceKm = res.data.routes[0].distance / 1000; + const durationSec = res.data.routes[0].duration; // Durée en secondes + totalTripDistance += distanceKm; + totalTripDuration += durationSec; const fuelL100 = window.VEHICLE_CONSUMPTION || 7; const fuelPrice = window.FUEL_PRICE || 1.85; const cost = (distanceKm / 100) * fuelL100 * fuelPrice; - const targetOrder = MAP_POINTS[res.index + 1].sort_order; + // 1. ON DÉCLARE LES POINTS D'ABORD + const startPt = MAP_POINTS[res.index]; + const endPt = MAP_POINTS[res.index + 1]; + + // 2. ON SAUVEGARDE EN MÉMOIRE ENSUITE (Correction du bug !) + window.TRANSIT_DATA = window.TRANSIT_DATA || {}; + window.TRANSIT_DATA[endPt.sort_order] = { + sec: durationSec, + from: startPt.location_name, + cost: cost, + }; + + // 🔥 CONSTRUCTION DU CONTENU DE LA MODALE + transitDetailsHtml += ` +
Aucun trajet calculé.
'; } }); - } - // 4. Chargement des badges Météo - if (typeof MAP_POINTS !== "undefined") { - MAP_POINTS.forEach((pt) => { - if (typeof loadWeatherForStep === "function") { - loadWeatherForStep(pt); + // 5. Fix Leaflet Resize + setTimeout(() => { + if (detailMap) { + detailMap.invalidateSize(); } - }); - } + }, 300); - // 5. Fix Leaflet Resize - setTimeout(() => { - if (detailMap) { - detailMap.invalidateSize(); + function drawFallbackLine(coords, color, weight) { + L.polyline(coords, { + color: color, + weight: weight || 3, + dashArray: "8, 8", + opacity: 0.7, + }).addTo(detailMap); } - }, 300); - - function drawFallbackLine(coords, color, weight) { - L.polyline(coords, { - color: color, - weight: weight || 3, - dashArray: "8, 8", - opacity: 0.7, - }).addTo(detailMap); } } @@ -706,6 +723,26 @@ function openPlanningModal(step) { let validItems = step.items.filter((it) => it.name !== "PF_TECHNICAL_POINT"); + window.CURRENT_PLANNING_STEP = step; // Mémorise l'étape en cours + + if (window.TRANSIT_DATA && window.TRANSIT_DATA[step.sort_order]) { + // Est-ce qu'on a déjà planifié ou ajouté ce trajet ? + let hasTransit = validItems.some((it) => it.expense_context === "transit"); + if (!hasTransit) { + const tData = window.TRANSIT_DATA[step.sort_order]; + const h = Math.max(1, Math.round(tData.sec / 3600)); // Arrondi en heures + validItems.push({ + id: "virtual-transit", + name: `Essence depuis ${tData.from}`, // Garde ce nom pour lier avec le budget + category: "transport", + expense_context: "transit", + duration: h, + notes: `Trajet GPS (${Math.round(tData.sec / 60)} min). Déplacez pour planifier la route.`, + is_virtual: true, + }); + } + } + if (!step.step_start_date || !step.step_end_date) { container.innerHTML = `${tr("hdl_js_missing_dates_msg")}