diff --git a/js/indices.js b/js/indices.js
index b3be4bc..1c6c5db 100644
--- a/js/indices.js
+++ b/js/indices.js
@@ -1,794 +1,214 @@
-"use strict";
+// Tableaux globaux
+let configIndices = []; // Données issues de la table "indice"
+let coursActionsData = []; // Données issues de la table "cours_actions"
-/*
- * =========================================================
- * CONFIGURATION
- * =========================================================
- */
+function chargerDonnees() {
+ // Chargement en parallèle des deux API
+ Promise.all([
+ fetch("./php/api_lecture_tableindice.php").then((res) => res.json()),
+ fetch("./php/api_indices.php").then((res) => res.json()),
+ ])
+ .then(([configResult, coursResult]) => {
+ if (configResult.status === "success") {
+ configIndices = configResult.data;
+ console.log(
+ "Configuration indices chargée :",
+ configIndices.length,
+ "éléments",
+ );
+ } else {
+ console.error("Erreur API config indices :", configResult.message);
+ }
-const API_URL = "../php/api_list_full_table_indice.php";
+ if (coursResult.status === "success") {
+ coursActionsData = coursResult.data;
+ console.log(
+ "Cours actions chargés :",
+ coursActionsData.length,
+ "éléments",
+ );
+ } else {
+ console.error("Erreur API cours_actions :", coursResult.message);
+ }
-/*
- * API de création.
- *
- * A créer côté PHP.
- */
-const API_CREATE_URL = "../php/api_create_indice.php";
+ // Mise à jour des vues
+ afficherSyntheseCartes();
+ alimenterBandeauxTicker();
-const tableauIndices = document.getElementById("tableauIndices");
-const compteur = document.getElementById("compteur");
-const message = document.getElementById("message");
+ // Statut de rafraîchissement
+ const statut = document.getElementById("statut");
+ if (statut) {
+ statut.innerText = `Dernière mise à jour : ${new Date().toLocaleTimeString()}`;
+ }
+ })
+ .catch((error) =>
+ console.error("Erreur réseau lors du chargement des API :", error),
+ );
+}
-const btnActualiser = document.getElementById("btnActualiser");
-const btnNouveau = document.getElementById("btnNouveau");
+function afficherSyntheseCartes() {
+ const container = document.getElementById("cards-container");
+ if (!container) return;
-const filtreIndice = document.getElementById("filtreIndice");
+ container.innerHTML = "";
-/*
- * =========================================================
- * MODAL
- * =========================================================
- */
-
-const modalNouvelEnregistrement = document.getElementById(
- "modalNouvelEnregistrement",
-);
-
-const formNouvelEnregistrement = document.getElementById(
- "formNouvelEnregistrement",
-);
-
-const btnFermerModal = document.getElementById("btnFermerModal");
-const btnAnnuler = document.getElementById("btnAnnuler");
-const btnSauvegarder = document.getElementById("btnSauvegarder");
-
-const messageNouveau = document.getElementById("messageNouveau");
-
-/*
- * =========================================================
- * CHAMPS FORMULAIRE
- * =========================================================
- */
-
-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");
-
-/*
- * =========================================================
- * DONNEES
- * =========================================================
- */
-
-let tousLesEnregistrements = [];
-
-/*
- * =========================================================
- * AFFICHER MESSAGE
- * =========================================================
- */
-
-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",
+ // CARTES : On se base STRICTEMENT sur le champ afficher == 1
+ const actionsAffichees = configIndices.filter(
+ (item) => String(item.afficher) === "1" || item.afficher == 1,
);
- message.classList.add("border");
+ if (actionsAffichees.length > 0) {
+ actionsAffichees.forEach((conf) => {
+ // Croisement avec les cours en direct
+ const d =
+ coursActionsData.find(
+ (item) => item.symbole === conf.symbole || item.isin === conf.isin,
+ ) || {};
- 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");
- }
-}
+ const ecartValeur = parseFloat(d.variation || 0);
+ const isPositive = ecartValeur >= 0;
+ const badgeColor = isPositive ? "text-emerald-400" : "text-rose-400";
+ const sign = isPositive ? "+" : "";
-/*
- * =========================================================
- * CACHER MESSAGE
- * =========================================================
- */
+ const volumeFormate = d.volume
+ ? parseInt(d.volume).toLocaleString("fr-FR")
+ : "--";
-function cacherMessage() {
- message.classList.add("hidden");
-}
+ container.innerHTML += `
+
+
+
+
+
+ ${conf.nom || conf.indice} (${conf.symbole || "N/A"})
+
+ ${conf.isin || "Aucun ISIN"}
+ ${conf.indice || ""}
+
-/*
- * =========================================================
- * ECHAPPER HTML
- * =========================================================
- */
+
+
+
+ ${sign}${ecartValeur.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 4 })}
+ (${sign}${parseFloat(d.variation_pct || 0).toFixed(2)}%)
+
+
+ ${d.date_cours || "--/--"}
+
+
+
-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 FILTRES
- * =========================================================
- */
-
-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",
+
+
+
+ Haut
+ ${d.plus_haut ? parseFloat(d.plus_haut).toLocaleString("fr-FR", { minimumFractionDigits: 2 }) : "--"}
+
+
+ Bas
+ ${d.plus_bas ? parseFloat(d.plus_bas).toLocaleString("fr-FR", { minimumFractionDigits: 2 }) : "--"}
+
+
+ Volume
+ ${volumeFormate}
+
+
+
+ `;
});
- });
-
- 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 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;
- } else if (filtre === "afficher") {
- /*
- * Afficher
- */
- enregistrementsFiltres = tousLesEnregistrements.filter(
- function (enregistrement) {
- return (
- Number(enregistrement.afficher) === 1 ||
- enregistrement.afficher === true ||
- enregistrement.afficher === "1"
- );
- },
- );
- } else if (filtre === "inactif") {
- /*
- * Inactif
- */
- enregistrementsFiltres = tousLesEnregistrements.filter(
- function (enregistrement) {
- return !(
- Number(enregistrement.actif) === 1 ||
- enregistrement.actif === true ||
- enregistrement.actif === "1"
- );
- },
- );
- } else if (filtre.startsWith("indice:")) {
- /*
- * 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é.
- |
-
-
+ container.innerHTML = `
+
+ Aucune action configurée à 'afficher = 1'.
+
`;
-
- return;
}
-
- /*
- * =======================================================
- * TRI 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 alimenterBandeauxTicker() {
+ const preparerDonneesTicker = (nomIndiceRecherche) => {
+ const cleanSearch = nomIndiceRecherche
+ .replace(/[^a-z0-9]/gi, "")
+ .toLowerCase();
-function afficherDonnees(data) {
- tousLesEnregistrements = Array.isArray(data) ? data : [];
-
- 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_URL, {
- method: "GET",
-
- credentials: "same-origin",
-
- cache: "no-store",
-
- headers: {
- Accept: "application/json",
- },
+ // BANDEAUX : On prend l'intégralité des actions disponibles pour l'indice (via coursActionsData ou configIndices)
+ let liste = coursActionsData.filter((item) => {
+ if (!item.indice) return false;
+ const cleanItemIndice = item.indice
+ .replace(/[^a-z0-9]/gi, "")
+ .toLowerCase();
+ return cleanItemIndice === cleanSearch;
});
- if (!response.ok) {
- throw new Error("Erreur HTTP " + response.status);
+ if (liste.length === 0) {
+ liste = configIndices.filter((item) => {
+ if (!item.indice) return false;
+ const cleanItemIndice = item.indice
+ .replace(/[^a-z0-9]/gi, "")
+ .toLowerCase();
+ return cleanItemIndice === cleanSearch;
+ });
}
- const result = await response.json();
+ // Fusion et formatage
+ let listeFinale = liste.map((conf) => {
+ let coursMatch =
+ coursActionsData.find(
+ (item) => item.symbole === conf.symbole || item.isin === conf.isin,
+ ) || conf;
+ return {
+ nom: conf.nom || coursMatch.nom || conf.symbole,
+ cours: coursMatch.cours || 0,
+ variation_pct: coursMatch.variation_pct || 0,
+ };
+ });
- if (!result || result.status !== "success") {
- throw new Error(
- result && result.message
- ? result.message
- : "L'API a retourné une erreur.",
- );
- }
+ // Tri alphabétique
+ listeFinale.sort((a, b) => a.nom.localeCompare(b.nom));
- 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() {
- formNouvelEnregistrement.reset();
-
- /*
- * Valeurs par défaut
- */
-
- nouvelActif.checked = true;
- nouvelAfficher.checked = true;
-
- messageNouveau.classList.add("hidden");
-
- modalNouvelEnregistrement.classList.remove("hidden");
-
- modalNouvelEnregistrement.classList.add("flex");
-
- setTimeout(function () {
- nouvelIndice.focus();
- }, 50);
-}
-
-/*
- * =========================================================
- * FERMER MODAL
- * =========================================================
- */
-
-function fermerModal() {
- modalNouvelEnregistrement.classList.add("hidden");
-
- modalNouvelEnregistrement.classList.remove("flex");
-}
-
-/*
- * =========================================================
- * MESSAGE MODAL
- * =========================================================
- */
-
-function afficherMessageNouveau(texte, type = "error") {
- messageNouveau.textContent = texte;
-
- messageNouveau.classList.remove(
- "hidden",
- "bg-rose-900/50",
- "border-rose-700",
- "text-rose-300",
- "bg-emerald-900/50",
- "border-emerald-700",
- "text-emerald-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",
- );
- }
-}
-
-/*
- * =========================================================
- * SAUVEGARDER NOUVEL ENREGISTREMENT
- * =========================================================
- */
-
-async function sauvegarderNouvelEnregistrement(event) {
- event.preventDefault();
-
- /*
- * Lecture des champs
- */
-
- 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,
+ return listeFinale;
};
- /*
- * Vérification
- */
+ remplirUnBandeau("ticker-cac40", preparerDonneesTicker("CAC40"));
+ remplirUnBandeau("ticker-ibex35", preparerDonneesTicker("IBEX35"));
+ remplirUnBandeau("ticker-dowjones", preparerDonneesTicker("DOWJONES"));
+}
- if (!donnees.indice || !donnees.nom || !donnees.isin || !donnees.symbole) {
- afficherMessageNouveau("Tous les champs texte sont obligatoires.");
+function remplirUnBandeau(elementId, donneesTriees) {
+ const container = document.getElementById(elementId);
+ if (!container) return;
+ if (donneesTriees.length === 0) {
+ container.innerHTML = `Aucune donnée disponible`;
return;
}
- /*
- * Désactivation bouton
- */
-
- btnSauvegarder.disabled = true;
-
- btnSauvegarder.textContent = "Sauvegarde...";
-
- messageNouveau.classList.add("hidden");
-
- 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),
+ let blocs = "";
+ donneesTriees.forEach((item) => {
+ const isPositive = parseFloat(item.variation_pct || 0) >= 0;
+ const colorClass = isPositive ? "text-emerald-400" : "text-rose-400";
+ const sign = isPositive ? "+" : "";
+ const coursFormate = parseFloat(item.cours || 0).toLocaleString("fr-FR", {
+ minimumFractionDigits: 2,
});
+ const variationFormatee = parseFloat(item.variation_pct || 0).toFixed(2);
- if (!response.ok) {
- throw new Error("Erreur HTTP " + response.status);
- }
+ blocs += `
+
+ ${item.nom}
+ ${coursFormate}
+ ${sign}${variationFormatee}%
+
+ `;
+ });
- const result = await response.json();
-
- if (!result || result.status !== "success") {
- throw new Error(
- result && result.message
- ? result.message
- : "Impossible de créer l'enregistrement.",
- );
- }
-
- /*
- * Fermeture
- */
-
- fermerModal();
-
- /*
- * Message
- */
-
- afficherMessage("L'enregistrement a été créé avec succès.", "success");
-
- /*
- * Rechargement
- */
-
- await chargerDonnees();
- } catch (error) {
- console.error("Erreur création enregistrement :", error);
-
- afficherMessageNouveau(error.message, "error");
- } finally {
- btnSauvegarder.disabled = false;
-
- btnSauvegarder.textContent = "Sauvegarder";
- }
+ container.innerHTML = blocs + blocs + blocs;
+ const dureeTotale = Math.max(20, donneesTriees.length * 4);
+ container.style.animationDuration = `${dureeTotale}s`;
}
-/*
- * =========================================================
- * EVENEMENTS
- * =========================================================
- */
-
-filtreIndice.addEventListener("change", function () {
- appliquerFiltre();
-});
-
-btnActualiser.addEventListener("click", function () {
- chargerDonnees();
-});
-
-btnNouveau.addEventListener("click", function () {
- ouvrirModal();
-});
-
-btnFermerModal.addEventListener("click", function () {
- fermerModal();
-});
-
-btnAnnuler.addEventListener("click", function () {
- fermerModal();
-});
-
-formNouvelEnregistrement.addEventListener(
- "submit",
- sauvegarderNouvelEnregistrement,
-);
-
-/*
- * Fermer en cliquant sur le fond
- */
-
-modalNouvelEnregistrement.addEventListener("click", function (event) {
- if (event.target === modalNouvelEnregistrement) {
- fermerModal();
- }
-});
-
-/*
- * Touche Echap
- */
-
-document.addEventListener("keydown", function (event) {
- if (
- event.key === "Escape" &&
- !modalNouvelEnregistrement.classList.contains("hidden")
- ) {
- fermerModal();
- }
-});
-
-/*
- * =========================================================
- * INITIALISATION
- * =========================================================
- */
-
-document.addEventListener("DOMContentLoaded", function () {
- chargerDonnees();
-});
+// Démarrage
+chargerDonnees();
+setInterval(chargerDonnees, 60000);