diff --git a/holidays.php b/holidays.php index 83139ea..131d874 100644 --- a/holidays.php +++ b/holidays.php @@ -34,7 +34,17 @@ if ($tab === 'holiday_detail' && isset($_GET['id'])) { require __DIR__ . '/modules/holidays/views/list.php'; } -// 4. Inclusion du JS global du module (Pont i18n déjà géré dans le header) -echo ''; +// 4. Inclusion des librairies globales du module (Flatpickr) +?> + + + + + + + +'; require __DIR__ . '/footer.php'; \ No newline at end of file diff --git a/modules/holidays/holidays.js b/modules/holidays/holidays.js index 5e92d44..1163cb3 100644 --- a/modules/holidays/holidays.js +++ b/modules/holidays/holidays.js @@ -1,5 +1,22 @@ window.PLANNING_MODIFIED = false; +// Variable globale pour Flatpickr +let cpDateRangePicker = null; + +// Écouteur pour activer le thème sombre de Flatpickr selon le thème global HouseHub +document.addEventListener("DOMContentLoaded", () => { + const checkDarkTheme = () => { + const isDark = + document.documentElement.getAttribute("data-theme") === "dark"; + const themeLink = document.getElementById("flatpickr-dark-theme"); + if (themeLink) themeLink.disabled = !isDark; + }; + checkDarkTheme(); + document + .getElementById("theme-toggle") + ?.addEventListener("click", () => setTimeout(checkDarkTheme, 50)); +}); + // ============================================================================ // FONCTION DE TRADUCTION JS & LANGUE COURANTE // ============================================================================ @@ -87,6 +104,11 @@ function openHolidayModal(mode) { if (mode === "add") { document.getElementById("modalTitle").innerText = tr("hdl_modal_title"); btnDelete.style.display = "none"; + + // Réinitialisation du calendrier global + document.getElementById("inp_start").value = ""; + document.getElementById("inp_end").value = ""; + initHolFlatpickr("", ""); } else { document.getElementById("modalTitle").innerText = tr( "hdl_quick_edit_title", @@ -114,18 +136,27 @@ function editHoliday(data) { 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_start").value = h.start_date || ""; - document.getElementById("inp_end").value = h.end_date || ""; document.getElementById("inp_food").value = h.budget_food > 0 ? h.budget_food : ""; document.getElementById("inp_extra").value = h.budget_extra > 0 ? h.budget_extra : ""; - document.getElementById("inp_notes").value = h.notes || ""; + + if (document.getElementById("inp_notes")) { + document.getElementById("inp_notes").value = h.notes || ""; + } const vehicleInput = document.getElementById("inp_vehicle_id"); if (vehicleInput) { vehicleInput.value = h.vehicle_id || ""; } + + // 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; + + initHolFlatpickr(startD, endD); } function deleteHoliday() { @@ -458,64 +489,100 @@ 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 (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 = ""; - searchBlock.style.display = "block"; + const holidayData = JSON.parse( + document.getElementById("holidayDataJson").textContent, + ).main; + let tripStartDate = + holidayData.start_date || new Date().toISOString().split("T")[0]; + + // ========================================== + // CONFIGURATION DES DATES FLATPICKR + // ========================================== + let defaultStart = ""; + let defaultEnd = ""; if (mode === "add") { document.getElementById("cpModalTitle").innerText = tr("hdl_btn_add_step"); - formBlock.style.display = "none"; btnDel.style.display = "none"; + searchBlock.style.display = "block"; + insertGroup.style.display = "block"; + document.getElementById("cp_old_sort_order").value = ""; document.getElementById("cp_name").value = ""; + switchCpTab("info"); + if (document.getElementById("cp_step_type")) { document.getElementById("cp_step_type").value = "stop"; 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) { + 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 += ``; + }); + } + + defaultStart = lastDate; + defaultEnd = lastDate; addCpExpenseLine(); } else if (mode === "edit" && data) { document.getElementById("cpModalTitle").innerText = tr("hdl_js_edit_step"); - formBlock.style.display = "block"; btnDel.style.display = "block"; + searchBlock.style.display = "none"; + 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; - document.getElementById("cp_start_date").value = data.step_start_date || ""; - document.getElementById("cp_end_date").value = data.step_end_date || ""; - // 🔥 PRE-REMPLISSAGE DU TYPE D'ÉTAPE ET UI DATES if (document.getElementById("cp_step_type")) { const type = data.step_type || "stop"; document.getElementById("cp_step_type").value = type; toggleStepDates(type); } - - // 🔥 PRE-REMPLISSAGE DE LA CASE RETOUR BASEE SUR LA GLOBALE if (document.getElementById("cp_set_as_return")) { document.getElementById("cp_set_as_return").checked = window.GLOBAL_RETURN_STEP_ID == data.sort_order; } + defaultStart = data.step_start_date || tripStartDate; + defaultEnd = data.step_end_date || tripStartDate; + if (data.items && data.items.length > 0) { let visibleCount = 0; data.items.forEach((it) => { @@ -540,10 +607,137 @@ function openCheckpointModal(mode, data = null) { addCpExpenseLine(); } } + + // Destruction et ré-instanciation de Flatpickr pour forcer le saut au bon mois + if (cpDateRangePicker) cpDateRangePicker.destroy(); + + document.getElementById("cp_start_date").value = defaultStart; + 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 = ""; + } + }, + }); + document.getElementById("checkpointModal").style.display = "flex"; document.body.classList.add("no-scroll"); } +// ========================================== +// 🎯 DATES GLOBALES DU VOYAGE (MODALE PRINCIPALE) +// ========================================== +let holDateRangePicker = null; + +function initHolFlatpickr(defaultStart, defaultEnd) { + if (holDateRangePicker) holDateRangePicker.destroy(); + + 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 + defaultDate: + defaultStart && defaultEnd && defaultStart !== defaultEnd + ? [defaultStart, defaultEnd] + : defaultStart + ? [defaultStart] + : [], + 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", + ); + } 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", + ); + } else { + document.getElementById("inp_start").value = ""; + document.getElementById("inp_end").value = ""; + } + }, + }); +} + +function switchCpTab(tabId) { + const btnInfo = document.getElementById("tabBtnInfo"); + const btnProg = document.getElementById("tabBtnProg"); + const tabInfo = document.getElementById("cpTabInfo"); + 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"; + } 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"; + } +} + +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; + + // On met à jour l'UI du calendrier + if (cpDateRangePicker) { + cpDateRangePicker.setDate([dateToSet, dateToSet]); + } + } +} + function searchPlace() { const q = document.getElementById("searchPlaceInput").value.trim(); if (q.length < 3) return; @@ -597,34 +791,41 @@ function addCpExpenseLine( itemDate = "", itemTime = "", itemDur = 1, - expenseContext = "local", // 🔥 FIX : On accepte le contexte dynamique + expenseContext = "local", ) { const container = document.getElementById("cpExpensesContainer"); const div = document.createElement("div"); div.className = "hol-form-row"; const isChecked = isPaid == 1 ? "checked" : ""; + // 💡 Nouveaux types (Les valeurs à 0 par défaut pour les visites gratuites) + let defaultAmount = amount; + if (category === "visit_free" && amount === "") defaultAmount = "0.00"; + div.innerHTML = `