diff --git a/frontend/index.html b/frontend/index.html index fc04504562c0b47b6103d75a24e13ab060b4ce94..d63f309f475ea5d69f4861128f02d19bfa4c5fc3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -82,170 +82,191 @@ </section><!-- End Hero --> <main id="main"> - <!-- ======= Details Section ======= --> - <section id="steps-analysis" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Texte à gauche --> - <div class="col-md-6" data-aos="fade-right"> - <h3>Analyse des Pas des Utilisateurs</h3> - <p> - Cette visualisation illustre les données sur le nombre de pas effectués par les utilisateurs - chaque mois au cours des années. Utilisez le slider ci-dessous pour explorer les données - par année et découvrir les tendances individuelles de chaque utilisateur. - </p> - <ul> - <li><i class="bi bi-check"></i> Analyse par mois et par année.</li> - <li><i class="bi bi-check"></i> Comparaison des pas entre utilisateurs.</li> - <li><i class="bi bi-check"></i> Visualisation interactive avec un slider.</li> - </ul> - </div> - <!-- Visualisation à droite --> - <div class="col-md-6"> - <div id="visualization" style="position: relative;"></div> - <div id="slider-container" style="margin-top: 20px;"></div> - </div> + <!-- ======= Details Section ======= --> + <section id="steps-analysis" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Texte à gauche --> + <div class="col-md-6" data-aos="fade-right"> + <h3>Analyse des Pas des Utilisateurs</h3> + <p> + Cette visualisation illustre les données sur le nombre de pas effectués par les utilisateurs + chaque mois au cours des années. Utilisez le slider ci-dessous pour explorer les données + par année et découvrir les tendances individuelles de chaque utilisateur. + </p> + <ul> + <li><i class="bi bi-check"></i> Analyse par mois et par année.</li> + <li><i class="bi bi-check"></i> Comparaison des pas entre utilisateurs.</li> + <li><i class="bi bi-check"></i> Visualisation interactive avec un slider.</li> + </ul> + </div> + <!-- Visualisation à droite --> + <div class="col-md-6"> + <div class="d-flex justify-content-between align-items-center my-3" id="steps-buttons-container"> + <button id="stepsPrevYear" class="btn btn-outline-dark" disabled><</button> + <span id="stepsCurrentYear" class="font-weight-bold" style="font-size: 1.5rem;">2024</span> + <button id="stepsNextYear" class="btn btn-outline-dark">></button> + </div> + <div id="steps-visualization" style="position: relative;"></div> + + </div> </div> - </section> - - <section id="distance-analysis" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Visualisation à gauche --> - <div class="col-md-6 order-md-1"> - <div id="distance-visualization" style="position: relative;"></div> - <div id="distance-slider-container" style="margin-top: 20px;"></div> - </div> - <!-- Texte à droite --> - <div class="col-md-6 order-md-2" data-aos="fade-left"> - <h3>Analyse de la Distance Parcourue</h3> - <p> - Découvrez comment la distance parcourue est liée aux calories brûlées pour chaque utilisateur. - Utilisez le slider pour explorer les données par année et comparez les performances des différents - membres. - </p> - <ul> - <li><i class="bi bi-check"></i> Relation distance-calories par mois et par année.</li> - <li><i class="bi bi-check"></i> Comparaison des membres.</li> - <li><i class="bi bi-check"></i> Visualisation interactive avec bulles et tooltip.</li> - </ul> - </div> + </div> + </section> + + <section id="distance-analysis" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation à gauche --> + <div class="col-md-6 order-md-1"> + <div id="distance-visualization" style="position: relative;"></div> + <div id="distance-slider-container" style="margin-top: 20px;"></div> + </div> + <!-- Texte à droite --> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse de la Distance Parcourue</h3> + <p> + Découvrez comment la distance parcourue est liée aux calories brûlées pour chaque utilisateur. + Utilisez le slider pour explorer les données par année et comparez les performances des différents + membres. + </p> + <ul> + <li><i class="bi bi-check"></i> Relation distance-calories par mois et par année.</li> + <li><i class="bi bi-check"></i> Comparaison des membres.</li> + <li><i class="bi bi-check"></i> Visualisation interactive avec bulles et tooltip.</li> + </ul> </div> </div> - </section> - - <section id="calories-analysis" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Texte à gauche --> - <div class="col-md-6" data-aos="fade-right"> - <h3>Analyse des Calories Brûlées</h3> - <p> - Cette visualisation explore les calories brûlées par chaque utilisateur au fil des mois. - Utilisez le slider pour naviguer entre les années et analyser les tendances des différents membres. - </p> - <ul> - <li><i class="bi bi-check"></i> Analyse des calories par mois et par année.</li> - <li><i class="bi bi-check"></i> Comparaison des utilisateurs.</li> - <li><i class="bi bi-check"></i> Visualisation interactive avec des aires.</li> - </ul> - </div> - <!-- Visualisation à droite --> - <div class="col-md-6"> - <div id="calories-visualization" style="position: relative;"></div> - <div id="calories-slider-container" style="margin-top: 20px;"></div> - </div> + </div> + </section> + + <section id="calories-analysis" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Texte à gauche --> + <div class="col-md-6" data-aos="fade-right"> + <h3>Analyse des Calories Brûlées</h3> + <p> + Cette visualisation explore les calories brûlées par chaque utilisateur au fil des mois. + Utilisez le slider pour naviguer entre les années et analyser les tendances des différents membres. + </p> + <ul> + <li><i class="bi bi-check"></i> Analyse des calories par mois et par année.</li> + <li><i class="bi bi-check"></i> Comparaison des utilisateurs.</li> + <li><i class="bi bi-check"></i> Visualisation interactive avec des aires.</li> + </ul> + </div> + <!-- Visualisation à droite --> + <div class="col-md-6"> + <div id="calories-visualization" style="position: relative;"></div> + + </div> </div> - </section> - - <section id="sleep-analysis" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Visualisation à gauche --> - <div class="col-md-6 order-md-1"> - <div id="sleep-visualization" style="position: relative;"></div> - <div id="slider-container" style="margin-top: 20px;"></div> - </div> - <!-- Texte à droite --> - <div class="col-md-6 order-md-2" data-aos="fade-left"> - <h3>Analyse des Heures de Sommeil</h3> - <p> - Cette visualisation met en évidence les heures de sommeil moyennes par mois pour chaque utilisateur. - Utilisez le slider pour explorer les données par année et visualiser les tendances individuelles. - </p> - <ul> - <li><i class="bi bi-check"></i> Analyse des heures de sommeil moyen par mois et par année.</li> - <li><i class="bi bi-check"></i> Comparaison entre utilisateurs.</li> - <li><i class="bi bi-check"></i> Détails interactifs par jour pour un mois donné.</li> - </ul> + </div> + </section> + + <section id="sleep-analysis" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation à gauche --> + <div class="col-md-6 order-md-1"> + <div class="d-flex justify-content-between align-items-center"> + <button id="prevYear" class="btn btn-outline-dark"> < </button> + <span id="currentYear" class="font-weight-bold" style="font-size: 1.5rem;">2024</span> + <button id="nextYear" class="btn btn-outline-dark"> > </button> </div> + <div id="sleep-visualization" style="position: relative;"></div> + + </div> + <!-- Texte à droite --> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse des Heures de Sommeil</h3> + <p> + Cette visualisation met en évidence les heures de sommeil moyennes par mois pour chaque utilisateur. + Utilisez le slider pour explorer les données par année et visualiser les tendances individuelles. + </p> + <ul> + <li><i class="bi bi-check"></i> Analyse des heures de sommeil moyen par mois et par année.</li> + <li><i class="bi bi-check"></i> Comparaison entre utilisateurs.</li> + <li><i class="bi bi-check"></i> Détails interactifs par jour pour un mois donné.</li> + </ul> + + </div> </div> - </section> - - <section id="sleep-activity-analysis" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Texte à gauche --> - <div class="col-md-6" data-aos="fade-right"> - <h3>Analyse du Sommeil et de l'Activité</h3> - <p> - Cette visualisation interactive présente la relation entre les pas effectués, - les heures de sommeil et les calories brûlées par semaine. - Utilisez le slider pour explorer les données semaine par semaine. - </p> - <ul> - <li><i class="bi bi-check"></i> Relation entre les pas, le sommeil et les calories brûlées.</li> - <li><i class="bi bi-check"></i> Comparaison hebdomadaire pour chaque utilisateur.</li> - <li><i class="bi bi-check"></i> Régression et tendances visuelles.</li> - </ul> - </div> - <!-- Visualisation à droite --> - <div class="col-md-6"> - <div id="sleep-activity-visualization" style="position: relative;"></div> - <div class="slider-container-sleep-activity d-flex align-items-center mt-3"> - <div class="slider-label">Date: <span id="date-label">2023-W39</span></div> - <input type="range" id="date-slider" class="slider mx-2" min="0" max="10" step="1" style="flex-grow: 1;"> - <button id="play-button" class="btn btn-primary">Play</button> - </div> + + </div> + <div id="modal" class="modal"> + <div class="modal-content"> + <span class="close">×</span> + <div id="detail-visualization"></div> + </div> + </div> + </section> + + <section id="sleep-activity-analysis" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Texte à gauche --> + <div class="col-md-6" data-aos="fade-right"> + <h3>Analyse du Sommeil et de l'Activité</h3> + <p> + Cette visualisation interactive présente la relation entre les pas effectués, + les heures de sommeil et les calories brûlées par semaine. + Utilisez le slider pour explorer les données semaine par semaine. + </p> + <ul> + <li><i class="bi bi-check"></i> Relation entre les pas, le sommeil et les calories brûlées.</li> + <li><i class="bi bi-check"></i> Comparaison hebdomadaire pour chaque utilisateur.</li> + <li><i class="bi bi-check"></i> Régression et tendances visuelles.</li> + </ul> + </div> + <!-- Visualisation à droite --> + <div class="col-md-6"> + <div id="sleep-activity-visualization" style="position: relative;"></div> + <div class="slider-container-sleep-activity d-flex align-items-center mt-3"> + <div class="slider-label">Date: <span id="date-label">2023-W39</span></div> + <input type="range" id="date-slider" class="slider mx-2" min="0" max="10" step="1" style="flex-grow: 1;"> + <button id="play-button" class="btn btn-primary">Play</button> </div> </div> </div> - </section> - - <section id="radial-distance-chart" class="details"> - <div class="container"> - <div class="row align-items-center"> - <!-- Visualisation à gauche --> - <div class="col-md-6 order-md-1"> - <div id="chart" class="d-flex justify-content-center align-items-center"></div> - </div> - <!-- Texte à droite --> - <div class="col-md-6 order-md-2" data-aos="fade-left"> - <h3>Analyse Radiale : Distance & Sommeil</h3> - <p> - Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine. - Vous pouvez sélectionner une personne pour explorer ses données. - </p> - <ul> - <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> - <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> - <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> - </ul> - <select class="form-select" id="personDropdown"> - <option value="Distance_Anis">Anis</option> - <option value="Distance_Maya">Maya</option> - <option value="Distance_Corentin">Corentin</option> - <option value="Distance_Amira">Amira</option> - </select> - </div> + </div> + </section> + + <section id="radial-distance-chart" class="details"> + <div class="container"> + <div class="row align-items-center"> + <!-- Visualisation à gauche --> + <div class="col-md-6 order-md-1"> + <div id="chart" class="d-flex justify-content-center align-items-center"></div> + </div> + <!-- Texte à droite --> + <div class="col-md-6 order-md-2" data-aos="fade-left"> + <h3>Analyse Radiale : Distance & Sommeil</h3> + <p> + Cette visualisation met en évidence la durée de sommeil moyenne en fonction de la distance parcourue chaque semaine. + Vous pouvez sélectionner une personne pour explorer ses données. + </p> + <ul> + <li><i class="bi bi-check"></i> Durée de sommeil moyenne par semaine.</li> + <li><i class="bi bi-check"></i> Distance totale parcourue chaque semaine.</li> + <li><i class="bi bi-check"></i> Visualisation intuitive et interactive en graphique radial.</li> + </ul> + <select class="form-select" id="personDropdown"> + <option value="Distance_Anis">Anis</option> + <option value="Distance_Maya">Maya</option> + <option value="Distance_Corentin">Corentin</option> + <option value="Distance_Amira">Amira</option> + </select> </div> </div> - </section> - </main> - + </div> + </section> +</main> + <div class="container"> diff --git a/static/css/style.css b/static/css/style.css index 4cb9e47e50b42cc00ed9ea3734c12c884b3f7f5a..b707ec46a6733d242e40528cc54f95e2b671ee7b 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1839,7 +1839,7 @@ button:hover { margin-bottom: 30px; /* Ajoutez un espacement avec les autres sections */ } -#visualization { +#steps-visualization { width: 100%; height: auto; padding: 10px; @@ -1993,14 +1993,6 @@ button:hover { } -#visualization { - width: 100%; - height: auto; - padding: 10px; - border-radius: 8px; - - overflow: hidden; -} #slider-container { margin-top: 15px; @@ -2039,16 +2031,21 @@ button:hover { margin-right: 10px; } -#tooltip { - position: absolute; - color: white; - padding: 8px; - border-radius: 5px; +.tooltip-sleep { + position: absolute; + background-color: rgba(0, 0, 0, 0.7); /* Fond noir semi-transparent */ + color: white; /* Texte blanc */ + padding: 5px 10px; + border-radius: 5px; /* Bordures arrondies */ font-size: 12px; - pointer-events: none; - display: none; + pointer-events: none; /* Empêche l'interaction avec le tooltip */ + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2); /* Ajout d'une ombre */ + transform: translate(-50%, -50%); /* Centrer autour de la souris */ + z-index: 9999; } + + /* Visu 5 */ /* Sleep and Activity Analysis */ #sleep-activity-analysis { @@ -2097,3 +2094,40 @@ button:hover { } +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.5); +} + +.modal-content { + background-color: #fff; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: 40%; + height: 60%; + overflow-y: auto; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; + cursor: pointer; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} diff --git a/static/js/main.js b/static/js/main.js index 74317201e89bbde0f38aef5daa30cecf60451b1b..1dffbb9900a6138114cbc35627a501dc876d670e 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -221,7 +221,6 @@ })(); -// VISU 1 function renderStepsVisualization() { fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter si nécessaire .then((response) => response.json()) @@ -237,25 +236,58 @@ function renderStepsVisualization() { const width = 800 - margin.left - margin.right; const height = 500 - margin.top - margin.bottom; - const svg = d3.select("#visualization") + const svgContainer = d3.select("#steps-visualization") .append("svg") .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") + .attr("height", height + margin.top + margin.bottom + 50); // +50 pour la légende + + // Ajout du titre + svgContainer.append("text") + .attr("x", (width + margin.left + margin.right) / 2.5 ) // Centré horizontalement + .attr("y", 20) // Positionné en haut + .attr("text-anchor", "middle") + .style("font-size", "18px") + .style("font-weight", "bold") + .text("Comparaison des pas des utilisateurs"); + + const svg = svgContainer.append("g") .attr("transform", `translate(${margin.left},${margin.top})`); - const years = [2021, 2022, 2023, 2024]; - const sliderContainer = d3.select("#slider-container"); + const years = [2022, 2023, 2024]; + const stepsPrevYearBtn = document.getElementById("stepsPrevYear"); + const stepsNextYearBtn = document.getElementById("stepsNextYear"); + const stepsCurrentYearDisplay = document.getElementById("stepsCurrentYear"); + + stepsPrevYearBtn.addEventListener("click", () => { + const currentYearIndex = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)); + if (currentYearIndex > 0) { + const newYear = years[currentYearIndex - 1]; + stepsCurrentYearDisplay.textContent = newYear; + updateVisualization(newYear); + + stepsNextYearBtn.disabled = false; + if (currentYearIndex - 1 === 0) { + stepsPrevYearBtn.disabled = true; + } + } + }); - const rangeSlider = sliderContainer.append("input") - .attr("type", "range") - .attr("min", 0) - .attr("max", years.length - 1) - .attr("value", years.indexOf(2024)) - .style("width", "60%"); + stepsNextYearBtn.addEventListener("click", () => { + const currentYearIndex = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)); + if (currentYearIndex < years.length - 1) { + const newYear = years[currentYearIndex + 1]; + stepsCurrentYearDisplay.textContent = newYear; + updateVisualization(newYear); + + stepsPrevYearBtn.disabled = false; + if (currentYearIndex + 1 === years.length - 1) { + stepsNextYearBtn.disabled = true; + } + } + }); - const yearDisplay = sliderContainer.append("span") - .text(years[years.indexOf(2024)]); + stepsPrevYearBtn.disabled = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)) === 0; + stepsNextYearBtn.disabled = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)) === years.length - 1; updateVisualization(2024); @@ -338,13 +370,26 @@ function renderStepsVisualization() { .attr("stroke", colorMap[member]) .attr("stroke-width", 2); }); - } - rangeSlider.on("input", function () { - const selectedYear = years[this.value]; - yearDisplay.text(selectedYear); - updateVisualization(selectedYear); - }); + const legend = svg.append("g") + .attr("transform", `translate(${width / 20}, ${height + 80})`); + + members.forEach((member, i) => { + const legendGroup = legend.append("g") + .attr("transform", `translate(${i * 150}, 0)`); + + legendGroup.append("rect") + .attr("width", 15) + .attr("height", 15) + .attr("fill", colorMap[member]); + + legendGroup.append("text") + .attr("x", 20) + .attr("y", 12) + .style("font-size", "12px") + .text(member); + }); + } }); } @@ -352,7 +397,6 @@ function renderStepsVisualization() { - // VISU 2 function renderDistanceVisualization() { fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter si nécessaire @@ -390,7 +434,7 @@ function renderDistanceVisualization() { .text(years[years.indexOf(2024)]); const tooltip = d3.select("body").append("div") - .attr("class", "tooltip") + .attr("class", "tooltip-distance") .style("opacity", 0); function updateVisualization(selectedYear) { @@ -629,135 +673,355 @@ function renderCaloriesVisualization() { // Visu4 function renderSleepVisualization() { fetch('../static/js/final_combined_with_all_data.json') // Adapter le chemin si nécessaire - .then((response) => response.json()) - .then((data) => { - const parseDate = d3.timeParse("%Y-%m-%d"); - const formatYear = d3.timeFormat("%Y"); - const formatMonth = d3.timeFormat("%Y-%m"); - const formatDay = d3.timeFormat("%d"); - const members = ["Corentin", "Maya", "Anis", "Amira"]; - - data.forEach(d => d.date = parseDate(d.date)); + .then((response) => response.json()) + .then((data) => { + const parseDate = d3.timeParse("%Y-%m-%d"); + const formatYear = d3.timeFormat("%Y"); + const formatMonth = d3.timeFormat("%Y-%m"); + const formatDay = d3.timeFormat("%d"); + const members = ["Corentin", "Maya", "Anis", "Amira"]; + data.forEach(d => d.date = parseDate(d.date)); - const margin = { top: 50, right: 230, bottom: 150, left: 70 }; - const width = 800 - margin.left - margin.right; - const height = 500 - margin.top - margin.bottom; + // Dimensions et marges + const margin = { top: 50, right: 230, bottom: 150, left: 70 }; + const width = 800 - margin.left - margin.right; + const height = 500 - margin.top - margin.bottom; - const svg = d3.select("#sleep-visualization") - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", `translate(${margin.left},${margin.top})`); + // Créer le conteneur SVG principal + const svg = d3.select("#sleep-visualization") + .append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", `translate(${margin.left},${margin.top})`); - const years = [2021, 2022, 2023, 2024]; - const sliderContainer = d3.select("#sleep-slider-container"); + // Créer un conteneur pour le graphique détaillé + const detailContainer = d3.select("#sleep-visualization") + .append("div") + .attr("id", "detail-container") + .style("margin-top", "20px"); - const rangeSlider = sliderContainer.append("input") - .attr("type", "range") - .attr("min", 0) - .attr("max", years.length - 1) - .attr("value", years.indexOf(2024)) - .style("width", "60%"); + // Définir les années et la plage d'affichage + const years = [2021, 2022, 2023, 2024]; + - const yearDisplay = sliderContainer.append("span") - .text(years[years.indexOf(2024)]); + // Créer le slider + // Ajoutez les boutons Bootstrap pour naviguer entre les années - const tooltip = d3.select("body").append("div") - .attr("class", "tooltip") - .style("opacity", 0); - function updateVisualization(selectedYear) { - const filteredData = data.filter(d => formatYear(d.date) === selectedYear.toString()); - const groupedData = d3.groups(filteredData, d => formatMonth(d.date)); - const colorMap = { - "Maya": "#0f7e06", - "Corentin": "#1d38e3", - "Anis": "#d6bff4", - "Amira": "#7e09bd" - }; +// Gestion des événements des boutons +let currentYearIndex = years.indexOf(2024); // Année initiale - const aggregatedData = groupedData.map(([month, records]) => { - const aggregated = { month }; - members.forEach(member => { - aggregated[`Sleep_${member}`] = d3.mean(records, d => d[`Sleep_${member}`] || 0); - }); - return aggregated; - }); +// Fonction pour mettre à jour l'état des boutons +function updateButtonStates() { + if (currentYearIndex <= 0) { + document.getElementById("prevYear").style.display = "hidden"; // Cacher le bouton précédent + } else { + document.getElementById("prevYear").style.display = "inline-block"; // Réafficher le bouton précédent + } - svg.selectAll("*").remove(); + if (currentYearIndex >= years.length - 1) { + document.getElementById("nextYear").style.display = "hidden"; // Cacher le bouton suivant + } else { + document.getElementById("nextYear").style.display = "inline-block"; // Réafficher le bouton suivant + } +} - svg.append("text") - .attr("x", width / 2) - .attr("y", margin.top - 60) - .attr("text-anchor", "middle") - .style("font-size", "16px") - .style("font-weight", "bold") - .text("Analyse des heures de sommeil par mois"); +// Initialiser l'état des boutons +updateButtonStates(); - const xScale = d3.scaleBand() - .domain(aggregatedData.map(d => d.month)) - .range([0, width]) - .padding(0.2); +// Événement pour le bouton précédent +document.getElementById("prevYear").addEventListener("click", () => { + if (currentYearIndex > 0) { + currentYearIndex--; + document.getElementById("currentYear").textContent = years[currentYearIndex]; + updateVisualization(years[currentYearIndex]); + updateButtonStates(); // Mettre à jour l'état des boutons + } +}); - const yScale = d3.scaleLinear() - .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Sleep_${member}`])))]).nice() - .range([height, 0]); +// Événement pour le bouton suivant +document.getElementById("nextYear").addEventListener("click", () => { + if (currentYearIndex < years.length - 1) { + currentYearIndex++; + document.getElementById("currentYear").textContent = years[currentYearIndex]; + updateVisualization(years[currentYearIndex]); + updateButtonStates(); // Mettre à jour l'état des boutons + } +}); - svg.append("g") - .attr("transform", `translate(0, ${height})`) - .call(d3.axisBottom(xScale)) - .selectAll("text") - .attr("transform", "rotate(-45)") - .style("text-anchor", "end"); + // Tooltip + const tooltip = d3.select("body").append("div") + .attr("class", "tooltip-sleep") + .style("opacity", 0); - svg.append("text") - .attr("x", width / 2) - .attr("y", height + 70) - .attr("text-anchor", "middle") - .style("font-size", "14px") - .text("Mois"); + // Fonction de mise à jour de la visualisation + function updateVisualization(selectedYear) { + const filteredData = data.filter(d => formatYear(d.date) === selectedYear.toString()); + const groupedData = d3.groups(filteredData, d => formatMonth(d.date)); + + const colorMap = { + "Maya": "#0f7e06", + "Corentin": "#1d38e3", + "Anis": "#d6bff4", + "Amira": "#7e09bd" + }; + + const aggregatedData = groupedData.map(([month, records]) => { + const aggregated = { month }; + members.forEach(member => { + aggregated[`Sleep_${member}`] = d3.mean(records, d => d[`Sleep_${member}`] || 0); + }); + return aggregated; + }); - svg.append("g").call(d3.axisLeft(yScale)); + svg.selectAll("*").remove(); + + svg.append("text") + .attr("x", width / 2) // Centrer horizontalement + .attr("y", margin.top - 60) // Positionner légèrement au-dessus du graphique + .attr("text-anchor", "middle") // Centrer le texte + .style("font-size", "16px") // Taille de police + .style("font-weight", "bold") // Gras + .text("Analyse des heures de sommeil par mois"); + + const xScale = d3.scaleBand() + .domain(aggregatedData.map(d => d.month)) + .range([0, width]) + .padding(0.2); + + const yScale = d3.scaleLinear() + .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Sleep_${member}`])))]).nice() + .range([height, 0]); + + const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(members); + + // Axe X + svg.append("g") + .attr("transform", `translate(0, ${height})`) + .call(d3.axisBottom(xScale)) + .selectAll("text") + .attr("transform", "rotate(-45)") + .style("text-anchor", "end"); + + // Légende de l'axe X + svg.append("text") + .attr("x", width / 2) + .attr("y", height + 70) + .attr("text-anchor", "middle") + .style("font-size", "14px") + .text("Mois"); + + // Axe Y + svg.append("g").call(d3.axisLeft(yScale)); + + // Légende de l'axe Y + svg.append("text") + .attr("transform", "rotate(-90)") + .attr("x", -height / 2) + .attr("y", -50) + .attr("text-anchor", "middle") + .style("font-size", "14px") + .text("Sommeil moyen (heures)"); + + // Légende des membres (rectangles colorés) + // Créer un conteneur pour la légende +const legend = svg.append("g") +.attr("transform", `translate(${width / 3}, ${height + margin.bottom - 50})`) // Positionner la légende en bas, centrée +.attr("text-anchor", "middle"); - svg.append("text") - .attr("transform", "rotate(-90)") - .attr("x", -height / 2) - .attr("y", -50) - .attr("text-anchor", "middle") - .style("font-size", "14px") - .text("Sommeil moyen (heures)"); +// Ajouter les éléments de la légende +members.forEach((member, i) => { +legend.append("rect") + .attr("x", i * 100 - (members.length * 50)) // Espacement horizontal entre les rectangles + .attr("y", 0) + .attr("width", 15) + .attr("height", 15) + .attr("fill", colorMap[member]); - members.forEach((member, i) => { - svg.selectAll(`.bar-sleep-${member}`) - .data(aggregatedData) - .enter() - .append("rect") - .attr("x", d => xScale(d.month) + i * (xScale.bandwidth() / members.length)) - .attr("y", d => yScale(d[`Sleep_${member}`])) - .attr("width", xScale.bandwidth() / members.length) - .attr("height", d => height - yScale(d[`Sleep_${member}`])) - .attr("fill", colorMap[member]) - .on("mouseover", function (event, d) { - tooltip.transition().duration(200).style("opacity", 0.9); - tooltip.html(`Mois : ${d.month}<br>Sommeil : ${d[`Sleep_${member}`].toFixed(2)} heures`) - .style("left", (event.pageX + 5) + "px") - .style("top", (event.pageY - 28) + "px"); - }) - .on("mouseout", function () { - tooltip.transition().duration(500).style("opacity", 0); - }); - }); - } +legend.append("text") + .attr("x", i * 100 - (members.length * 50) + 20) // Texte à côté du rectangle + .attr("y", 12) + .text(member) + .style("font-size", "12px") + .attr("text-anchor", "start"); +}); - updateVisualization(years[3]); +// Ajouter un élément pour "Pas de données" +legend.append("rect") +.attr("x", members.length * 100 - (members.length * 50)) // Position pour le rectangle gris +.attr("y", 0) +.attr("width", 15) +.attr("height", 15) +.attr("fill", "lightgrey"); - rangeSlider.on("input", function () { - const selectedYear = years[this.value]; - yearDisplay.text(selectedYear); - updateVisualization(selectedYear); +legend.append("text") +.attr("x", members.length * 100 - (members.length * 50) + 20) // Texte à côté du rectangle gris +.attr("y", 12) +.text("Pas de données") +.style("font-size", "12px") +.attr("text-anchor", "start"); + + // Barres pour chaque membre + members.forEach((member, i) => { + svg.selectAll(`.bar-sleep-${member}`) + .data(aggregatedData) + .enter() + .append("rect") + .attr("x", d => xScale(d.month) + i * (xScale.bandwidth() / members.length)) + .attr("y", d => { + const value = d[`Sleep_${member}`]; + return value === -1.0 ? yScale(2) : yScale(value); // Placer les -1.0 à une hauteur fixe, ici 2 heures + }) + .attr("width", xScale.bandwidth() / members.length) + .attr("height", d => { + const value = d[`Sleep_${member}`]; + return value === -1.0 ? height - yScale(2) : height - yScale(value); // Barres grisées si -1.0 + }) + .attr("fill", d => { + const value = d[`Sleep_${member}`]; + return value === -1.0 ? "#D3D3D3" : colorMap[member]; // Gris pour -1.0 + }) + .on("mouseover", function(event, d) { + tooltip.transition().duration(200).style("opacity", .9); // Transition d'apparition du tooltip + tooltip.html(`Mois : ${d.month}<br>Sommeil moyen : ${d[`Sleep_${member}`] === -1.0 ? "Pas de données" : d[`Sleep_${member}`].toFixed(2)} heures`) // Affichage du tooltip + .style("left", (event.pageX + 5) + "px") + .style("top", (event.pageY - 28) + "px"); + }) + .on("mouseout", function() { + tooltip.transition().duration(500).style("opacity", 0); // Transition de disparition du tooltip + }) + .on("click", (event, d) => { + const memberColor = colorMap[member]; // Récupérer la couleur du membre + showDetailChart(d.month, member, selectedYear, memberColor); // Passer la couleur à la fonction showDetailChart + }); }); + + } + + // Fonction pour afficher les détails + function showDetailChart(month, member, year, memberColor) { + // Affiche le modal + const modal = d3.select("#modal"); + modal.style("display", "block"); + + // Fermer le modal + modal.select(".close").on("click", () => { + modal.style("display", "none"); + d3.select("#detail-visualization").selectAll("*").remove(); + }); + + const detailContainer = d3.select("#detail-visualization"); + detailContainer.selectAll("*").remove(); + + const filteredData = data.filter(d => formatYear(d.date) === year.toString() && formatMonth(d.date) === month); + + const dailyData = d3.groups(filteredData, d => formatDay(d.date)).map(([day, records]) => ({ + day: day, + value: d3.mean(records, d => d[`Sleep_${member}`] || 0) + })); + + const detailSvg = detailContainer.append("svg") + .attr("width", 600) + .attr("height", 400) + .append("g") + .attr("transform", "translate(50, 50)"); + + const xScale = d3.scaleBand() + .domain(dailyData.map(d => d.day)) + .range([0, 500]) + .padding(0.1); + + const yScale = d3.scaleLinear() + .domain([0, d3.max(dailyData, d => d.value)]).nice() + .range([300, 0]); + + // Ajout de l'axe X + detailSvg.append("g") + .attr("transform", "translate(0, 300)") + .call(d3.axisBottom(xScale)); + + // Ajout de l'axe Y + detailSvg.append("g") + .call(d3.axisLeft(yScale)); + + // Titre du graphique + detailSvg.append("text") + .attr("x", 250) + .attr("y", -20) + .attr("text-anchor", "middle") + .style("font-size", "16px") + .text(`${member} - Sommeil du mois de ${month} (${year})`); + + // Ajouter la légende de l'axe X (Jours) + detailSvg.append("text") + .attr("x", 250) + .attr("y", 340) + .attr("text-anchor", "middle") + .style("font-size", "12px") + .text("Jours du mois"); + + // Ajouter la légende de l'axe Y (Heures de sommeil) + detailSvg.append("text") + .attr("transform", "rotate(-90)") + .attr("x", -200) + .attr("y", -40) + .attr("text-anchor", "middle") + .style("font-size", "12px") + .text("Heures de sommeil moyen"); + + + // Dessin des barres + detailSvg.selectAll(".bar") + .data(dailyData) + .enter() + .append("rect") + .attr("x", d => xScale(d.day)) + .attr("y", d => { + const value = d.value; + return value < 0 || value === null ? yScale(2) : yScale(value); + }) + .attr("width", xScale.bandwidth()) + .attr("height", d => { + const value = d.value; + return value < 0 || value === null ? 300 - yScale(2) : 300 - yScale(value); + }) + .attr("fill", d => { + const value = d.value; + return value < 0 || value === null ? "lightgrey" : memberColor; + }) + .on("mouseover", function(event, d) { + tooltip.transition().duration(200).style("opacity", 1); + tooltip.html(` + <div style="text-align: center;"> + <strong>Jour :</strong> ${d.day}<br> + <strong>Sommeil :</strong> ${d.value === -1.0 || d.value === null ? "Pas de données" : d.value.toFixed(2)} heures + </div> + `); + }) + .on("mousemove", function(event) { + tooltip + .style("left", (event.pageX + 15) + "px") // Décalage pour positionner le tooltip + .style("top", (event.pageY + 15) + "px"); + }) + .on("mouseout", function() { + tooltip.transition().duration(500).style("opacity", 0); + }); + + } + + + + // Initialisation de la visualisation + updateVisualization(years[3]); + + // Mettre à jour la visualisation lorsque le slider est déplacé + rangeSlider.on("input", function() { + const selectedYear = years[this.value]; + yearDisplay.text(selectedYear); + updateVisualization(selectedYear); + }); }); }