diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89de242..c4c3e47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,18 @@ et ce projet respecte le [Versionnement Sémantique](https://semver.org/lang/fr/
---
+## [3.1.3] - 2026-05-28
+
+### Ajouté
+- **Cockpit Holistique à 6 Piliers :** Extension du tableau de bord de 4 à 6 indicateurs circulaires SVG autonomes représentant l'intégralité des dimensions de l'athlète (Entraînement, Récupération, Nutrition, Hydratation, Soins, Matériel).
+- **Usine à Badges Dynamique :** Implémentation d'un générateur d'activités et de tâches de maintenance à la volée via un formulaire JavaScript natif interconnecté au moteur de planification.
+- **Sécurisation de la Supply Chain (SRI) :** Intégration des verrous d'intégrité cryptographique `integrity` (SHA-384) sur les liaisons CDN externes pour se conformer aux exigences de sécurité strictes de SonarQube.
+
+### Modifié
+- **Recentrage Produit (MVP) :** Nettoyage architectural complet de l'interface pour éliminer les briques tierces obsolètes (Espace Cohorte, Administrateur, MCO Infra) et recentrer l'application sur le profil unique de l'athlète d'endurance.
+- **Moteur de Rendu Temporel :** Refonte de la logique événementielle dans `app.js` pour détruire et réassigner dynamiquement les écouteurs de glisser-déposer (*Drag & Drop*) sur les nouveaux composants injectés en cours de session.
+- **Feuille de Style Souveraine :** Élimination des conflits d'opacité liés aux classes de transition natives de Bootstrap au profit d'un contrôle d'affichage exclusif et performant en CSS pur.
+
## [3.1.0] - 2026-05-27
### Ajouté
- **Moteur Drag & Drop Natif :** Implémentation d'un système de planification agile (Kanban) dans `app.js` permettant de glisser-déposer les séances d'entraînement à la souris sans aucune dépendance logicielle externe.
diff --git a/app.js b/app.js
index a713383..4b8c587 100644
--- a/app.js
+++ b/app.js
@@ -1,83 +1,148 @@
-document.addEventListener("DOMContentLoaded", () => {
- // --- NAV SWITCHER DES ONGLETS LATÉRAUX ---
- const navLinks = document.querySelectorAll(".nav-link");
- const tabContents = document.querySelectorAll(".tab-content");
+/**
+ * ====================================================================
+ * RYM HORIZON — ENGINE APPLICATION (v3.1.3)
+ * ====================================================================
+ * Philosophie : Code découplé, souveraineté et performance pure.
+ * Architecture : Gestionnaire de vues + Moteur Drag & Drop + Usine à badges.
+ * Conformité : Sans dépendances tierces intrusives.
+ */
- navLinks.forEach(link => {
- link.addEventListener("click", (e) => {
- e.preventDefault();
- navLinks.forEach(l => l.classList.remove("active"));
- tabContents.forEach(tc => tc.classList.remove("active"));
+document.addEventListener('DOMContentLoaded', () => {
+
+ // --- INTÉGRATION DE LA CHRONOLOGIE (SESSION LOG) ---
+ console.log("RYM Horizon Engine v3.1.3 : Initialisation du système...");
- link.classList.add("active");
- const tabId = link.getAttribute("data-tab");
- const targetSection = document.getElementById(tabId);
- if (targetSection) {
- targetSection.classList.add("active");
- }
+ // ==========================================
+ // MODULE 1 : MANAGER DE NAVIGATION SOUVERAIN
+ // ==========================================
+ const tabButtons = document.querySelectorAll('.nav-view-switcher .nav-link');
+ const tabPanes = document.querySelectorAll('.tab-content .tab-pane');
+
+ tabButtons.forEach(button => {
+ button.addEventListener('click', (e) => {
+ const targetTabId = button.getAttribute('data-tab');
+
+ // 1. Mise à jour de l'état actif sur les boutons du menu
+ tabButtons.forEach(btn => btn.classList.remove('active'));
+ button.classList.add('active');
+
+ // 2. Commutation stricte des conteneurs de vues (Géré via CSS display !important)
+ tabPanes.forEach(pane => {
+ if (pane.id === targetTabId) {
+ pane.classList.add('active');
+ } else {
+ pane.classList.remove('active');
+ }
+ });
+ console.log(`[Navigation] Passage sur la vue : ${targetTabId}`);
});
});
- // --- DRAG & DROP ENGINE INTELLIGENT ---
- const draggables = document.querySelectorAll(".rym-draggable-workout");
- const dropZones = document.querySelectorAll(".drop-zone");
- function recalculerChargeHebdo() {
- let totalSéances = 0;
+ // ==========================================
+ // MODULE 2 : USINE DE CRÉATION DE BADGES (À LA VOLÉE)
+ // ==========================================
+ const generatorForm = document.getElementById('rym-coach-generator');
+ const productionZone = document.getElementById('badge-production-zone');
+
+ // Dictionnaire de configuration des composants (Couleurs CSS natifs et Icônes)
+ const typeDictionary = {
+ run: { cssClass: 'run', icon: 'bi-lightning-fill' },
+ bike: { cssClass: 'bike', icon: 'bi-bicycle' },
+ swim: { cssClass: 'swim', icon: 'bi-water' },
+ matos: { cssClass: 'run', icon: 'bi-gear-wide-connected', customStyle: 'background: #fef2f2; color: #991b1b; border: 1px dashed #f87171;' },
+ soins: { cssClass: 'swim', icon: 'bi-heart-pulse-fill', customStyle: 'background: #faf5ff; color: #6b21a8; border: 1px dashed #c084fc;' }
+ };
+
+ generatorForm.addEventListener('submit', (e) => {
+ e.preventDefault(); // Bloque le rechargement de la page pour préserver le cache local
+
+ const type = document.getElementById('gen-type').value;
+ const title = document.getElementById('gen-title').value;
+ const config = typeDictionary[type];
+ const uniqueId = `w-generated-${Date.now()}`; // Identifiant unique basé sur le timestamp
+
+ // Nettoyage de la zone de production
+ productionZone.innerHTML = '';
+
+ // Création du nœud DOM du badge
+ const badge = document.createElement('div');
+ badge.id = uniqueId;
+ badge.className = `workout ${config.cssClass} rym-draggable-workout`;
+ badge.setAttribute('draggable', 'true');
- dropZones.forEach(zone => {
- totalSéances += zone.querySelectorAll(".rym-draggable-workout").length;
+ if (config.customStyle) {
+ badge.setAttribute('style', config.customStyle);
+ }
+
+ // Injection du contenu structuré
+ badge.innerHTML = ` ${title}`;
+
+ // Activation immédiate de la mécanique Drag sur le nouveau composant
+ attachDragEventsToElement(badge);
+
+ // Rendu final dans l'interface
+ productionZone.appendChild(badge);
+ generatorForm.reset();
+ console.log(`[Engine] Nouveau badge instancié avec succès ID: ${uniqueId}`);
+ });
+
+
+ // ==========================================
+ // MODULE 3 : MOTEUR DRAG & DROP MULTI-CIBLES
+ // ==========================================
+ let draggedElement = null;
+
+ /**
+ * Attache les écouteurs d'événements Drag à un élément unique
+ * @param {HTMLElement} element
+ */
+ function attachDragEventsToElement(element) {
+ element.addEventListener('dragstart', (e) => {
+ draggedElement = element;
+ e.dataTransfer.setData('text/plain', element.id);
+ element.style.opacity = '0.4';
+ console.log(`[Drag] Début du déplacement de l'élément : ${element.id}`);
});
- // Calcul dynamique : 25% de charge par bloc actif, plafonné à 100%
- const nouvelleCharge = Math.min(totalSéances * 25, 100);
-
- // Cibler le deuxième spinner du dashboard (Charge Hebdo)
- const spinnerCharge = document.querySelectorAll(".rym-spinner-wrapper")[1];
- if (spinnerCharge) {
- const cercleStroke = spinnerCharge.querySelector(".circle-stroke");
- const texteValeur = spinnerCharge.querySelector(".rym-spinner-text span");
-
- texteValeur.textContent = nouvelleCharge;
- cercleStroke.setAttribute("stroke-dasharray", `${nouvelleCharge}, 100`);
- }
+ element.addEventListener('dragend', () => {
+ element.style.opacity = '1';
+ draggedElement = null;
+ });
}
- draggables.forEach(draggable => {
- draggable.addEventListener("dragstart", (e) => {
- e.dataTransfer.setData("text/plain", draggable.id);
- setTimeout(() => { draggable.style.display = "none"; }, 0);
- });
+ // Initialisation du moteur Drag sur les cartes pré-existantes au boot
+ document.querySelectorAll('.rym-draggable-workout').forEach(attachDragEventsToElement);
- draggable.addEventListener("dragend", () => {
- draggable.style.display = "block";
- });
- });
+ // Configuration des zones de réception (Les jours de la semaine)
+ const dropZones = document.querySelectorAll('.drop-zone');
dropZones.forEach(zone => {
- zone.addEventListener("dragover", (e) => {
- e.preventDefault();
- zone.classList.add("drag-over");
+ zone.addEventListener('dragover', (e) => {
+ e.preventDefault(); // Indispensable pour autoriser le drop dans le navigateur
+ zone.classList.add('drag-over');
});
- zone.addEventListener("dragleave", () => {
- zone.classList.remove("drag-over");
+ zone.addEventListener('dragleave', () => {
+ zone.classList.remove('drag-over');
});
- zone.addEventListener("drop", (e) => {
+ zone.addEventListener('drop', (e) => {
e.preventDefault();
- zone.classList.remove("drag-over");
-
- const id = e.dataTransfer.getData("text/plain");
- const draggableElement = document.getElementById(id);
-
- if (draggableElement) {
- zone.appendChild(draggableElement);
- recalculerChargeHebdo();
+ zone.classList.remove('drag-over');
+
+ if (draggedElement) {
+ // Déplacement physique du nœud dans le conteneur du jour ciblé
+ zone.appendChild(draggedElement);
+
+ // Si le badge provient de la zone de production, on remet le texte d'attente
+ if (productionZone.children.length === 0) {
+ productionZone.innerHTML = 'Aucun badge généré';
+ }
+
+ console.log(`[Drop] Élément réassigné au jour : ${zone.getAttribute('data-day')}`);
}
});
});
- // Calcul initial
- recalculerChargeHebdo();
});
diff --git a/index.html b/index.html
index 1101329..6b47703 100644
--- a/index.html
+++ b/index.html
@@ -3,244 +3,261 @@
- RYM Horizon | Écosystème Souverain
-
-
+ RYM Horizon — Cockpit Intégré v3.1.3
+
+
-
-
-
-
-
-
-
-
- MCO Dashboard
-
-
Status Infra
-
● Online - France (OVH)
-
-
-
-
- RYM Bank
-
-
Solde Courant
- 12 450,80 €
-
-
-
-
-
-
RYM Coach | Mon Planning
- Saison : Triathlon
-
-
-
-
Indicateurs de Performance & Souveraineté
-
-
-
-
-
Vue Hebdomadaire Dynamique
-
-
-
-
-
Lundi
-
- Natation : 2500m
-
-
-
-
-
-
-
Mardi
-
- VMA Balayage
-
-
-
-
-
-
-
Mercredi
-
- Vélo Tempête
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Agenda Précision Premium
-
Zone réservée aux flux haute précision asynchrones.
-
-
-
-
-
-
Espace Expert | Dashboard Cohorte
-
-
-
-
-
-
-
-
-
-
- | Athlète | Santé | Sync API | Action |
-
-
- Jean Dupont Ironman Nice 2026 |
- Optimal |
- |
- |
-
-
-
-
-
-
-
-
-
Bilan Anthropométrique (Cible)
-
-
Poids: 74.5 kg
Âge: 32
-
-
Cou: 39cm
Poitrine: 104cm
-
Hanches: 92cm
Taille: 80cm
-
Cuisse (D/G): 58/58
Mollet (D/G): 39/39
-
-
- Données Chiffrées (Souveraineté RYM)
-
-
-
-
-
-
-
+
+
+
+
RymHorizon
+
+
+
+
+
+
+
+
+
+ RYM BANK
+ 19 420,00 €
+
+
+
-
+
+
+
+
+
+
+
+
+
+
EntraînementCharge Hebdo
+
+
+
+
RécupérationVRC / Sommeil
+
+
+
+
NutritionMacro Cibles
+
+
+
+
HydratationEau / Électro
+
+
+
+
Soins / SantéFatigue Musc.
+
+
+
+
MatérielMCO Critique
+
+
+
+
+
+
+
+
+
+
+
Aujourd'hui, Samedi 23 mai
+
+
MATIN
+
Natation - 1h15 Endurance (2500m)
+
+
+
APRÈS-MIDI
+
Repos actif / Préparation vélo
+
+
+
SOIR
+
Course - 45' Footing souple
+
+
+
+
+
+
+
+
+
+
RYM Coach Engine
+
Créez une activité ou une tâche de maintenance et planifiez-la par Drag & Drop.
+
+
+
+
+
+
+
+ Aucun badge généré
+
+
+
+
+
+
+
+
Timeline Hebdomadaire
+ Semaine 21 — Bloc Intensité
+
+
+
+
+
+
+
+
+
+
+
Semaine 1
+
+
Focus : Volume Foncier
+
+
+
+
+
Semaine 2
+
+
Focus : Charge Progressive
+
+
+
+
+
Semaine 3
+
+
Focus : Assimilation / Repos
+
+
+
+
+
Semaine 4
+
+
Focus : Intensité Cible
+
+
+
+
+
+
+
+
+
+
Saison 2026 : Road to Ironman Vitoria
+
Cycle de 24 semaines — Actuellement en Phase de Spécificité 1.
+
+ Prep Foncière
+ Prep Spécifique
+ Affûtage
+
+
+
+
J - 142
+
Avant l'objectif A
+
+
+
+
+
+
+
-