From 0a536140ab841246c8e4c630d3c2fe1898f3b573 Mon Sep 17 00:00:00 2001 From: Alexis Burnaz <48258099+alxsbrz@users.noreply.github.com> Date: Mon, 22 Dec 2025 12:24:30 +0100 Subject: [PATCH] =?UTF-8?q?fix=20:=20bouton=20desactiv=C3=A9=20pendant=20l?= =?UTF-8?q?e=20t=C3=A9l=C3=A9chargement=20+=20plus=20de=20abort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecole/public/css/historiqueParcours.css | 10 ++- ecole/public/js/historiqueParcours.js | 82 ++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/ecole/public/css/historiqueParcours.css b/ecole/public/css/historiqueParcours.css index 0dbd7a73..5ec09a6f 100644 --- a/ecole/public/css/historiqueParcours.css +++ b/ecole/public/css/historiqueParcours.css @@ -229,4 +229,12 @@ td.nc-value { } #historiqueParcours tr.shown > td { background: #fffdf5; } - .parcours-details { font-size: 0.95rem; } \ No newline at end of file + .parcours-details { font-size: 0.95rem; } + + +/* Style pour les boutons d'export désactivés */ +#divBtnFilter button:disabled { + opacity: 0.5 !important; + cursor: not-allowed !important; + pointer-events: none !important; +} diff --git a/ecole/public/js/historiqueParcours.js b/ecole/public/js/historiqueParcours.js index 39df752d..16dc0f99 100644 --- a/ecole/public/js/historiqueParcours.js +++ b/ecole/public/js/historiqueParcours.js @@ -18,8 +18,30 @@ document.addEventListener("DOMContentLoaded", async function () { // Initialiser DataTables en mode server-side (obligé pour pagination) const table = initServerSideDataTable(); + // Variable pour suivre l'état des exports + let isExporting = false; + + // Fonction pour désactiver/activer les boutons d'export + function setExportButtonsState(disabled) { + isExporting = disabled; + const buttons = ["#exportCSV", "#exportCSVFilter", "#exportXlxs", "#exportXlxsFilter"]; + buttons.forEach(selector => { + const $btn = $(selector); + $btn.prop("disabled", disabled); + if (disabled) { + $btn.css("opacity", "0.5"); + $btn.css("cursor", "not-allowed"); + } else { + $btn.css("opacity", "1"); + $btn.css("cursor", "pointer"); + } + }); + } + // Exports CSV/XLSX $("#exportCSV").on("click", function () { + if (isExporting) return; + const dt = $("#historiqueParcours").DataTable(); if (!dt) { displayError("Impossible d'accéder à la table de données."); @@ -32,6 +54,8 @@ document.addEventListener("DOMContentLoaded", async function () { return; } + setExportButtonsState(true); + const payload = { mode: "full", // export total search: { value: "" }, @@ -62,12 +86,18 @@ document.addEventListener("DOMContentLoaded", async function () { a.click(); URL.revokeObjectURL(url); a.remove(); + setExportButtonsState(false); }) - .catch(() => displayError("Export CSV (complet) impossible")); + .catch((err) => { + displayError("Export CSV (complet) impossible"); + setExportButtonsState(false); + }); }); $("#exportCSVFilter").on("click", function () { + if (isExporting) return; + const dt = $("#historiqueParcours").DataTable(); if (!dt) { displayError("Impossible d'accéder à la table de données."); @@ -80,6 +110,8 @@ document.addEventListener("DOMContentLoaded", async function () { return; } + setExportButtonsState(true); + const payload = { mode: "filtered", // export avec les filtres/colonnes/tri actuels search: { value: dt.search() || "" }, // recherche globale @@ -108,12 +140,18 @@ document.addEventListener("DOMContentLoaded", async function () { a.click(); URL.revokeObjectURL(url); a.remove(); + setExportButtonsState(false); }) - .catch(() => displayError("Export CSV (filtré) impossible")); + .catch((err) => { + displayError("Export CSV (filtré) impossible"); + setExportButtonsState(false); + }); }); $("#exportXlxs").on("click", function () { + if (isExporting) return; + const dt = $("#historiqueParcours").DataTable(); if (!dt) { displayError("Impossible d'accéder à la table de données."); @@ -126,6 +164,8 @@ document.addEventListener("DOMContentLoaded", async function () { return; } + setExportButtonsState(true); + const payload = { mode: "full", search: { value: "" }, @@ -145,12 +185,18 @@ document.addEventListener("DOMContentLoaded", async function () { a.href = url; a.download = "historique_parcours_complet.xls"; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); a.remove(); + setExportButtonsState(false); }) - .catch(() => displayError("Export XLS (complet) impossible")); + .catch((err) => { + displayError("Export XLS (complet) impossible"); + setExportButtonsState(false); + }); }); $("#exportXlxsFilter").on("click", function () { + if (isExporting) return; + const dt = $("#historiqueParcours").DataTable(); if (!dt) { displayError("Impossible d'accéder à la table de données."); @@ -163,6 +209,8 @@ document.addEventListener("DOMContentLoaded", async function () { return; } + setExportButtonsState(true); + const payload = { mode: "filtered", // export avec les filtres/colonnes/tri actuels search: { value: dt.search() || "" }, // recherche globale @@ -191,8 +239,12 @@ document.addEventListener("DOMContentLoaded", async function () { a.click(); URL.revokeObjectURL(url); a.remove(); + setExportButtonsState(false); }) - .catch(() => displayError("Export XLS (filtré) impossible")); + .catch((err) => { + displayError("Export XLS (filtré) impossible"); + setExportButtonsState(false); + }); }); @@ -243,21 +295,29 @@ function initServerSideDataTable() { search: data.search || { value: "" }, }; - if (inflightController) inflightController.abort(); // action en cours inflightController = new AbortController(); + const currentController = inflightController; fetch("/historiqueParcours/datatable", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), - signal: inflightController.signal, + signal: currentController.signal, }) .then(res => { + // Vérifier si la requête a été annulée + if (currentController.signal.aborted || inflightController !== currentController) { + return null; + } if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }) .then(payload => { + // Vérifier si la requête a été annulée + if (currentController.signal.aborted || inflightController !== currentController) { + return; + } if (!payload || typeof payload !== 'object') { throw new Error("Réponse invalide du serveur"); } @@ -269,7 +329,15 @@ function initServerSideDataTable() { }); }) .catch(err => { - if (err && err.name === "AbortError") return; + // Ignorer silencieusement toutes les erreurs d'abort + if (err && (err.name === "AbortError" || err.name === "DOMException")) { + return; + } + // Vérifier aussi si le signal a été aborted + if (currentController.signal.aborted || inflightController !== currentController) { + return; + } + // Seulement afficher une erreur si ce n'est PAS un abort displayError("Failed to fetch data. Please try again later."); callback({ draw: 0, recordsTotal: 0, recordsFiltered: 0, data: [] }); });