Skip to content
Snippets Groups Projects
main.js 48.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    /**
    * Template Name: Bootslander
    * Template URL: https://bootstrapmade.com/bootslander-free-bootstrap-landing-page-template/
    * Updated: Mar 17 2024 with Bootstrap v5.3.3
    * Author: BootstrapMade.com
    * License: https://bootstrapmade.com/license/
    */
    
    (function() {
      "use strict";
    
      /**
       * Easy selector helper function
       */
      const select = (el, all = false) => {
        el = el.trim()
        if (all) {
          return [...document.querySelectorAll(el)]
        } else {
          return document.querySelector(el)
        }
      }
    
      /**
       * Easy event listener function
       */
      const on = (type, el, listener, all = false) => {
        let selectEl = select(el, all)
        if (selectEl) {
          if (all) {
            selectEl.forEach(e => e.addEventListener(type, listener))
          } else {
            selectEl.addEventListener(type, listener)
          }
        }
      }
    
      /**
       * Easy on scroll event listener 
       */
      const onscroll = (el, listener) => {
        el.addEventListener('scroll', listener)
      }
    
      /**
       * Navbar links active state on scroll
       */
      let navbarlinks = select('#navbar .scrollto', true)
      const navbarlinksActive = () => {
        let position = window.scrollY + 200
        navbarlinks.forEach(navbarlink => {
          if (!navbarlink.hash) return
          let section = select(navbarlink.hash)
          if (!section) return
          if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
            navbarlink.classList.add('active')
          } else {
            navbarlink.classList.remove('active')
          }
        })
      }
      window.addEventListener('load', navbarlinksActive)
      onscroll(document, navbarlinksActive)
    
      /**
       * Scrolls to an element with header offset
       */
      const scrollto = (el) => {
        let header = select('#header')
        let offset = header.offsetHeight
    
        if (!header.classList.contains('header-scrolled')) {
          offset -= 20
        }
    
        let elementPos = select(el).offsetTop
        window.scrollTo({
          top: elementPos - offset,
          behavior: 'smooth'
        })
      }
    
      /**
       * Toggle .header-scrolled class to #header when page is scrolled
       */
      let selectHeader = select('#header')
      if (selectHeader) {
        const headerScrolled = () => {
          if (window.scrollY > 100) {
            selectHeader.classList.add('header-scrolled')
          } else {
            selectHeader.classList.remove('header-scrolled')
          }
        }
        window.addEventListener('load', headerScrolled)
        onscroll(document, headerScrolled)
      }
    
      /**
       * Back to top button
       */
      let backtotop = select('.back-to-top')
      if (backtotop) {
        const toggleBacktotop = () => {
          if (window.scrollY > 100) {
            backtotop.classList.add('active')
          } else {
            backtotop.classList.remove('active')
          }
        }
        window.addEventListener('load', toggleBacktotop)
        onscroll(document, toggleBacktotop)
      }
    
      /**
       * Mobile nav toggle
       */
      on('click', '.mobile-nav-toggle', function(e) {
        select('#navbar').classList.toggle('navbar-mobile')
        this.classList.toggle('bi-list')
        this.classList.toggle('bi-x')
      })
    
      /**
       * Mobile nav dropdowns activate
       */
      on('click', '.navbar .dropdown > a', function(e) {
        if (select('#navbar').classList.contains('navbar-mobile')) {
          e.preventDefault()
          this.nextElementSibling.classList.toggle('dropdown-active')
        }
      }, true)
    
      /**
       * Scrool with ofset on links with a class name .scrollto
       */
      on('click', '.scrollto', function(e) {
        if (select(this.hash)) {
          e.preventDefault()
    
          let navbar = select('#navbar')
          if (navbar.classList.contains('navbar-mobile')) {
            navbar.classList.remove('navbar-mobile')
            let navbarToggle = select('.mobile-nav-toggle')
            navbarToggle.classList.toggle('bi-list')
            navbarToggle.classList.toggle('bi-x')
          }
          scrollto(this.hash)
        }
      }, true)
    
      /**
       * Scroll with ofset on page load with hash links in the url
       */
      window.addEventListener('load', () => {
        if (window.location.hash) {
          if (select(window.location.hash)) {
            scrollto(window.location.hash)
          }
        }
      });
    
      /**
       * Preloader
       */
      let preloader = select('#preloader');
      if (preloader) {
        window.addEventListener('load', () => {
          preloader.remove()
        });
      }
    
      /**
       * Initiate glightbox
       */
      const glightbox = GLightbox({
        selector: '.glightbox'
      });
    
      /**
       * Initiate gallery lightbox 
       */
      const galleryLightbox = GLightbox({
        selector: '.gallery-lightbox'
      });
    
      /**
       * Testimonials slider
       */
      new Swiper('.testimonials-slider', {
        speed: 600,
        loop: true,
        autoplay: {
          delay: 5000,
          disableOnInteraction: false
        },
        slidesPerView: 'auto',
        pagination: {
          el: '.swiper-pagination',
          type: 'bullets',
          clickable: true
        }
      });
    
      /**
       * Animation on scroll
       */
      window.addEventListener('load', () => {
        AOS.init({
          duration: 1000,
          easing: 'ease-in-out',
          once: true,
          mirror: false
        })
      });
    
      /**
       * Initiate Pure Counter 
       */
      new PureCounter();
    
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    })();
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
    function renderStepsVisualization() {
      fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter 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 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;
    
    
              const svgContainer = d3.select("#steps-visualization")
    
                  .append("svg")
                  .attr("width", width + margin.left + margin.right)
    
                  .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 = [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;
                      }
                  }
              });
    
              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;
                      }
                  }
              });
    
              stepsPrevYearBtn.disabled = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)) === 0;
              stepsNextYearBtn.disabled = years.indexOf(parseInt(stepsCurrentYearDisplay.textContent)) === years.length - 1;
    
    
              updateVisualization(2024);
    
              function updateVisualization(selectedYear) {
                  const filteredData = data.filter(d => formatYear(d.date) === selectedYear.toString());
    
                  if (filteredData.length === 0) {
                      console.log(`Aucune donnée pour l'année ${selectedYear}`);
                      return;
                  }
    
                  const groupedData = d3.groups(filteredData, d => formatMonth(d.date));
    
                  const aggregatedData = groupedData.map(([month, records]) => {
                      const aggregated = { month };
                      members.forEach(member => {
                          aggregated[`Steps_${member}`] = d3.mean(records, d => d[`Steps_${member}`] || 0);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                      });
    
                      return aggregated;
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                  });
    
                  svg.selectAll("*").remove();
    
    
                  const xScale = d3.scaleBand()
                      .domain(aggregatedData.map(d => d.month))
                      .range([0, width])
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
                  const yScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Steps_${member}`])))]).nice()
                      .range([height, 0]);
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
                  const colorMap = {
                      "Maya": "#0f7e06",
                      "Corentin": "#1d38e3",
                      "Anis": "#d6bff4",
                      "Amira": "#7e09bd"
                  };
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
                  svg.append("g")
    
                      .attr("transform", `translate(0, ${height})`)
                      .call(d3.axisBottom(xScale))
                      .selectAll("text")
                      .attr("transform", "rotate(-45)")
                      .style("text-anchor", "end");
    
                  svg.append("g").call(d3.axisLeft(yScale));
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", height + 70)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Mois");
    
                  svg.append("text")
                      .attr("transform", "rotate(-90)")
                      .attr("x", -height / 2)
                      .attr("y", -50)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Nombre de pas");
    
                  members.forEach((member) => {
                      const lineData = aggregatedData.map(d => {
                          let steps = d[`Steps_${member}`] === -1.0 ? 0 : d[`Steps_${member}`];
                          return { month: d.month, steps };
    
                      const line = d3.line()
                          .x(d => xScale(d.month) + xScale.bandwidth() / 2)
                          .y(d => yScale(d.steps))
                          .defined(d => d.steps !== 0);
    
                      svg.append("path")
                          .data([lineData])
                          .attr("class", `line-${member}`)
                          .attr("d", line)
                          .attr("fill", "none")
                          .attr("stroke", colorMap[member])
                          .attr("stroke-width", 2);
    
                  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);
                  });
              }
    
    // VISU 2
    function renderDistanceVisualization() {
      fetch('../static/js/final_combined_with_all_data.json') // Chemin à adapter 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 members = ["Corentin", "Maya", "Anis", "Amira"];
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
              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;
    
              const svg = d3.select("#distance-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 = [2023, 2024];
              const sliderContainer = d3.select("#distance-slider-container");
    
              const rangeSlider = sliderContainer.append("input")
                  .attr("type", "range")
                  .attr("min", 0)
                  .attr("max", years.length - 1)
                  .attr("value", years.indexOf(2024))
    
                  .style("width", "40%");
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
              const yearDisplay = sliderContainer.append("span")
                  .text(years[years.indexOf(2024)]);
    
              const tooltip = d3.select("body").append("div")
    
                  .attr("class", "tooltip-distance")
    
                  .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"
                  };
    
                  const aggregatedData = groupedData.map(([month, records]) => {
                      const aggregated = { month };
                      members.forEach(member => {
                          aggregated[`Distance_${member}`] = d3.mean(records, d => {
                              const distance = d[`Distance_${member}`];
                              return (distance !== -1) ? distance : undefined;
                          }) || 0;
    
                          aggregated[`Calories_${member}`] = d3.mean(records, d => {
                              const calories = d[`Calories_${member}`];
                              return (calories !== -1) ? calories : undefined;
                          }) || 0;
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
                      });
    
                      return aggregated;
    
                  svg.selectAll("*").remove();
    
                  const bubbleSizeScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Distance_${member}`])) )])
                      .range([2, 20]);
    
                  const xScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Distance_${member}`])) )])
                      .range([0, width]);
    
                  const yScale = d3.scaleLinear()
                      .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Calories_${member}`])) )])
                      .range([height, 0]);
    
                  svg.append("text")
                      .attr("x", width / 2)
    
                      .attr("y", margin.top - 75)
    
                      .attr("text-anchor", "middle")
                      .style("font-size", "16px")
                      .style("font-weight", "bold")
                      .text("Comparaison des calories brûlées vs distance parcourue");
    
                  svg.append("g")
                      .attr("transform", `translate(0, ${height})`)
                      .call(d3.axisBottom(xScale));
    
                  svg.append("text")
                      .attr("x", width / 2)
                      .attr("y", height + 70)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Distance (KM)");
    
                  svg.append("g").call(d3.axisLeft(yScale));
    
                  svg.append("text")
                      .attr("transform", "rotate(-90)")
                      .attr("x", -height / 2)
                      .attr("y", -50)
                      .attr("text-anchor", "middle")
                      .style("font-size", "14px")
                      .text("Calories brûlées");
    
                  members.forEach((member) => {
                      svg.selectAll(`.bubble-${member}`)
                          .data(aggregatedData)
                          .enter()
                          .append("circle")
                          .attr("cx", d => xScale(d[`Distance_${member}`]))
                          .attr("cy", d => yScale(d[`Calories_${member}`]))
    
                          .attr("r", d => 0.8*  bubbleSizeScale(d[`Distance_${member}`]))
    
                          .attr("fill", d => {
                              return (d[`Distance_${member}`] === 0 || d[`Calories_${member}`] === 0) ? "gray" : colorMap[member];
                          })
                          .style("opacity", 0.7)
                          .on("mouseover", function(event, d) {
                              tooltip.transition().duration(200).style("opacity", .9);
                              tooltip.html(`Mois : ${d.month}<br>Distance : ${d[`Distance_${member}`]} km<br>Calories : ${d[`Calories_${member}`]} cal`)
                                  .style("left", (event.pageX + 5) + "px")
                                  .style("top", (event.pageY - 28) + "px");
                          })
                          .on("mouseout", function() {
                              tooltip.transition().duration(500).style("opacity", 0);
                          });
    
              updateVisualization(years[1]);
    
              rangeSlider.on("input", function() {
                  const selectedYear = years[this.value];
                  yearDisplay.text(selectedYear);
                  updateVisualization(selectedYear);
              });
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
          });
    
    }
    
    //VISU 3
    function renderCaloriesVisualization() {
      fetch('../static/js/final_combined_with_all_data.json')
        .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 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;
    
          const svg = d3.select("#calories-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 = [2022, 2023, 2024];
          const sliderContainer = d3.select("#calories-slider-container");
    
          const rangeSlider = sliderContainer.append("input")
            .attr("type", "range")
            .attr("min", 0)
            .attr("max", years.length - 1)
            .attr("value", years.indexOf(2024))
    
            .style("width", "40%");
    
    
          const yearDisplay = sliderContainer.append("span")
            .text(years[years.indexOf(2024)]);
    
          updateVisualization(2024);
    
          function updateVisualization(selectedYear) {
            const filteredData = data.filter(d => formatYear(d.date) === selectedYear.toString());
    
            if (filteredData.length === 0) {
              console.log(`Aucune donnée pour l'année ${selectedYear}`);
              return;
            }
    
            const groupedData = d3.groups(filteredData, d => formatMonth(d.date));
    
            const aggregatedData = groupedData.map(([month, records]) => {
              const aggregated = { month };
              members.forEach(member => {
                aggregated[`Calories_${member}`] = d3.mean(records, d => d[`Calories_${member}`] || 0);
              });
              return aggregated;
            });
    
            svg.selectAll("*").remove();
    
            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 calories brûlées 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[`Calories_${member}`])))]).nice()
              .range([height, 0]);
    
            const colorScale = d3.scaleOrdinal()
              .domain(members)
              .range(["#1d38e3", "#0f7e06", "#d6bff4", "#7e09bd"]);
    
            svg.append("g")
              .attr("transform", `translate(0, ${height})`)
              .call(d3.axisBottom(xScale))
              .selectAll("text")
              .attr("transform", "rotate(-45)")
              .style("text-anchor", "end");
    
            svg.append("text")
              .attr("x", width / 2)
              .attr("y", height + 70)
              .attr("text-anchor", "middle")
              .style("font-size", "14px")
              .text("Mois");
    
            svg.append("g").call(d3.axisLeft(yScale));
    
            svg.append("text")
              .attr("transform", "rotate(-90)")
              .attr("x", -height / 2)
              .attr("y", -50)
              .attr("text-anchor", "middle")
              .style("font-size", "14px")
              .text("Calories brûlées");
    
            members.forEach(member => {
              const areaData = aggregatedData.map(d => ({
                month: d.month,
                calories: d[`Calories_${member}`] || 0
              }));
    
              const area = d3.area()
                .x(d => xScale(d.month) + xScale.bandwidth() / 2)
    
                .y0(yScale(0.8))
                .y1(d => yScale(d.calories ));
    
    
              svg.append("path")
                .data([areaData])
                .attr("fill", colorScale(member))
                .attr("opacity", 0.8)
                .attr("d", area);
            });
          }
    
    RABEHI AMIRA p2312013's avatar
    RABEHI AMIRA p2312013 committed
    
    
          rangeSlider.on("input", function () {
            const selectedYear = years[this.value];
            yearDisplay.text(selectedYear);
            updateVisualization(selectedYear);
    
    // 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));
    
          // 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;
    
          // 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})`);
    
          // 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");
    
          // Définir les années et la plage d'affichage
          const years = [2021, 2022, 2023, 2024];
          
    
          // Créer le slider
          // Ajoutez les boutons Bootstrap pour naviguer entre les années
    
    // Gestion des événements des boutons
    let currentYearIndex = years.indexOf(2024); // Année initiale
    
    // 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
        }
    
        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
        }
    }
    
    // Initialiser l'état des boutons
    updateButtonStates();
    
    // É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
        }
    });
    
    // É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
        }
    });
    
          // Tooltip
          const tooltip = d3.select("body").append("div")
              .attr("class", "tooltip-sleep")
              .style("opacity", 0);
    
          // 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.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");
    
    // 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]);
    
    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");
    });
    
    // 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");
    
    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;">