// Événement de vérification d'authentification document.addEventListener('DOMContentLoaded', function () { // Vérification de la page sur laquelle nous sommes if (window.location.pathname !== '/auth') { // Récupération du token JWT du localStorage const token = localStorage.getItem('jwtToken'); // Si pas de token, redirige vers la page d'authentification if (!token) { window.location.replace('/auth'); } else { // Si un token existe, vérification de sa validité fetch('/auth/verifyToken', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token } }) .then(res => res.json()) .then(data => { // Si le token n'est pas valide, redirige vers la page d'authentification if (!data.valid) { localStorage.removeItem('jwtToken'); window.location.replace('/auth'); } }) .catch(error => { console.error('Error:', error); }); } } }); // Événement du bouton déconnexion document.addEventListener('DOMContentLoaded', function (event) { // Empêche le comportement par défaut du formulaire event.preventDefault(); const logoutBtn = document.getElementById('logoutBtn'); if (window.location.pathname === '/auth') { logoutBtn.style.display = 'none'; } else { logoutBtn.addEventListener('click', function () { // Suppression du token JWT du localStorage et redirection localStorage.removeItem('jwtToken'); window.location.replace('/auth'); }); } }); // Événement d'initialisation des Materialize content document.addEventListener('DOMContentLoaded', function () { // Init dropdown var sidenav = document.querySelectorAll('.sidenav'); M.Sidenav.init(sidenav); // Init Modal var modals = document.querySelectorAll('.modal'); M.Modal.init(modals); }); // Événement condition de groupe pour les menus document.addEventListener('DOMContentLoaded', function () { const token = localStorage.getItem('jwtToken'); if (token) { const decoded = jwt_decode(token); const userAuthGroupe = decoded.userAuthGroupe; const reportingItem = document.getElementById("reportingSidenavSelect"); const adminItem = document.getElementById("adminSidenavSelect"); // Cache par défaut reportingItem.style.display = 'none'; adminItem.style.display = 'none'; if (userAuthGroupe === 'MANAGER' || userAuthGroupe === 'ADMIN') { reportingItem.style.display = 'block'; } if (userAuthGroupe === 'ADMIN') { adminItem.style.display = 'block'; } } }); // Fonction pour extraire le numéro de parcours de l'URL function getNumParcoursFromURL() { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('numParcours'); } // Fonction pour extraire le sous-menu de l'URL function getSubmenuFromURL() { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('submenu'); } // Fonction pour vérifier si une valeur est nulle ou non définie function isNullOrUndefined(value) { return value === null || value === undefined; } // Fonction pour éviter la duplication du code fetch async function fetchWithJson(url, method, body = null, useLoader = true) { let loaderShown = false; if (useLoader && typeof showLoader === 'function') { showLoader(); loaderShown = true; } try { const options = { method: method, headers: { 'Content-Type': 'application/json' }, }; if (body) options.body = JSON.stringify(body); const response = await fetch(url, options); if (!response.ok) throw new Error('Réseau ou erreur serveur'); const data = await response.json(); if (loaderShown && typeof hideLoader === 'function') { hideLoader(); } return data; } catch (error) { // Toujours cacher le loader en cas d'erreur if (loaderShown && typeof hideLoader === 'function') { hideLoader(); } throw error; } } // Fonction de load du parcours en session storage async function loadParcours(numParcours) { try { const data = await fetchWithJson(`/parcours/read/${numParcours}`, 'GET'); if (data.valid) { sessionStorage.setItem('parcours', JSON.stringify(data.parcours)); } else { sessionStorage.setItem('parcours', null); sessionStorage.setItem('admins', JSON.stringify(data.admins)); console.log("Parcours introuvable"); } } catch (error) { console.error("Erreur lors de la récupération des informations parcours :", error); } } // Fonction générique de toggle function toggler(btn, div) { if (document.getElementById(btn).checked) { document.getElementById(div).style.display = 'block'; } else { document.getElementById(div).style.display = 'none'; } } // Fonction de load du parcours en session storage async function loadContrat(idContrat) { try { const data = await fetchWithJson(`/contrat/read/id/${idContrat}`, 'GET'); if (data.valid) { sessionStorage.setItem('contrat', JSON.stringify(data.contrat)); } else { sessionStorage.setItem('contrat', null); console.log("Contrat introuvable"); } } catch (error) { console.error("Erreur lors de la récupération des informations contrat :", error); } } // ========== Fonctions utilitaires génériques ========== /** * Formatage des dates (ISO vers format français) * @param {string} iso - Date au format ISO * @param {boolean} withTime - Inclure l'heure (défaut: true) * @returns {string} Date formatée (dd/mm/yyyy ou dd/mm/yyyy hh:mm) */ function fmtDate(iso, withTime = true) { // Si la valeur est null, undefined, ou vide if (!iso || (typeof iso === 'string' && iso.trim() === "")) return "NC"; // Convertir en string si ce n'est pas déjà le cas const dateStr = String(iso).trim(); // Vérifier les valeurs invalides connues if (dateStr === "00/00/0000" || dateStr === "00/00" || dateStr === "null" || dateStr === "undefined") { return "NC"; } let d; // Si c'est déjà au format jj/mm/aaaa (format français) if (dateStr.includes("/") && dateStr.split("/").length === 3) { const parts = dateStr.split("/"); const day = parseInt(parts[0], 10); const month = parseInt(parts[1], 10); const year = parseInt(parts[2], 10); // Vérifier si les valeurs sont valides if (isNaN(day) || isNaN(month) || isNaN(year)) { return "NC"; } // Si le jour ou le mois est 00, considérer comme invalide if (day === 0 || month === 0) { return "NC"; } // Si le mois est invalide if (month < 1 || month > 12) { return "NC"; } // Si l'année est 0000, afficher juste jj/mm (sans l'année) if (year === 0) { return `${String(day).padStart(2, "0")}/${String(month).padStart(2, "0")}`; } // Si l'année est valide, créer une vraie date et valider const monthIndex = month - 1; // Les mois commencent à 0 d = new Date(year, monthIndex, day); // Vérifier si la date est valide (ex: 31/02/2000 serait invalide) if (d.getDate() !== day || d.getMonth() !== monthIndex || d.getFullYear() !== year) { return "NC"; } // Formater la date const dd = String(d.getDate()).padStart(2, "0"); const mm = String(d.getMonth() + 1).padStart(2, "0"); const yyyy = d.getFullYear(); if (!withTime) return `${dd}/${mm}/${yyyy}`; const hh = String(d.getHours()).padStart(2, "0"); const mi = String(d.getMinutes()).padStart(2, "0"); return `${dd}/${mm}/${yyyy} ${hh}:${mi}`; } // Si c'est au format jj/mm (pour date d'échéance) else if (dateStr.includes("/") && dateStr.split("/").length === 2) { const parts = dateStr.split("/"); const day = parseInt(parts[0], 10); const month = parseInt(parts[1], 10); // Pour l'échéance, on retourne juste jj/mm if (isNaN(day) || isNaN(month) || day === 0 || month === 0 || month > 12) { return "NC"; } return `${String(day).padStart(2, "0")}/${String(month).padStart(2, "0")}`; } // Sinon, essayer de parser comme date ISO else { d = new Date(dateStr); if (isNaN(d.getTime())) return "NC"; const dd = String(d.getDate()).padStart(2, "0"); const mm = String(d.getMonth() + 1).padStart(2, "0"); const yyyy = d.getFullYear(); if (!withTime) return `${dd}/${mm}/${yyyy}`; const hh = String(d.getHours()).padStart(2, "0"); const mi = String(d.getMinutes()).padStart(2, "0"); return `${dd}/${mm}/${yyyy} ${hh}:${mi}`; } } /** * Crée un élément key-value pour l'affichage de détails * @param {string} label - Libellé * @param {*} value - Valeur (booléen converti en Oui/Non) * @returns {string} HTML formaté */ function kv(label, value) { const v = (value === true) ? "Oui" : (value === false) ? "Non" : (value ?? "NC"); return `
${safeTitle}
${safeDescription}