From 89d84021f38cd686cda237e5fe4c9ba69fbb28bc Mon Sep 17 00:00:00 2001 From: jfgiraud Date: Thu, 20 Aug 2026 07:56:32 +0000 Subject: [PATCH] first commit --- css/style.css | 19 ++++++ index.html | 116 +++++++++++++++++++++++++++++++++ js/indices.js | 152 ++++++++++++++++++++++++++++++++++++++++++++ php/api_indices.php | 50 +++++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 css/style.css create mode 100644 index.html create mode 100644 js/indices.js create mode 100644 php/api_indices.php diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..c7a6c50 --- /dev/null +++ b/css/style.css @@ -0,0 +1,19 @@ +/* Animation de défilement infini pour les bandeaux */ +@keyframes scroll { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-50%); + } +} + +.animate-ticker { + display: flex; + width: max-content; + animation: scroll linear infinite; +} + +.animate-ticker:hover { + animation-play-state: paused; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..a68e92c --- /dev/null +++ b/index.html @@ -0,0 +1,116 @@ + + + + + + Suivi des Indices Boursiers + + + + + + + +
+ +
+
+

+ Tableau de Bord Boursier +

+

+ CAC 40, IBEX 35 & Dow Jones en temps réel +

+
+
+ Chargement des données... +
+
+ + +
+ +
+ + +
+
+

+ Historique récent des cotations +

+ 0 enregistrements +
+
+ + + + + + + + + + + + + +
IndiceNomCoursVariation (%)Date
+
+
+
+ + + + + + + + diff --git a/js/indices.js b/js/indices.js new file mode 100644 index 0000000..e59fbb6 --- /dev/null +++ b/js/indices.js @@ -0,0 +1,152 @@ +// Tableau global JavaScript pour stocker les données +let indicesData = []; + +function chargerDonnees() { + fetch("./php/api_indices.php") + .then((response) => response.json()) + .then((result) => { + if (result.status === "success") { + indicesData = result.data; + + // Mise à jour de l'affichage + 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`; + } else { + console.error("Erreur API :", result.message); + } + }) + .catch((error) => console.error("Erreur réseau :", error)); +} + +function afficherSyntheseCartes() { + const container = document.getElementById("cards-container"); + container.innerHTML = ""; + + const indicesUniques = [...new Set(indicesData.map((item) => item.indice))]; + + indicesUniques.forEach((nomIndice) => { + const dernier = indicesData.find((item) => item.indice === nomIndice); + if (!dernier) return; + + 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 ? "+" : ""; + + container.innerHTML += ` +
+
+
+ ${dernier.indice} + + ${sign}${parseFloat(dernier.variation_pct).toFixed(2)}% + +
+

${dernier.nom || dernier.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 = `Aucune donnée disponible`; + return; + } + + indicesData.forEach((row) => { + const isPositive = parseFloat(row.variation_pct) >= 0; + const textColor = isPositive ? "text-emerald-400" : "text-rose-400"; + const sign = isPositive ? "+" : ""; + + tbody.innerHTML += ` + + ${row.indice} + ${row.nom || "-"} + ${parseFloat(row.cours).toLocaleString("fr-FR")} + ${sign}${parseFloat(row.variation_pct).toFixed(2)}% + ${row.date_cours} + + `; + }); +} + +function alimenterBandeauxTicker() { + // Filtrer et trier par ordre alphabétique sur le champ 'nom' (ou 'indice' si 'nom' est vide) + const preparerDonneesTicker = (nomIndice) => { + let liste = indicesData.filter((item) => item.indice === nomIndice); + + // 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); + }); + + return liste; + }; + + remplirUnBandeau("ticker-cac40", preparerDonneesTicker("CAC40")); + remplirUnBandeau("ticker-ibex35", preparerDonneesTicker("IBEX35")); + remplirUnBandeau("ticker-dowjones", preparerDonneesTicker("DOWJONES")); +} + +function remplirUnBandeau(elementId, donneesTriees) { + const container = document.getElementById(elementId); + if (!container) return; + + if (donneesTriees.length === 0) { + container.innerHTML = `Aucune donnée disponible`; + return; + } + + let htmlContent = ""; + + // On génère une séquence d'éléments + const genererBlocs = () => { + let blocs = ""; + donneesTriees.forEach((item) => { + const isPositive = parseFloat(item.variation_pct) >= 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); + + blocs += ` +
+ ${nomAffichage} + ${coursFormate} + ${sign}${variationFormatee}% +
+ `; + }); + return blocs; + }; + + // On duplique le contenu pour assurer un effet de défilement infini fluide sans saccade + htmlContent = genererBlocs() + genererBlocs(); + container.innerHTML = htmlContent; +} + +// Lancer au chargement initial +chargerDonnees(); + +// Rafraîchir automatiquement toutes les 60 secondes +setInterval(chargerDonnees, 60000); diff --git a/php/api_indices.php b/php/api_indices.php new file mode 100644 index 0000000..7659db1 --- /dev/null +++ b/php/api_indices.php @@ -0,0 +1,50 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, +]; + +try { + $pdo = new PDO($dsn, $user, $pass, $options); + + // Requête UNION pour récupérer : + // - Les 40 derniers pour le CAC40 + // - Les 35 derniers pour l'IBEX35 + // - Les 30 derniers pour le DOWJONES + // (Triés à chaque fois par date_cours décroissante) + $sql = " + (SELECT * FROM cours_actions WHERE indice = 'CAC40' ORDER BY date_cours DESC LIMIT 40) + UNION ALL + (SELECT * FROM cours_actions WHERE indice = 'IBEX35' ORDER BY date_cours DESC LIMIT 35) + UNION ALL + (SELECT * FROM cours_actions WHERE indice = 'DOWJONES' ORDER BY date_cours DESC LIMIT 30) + "; + + $stmt = $pdo->query($sql); + $data = $stmt->fetchAll(); + + // Renvoyer les données sous format JSON propre + echo json_encode([ + "status" => "success", + "count" => count($data), + "data" => $data + ], JSON_PRETTY_PRINT); +} catch (\PDOException $e) { + http_response_code(500); + echo json_encode([ + "status" => "error", + "message" => $e->getMessage() + ]); +}