Compare commits

..

No commits in common. "main" and "v3.0.0" have entirely different histories.
main ... v3.0.0

6 changed files with 61 additions and 262 deletions

View File

@ -1,9 +0,0 @@
{
"rules": {
"block-no-empty": true,
"color-no-invalid-hex": true,
"comment-no-empty": true,
"declaration-block-no-duplicate-properties": true,
"declaration-block-no-shorthand-property-overrides": true
}
}

View File

@ -7,16 +7,6 @@ et ce projet respecte le [Versionnement Sémantique](https://semver.org/lang/fr/
--- ---
## [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.
### Modifié
- **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.
## [3.0.0] - 2026-05-23 ## [3.0.0] - 2026-05-23
### Ajouté ### Ajouté

88
app.js
View File

@ -1,60 +1,44 @@
document.addEventListener("DOMContentLoaded", () => { /**
// --- GESTION DES ONGLETS EXISTANTE --- * RYM Horizon - Core Navigation Script
const navLinks = document.querySelectorAll(".nav-link"); * @description Gestionnaire d'onglets découplé conforme aux standards W3C
const tabContents = document.querySelectorAll(".tab-content"); */
document.addEventListener('DOMContentLoaded', () => {
// Sélecteurs d'éléments de navigation
const navLinks = document.querySelectorAll('.sidebar .nav-link');
const tabContents = document.querySelectorAll('.tab-content');
/**
* Bascule l'affichage vers l'onglet ciblé
* @param {string} targetTabId - L'identifiant de la section à afficher
*/
const switchTab = (targetTabId) => {
// 1. Masquer tous les contenus actifs
tabContents.forEach(content => content.classList.remove('active'));
// 2. Afficher le contenu demandé
const activeTarget = document.getElementById(targetTabId);
if (activeTarget) {
activeTarget.classList.add('active');
}
};
// Attribution dynamique des écouteurs d'événements
navLinks.forEach(link => { navLinks.forEach(link => {
link.addEventListener("click", (e) => { link.addEventListener('click', (event) => {
e.preventDefault(); event.preventDefault();
navLinks.forEach(l => l.classList.remove("active"));
tabContents.forEach(tc => tc.classList.remove("active"));
link.classList.add("active"); // Récupération de l'ID cible via le dataset
const tabId = link.getAttribute("data-tab"); const targetTab = link.getAttribute('data-tab');
document.getElementById(tabId).classList.add("active");
});
});
// --- OPTION 1 : DRAG & DROP ENGINE (AAA SOUVERAIN) --- if (targetTab) {
const draggables = document.querySelectorAll(".rym-draggable-workout"); // Gestion de l'état graphique de la navigation
const dropZones = document.querySelectorAll(".drop-zone"); navLinks.forEach(item => item.classList.remove('active'));
link.classList.add('active');
draggables.forEach(draggable => { // Exécution de la bascule logique
draggable.addEventListener("dragstart", (e) => { switchTab(targetTab);
e.dataTransfer.setData("text/plain", draggable.id);
// Petit délai pour l'effet visuel de déplacement
setTimeout(() => {
draggable.style.display = "none";
}, 0);
});
draggable.addEventListener("dragend", () => {
draggable.style.display = "block";
});
});
dropZones.forEach(zone => {
zone.addEventListener("dragover", (e) => {
e.preventDefault(); // Indispensable pour autoriser le drop
zone.classList.add("drag-over");
});
zone.addEventListener("dragleave", () => {
zone.classList.remove("drag-over");
});
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}`);
} }
}); });
}); });

View File

@ -1,21 +0,0 @@
export default [
{
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
window: "readonly",
document: "readonly",
console: "readonly",
setTimeout: "readonly"
}
},
rules: {
"no-unused-vars": "warn",
"no-undef": "error",
"no-console": "off",
"eqeqeq": "error",
"curly": "error"
}
}
];

View File

@ -69,124 +69,36 @@
<span class="badge bg-primary">Saison : Triathlon</span> <span class="badge bg-primary">Saison : Triathlon</span>
</div> </div>
<div class="card card-rym p-4 shadow-sm mb-4">
<h5 class="mb-3 text-primary"><i class="bi bi-activity me-2"></i>Indicateurs de Performance & Souveraineté</h5>
<div class="row row-cols-2 row-cols-sm-2 row-cols-md-4 g-3 text-center justify-content-center">
<div class="col">
<div class="rym-spinner-wrapper">
<svg class="rym-spinner-svg" viewBox="0 0 36 36">
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle-stroke text-success" stroke-dasharray="82, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="rym-spinner-text">
<span class="fw-bold fs-5">82</span><small>%</small>
</div>
</div>
<div class="small fw-semibold mt-2">Fraîcheur VRC</div>
</div>
<div class="col">
<div class="rym-spinner-wrapper">
<svg class="rym-spinner-svg" viewBox="0 0 36 36">
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle-stroke text-warning" stroke-dasharray="65, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="rym-spinner-text">
<span class="fw-bold fs-5">65</span><small>%</small>
</div>
</div>
<div class="small fw-semibold mt-2">Charge Hebdo</div>
</div>
<div class="col">
<div class="rym-spinner-wrapper">
<svg class="rym-spinner-svg" viewBox="0 0 36 36">
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle-stroke text-info" stroke-dasharray="45, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="rym-spinner-text">
<span class="fw-bold fs-5">45</span><small>%</small>
</div>
</div>
<div class="small fw-semibold mt-2">Chaîne Vélo</div>
</div>
<div class="col">
<div class="rym-spinner-wrapper">
<svg class="rym-spinner-svg" viewBox="0 0 36 36">
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle-stroke text-danger" stroke-dasharray="90, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="rym-spinner-text">
<span class="fw-bold fs-5">90</span><small>%</small>
</div>
</div>
<div class="small fw-semibold mt-2">Paires Carbone</div>
</div>
</div>
</div>
<div class="card card-rym p-4 shadow-sm mb-5"> <div class="card card-rym p-4 shadow-sm mb-5">
<h5 class="mb-3 text-primary"><i class="bi bi-calendar-week me-2"></i>Vue Hebdomadaire Dynamique</h5> <h5 class="mb-3 text-primary"><i class="bi bi-calendar-week me-2"></i>Vue Hebdomadaire (Standard)</h5>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-4 row-cols-lg-7 g-2" id="planning-kanban"> <div class="table-responsive">
<table class="table table-bordered text-center align-middle">
<div class="col"> <thead class="bg-dark text-white">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Lun"> <tr><th>Moment</th><th>Lun</th><th>Mar</th><th>Mer</th><th>Jeu</th><th>Ven</th><th>Sam</th><th>Dim</th></tr>
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Lundi</div> </thead>
<div class="rym-draggable-workout bg-primary text-white p-2 rounded mb-2 shadow-sm small" draggable="true" id="w1"> <tbody>
<i class="bi bi-water me-1"></i> Natation : 2500m <tr><td class="bg-light fw-bold">Matin</td><td><i class="bi bi-water"></i></td><td>-</td><td><i class="bi bi-bicycle"></i></td><td><i class="bi bi-water"></i></td><td>-</td><td class="bg-info-subtle">Loisir</td><td class="bg-danger-subtle">Repos</td></tr>
</div> <tr><td class="bg-light fw-bold">A-M</td><td>-</td><td><i class="bi bi-lightning"></i></td><td>-</td><td>-</td><td><i class="bi bi-lightning"></i></td><td><i class="bi bi-bicycle"></i></td><td>-</td></tr>
</div> <tr><td class="bg-light fw-bold">Soir</td><td>-</td><td>-</td><td><i class="bi bi-person-walking"></i></td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
</div> </tbody>
</table>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Mar">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Mardi</div>
<div class="rym-draggable-workout bg-warning text-dark p-2 rounded mb-2 shadow-sm small" draggable="true" id="w2">
<i class="bi bi-lightning me-1"></i> VMA Balayage
</div>
</div>
</div>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Mer">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Mercredi</div>
<div class="rym-draggable-workout bg-info text-dark p-2 rounded mb-2 shadow-sm small" draggable="true" id="w3">
<i class="bi bi-bicycle me-1"></i> Vélo Tempête
</div>
</div>
</div>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Jeu">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Jeudi</div>
</div>
</div>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Ven">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Vendredi</div>
</div>
</div>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Sam">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Samedi</div>
</div>
</div>
<div class="col">
<div class="bg-light p-2 rounded border h-100 drop-zone" data-day="Dim">
<div class="fw-bold text-center border-bottom pb-1 mb-2 bg-dark text-white rounded-top small">Dimanche</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="card card-rym p-4 premium-zone shadow-lg"> <div class="card card-rym p-4 premium-zone shadow-lg">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="fw-bold text-dark"><i class="bi bi-clock me-2"></i>Agenda Précision Premium (24h/24)</h5>
<button class="btn btn-warning btn-sm fw-bold">Passer au Premium</button>
</div>
<div class="bg-light rounded p-3 agenda-scroll">
<div class="hour-row"><div class="col-1 fw-bold border-end pe-2">06:00</div><div class="col-11 ps-3 text-muted">Sommeil / Réveil</div></div>
<div class="hour-row"><div class="col-1 fw-bold border-end pe-2">07:00</div><div class="col-11 ps-3 bg-primary-subtle text-primary">Natation : 2500m Seuil</div></div>
<div class="hour-row"><div class="col-1 fw-bold border-end pe-2">08:00</div><div class="col-11 ps-3">Petit-Déjeuner & Hydratation</div></div>
<div class="hour-row"><div class="col-1 fw-bold border-end pe-2 text-primary">...</div><div class="col-11 ps-3">...</div></div>
<div class="hour-row"><div class="col-1 fw-bold border-end pe-2">23:00</div><div class="col-11 ps-3 text-muted">Repos / Nuit</div></div>
</div>
</div>
</section>
<section id="clients" class="tab-content"> <section id="clients" class="tab-content">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">

View File

@ -91,60 +91,3 @@ body {
from { opacity: 0; transform: translateY(4px); } from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; transform: translateY(0); }
} }
/* --- CONFIGURATION DES SPINNERS (OPTION 2) --- */
.rym-spinner-wrapper {
position: relative;
width: 90px;
height: 90px;
margin: 0 auto;
}
.rym-spinner-svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.circle-bg {
fill: none;
stroke: #e6e6e6;
stroke-width: 2.8;
}
.circle-stroke {
fill: none;
stroke-width: 2.8;
stroke-linecap: round;
transition: stroke-dasharray 0.5s ease;
}
.rym-spinner-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
/* --- DRAG AND DROP KANBAN (OPTION 1) --- */
.drop-zone {
min-height: 140px;
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.drop-zone.drag-over {
background-color: #e2e8f0 !important;
border: 2px dashed #0dcaf0 !important;
}
.rym-draggable-workout {
cursor: grab;
transition: transform 0.1s ease, opacity 0.1s ease;
user-select: none;
}
.rym-draggable-workout:active {
cursor: grabbing;
opacity: 0.5;
}