diff --git a/.gitea/workflows/secops.yml b/.gitea/workflows/secops.yml
new file mode 100644
index 0000000..1c69001
--- /dev/null
+++ b/.gitea/workflows/secops.yml
@@ -0,0 +1,42 @@
+name: DevSecOps Pipeline - Universal Quality Gate
+
+on:
+ push:
+ branches: [ "main", "develop" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ health-check:
+ name: System & Environment Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: System Check
+ run: |
+ echo "🚀 Pipeline CI/CD opérationnel !"
+ uname -a
+
+ gitleaks-scan:
+ name: Secret Detection (Gitleaks)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Run Gitleaks via Podman
+ run: |
+ echo "🔍 Lancement du scan Gitleaks..."
+ podman run --rm -v "$PWD":/path:z docker.io/zricethezav/gitleaks:v8.18.2 detect --source="/path" -v
+
+ trivy-iac-scan:
+ name: IaC Security & Vulnerability Scan (Trivy)
+ runs-on: ubuntu-latest
+ needs: gitleaks-scan
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Run Trivy IaC Scan
+ run: |
+ echo "🛡️ Analyse des fichiers d'infrastructure et des vulnérabilités..."
+ podman run --rm -v "$PWD":/path:z docker.io/aquasec/trivy:0.49.1 config /path
diff --git a/.gitignore b/.gitignore
index 3055b57..f5607ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,7 @@ dist/
build/
.sass-cache/
.cache/
+
+# --- Nginx erreurs fichiers ---
+# A remplir au fur et à mesure
+50x.html
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 439fc1e..c4c3e47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,13 +7,29 @@ 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.
- **Cockpit Physiologique & Matériel :** Intégration de 4 indicateurs de performance (Fraîcheur VRC, Charge Hebdo, Usure Chaîne Vélo, Paires Carbone) basés sur des tracés SVG natifs et légers.
- **Gouvernance & Linters :** Déploiement des fichiers de configuration modernes `eslint.config.js` (Format Flat Config de niveau industriel) et `.stylelintrc.json` à la racine du projet.
+- **Moteur Kanban Métier :** Implémentation du système de Drag & Drop natif dans l'onglet RYM Coach permettant de réorganiser le planning hebdomadaire.
+- **Calculateur de Charge Dynamique :** Automatisation du recalcul du spinner "Charge Hebdo" en JavaScript à chaque déplacement d'entraînement.
### Modifié
+- **Architecture de Navigation :** Consolidation du routage événementiel pour assurer la permutation étanche entre les onglets MCO Dashboard, RYM Bank, RYM Coach et Suivi Cohorte.
+- **Design System :** Correction structurelle des imbrications de conteneurs Bootstrap 5 pour garantir un affichage fluide de la zone Expert.
- **Design System Responsif :** Optimisation de la structure Bootstrap 5 pour forcer l'alignement des "spinners" sur une seule ligne (`row`) sur écran desktop et une répartition fluide sur mobile.
- **Pont de Développement :** Restructuration du conteneur de validation openSUSE Leap 15.6 via un montage de volume Podman sécurisé (`:Z`) permettant l'audit du code en temps réel depuis l'hôte Fedora.
diff --git a/app.js b/app.js
index fb38959..4b8c587 100644
--- a/app.js
+++ b/app.js
@@ -1,61 +1,148 @@
-document.addEventListener("DOMContentLoaded", () => {
- // --- GESTION DES ONGLETS EXISTANTE ---
- 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");
- document.getElementById(tabId).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}`);
});
});
- // --- OPTION 1 : DRAG & DROP ENGINE (AAA SOUVERAIN) ---
- const draggables = document.querySelectorAll(".rym-draggable-workout");
- const dropZones = document.querySelectorAll(".drop-zone");
- draggables.forEach(draggable => {
- draggable.addEventListener("dragstart", (e) => {
- e.dataTransfer.setData("text/plain", draggable.id);
- // Petit délai pour l'effet visuel de déplacement
- setTimeout(() => {
- draggable.style.display = "none";
- }, 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');
- draggable.addEventListener("dragend", () => {
- draggable.style.display = "block";
- });
+ // 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');
+
+ 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}`);
+ });
+
+ element.addEventListener('dragend', () => {
+ element.style.opacity = '1';
+ draggedElement = null;
+ });
+ }
+
+ // Initialisation du moteur Drag sur les cartes pré-existantes au boot
+ document.querySelectorAll('.rym-draggable-workout').forEach(attachDragEventsToElement);
+
+ // 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(); // Indispensable pour autoriser le drop
- 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);
- // Ici se branchera l'appel API asynchrone vers ton Gitea/Serveur
- const targetDay = zone.getAttribute("data-day");
- console.log(`Séance déplacée avec succès sur le jour : ${targetDay}`);
+ 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')}`);
}
});
});
+
});
diff --git a/index.html b/index.html
index b26d929..6b47703 100644
--- a/index.html
+++ b/index.html
@@ -3,240 +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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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
+
+
+
+
+
+
+
-