-
- - Tableau de Bord Boursier -
-- CAC 40, IBEX 35 & Dow Jones en temps réel -
-
- Chargement des données...
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+ + Tableau de bord boursier +
++ Suivi des cours, variations et analyses de vos portefeuilles et + indices financiers. +
+
+
+ Dernière mise à jour : --:--:--
+
+
+
+
-
-
-
-
@@ -84,7 +69,6 @@
>
-
- - Historique récent des cotations -
- 0 enregistrements -
-
-
-
-
- | Indice | -Nom | -Cours | -Variation (%) | -Date | -
|---|
-
CAC40
-
response.json())
- .then((result) => {
- if (result.status === "success") {
- // Stockage dans le tableau global JS
- indicesData = result.data;
-
- // Mise à jour de tous les affichages
- afficherSyntheseCartes();
- afficherTableauDetails();
- alimenterBandeauxTicker();
-
- // Mise à jour du statut
- document.getElementById("statut").innerText =
- `Dernière mise à jour : ${new Date().toLocaleTimeString()}`;
- document.getElementById("counter").innerText =
- `${indicesData.length} enregistrements`;
+ // 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 :", result.message);
+ console.error("Erreur API config indices :", configResult.message);
+ }
+
+ 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);
+ }
+
+ // Mise à jour des vues
+ afficherSyntheseCartes();
+ alimenterBandeauxTicker();
+
+ // 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 :", error));
+ .catch((error) =>
+ console.error("Erreur réseau lors du chargement des API :", error),
+ );
}
function afficherSyntheseCartes() {
const container = document.getElementById("cards-container");
+ if (!container) return;
+
container.innerHTML = "";
- // Trouver les indices uniques présents dans le tableau
- const indicesUniques = [...new Set(indicesData.map((item) => item.indice))];
+ // CARTES : On se base STRICTEMENT sur le champ afficher == 1
+ const actionsAffichees = configIndices.filter(
+ (item) => String(item.afficher) === "1" || item.afficher == 1,
+ );
- indicesUniques.forEach((nomIndice) => {
- // Récupère le premier élément (le plus récent) pour cet indice
- const dernier = indicesData.find((item) => item.indice === nomIndice);
- if (!dernier) return;
+ 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,
+ ) || {};
- const isPositive = parseFloat(dernier.variation_pct) >= 0;
- const badgeColor = isPositive
- ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20"
- : "bg-rose-500/10 text-rose-400 border-rose-500/20";
- const sign = isPositive ? "+" : "";
+ const ecartValeur = parseFloat(d.variation || 0);
+ const isPositive = ecartValeur >= 0;
+ const badgeColor = isPositive ? "text-emerald-400" : "text-rose-400";
+ const sign = isPositive ? "+" : "";
- container.innerHTML += `
-
-
-
- ${dernier.indice}
-
- ${sign}${parseFloat(dernier.variation_pct).toFixed(2)}%
-
-
- ${dernier.nom || dernier.indice}
+ const volumeFormate = d.volume + ? parseInt(d.volume).toLocaleString("fr-FR") + : "--"; + + container.innerHTML += ` +
+
+
- ${row.indice}
- ${row.nom || "-"}
- ${parseFloat(row.cours).toLocaleString("fr-FR")}
- ${sign}${parseFloat(row.variation_pct).toFixed(2)}%
- ${row.date_cours}
-
- `;
- });
}
function alimenterBandeauxTicker() {
- const preparerDonneesTicker = (nomIndice) => {
- let liste = indicesData.filter((item) => item.indice === nomIndice);
+ const preparerDonneesTicker = (nomIndiceRecherche) => {
+ const cleanSearch = nomIndiceRecherche
+ .replace(/[^a-z0-9]/gi, "")
+ .toLowerCase();
- // Tri alphabétique
- liste.sort((a, b) => {
- let valA = (a.nom || a.indice).toLowerCase();
- let valB = (b.nom || b.indice).toLowerCase();
- return valA.localeCompare(valB);
+ // 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;
});
- return liste;
+ 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;
+ });
+ }
+
+ // 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,
+ };
+ });
+
+ // Tri alphabétique
+ listeFinale.sort((a, b) => a.nom.localeCompare(b.nom));
+
+ return listeFinale;
};
remplirUnBandeau("ticker-cac40", preparerDonneesTicker("CAC40"));
@@ -112,46 +178,37 @@ function alimenterBandeauxTicker() {
function remplirUnBandeau(elementId, donneesTriees) {
const container = document.getElementById(elementId);
- if (!container) {
- console.error("Élément introuvable :", elementId);
- return;
- }
+ if (!container) return;
if (donneesTriees.length === 0) {
- container.innerHTML = `Aucune donnée disponible`;
+ container.innerHTML = `Aucune donnée disponible`;
return;
}
let blocs = "";
donneesTriees.forEach((item) => {
- const isPositive = parseFloat(item.variation_pct) >= 0;
+ const isPositive = parseFloat(item.variation_pct || 0) >= 0;
const colorClass = isPositive ? "text-emerald-400" : "text-rose-400";
const sign = isPositive ? "+" : "";
- const nomAffichage = item.nom || item.indice;
- const coursFormate = parseFloat(item.cours).toLocaleString("fr-FR");
- const variationFormatee = parseFloat(item.variation_pct).toFixed(2);
+ const coursFormate = parseFloat(item.cours || 0).toLocaleString("fr-FR", {
+ minimumFractionDigits: 2,
+ });
+ const variationFormatee = parseFloat(item.variation_pct || 0).toFixed(2);
blocs += `
-
+ Aucune donnée disponible `;
- return;
+
+
+ `;
+ });
+ } else {
+ container.innerHTML = `
+
+
- + ${conf.nom || conf.indice} (${conf.symbole || "N/A"}) +
+ ${conf.isin || "Aucun ISIN"} + ${conf.indice || ""}
- ${parseFloat(dernier.cours).toLocaleString("fr-FR")}
- ${dernier.date_cours}
+
+
- `;
- });
-}
-function afficherTableauDetails() {
- const tbody = document.getElementById("table-body");
- tbody.innerHTML = "";
-
- if (indicesData.length === 0) {
- tbody.innerHTML = `
+
+ ${d.cours ? parseFloat(d.cours).toLocaleString("fr-FR", { minimumFractionDigits: 2 }) : "--"}
+
+
+ ${sign}${ecartValeur.toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 4 })}
+ (${sign}${parseFloat(d.variation_pct || 0).toFixed(2)}%)
+
+
+ ${d.date_cours || "--/--"}
+
+
+
+ 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}
+
+
+ Aucune action configurée à 'afficher = 1'.
+
+ `;
}
-
- indicesData.forEach((row) => {
- const isPositive = parseFloat(row.variation_pct) >= 0;
- const textColor = isPositive ? "text-emerald-400" : "text-rose-400";
- const sign = isPositive ? "+" : "";
-
- tbody.innerHTML += `
-
- ${nomAffichage}
- ${coursFormate}
- ${sign}${variationFormatee}%
-
- `;
+
+ ${item.nom}
+ ${coursFormate}
+ ${sign}${variationFormatee}%
+
+ `;
});
- // Duplication du contenu pour la boucle infinie fluide
- container.innerHTML = blocs + blocs;
-
- // Vitesse uniforme et lente
- const dureeParElement = 6;
- const dureeTotale = Math.max(30, donneesTriees.length * dureeParElement);
-
+ container.innerHTML = blocs + blocs + blocs;
+ const dureeTotale = Math.max(20, donneesTriees.length * 4);
container.style.animationDuration = `${dureeTotale}s`;
}
-// Lancer le chargement des données au démarrage de la page
+// Démarrage
chargerDonnees();
-
-// Rafraîchir automatiquement toutes les 60 secondes
setInterval(chargerDonnees, 60000);
diff --git a/php/api_lecture_tableindice.php b/php/api_lecture_tableindice.php
new file mode 100644
index 0000000..1c2dd9f
--- /dev/null
+++ b/php/api_lecture_tableindice.php
@@ -0,0 +1,28 @@
+setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ // Requête pour récupérer les actions configurées avec afficher = 1 dans la table indice
+ $stmt = $pdo->prepare("SELECT * FROM indice WHERE afficher = 1");
+ $stmt->execute();
+ $actionsConfig = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ echo json_encode([
+ "status" => "success",
+ "data" => $actionsConfig
+ ]);
+} catch (PDOException $e) {
+ echo json_encode([
+ "status" => "error",
+ "message" => $e->getMessage()
+ ]);
+}