diff --git a/modules/holidays/holidays.css b/modules/holidays/holidays.css index 368b4ac..1ac5583 100644 --- a/modules/holidays/holidays.css +++ b/modules/holidays/holidays.css @@ -3,7 +3,6 @@ /* ========================================================================== 1. VARIABLES & BASE ========================================================================== */ - body.no-scroll { overflow: hidden !important; } @@ -224,7 +223,6 @@ body.no-scroll { .hol-hero-overlay { position: absolute; inset: 0; - z-index: 1; } @@ -279,7 +277,6 @@ body.no-scroll { gap: 6px; } -/* --- PILLS (Metrics Route & Logistique) --- */ .hol-hero-pills { display: flex; gap: 10px; @@ -320,7 +317,6 @@ body.no-scroll { opacity: 1; } -/* --- FINANCES GLOBALES --- */ .hol-hero-finances { display: flex; justify-content: space-between; @@ -366,7 +362,6 @@ body.no-scroll { .hol-label-paid-text { color: #a7f3d0; } - .hol-label-saved-text { color: #bfdbfe; } @@ -436,22 +431,18 @@ body.no-scroll { align-items: center; gap: 4px; } - .hol-legend-color { display: inline-block; width: 12px; height: 4px; border-radius: 2px; } - .hol-legend-color.aller { background: #3b82f6; } - .hol-legend-color.interm { background: #8b5cf6; } - .hol-legend-color.retour { background: transparent; height: 0; @@ -544,12 +535,10 @@ body.no-scroll { background: #ecfdf5; color: #059669; } - .hol-step-icon-destination { background: #fef2f2; color: #e11d48; } - .hol-step-icon-stop { background: transparent; color: #64748b; @@ -698,7 +687,6 @@ body.no-scroll { color: #10b981; margin-left: 5px; } - .status-pending { color: #f59e0b; margin-left: 5px; @@ -820,7 +808,7 @@ body.no-scroll { } /* ========================================================================== - 9. MODALES & DIALOGUES + 9. MODALES & DIALOGUES (Sécurisation complète) ========================================================================== */ .pf-modal { display: none; @@ -852,7 +840,6 @@ body.no-scroll { margin: auto; } -/* --- Modale Étape (Checkpoint) --- */ .hol-modal-cp { max-width: 600px; padding: 0; @@ -1014,6 +1001,15 @@ body.no-scroll { display: none; } +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 24px; + padding-top: 16px; + border-top: 1px solid var(--border-light); +} + /* --- Modale Planning Global --- */ .hol-modal-planning { max-width: 95vw; @@ -1107,15 +1103,6 @@ body.no-scroll { gap: 8px; } -.modal-footer { - display: flex; - justify-content: flex-end; - gap: 10px; - margin-top: 24px; - padding-top: 16px; - border-top: 1px solid var(--border-light); -} - /* ========================================================================== 10. PLANNING DRAG & DROP ========================================================================== */ @@ -1511,3 +1498,58 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover { display: none !important; } } + +/* Modale JS d'ajout de dépense */ +.hol-form-row { + display: flex; + flex-direction: column; + gap: 8px; + padding: 12px; + background: var(--bg-page); + border-radius: 8px; + border: 1px solid var(--border-light); + margin-bottom: 10px; +} +.hol-form-inner { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + width: 100%; +} +.hol-form-select { + flex: 0 0 50px; + padding: 8px 4px; + font-size: 1.1rem; + cursor: pointer; + margin: 0 !important; +} +.hol-form-text { + flex: 1; + min-width: 140px; + padding: 8px; + font-size: 0.9rem; + margin: 0 !important; +} +.hol-form-number { + flex: 0 0 90px; + text-align: right; + padding: 8px; + font-size: 0.9rem; + font-weight: bold; + color: var(--primary); + margin: 0 !important; +} +.hol-form-paid-label { + display: flex; + align-items: center; + cursor: pointer; + flex: 0 0 60px; + margin: 0 !important; +} +.hol-form-paid-text { + font-size: 0.8rem; + margin-left: 6px; + font-weight: 600; + color: var(--text-muted); +} diff --git a/modules/holidays/holidays.js b/modules/holidays/holidays.js index 9076d8a..578641b 100644 --- a/modules/holidays/holidays.js +++ b/modules/holidays/holidays.js @@ -27,6 +27,42 @@ function tr(key) { var currentLang = document.documentElement.lang === "ca" ? "ca-ES" : "fr-FR"; var selectedItemIdForMove = null; +// ============================================================================ +// UTILITAIRES DE GESTION DES MODALES / ONGLETS +// ============================================================================ + +function toggleTripMap() { + const mapWrapper = document.getElementById("tripMapWrapper"); + if (!mapWrapper) return; + + if (mapWrapper.classList.contains("hol-hidden")) { + mapWrapper.classList.remove("hol-hidden"); + if (typeof detailMap !== "undefined" && detailMap !== null) { + setTimeout(() => detailMap.invalidateSize(), 100); + } + } else { + mapWrapper.classList.add("hol-hidden"); + } +} + +function switchHolTab(tabId, btn) { + document + .querySelectorAll(".hol-tab-btn") + .forEach((b) => b.classList.remove("active")); + document + .querySelectorAll(".hol-tab-content") + .forEach((c) => (c.style.display = "none")); + if (btn) btn.classList.add("active"); + const tab = document.getElementById(tabId); + if (tab) tab.style.display = "block"; +} + +function closeCheckpointModal() { + const modal = document.getElementById("checkpointModal"); + if (modal) modal.style.display = "none"; + document.body.classList.remove("no-scroll"); +} + // ============================================================================ // UTILITAIRES MÉTÉO // ============================================================================ @@ -98,30 +134,41 @@ function openHolidayModal(mode) { const form = document.getElementById("holidayForm"); const btnDelete = document.getElementById("btn_delete"); - form.reset(); - document.getElementById("inp_id").value = ""; + if (form) form.reset(); + if (document.getElementById("inp_id")) + document.getElementById("inp_id").value = ""; if (mode === "add") { - document.getElementById("modalTitle").innerText = tr("hdl_modal_title"); - btnDelete.style.display = "none"; + if (document.getElementById("modalTitle")) + document.getElementById("modalTitle").innerText = tr("hdl_modal_title"); + if (btnDelete) btnDelete.style.display = "none"; // Réinitialisation du calendrier global - document.getElementById("inp_start").value = ""; - document.getElementById("inp_end").value = ""; + if (document.getElementById("inp_start")) + document.getElementById("inp_start").value = ""; + if (document.getElementById("inp_end")) + document.getElementById("inp_end").value = ""; initHolFlatpickr("", ""); } else { - document.getElementById("modalTitle").innerText = tr( - "hdl_quick_edit_title", - ); - btnDelete.style.display = "block"; + if (document.getElementById("modalTitle")) + document.getElementById("modalTitle").innerText = tr( + "hdl_quick_edit_title", + ); + if (btnDelete) btnDelete.style.display = "block"; } - modal.style.display = "flex"; - setTimeout(() => document.getElementById("inp_title").focus(), 100); + if (modal) { + modal.style.display = "flex"; + setTimeout(() => { + if (document.getElementById("inp_title")) + document.getElementById("inp_title").focus(); + }, 100); + } } function closeHolidayModal() { - document.getElementById("holidayModal").style.display = "none"; + const modal = document.getElementById("holidayModal"); + if (modal) modal.style.display = "none"; document.body.classList.remove("no-scroll"); } @@ -132,14 +179,23 @@ function editHoliday(data) { openHolidayModal("edit"); - document.getElementById("inp_id").value = h.id; - document.getElementById("inp_title").value = h.title; - document.getElementById("inp_status").value = h.status; - document.getElementById("inp_period").value = h.period_hint || ""; - document.getElementById("inp_food").value = - h.budget_food > 0 ? h.budget_food : ""; - document.getElementById("inp_extra").value = - h.budget_extra > 0 ? h.budget_extra : ""; + if (document.getElementById("inp_id")) + document.getElementById("inp_id").value = h.id; + if (document.getElementById("inp_title")) + document.getElementById("inp_title").value = h.title; + if (document.getElementById("inp_status")) + document.getElementById("inp_status").value = h.status; + if (document.getElementById("inp_period")) + document.getElementById("inp_period").value = h.period_hint || ""; + + if (document.getElementById("inp_food")) { + document.getElementById("inp_food").value = + h.budget_food > 0 ? h.budget_food : ""; + } + if (document.getElementById("inp_extra")) { + document.getElementById("inp_extra").value = + h.budget_extra > 0 ? h.budget_extra : ""; + } if (document.getElementById("inp_notes")) { document.getElementById("inp_notes").value = h.notes || ""; @@ -153,8 +209,10 @@ function editHoliday(data) { // Initialisation des dates const startD = h.start_date || ""; const endD = h.end_date || ""; - document.getElementById("inp_start").value = startD; - document.getElementById("inp_end").value = endD; + if (document.getElementById("inp_start")) + document.getElementById("inp_start").value = startD; + if (document.getElementById("inp_end")) + document.getElementById("inp_end").value = endD; initHolFlatpickr(startD, endD); } @@ -489,22 +547,35 @@ function panMapTo(lat, lng) { // ============================================================================ function openCheckpointModal(mode, data = null) { const searchBlock = document.getElementById("cpSearchBlock"); + const formBlock = document.getElementById("formCheckpoint"); const container = document.getElementById("cpExpensesContainer"); const btnDel = document.getElementById("btnDeleteCp"); const insertGroup = document.getElementById("cp_insert_group"); const insertSelect = document.getElementById("cp_insert_after"); - container.innerHTML = ""; + if (container) container.innerHTML = ""; + + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = ""; + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = ""; if (document.getElementById("searchPlaceInput")) document.getElementById("searchPlaceInput").value = ""; if (document.getElementById("searchResults")) document.getElementById("searchResults").innerHTML = ""; - const holidayData = JSON.parse( - document.getElementById("holidayDataJson").textContent, - ).main; - let tripStartDate = - holidayData.start_date || new Date().toISOString().split("T")[0]; + if (searchBlock) searchBlock.style.display = "block"; + if (insertGroup) insertGroup.style.display = "block"; + + // Sécurisation de la lecture du JSON (Empêche un crash sur la vue "Liste") + const holidayDataJsonEl = document.getElementById("holidayDataJson"); + let tripStartDate = new Date().toISOString().split("T")[0]; + if (holidayDataJsonEl) { + try { + const parsed = JSON.parse(holidayDataJsonEl.textContent); + tripStartDate = parsed.main.start_date || tripStartDate; + } catch (e) {} + } // ========================================== // CONFIGURATION DES DATES FLATPICKR @@ -513,68 +584,89 @@ function openCheckpointModal(mode, data = null) { let defaultEnd = ""; if (mode === "add") { - document.getElementById("cpModalTitle").innerText = tr("hdl_btn_add_step"); - btnDel.style.display = "none"; - searchBlock.style.display = "block"; - insertGroup.style.display = "block"; + if (document.getElementById("cpModalTitle")) + document.getElementById("cpModalTitle").innerText = + tr("hdl_btn_add_step"); + if (formBlock) formBlock.style.display = "none"; + if (btnDel) btnDel.style.display = "none"; - document.getElementById("cp_old_sort_order").value = ""; - document.getElementById("cp_name").value = ""; + if (document.getElementById("cp_old_sort_order")) + document.getElementById("cp_old_sort_order").value = ""; + if (document.getElementById("cp_name")) + document.getElementById("cp_name").value = ""; switchCpTab("info"); if (document.getElementById("cp_step_type")) { document.getElementById("cp_step_type").value = "stop"; - toggleStepDates("stop"); + if (typeof toggleStepDates === "function") toggleStepDates("stop"); } - if (document.getElementById("cp_set_as_return")) + + if (document.getElementById("cp_set_as_return")) { document.getElementById("cp_set_as_return").checked = false; + } let lastDate = tripStartDate; - if (window.MAP_POINTS && window.MAP_POINTS.length > 0) { + if ( + typeof window.MAP_POINTS !== "undefined" && + window.MAP_POINTS.length > 0 + ) { const lastStep = window.MAP_POINTS[window.MAP_POINTS.length - 1]; lastDate = lastStep.step_end_date || lastStep.step_start_date || tripStartDate; } - insertSelect.innerHTML = ``; - if (window.MAP_POINTS && window.MAP_POINTS.length > 0) { - window.MAP_POINTS.forEach((step) => { - let dateStr = ""; - if ( - step.step_start_date && - step.step_end_date && - step.step_start_date !== step.step_end_date - ) { - dateStr = ` (${new Date(step.step_start_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })} > ${new Date(step.step_end_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })})`; - } else if (step.step_start_date) { - dateStr = ` (${new Date(step.step_start_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })})`; - } - insertSelect.innerHTML += ``; - }); + if (insertSelect) { + insertSelect.innerHTML = ``; + if ( + typeof window.MAP_POINTS !== "undefined" && + window.MAP_POINTS.length > 0 + ) { + window.MAP_POINTS.forEach((step) => { + let dateStr = ""; + if ( + step.step_start_date && + step.step_end_date && + step.step_start_date !== step.step_end_date + ) { + dateStr = ` (${new Date(step.step_start_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })} > ${new Date(step.step_end_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })})`; + } else if (step.step_start_date) { + dateStr = ` (${new Date(step.step_start_date).toLocaleDateString(window.appLang, { day: "2-digit", month: "2-digit" })})`; + } + insertSelect.innerHTML += ``; + }); + } } defaultStart = lastDate; defaultEnd = lastDate; addCpExpenseLine(); } else if (mode === "edit" && data) { - document.getElementById("cpModalTitle").innerText = tr("hdl_js_edit_step"); - btnDel.style.display = "block"; - searchBlock.style.display = "none"; - insertGroup.style.display = "none"; + if (document.getElementById("cpModalTitle")) + document.getElementById("cpModalTitle").innerText = + tr("hdl_js_edit_step"); + if (formBlock) formBlock.style.display = "block"; + if (btnDel) btnDel.style.display = "block"; + if (searchBlock) searchBlock.style.display = "none"; + if (insertGroup) insertGroup.style.display = "none"; switchCpTab("prog"); - document.getElementById("cp_lat").value = data.lat; - document.getElementById("cp_lng").value = data.lng; - document.getElementById("cp_old_sort_order").value = data.sort_order; - document.getElementById("cp_name").value = data.location_name; + if (document.getElementById("cp_lat")) + document.getElementById("cp_lat").value = data.lat; + if (document.getElementById("cp_lng")) + document.getElementById("cp_lng").value = data.lng; + if (document.getElementById("cp_old_sort_order")) + document.getElementById("cp_old_sort_order").value = data.sort_order; + if (document.getElementById("cp_name")) + document.getElementById("cp_name").value = data.location_name; if (document.getElementById("cp_step_type")) { const type = data.step_type || "stop"; document.getElementById("cp_step_type").value = type; - toggleStepDates(type); + if (typeof toggleStepDates === "function") toggleStepDates(type); } + if (document.getElementById("cp_set_as_return")) { document.getElementById("cp_set_as_return").checked = window.GLOBAL_RETURN_STEP_ID == data.sort_order; @@ -610,50 +702,62 @@ function openCheckpointModal(mode, data = null) { } } - // Destruction et ré-instanciation de Flatpickr pour forcer le saut au bon mois - if (cpDateRangePicker) cpDateRangePicker.destroy(); + // Destruction et ré-instanciation de Flatpickr + if (typeof cpDateRangePicker !== "undefined" && cpDateRangePicker) { + cpDateRangePicker.destroy(); + } - document.getElementById("cp_start_date").value = defaultStart; - document.getElementById("cp_end_date").value = defaultEnd; + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = defaultStart; + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = defaultEnd; - cpDateRangePicker = flatpickr("#cp_date_range", { - mode: "range", - altInput: true, // 💡 NOUVEAU : Crée un champ de présentation séparé - altFormat: "d/m", // 💡 NOUVEAU : Format ultra compact (ex: 15/08) - dateFormat: "Y-m-d", // Format technique (MariaDB) conservé en arrière-plan - defaultDate: - defaultStart && defaultEnd && defaultStart !== defaultEnd - ? [defaultStart, defaultEnd] - : [defaultStart], - locale: window.appLang === "ca-ES" ? "cat" : "fr", - onChange: function (selectedDates, dateStr, instance) { - if (selectedDates.length === 2) { - document.getElementById("cp_start_date").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); - document.getElementById("cp_end_date").value = instance.formatDate( - selectedDates[1], - "Y-m-d", - ); - } else if (selectedDates.length === 1) { - document.getElementById("cp_start_date").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); - document.getElementById("cp_end_date").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); - } else { - document.getElementById("cp_start_date").value = ""; - document.getElementById("cp_end_date").value = ""; - } - }, - }); + if (typeof flatpickr !== "undefined") { + cpDateRangePicker = flatpickr("#cp_date_range", { + mode: "range", + altInput: true, + altFormat: "d/m", + dateFormat: "Y-m-d", + defaultDate: + defaultStart && defaultEnd && defaultStart !== defaultEnd + ? [defaultStart, defaultEnd] + : [defaultStart], + locale: window.appLang === "ca-ES" ? "cat" : "fr", + onChange: function (selectedDates, dateStr, instance) { + if (selectedDates.length === 2) { + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = + instance.formatDate(selectedDates[0], "Y-m-d"); + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = instance.formatDate( + selectedDates[1], + "Y-m-d", + ); + } else if (selectedDates.length === 1) { + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = + instance.formatDate(selectedDates[0], "Y-m-d"); + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = instance.formatDate( + selectedDates[0], + "Y-m-d", + ); + } else { + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = ""; + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = ""; + } + }, + }); + } - document.getElementById("checkpointModal").style.display = "flex"; - document.body.classList.add("no-scroll"); + // AFFICHER LA MODALE PROPREMENT + const modal = document.getElementById("checkpointModal"); + if (modal) { + modal.style.display = "flex"; + document.body.classList.add("no-scroll"); + } } // ========================================== @@ -663,12 +767,13 @@ let holDateRangePicker = null; function initHolFlatpickr(defaultStart, defaultEnd) { if (holDateRangePicker) holDateRangePicker.destroy(); + if (typeof flatpickr === "undefined") return; holDateRangePicker = flatpickr("#hol_date_range", { mode: "range", altInput: true, - altFormat: "d/m", // 💡 Format ultra compact (ex: 15/08) - dateFormat: "Y-m-d", // 💡 Le vrai format envoyé au serveur + altFormat: "d/m", + dateFormat: "Y-m-d", defaultDate: defaultStart && defaultEnd && defaultStart !== defaultEnd ? [defaultStart, defaultEnd] @@ -678,26 +783,32 @@ function initHolFlatpickr(defaultStart, defaultEnd) { locale: window.appLang === "ca-ES" ? "cat" : "fr", onChange: function (selectedDates, dateStr, instance) { if (selectedDates.length === 2) { - document.getElementById("inp_start").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); - document.getElementById("inp_end").value = instance.formatDate( - selectedDates[1], - "Y-m-d", - ); + if (document.getElementById("inp_start")) + document.getElementById("inp_start").value = instance.formatDate( + selectedDates[0], + "Y-m-d", + ); + if (document.getElementById("inp_end")) + document.getElementById("inp_end").value = instance.formatDate( + selectedDates[1], + "Y-m-d", + ); } else if (selectedDates.length === 1) { - document.getElementById("inp_start").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); - document.getElementById("inp_end").value = instance.formatDate( - selectedDates[0], - "Y-m-d", - ); + if (document.getElementById("inp_start")) + document.getElementById("inp_start").value = instance.formatDate( + selectedDates[0], + "Y-m-d", + ); + if (document.getElementById("inp_end")) + document.getElementById("inp_end").value = instance.formatDate( + selectedDates[0], + "Y-m-d", + ); } else { - document.getElementById("inp_start").value = ""; - document.getElementById("inp_end").value = ""; + if (document.getElementById("inp_start")) + document.getElementById("inp_start").value = ""; + if (document.getElementById("inp_end")) + document.getElementById("inp_end").value = ""; } }, }); @@ -710,19 +821,27 @@ function switchCpTab(tabId) { const tabProg = document.getElementById("cpTabProg"); if (tabId === "info") { - btnInfo.style.borderBottomColor = "var(--primary)"; - btnInfo.style.color = "var(--primary)"; - btnProg.style.borderBottomColor = "transparent"; - btnProg.style.color = "var(--text-muted)"; - tabInfo.style.display = "block"; - tabProg.style.display = "none"; + if (btnInfo) { + btnInfo.style.borderBottomColor = "var(--primary)"; + btnInfo.style.color = "var(--primary)"; + } + if (btnProg) { + btnProg.style.borderBottomColor = "transparent"; + btnProg.style.color = "var(--text-muted)"; + } + if (tabInfo) tabInfo.style.display = "block"; + if (tabProg) tabProg.style.display = "none"; } else { - btnProg.style.borderBottomColor = "var(--primary)"; - btnProg.style.color = "var(--primary)"; - btnInfo.style.borderBottomColor = "transparent"; - btnInfo.style.color = "var(--text-muted)"; - tabProg.style.display = "block"; - tabInfo.style.display = "none"; + if (btnProg) { + btnProg.style.borderBottomColor = "var(--primary)"; + btnProg.style.color = "var(--primary)"; + } + if (btnInfo) { + btnInfo.style.borderBottomColor = "transparent"; + btnInfo.style.color = "var(--text-muted)"; + } + if (tabProg) tabProg.style.display = "block"; + if (tabInfo) tabInfo.style.display = "none"; } } @@ -730,8 +849,10 @@ function injectDynamicDates(selectEl) { const selectedOpt = selectEl.options[selectEl.selectedIndex]; if (selectedOpt && selectedOpt.dataset.enddate) { const dateToSet = selectedOpt.dataset.enddate; - document.getElementById("cp_start_date").value = dateToSet; - document.getElementById("cp_end_date").value = dateToSet; + if (document.getElementById("cp_start_date")) + document.getElementById("cp_start_date").value = dateToSet; + if (document.getElementById("cp_end_date")) + document.getElementById("cp_end_date").value = dateToSet; // On met à jour l'UI du calendrier if (cpDateRangePicker) { @@ -745,6 +866,8 @@ function searchPlace() { if (q.length < 3) return; const resultsDiv = document.getElementById("searchResults"); + if (!resultsDiv) return; + resultsDiv.innerHTML = `${tr("hdl_js_search_loading")}`; fetch( @@ -777,10 +900,14 @@ function searchPlace() { } function selectPlace(lat, lng, fullName) { - document.getElementById("cp_lat").value = lat; - document.getElementById("cp_lng").value = lng; - document.getElementById("cp_name").value = fullName.split(",")[0].trim(); - document.getElementById("formCheckpoint").style.display = "block"; + if (document.getElementById("cp_lat")) + document.getElementById("cp_lat").value = lat; + if (document.getElementById("cp_lng")) + document.getElementById("cp_lng").value = lng; + if (document.getElementById("cp_name")) + document.getElementById("cp_name").value = fullName.split(",")[0].trim(); + if (document.getElementById("formCheckpoint")) + document.getElementById("formCheckpoint").style.display = "block"; } function addCpExpenseLine( @@ -798,6 +925,8 @@ function addCpExpenseLine( itemLng = "", ) { const container = document.getElementById("cpExpensesContainer"); + if (!container) return; // Sécurité si l'élément n'existe pas dans le DOM (ex: page liste) + const div = document.createElement("div"); div.className = "hol-form-row"; const isChecked = isPaid == 1 ? "checked" : ""; @@ -858,7 +987,7 @@ function toggleItemSearch(btn) { searchBox.style.display === "none" ? "block" : "none"; } -function searchItemPlace(btn) { +async function searchItemPlace(btn) { const container = btn.closest(".hol-item-search-box"); const input = container.querySelector(".item-search-input"); const resultsDiv = container.querySelector(".item-search-results"); @@ -867,34 +996,33 @@ function searchItemPlace(btn) { resultsDiv.innerHTML = `Recherche en cours...`; - fetch( - "/modules/holidays/includes/api/geocode.php?limit=5&q=" + - encodeURIComponent(q), - ) - .then((res) => res.json()) - .then((data) => { - resultsDiv.innerHTML = ""; - if (data.error || !data.results || data.results.length === 0) { - resultsDiv.innerHTML = `Aucun résultat`; - return; - } - data.results.forEach((place) => { - const b = document.createElement("button"); - b.type = "button"; - b.className = "pf-btn btn-secondary"; - b.style.textAlign = "left"; - b.style.padding = "8px"; - b.style.marginBottom = "4px"; - b.style.width = "100%"; - b.innerText = "📍 " + place.display_name; - b.onclick = () => - selectItemPlace(container, place.lat, place.lng, place.display_name); - resultsDiv.appendChild(b); - }); - }) - .catch((err) => { - resultsDiv.innerHTML = `Erreur de connexion`; + try { + const data = await pachaFetch( + "/modules/holidays/includes/api/geocode.php?limit=5&q=" + + encodeURIComponent(q), + { method: "GET" }, + ); + resultsDiv.innerHTML = ""; + if (data.error || !data.results || data.results.length === 0) { + resultsDiv.innerHTML = `Aucun résultat`; + return; + } + data.results.forEach((place) => { + const b = document.createElement("button"); + b.type = "button"; + b.className = "pf-btn btn-secondary"; + b.style.textAlign = "left"; + b.style.padding = "8px"; + b.style.marginBottom = "4px"; + b.style.width = "100%"; + b.innerText = "📍 " + place.display_name; + b.onclick = () => + selectItemPlace(container, place.lat, place.lng, place.display_name); + resultsDiv.appendChild(b); }); + } catch (err) { + resultsDiv.innerHTML = `Erreur de connexion`; + } } function selectItemPlace(container, lat, lng, displayName) { @@ -940,10 +1068,12 @@ function saveCheckpointOrder() { formData.append("holiday_id", holidayId); formData.append("locations", JSON.stringify(locations)); - fetch("/modules/holidays/includes/api/reorder_checkpoints.php", { + pachaFetch("/modules/holidays/includes/api/reorder_checkpoints.php", { method: "POST", body: formData, - }).then(() => window.location.reload()); + }) + .then(() => window.location.reload()) + .catch(console.error); } function moveStepMobile(btn, direction) { @@ -967,7 +1097,6 @@ function moveStepMobile(btn, direction) { } } -// 🔥 NOUVEAU : Fonction réutilisable pour ré-attacher les événements function initStepDragAndDrop() { const checkpoints = document.querySelectorAll(".hol-checkpoint-draggable"); const container = checkpoints[0]?.parentElement; @@ -976,7 +1105,6 @@ function initStepDragAndDrop() { const isMobile = window.innerWidth <= 768; let draggedItem = null; - // Nettoyage des anciens écouteurs en clonant les nœuds (Indispensable après un refresh silencieux) checkpoints.forEach((item) => { const clone = item.cloneNode(true); if (item.parentNode) item.parentNode.replaceChild(clone, item); @@ -1035,7 +1163,6 @@ function initStepDragAndDrop() { } } -// On lance l'initialisation au démarrage de la page document.addEventListener("DOMContentLoaded", initStepDragAndDrop); // ============================================================================ @@ -1044,10 +1171,11 @@ document.addEventListener("DOMContentLoaded", initStepDragAndDrop); window.PLANNING_ALL_UNPLACED = []; window.CURRENT_PLANNING_FILTER_DATE = null; window.PLANNING_ITEM_MAP = {}; -window.CURRENT_DRAG_DURATION = 1; // Variable pour la surbrillance multi-cases +window.CURRENT_DRAG_DURATION = 1; function closePlanningModal() { - document.getElementById("planningModal").style.display = "none"; + const modal = document.getElementById("planningModal"); + if (modal) modal.style.display = "none"; document.body.classList.remove("no-scroll"); } @@ -1073,8 +1201,8 @@ function openGlobalPlanningModal() { window.PLANNING_ALL_UNPLACED = []; window.PLANNING_ITEM_MAP = {}; - // 1. Collecte de TOUS les éléments - window.MAP_POINTS.forEach((step) => { + const mapPoints = window.MAP_POINTS || []; + mapPoints.forEach((step) => { let validItems = step.items.filter((it) => { if (it.name === "PF_TECHNICAL_POINT") return false; if (it.category === "transport" && it.expense_context !== "transit") @@ -1120,7 +1248,6 @@ function openGlobalPlanningModal() { }); }); - // 2. Génération des dates globales let datesToDisplay = []; let curr = new Date(holidayData.start_date); let endD = new Date(holidayData.end_date); @@ -1129,26 +1256,20 @@ function openGlobalPlanningModal() { curr.setDate(curr.getDate() + 1); } - // 3. Construction de l'interface (Avec règles CSS injectées) let html = `
Aucun document pour cette étape.
'; - } - }) - .catch(() => { + listContainer.insertAdjacentHTML("beforeend", docHtml); + }); + } else { listContainer.innerHTML = - 'Erreur lors du chargement.
'; - }); + 'Aucun document pour cette étape.
'; + } + } catch (err) { + listContainer.innerHTML = + 'Erreur lors du chargement.
'; + } } function closeDocsModal() { @@ -2000,13 +2088,10 @@ function closeDocsModal() { document.body.classList.remove("no-scroll"); } -function handleFileUpload(input) { - // 1. LE VERROU : Si un envoi est déjà en cours, on bloque tout ! +async function handleFileUpload(input) { if (window.isUploadingDocs) return; - if (!input.files || input.files.length === 0) return; - // On ferme le verrou window.isUploadingDocs = true; const file = input.files[0]; @@ -2018,7 +2103,7 @@ function handleFileUpload(input) { statusDiv.innerHTML = "Fichier trop lourd (Max 5Mo)."; input.value = ""; - window.isUploadingDocs = false; // On rouvre le verrou + window.isUploadingDocs = false; return; } @@ -2030,19 +2115,16 @@ function handleFileUpload(input) { fd.append("item_id", window.currentDocsStepId); fd.append("file", file); - fetch("/modules/holidays/includes/api/upload_attachment.php", { - method: "POST", - body: fd, - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - statusDiv.innerHTML = `✅ Sauvegardé !`; - - const emptyMsg = listContainer.querySelector("p"); - if (emptyMsg) emptyMsg.remove(); - - const docHtml = ` + try { + const data = await pachaFetch( + "/modules/holidays/includes/api/upload_attachment.php", + { method: "POST", body: fd }, + ); + if (data.success) { + statusDiv.innerHTML = `✅ Sauvegardé !`; + const emptyMsg = listContainer.querySelector("p"); + if (emptyMsg) emptyMsg.remove(); + const docHtml = `Aucun document pour cette étape.
'; - } - } else { - alert("Erreur : " + data.error); - row.style.opacity = "1"; + try { + const data = await pachaFetch( + "/modules/holidays/includes/api/delete_attachment.php", + { method: "POST", body: fd }, + ); + if (data.success) { + row.remove(); + const listContainer = document.getElementById("docsListContainer"); + if (listContainer.children.length === 0) { + listContainer.innerHTML = + 'Aucun document pour cette étape.
'; } - }) - .catch(() => { - alert("Erreur réseau lors de la suppression."); + } else { + alert("Erreur : " + data.error); row.style.opacity = "1"; - }); + } + } catch (err) { + alert("Erreur réseau lors de la suppression."); + row.style.opacity = "1"; + } } // ============================================================================ -// GÉNÉRATION DU CARNET DE VOYAGE (PDF Côté Client) - VERSION TEXTE BRUT +// GÉNÉRATION DU CARNET DE VOYAGE (PDF Côté Client) // ============================================================================ window.generateTravelBook = function () { const element = document.getElementById("travelBookTemplate"); @@ -2124,25 +2198,20 @@ window.generateTravelBook = function () { btn.innerHTML = "⏳ Génération..."; btn.disabled = true; - // Options ajustées avec un fond blanc forcé const opt = { - margin: 10, // 10mm de marge + margin: 10, filename: "Carnet_de_Route.pdf", image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, useCORS: true, backgroundColor: "#ffffff" }, jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }, }; - // 🔥 L'ASTUCE MAGIQUE : - // On extrait le HTML en texte brut et on l'encapsule dans un bloc 100% blanc. - // Plus aucun conflit possible avec l'affichage de ta page web ! const htmlString = `