<div class="assiduite-bubble"> </div> <script> const mt_start = {{ t_start }}; const mt_end = {{ t_end }}; /** * Création de la minitiline d'un étudiant * @param {Array[Assiduité]} assiduitesArray * @returns {HTMLElement} l'élément correspondant à la mini timeline */ function createMiniTimeline(assiduitesArray, day = null) { const array = [...assiduitesArray]; const dateiso = day == null ? document.getElementById("tl_date").value : day; const timeline = document.createElement("div"); timeline.className = "mini-timeline"; if (isSingleEtud()) { timeline.classList.add("single"); } const timelineDate = new Date(dateiso).startOf("day"); const dayStart = timelineDate.clone().add(mt_start, "hours"); const dayEnd = timelineDate.clone().add(mt_end, "hours"); const dayDuration = new Duration(dayStart, dayEnd).minutes; timeline.appendChild(setMiniTick(timelineDate, dayStart, dayDuration)); if (day == null) { const tlTimes = getTimeLineTimes(); array.push({ date_debut: tlTimes.deb.format(), date_fin: tlTimes.fin.format(), etat: "CRENEAU", }); } array.forEach((assiduité) => { let startDate = new Date(assiduité.date_debut); let endDate = new Date(assiduité.date_fin); if (startDate.isBefore(dayStart)) { startDate = dayEnd.clone().startOf("day").add(mt_start, "hours"); } if (endDate.isAfter(dayEnd)) { endDate = dayEnd.clone().startOf("day").add(mt_end, "hours"); } const block = document.createElement("div"); block.className = "mini-timeline-block"; const duration = new Duration(startDate, endDate).minutes; const startOffset = new Duration(dayStart, startDate).minutes; const leftPercentage = (startOffset / dayDuration) * 100; const widthPercentage = (duration / dayDuration) * 100; block.style.left = `${leftPercentage}%`; block.style.width = `${widthPercentage}%`; if (assiduité.etat != "CRENEAU") { block.addEventListener("click", () => { let deb = startDate.getHours() + startDate.getMinutes() / 60; let fin = endDate.getHours() + endDate.getMinutes() / 60; deb = Math.max(mt_start, deb); fin = Math.min(mt_end, fin); if (day == null) setPeriodValues(deb, fin); if (isSingleEtud()) { updateSelectedSelect(getCurrentAssiduiteModuleImplId()); updateJustifyBtn(); } try { if (isCalendrier()) { window.location = `ListeAssiduitesEtud?etudid=${etudid}&assiduite_id=${assiduité.assiduite_id}` } } catch { } }); //ajouter affichage assiduites on over setupAssiduiteBuble(block, assiduité); } const action = (justificatifs) => { if (justificatifs.length > 0) { let j = "invalid_justified"; justificatifs.forEach((ju) => { if (ju.etat == "VALIDE") { j = "justified"; } }); block.classList.add(j); } }; if (assiduité.etudid) { getJustificatifFromPeriod( { deb: new Date(assiduité.date_debut), fin: new Date(assiduité.date_fin), }, assiduité.etudid, action ); } switch (assiduité.etat) { case "PRESENT": block.classList.add("present"); break; case "RETARD": block.classList.add("retard"); break; case "ABSENT": block.classList.add("absent"); break; case "CRENEAU": block.classList.add("creneau"); break; default: block.style.backgroundColor = "white"; } timeline.appendChild(block); }); return timeline; } /** * Ajout de la visualisation des assiduités de la mini timeline * @param {HTMLElement} el l'élément survollé * @param {Assiduité} assiduite l'assiduité représentée par l'élément */ function setupAssiduiteBuble(el, assiduite) { if (!assiduite) return; el.addEventListener("mouseenter", (event) => { const bubble = document.querySelector(".assiduite-bubble"); bubble.className = "assiduite-bubble"; bubble.classList.add("is-active", assiduite.etat.toLowerCase()); bubble.innerHTML = ""; const idDiv = document.createElement("div"); idDiv.className = "assiduite-id"; idDiv.textContent = `${getModuleImpl(assiduite)}`; bubble.appendChild(idDiv); const periodDivDeb = document.createElement("div"); periodDivDeb.className = "assiduite-period"; periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`; bubble.appendChild(periodDivDeb); const periodDivFin = document.createElement("div"); periodDivFin.className = "assiduite-period"; periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`; bubble.appendChild(periodDivFin); const stateDiv = document.createElement("div"); stateDiv.className = "assiduite-state"; stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`; bubble.appendChild(stateDiv); const userIdDiv = document.createElement("div"); userIdDiv.className = "assiduite-user_id"; userIdDiv.textContent = `saisie le ${formatDateModal( assiduite.entry_date, " à " )}`; if (assiduite.user_id != null) { userIdDiv.textContent += `\npar ${assiduite.user_nom_complet}` } bubble.appendChild(userIdDiv); bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`; bubble.style.top = `${event.clientY + 20}px`; }); el.addEventListener("mouseout", () => { const bubble = document.querySelector(".assiduite-bubble"); bubble.classList.remove("is-active"); }); } function setMiniTick(timelineDate, dayStart, dayDuration) { const endDate = timelineDate.clone().startOf("day"); endDate.setHours(13, 0); const duration = new Duration(dayStart, endDate).minutes; const widthPercentage = (duration / dayDuration) * 100; const tick = document.createElement('span'); tick.className = "mini_tick" tick.textContent = "13h" tick.style.left = `${widthPercentage}%` return tick } </script> <style> .assiduite-bubble { position: fixed; display: none; background-color: #f9f9f9; border-radius: 5px; padding: 8px; border: 3px solid #ccc; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); font-size: 12px; line-height: 1.4; z-index: 500; } .assiduite-bubble.is-active { display: block; } .assiduite-bubble::before { content: ""; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); border-width: 6px; border-style: solid; border-color: transparent transparent #f9f9f9 transparent; } .assiduite-bubble::after { content: ""; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); border-width: 5px; border-style: solid; border-color: transparent transparent #ccc transparent; } .assiduite-id, .assiduite-period, .assiduite-state, .assiduite-user_id { margin-bottom: 4px; } .assiduite-bubble.absent { border-color: #F1A69C !important; } .assiduite-bubble.present { border-color: #9CF1AF !important; } .assiduite-bubble.retard { border-color: #F1D99C !important; } .mini-timeline { height: 7px; border: 1px solid black; position: relative; background-color: white; } .mini-timeline.single { height: 9px; } .mini-timeline-block { position: absolute; height: 100%; z-index: 1; } #page-assiduite-content .mini-timeline-block { cursor: pointer; } .mini_tick { position: absolute; text-align: start; top: -40px; transform: translateX(-50%); z-index: 50; } .mini_tick::after { display: block; content: "|"; position: absolute; bottom: -2px; z-index: 2; } .mini-timeline-block.creneau { outline: 3px solid #7059FF; pointer-events: none; } .mini-timeline-block.absent { background-color: #F1A69C !important; } .mini-timeline-block.present { background-color: #9CF1AF !important; } .mini-timeline-block.retard { background-color: #F1D99C !important; } .mini-timeline-block.justified { background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px); } .mini-timeline-block.invalid_justified { background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #d61616 4px, #d61616 8px); } </style>