"use strict"; /* * ========================================================= * CONFIGURATION * ========================================================= */ const API_LIST_URL = "../php/api_list_full_table_indice.php"; const API_CREATE_URL = "../php/api_create_table_indice.php"; const API_UPDATE_URL = "../php/api_update_table_indice.php"; /* * ========================================================= * ELEMENTS TABLEAU * ========================================================= */ const tableauIndices = document.getElementById("tableauIndices"); const compteur = document.getElementById("compteur"); const message = document.getElementById("message"); const btnActualiser = document.getElementById("btnActualiser"); const filtreIndice = document.getElementById("filtreIndice"); /* * ========================================================= * ELEMENTS NOUVEAU * ========================================================= */ const btnNouveau = document.getElementById("btnNouveau"); 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"); /* * ========================================================= * DONNEES * ========================================================= */ let tousLesEnregistrements = []; /* * ========================================================= * ID EN COURS DE MODIFICATION * ========================================================= */ let idEnregistrementModification = null; /* * ========================================================= * 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"); } /* * ========================================================= * AFFICHER MESSAGE NOUVEAU / MODIFICATION * ========================================================= */ function afficherMessageFormulaire(texte, type = "error") { 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 { messageNouveau.classList.add( "bg-rose-900/50", "border-rose-700", "text-rose-300", ); } } /* * ========================================================= * CACHER MESSAGE FORMULAIRE * ========================================================= */ function cacherMessageFormulaire() { 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, "'"); } /* * ========================================================= * FORMAT DATE * ========================================================= */ function formaterDate(date) { if (!date) { return ""; } return String(date); } /* * ========================================================= * VALEUR BOOLEAN * ========================================================= */ function estActif(valeur) { return Number(valeur) === 1 || valeur === true || valeur === "1"; } /* * ========================================================= * CREER CHECKBOX * ========================================================= */ function creerCheckbox(valeur) { const checked = estActif(valeur); return `
`; } /* * ========================================================= * CREER BOUTON MODIFIER * ========================================================= */ function creerBoutonModifier(id) { 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(formaterDate(enregistrement.updated_at))} ${echapperHTML(formaterDate(enregistrement.created_at))} ${creerBoutonModifier(enregistrement.id)} `; } /* * ========================================================= * CONSTRUIRE LES OPTIONS DU FILTRE * ========================================================= */ function construireFiltres() { 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", }); }); const filtreActuel = filtreIndice.value; 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; }); if (optionExiste) { filtreIndice.value = filtreActuel; } else { filtreIndice.value = "tout"; } } /* * ========================================================= * APPLIQUER FILTRE * ========================================================= */ function appliquerFiltre() { const filtre = filtreIndice.value; let enregistrementsFiltres; /* * Tout */ if (filtre === "tout") { enregistrementsFiltres = tousLesEnregistrements; /* * Afficher */ } else if (filtre === "afficher") { enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return estActif(enregistrement.afficher); }, ); /* * Inactif */ } else if (filtre === "inactif") { enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return !estActif(enregistrement.actif); }, ); /* * Indice */ } else if (filtre.startsWith("indice:")) { const indiceRecherche = filtre.substring("indice:".length); enregistrementsFiltres = tousLesEnregistrements.filter( function (enregistrement) { return ( String(enregistrement.indice).toLowerCase() === String(indiceRecherche).toLowerCase() ); }, ); /* * Sécurité */ } else { enregistrementsFiltres = tousLesEnregistrements; } /* * Toujours trier par ID croissant. */ enregistrementsFiltres = [...enregistrementsFiltres].sort(function (a, b) { return Number(a.id) - Number(b.id); }); 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; } let html = ""; enregistrements.forEach(function (enregistrement) { html += creerLigne(enregistrement); }); tableauIndices.innerHTML = html; compteur.textContent = enregistrements.length + (enregistrements.length > 1 ? " enregistrements" : " enregistrement"); } /* * ========================================================= * AFFICHER DONNEES * ========================================================= */ function afficherDonnees(data) { tousLesEnregistrements = Array.isArray(data) ? data : []; /* * Tri principal par ID croissant. */ tousLesEnregistrements.sort(function (a, b) { return Number(a.id) - Number(b.id); }); construireFiltres(); appliquerFiltre(); } /* * ========================================================= * CHARGER 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 POPUP NOUVEAU * ========================================================= */ function ouvrirModalNouveau() { formNouvelEnregistrement.reset(); /* * Valeurs par défaut */ nouvelActif.checked = true; nouvelAfficher.checked = true; cacherMessageFormulaire(); modalNouvelEnregistrement.classList.remove("hidden"); modalNouvelEnregistrement.classList.add("flex"); /* * Focus */ setTimeout(function () { nouvelIndice.focus(); }, 100); } /* * ========================================================= * FERMER POPUP * ========================================================= */ function fermerModal() { modalNouvelEnregistrement.classList.add("hidden"); modalNouvelEnregistrement.classList.remove("flex"); idEnregistrementModification = null; cacherMessageFormulaire(); } /* * ========================================================= * CREER NOUVEL ENREGISTREMENT * ========================================================= */ async function creerNouvelEnregistrement() { const indice = nouvelIndice.value.trim(); const nom = nouveauNom.value.trim(); const isin = nouvelIsin.value.trim().toUpperCase(); const symbole = nouveauSymbole.value.trim().toUpperCase(); const actif = nouvelActif.checked ? 1 : 0; const afficher = nouvelAfficher.checked ? 1 : 0; /* * Validation */ if (!indice || !nom || !isin || !symbole) { afficherMessageFormulaire("Tous les champs sont obligatoires.", "error"); return; } /* * Désactivation du bouton */ const bouton = document.getElementById("btnSauvegarder"); bouton.disabled = true; bouton.textContent = "Sauvegarde..."; cacherMessageFormulaire(); try { const response = await fetch(API_CREATE_URL, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify({ indice: indice, nom: nom, isin: isin, symbole: symbole, actif: actif, afficher: afficher, }), }); const result = await response.json(); if (!response.ok || !result || result.status !== "success") { throw new Error( result && result.message ? result.message : "Erreur lors de la création.", ); } /* * Fermeture popup */ fermerModal(); /* * Message général */ afficherMessage("L'enregistrement a été créé avec succès.", "success"); /* * Rechargement */ await chargerDonnees(); } catch (error) { console.error("Erreur création :", error); afficherMessageFormulaire(error.message, "error"); } finally { bouton.disabled = false; bouton.textContent = "Sauvegarder"; } } /* * ========================================================= * OUVRIR POPUP MODIFICATION * ========================================================= * * On réutilise exactement la même popup HTML. * * ========================================================= */ function ouvrirModalModification(id) { const enregistrement = tousLesEnregistrements.find(function (item) { return Number(item.id) === Number(id); }); if (!enregistrement) { afficherMessage("Impossible de trouver l'enregistrement.", "error"); return; } /* * Mémorisation de l'ID */ idEnregistrementModification = Number(enregistrement.id); /* * Remplissage du formulaire */ nouvelIndice.value = enregistrement.indice || ""; nouveauNom.value = enregistrement.nom || ""; nouvelIsin.value = enregistrement.isin || ""; nouveauSymbole.value = enregistrement.symbole || ""; nouvelActif.checked = estActif(enregistrement.actif); nouvelAfficher.checked = estActif(enregistrement.afficher); /* * Message */ cacherMessageFormulaire(); /* * Modifier le titre de la popup */ const titre = modalNouvelEnregistrement.querySelector("h2"); if (titre) { titre.textContent = "Modifier l'enregistrement"; } /* * Modifier le sous-titre */ const sousTitre = modalNouvelEnregistrement.querySelector("h2 + p"); if (sousTitre) { sousTitre.textContent = "Modifier une action ou un indice"; } /* * Modifier le texte du bouton */ const bouton = document.getElementById("btnSauvegarder"); bouton.textContent = "Sauvegarder"; /* * Ouverture */ modalNouvelEnregistrement.classList.remove("hidden"); modalNouvelEnregistrement.classList.add("flex"); /* * Focus */ setTimeout(function () { nouvelIndice.focus(); }, 100); } /* * ========================================================= * MODIFIER ENREGISTREMENT * ========================================================= */ async function modifierEnregistrement() { if (idEnregistrementModification === null) { afficherMessageFormulaire("Aucun enregistrement sélectionné.", "error"); return; } const indice = nouvelIndice.value.trim(); const nom = nouveauNom.value.trim(); const isin = nouvelIsin.value.trim().toUpperCase(); const symbole = nouveauSymbole.value.trim().toUpperCase(); const actif = nouvelActif.checked ? 1 : 0; const afficher = nouvelAfficher.checked ? 1 : 0; /* * Validation */ if (!indice || !nom || !isin || !symbole) { afficherMessageFormulaire("Tous les champs sont obligatoires.", "error"); return; } /* * Bouton */ const bouton = document.getElementById("btnSauvegarder"); bouton.disabled = true; bouton.textContent = "Sauvegarde..."; cacherMessageFormulaire(); try { const response = await fetch(API_UPDATE_URL, { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify({ id: idEnregistrementModification, indice: indice, nom: nom, isin: isin, symbole: symbole, actif: actif, afficher: afficher, }), }); const result = await response.json(); if (!response.ok || !result || result.status !== "success") { throw new Error( result && result.message ? result.message : "Erreur lors de la modification.", ); } /* * Fermeture */ fermerModal(); /* * Message */ afficherMessage("L'enregistrement a été modifié avec succès.", "success"); /* * Rechargement */ await chargerDonnees(); } catch (error) { console.error("Erreur modification :", error); afficherMessageFormulaire(error.message, "error"); } finally { bouton.disabled = false; bouton.textContent = "Sauvegarder"; } } /* * ========================================================= * CHANGEMENT DE FILTRE * ========================================================= */ filtreIndice.addEventListener("change", function () { appliquerFiltre(); }); /* * ========================================================= * BOUTON ACTUALISER * ========================================================= */ btnActualiser.addEventListener("click", function () { chargerDonnees(); }); /* * ========================================================= * BOUTON NOUVEAU * ========================================================= */ btnNouveau.addEventListener("click", function () { ouvrirModalNouveau(); }); /* * ========================================================= * FERMER POPUP * ========================================================= */ btnFermerModal.addEventListener("click", function () { fermerModal(); }); btnAnnuler.addEventListener("click", function () { fermerModal(); }); /* * ========================================================= * SUBMIT FORMULAIRE * ========================================================= */ formNouvelEnregistrement.addEventListener("submit", function (event) { event.preventDefault(); /* * Si un ID est défini : * modification. * * Sinon : * création. */ if (idEnregistrementModification !== null) { modifierEnregistrement(); } else { creerNouvelEnregistrement(); } }); /* * ========================================================= * BOUTONS MODIFIER * ========================================================= * * Les boutons sont créés dynamiquement. * On utilise donc la délégation d'événement. * * ========================================================= */ tableauIndices.addEventListener("click", function (event) { const bouton = event.target.closest(".btnModifier"); if (!bouton) { return; } const id = bouton.dataset.id; if (!id) { return; } ouvrirModalModification(id); }); /* * ========================================================= * FERMER AVEC ECHAP * ========================================================= */ document.addEventListener("keydown", function (event) { if ( event.key === "Escape" && !modalNouvelEnregistrement.classList.contains("hidden") ) { fermerModal(); } }); /* * ========================================================= * INITIALISATION * ========================================================= */ document.addEventListener("DOMContentLoaded", function () { chargerDonnees(); });