"use strict"; /* * ========================================================= * CONFIGURATION * ========================================================= */ const API_LIST_URL = "../php/api_list_full_table_indice.php"; const API_CREATE_URL = "../php/api_create_table_indice.php"; /* * ========================================================= * ELEMENTS DOM * ========================================================= */ const tableauIndices = document.getElementById("tableauIndices"); const compteur = document.getElementById("compteur"); const message = document.getElementById("message"); const btnActualiser = document.getElementById("btnActualiser"); const btnNouveau = document.getElementById("btnNouveau"); const filtreIndice = document.getElementById("filtreIndice"); const modalNouvelEnregistrement = document.getElementById( "modalNouvelEnregistrement", ); const btnFermerModal = document.getElementById("btnFermerModal"); const btnAnnuler = document.getElementById("btnAnnuler"); const formNouvelEnregistrement = document.getElementById( "formNouvelEnregistrement", ); const nouvelIndice = document.getElementById("nouvelIndice"); const nouveauNom = document.getElementById("nouveauNom"); const nouvelIsin = document.getElementById("nouvelIsin"); const nouveauSymbole = document.getElementById("nouveauSymbole"); const nouvelActif = document.getElementById("nouvelActif"); const nouvelAfficher = document.getElementById("nouvelAfficher"); const messageNouveau = document.getElementById("messageNouveau"); const btnSauvegarder = document.getElementById("btnSauvegarder"); /* * ========================================================= * DONNEES * ========================================================= */ let tousLesEnregistrements = []; /* * ========================================================= * AFFICHER MESSAGE GENERAL * ========================================================= */ function afficherMessage(texte, type = "info") { message.textContent = texte; message.classList.remove( "hidden", "bg-emerald-900/50", "border-emerald-700", "text-emerald-300", "bg-rose-900/50", "border-rose-700", "text-rose-300", "bg-slate-900", "border-slate-700", "text-slate-300", ); message.classList.add("border"); if (type === "success") { message.classList.add( "bg-emerald-900/50", "border-emerald-700", "text-emerald-300", ); } else if (type === "error") { message.classList.add("bg-rose-900/50", "border-rose-700", "text-rose-300"); } else { message.classList.add("bg-slate-900", "border-slate-700", "text-slate-300"); } } /* * ========================================================= * CACHER MESSAGE GENERAL * ========================================================= */ function cacherMessage() { message.classList.add("hidden"); } /* * ========================================================= * MESSAGE MODAL * ========================================================= */ function afficherMessageNouveau(texte, type = "info") { messageNouveau.textContent = texte; messageNouveau.classList.remove( "hidden", "bg-emerald-900/50", "border-emerald-700", "text-emerald-300", "bg-rose-900/50", "border-rose-700", "text-rose-300", "bg-slate-900", "border-slate-700", "text-slate-300", ); messageNouveau.classList.add("border"); if (type === "success") { messageNouveau.classList.add( "bg-emerald-900/50", "border-emerald-700", "text-emerald-300", ); } else if (type === "error") { messageNouveau.classList.add( "bg-rose-900/50", "border-rose-700", "text-rose-300", ); } else { messageNouveau.classList.add( "bg-slate-900", "border-slate-700", "text-slate-300", ); } } /* * ========================================================= * CACHER MESSAGE MODAL * ========================================================= */ function cacherMessageNouveau() { messageNouveau.classList.add("hidden"); messageNouveau.textContent = ""; } /* * ========================================================= * ECHAPPER HTML * ========================================================= */ function echapperHTML(valeur) { if (valeur === null || valeur === undefined) { return ""; } return String(valeur) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /* * ========================================================= * CHECKBOX * ========================================================= */ function creerCheckbox(valeur) { const checked = Number(valeur) === 1 || valeur === true || valeur === "1"; return `
`; } /* * ========================================================= * CREER UNE LIGNE * ========================================================= */ function creerLigne(enregistrement) { return ` ${echapperHTML(enregistrement.id)} ${echapperHTML(enregistrement.indice)} ${echapperHTML(enregistrement.nom)} ${echapperHTML(enregistrement.isin)} ${echapperHTML(enregistrement.symbole)} ${creerCheckbox(enregistrement.actif)} ${creerCheckbox(enregistrement.afficher)} ${echapperHTML(enregistrement.updated_at)} ${echapperHTML(enregistrement.created_at)} `; } /* * ========================================================= * CONSTRUIRE LES FILTRES * ========================================================= */ function construireFiltres() { const filtreActuel = filtreIndice.value; const indices = [ ...new Set( tousLesEnregistrements .map(function (enregistrement) { return enregistrement.indice; }) .filter(function (indice) { return ( indice !== null && indice !== undefined && String(indice).trim() !== "" ); }), ), ]; indices.sort(function (a, b) { return String(a).localeCompare(String(b), "fr", { sensitivity: "base", }); }); filtreIndice.innerHTML = ""; /* * Tout */ const optionTout = document.createElement("option"); optionTout.value = "tout"; optionTout.textContent = "Tout"; filtreIndice.appendChild(optionTout); /* * Afficher */ const optionAfficher = document.createElement("option"); optionAfficher.value = "afficher"; optionAfficher.textContent = "Afficher"; filtreIndice.appendChild(optionAfficher); /* * Inactif */ const optionInactif = document.createElement("option"); optionInactif.value = "inactif"; optionInactif.textContent = "Inactif"; filtreIndice.appendChild(optionInactif); /* * Indices */ indices.forEach(function (indice) { const option = document.createElement("option"); option.value = "indice:" + indice; option.textContent = indice; filtreIndice.appendChild(option); }); /* * Restaurer le filtre */ const optionExiste = Array.from(filtreIndice.options).some(function (option) { return option.value === filtreActuel; }); filtreIndice.value = optionExiste ? filtreActuel : "tout"; } /* * ========================================================= * APPLIQUER LE FILTRE * ========================================================= */ function appliquerFiltre() { const filtre = filtreIndice.value; let enregistrementsFiltres; if (filtre === "tout") { enregistrementsFiltres = tousLesEnregistrements; } else if (filtre === "afficher") { enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return Number(enregistrement.afficher) === 1; }, ); } else if (filtre === "inactif") { enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return Number(enregistrement.actif) === 0; }, ); } else if (filtre.startsWith("indice:")) { const indiceRecherche = filtre.substring("indice:".length); enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return ( String(enregistrement.indice).toLowerCase() === String(indiceRecherche).toLowerCase() ); }, ); } else { enregistrementsFiltres = tousLesEnregistrements; } afficherDonneesFiltrees(enregistrementsFiltres); } /* * ========================================================= * AFFICHER DONNEES FILTREES * ========================================================= */ function afficherDonneesFiltrees(enregistrements) { tableauIndices.innerHTML = ""; if (!Array.isArray(enregistrements) || enregistrements.length === 0) { compteur.textContent = "0 enregistrement"; tableauIndices.innerHTML = ` Aucun enregistrement correspondant au filtre sélectionné. `; return; } /* * IMPORTANT : * tri systématique par ID croissant. */ const donneesTriees = [...enregistrements].sort(function (a, b) { return Number(a.id) - Number(b.id); }); let html = ""; donneesTriees.forEach(function (enregistrement) { html += creerLigne(enregistrement); }); tableauIndices.innerHTML = html; compteur.textContent = donneesTriees.length + (donneesTriees.length > 1 ? " enregistrements" : " enregistrement"); } /* * ========================================================= * AFFICHER DONNEES * ========================================================= */ function afficherDonnees(data) { tousLesEnregistrements = Array.isArray(data) ? data : []; /* * Tri global par ID croissant. */ tousLesEnregistrements.sort(function (a, b) { return Number(a.id) - Number(b.id); }); construireFiltres(); appliquerFiltre(); } /* * ========================================================= * CHARGER LES DONNEES * ========================================================= */ async function chargerDonnees() { btnActualiser.disabled = true; btnActualiser.textContent = "↻ Chargement..."; afficherMessage("Chargement des indices et des actions...", "info"); try { const response = await fetch(API_LIST_URL, { method: "GET", credentials: "same-origin", cache: "no-store", headers: { Accept: "application/json", }, }); if (!response.ok) { throw new Error("Erreur HTTP " + response.status); } const result = await response.json(); if (!result || result.status !== "success") { throw new Error( result && result.message ? result.message : "L'API a retourné une erreur.", ); } afficherDonnees(result.data); cacherMessage(); } catch (error) { console.error("Erreur chargement indices/actions :", error); tableauIndices.innerHTML = ` Impossible de charger les données. `; compteur.textContent = "Erreur de chargement"; afficherMessage( "Erreur lors de la récupération des données : " + error.message, "error", ); } finally { btnActualiser.disabled = false; btnActualiser.textContent = "↻ Actualiser"; } } /* * ========================================================= * OUVRIR MODAL * ========================================================= */ function ouvrirModal() { cacherMessageNouveau(); /* * Valeurs par défaut */ formNouvelEnregistrement.reset(); nouvelActif.checked = true; nouvelAfficher.checked = true; modalNouvelEnregistrement.classList.remove("hidden"); modalNouvelEnregistrement.classList.add("flex"); /* * Focus sur Indice */ setTimeout(function () { nouvelIndice.focus(); }, 100); } /* * ========================================================= * FERMER MODAL * ========================================================= */ function fermerModal() { modalNouvelEnregistrement.classList.add("hidden"); modalNouvelEnregistrement.classList.remove("flex"); cacherMessageNouveau(); } /* * ========================================================= * SAUVEGARDER NOUVEL ENREGISTREMENT * ========================================================= */ async function sauvegarderNouvelEnregistrement() { const donnees = { indice: nouvelIndice.value.trim(), nom: nouveauNom.value.trim(), isin: nouvelIsin.value.trim().toUpperCase(), symbole: nouveauSymbole.value.trim().toUpperCase(), actif: nouvelActif.checked ? 1 : 0, afficher: nouvelAfficher.checked ? 1 : 0, }; /* * Validation côté client */ if (!donnees.indice) { afficherMessageNouveau("Le champ Indice est obligatoire.", "error"); nouvelIndice.focus(); return; } if (!donnees.nom) { afficherMessageNouveau("Le champ Nom est obligatoire.", "error"); nouveauNom.focus(); return; } if (!donnees.isin) { afficherMessageNouveau("Le champ ISIN est obligatoire.", "error"); nouvelIsin.focus(); return; } if (!donnees.symbole) { afficherMessageNouveau("Le champ Symbole est obligatoire.", "error"); nouveauSymbole.focus(); return; } /* * Désactivation du bouton */ btnSauvegarder.disabled = true; btnSauvegarder.textContent = "Sauvegarde..."; cacherMessageNouveau(); try { const response = await fetch(API_CREATE_URL, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(donnees), }); const result = await response.json(); if (!response.ok || !result || result.status !== "success") { throw new Error( result && result.message ? result.message : "Impossible de créer l'enregistrement.", ); } /* * Succès */ afficherMessageNouveau("Enregistrement créé avec succès.", "success"); /* * On recharge la table. */ await chargerDonnees(); /* * Petite pause pour laisser voir * le message de succès. */ setTimeout(function () { fermerModal(); }, 500); } catch (error) { console.error("Erreur création enregistrement :", error); afficherMessageNouveau("Erreur : " + error.message, "error"); } finally { btnSauvegarder.disabled = false; btnSauvegarder.textContent = "Sauvegarder"; } } /* * ========================================================= * CHANGEMENT DE FILTRE * ========================================================= */ filtreIndice.addEventListener("change", function () { appliquerFiltre(); }); /* * ========================================================= * BOUTON ACTUALISER * ========================================================= */ btnActualiser.addEventListener("click", function () { chargerDonnees(); }); /* * ========================================================= * BOUTON NOUVEAU * ========================================================= */ btnNouveau.addEventListener("click", function () { ouvrirModal(); }); /* * ========================================================= * FERMETURE MODAL * ========================================================= */ btnFermerModal.addEventListener("click", function () { fermerModal(); }); btnAnnuler.addEventListener("click", function () { fermerModal(); }); /* * ========================================================= * CLIC SUR LE FOND DU MODAL * ========================================================= */ modalNouvelEnregistrement.addEventListener("click", function (event) { if (event.target === modalNouvelEnregistrement) { fermerModal(); } }); /* * ========================================================= * TOUCHE ESC * ========================================================= */ document.addEventListener("keydown", function (event) { if ( event.key === "Escape" && !modalNouvelEnregistrement.classList.contains("hidden") ) { fermerModal(); } }); /* * ========================================================= * SOUMISSION FORMULAIRE * ========================================================= */ formNouvelEnregistrement.addEventListener("submit", function (event) { event.preventDefault(); sauvegarderNouvelEnregistrement(); }); /* * ========================================================= * ISIN EN MAJUSCULES * ========================================================= */ nouvelIsin.addEventListener("input", function () { nouvelIsin.value = nouvelIsin.value.toUpperCase(); }); /* * ========================================================= * SYMBOLE EN MAJUSCULES * ========================================================= */ nouveauSymbole.addEventListener("input", function () { nouveauSymbole.value = nouveauSymbole.value.toUpperCase(); }); /* * ========================================================= * INITIALISATION * ========================================================= */ document.addEventListener("DOMContentLoaded", function () { chargerDonnees(); });