{% block pageContent %} {% include "assiduites/widgets/alert.j2" %} <div class="pageContent"> {{minitimeline | safe }} <h2>Assiduités de {{sco.etud.nomprenom}}</h2> <div class="calendrier"> </div> <div class="annee"> <span>Année scolaire 2022-2023 Changer année: </span> <select name="" id="annee" onchange="setterAnnee(this.value)"> </select> </div> <div class="legende"> <h3>Calendrier</h3> <p>Les jours non travaillés sont affiché en violet</p> <p>Les jours possèdant une bordure "bleu" sont des jours où des assiduités ont été justifiées par un justificatif valide</p> <p>Les jours possèdant une bordure "rouge" sont des jours où des assiduités ont été justifiées par un justificatif non valide</p> <p>Le jour sera affiché en : </p> <ul> <li>Rouge : S'il y a une assiduité "Absent"</li> <li>Orange : S'il y a une assiduité "Retard" et pas d'assiduité "Absent"</li> <li>Vert : S'il y a une assiduité "Present" et pas d'assiduité "Absent" ni "Retard"</li> <li>Blanc : S'il n'y a pas d'assiduité</li> </ul> <p>Vous pouvez passer votre curseur sur les jours colorés afin de voir les assiduités de cette journée.</p> </div> </div> <style> .pageContent { margin-top: 1vh; max-width: var(--sco-content-max-width); } .calendrier { display: flex; justify-content: space-evenly; flex-wrap: wrap; border: 1px solid #444; } .month h2 { text-align: center; } .day { border: 1px solid #444; border-radius: 8px; padding: 0 5px; text-align: center; margin: 2px; cursor: default; font-size: 12px; position: relative; } .day.est_just { border-left: 10px solid #7059FF; } .day.est_just.invalide { border-left: 10px solid #f64e4e; } .day .dayline { position: absolute; display: none; left: -237%; bottom: -420%; z-index: 50; width: 250px; height: 75px; background-color: #dedede; border-radius: 15px; padding: 5px; } .day:hover .dayline { display: block; } .dayline .mini-timeline { margin-top: 14%; } .dayline .mini_tick { position: absolute; text-align: center; top: 0; transform: translateY(-110%); z-index: 50; } .dayline .mini_tick::after { display: block; content: "|"; position: absolute; bottom: -69%; z-index: 2; transform: translateX(200%); } </style> <script> function getDaysBetweenDates(start, end) { let now = moment(start); let dates = []; while (now.isSameOrBefore(end)) { dates.push(now.clone()); now.add(1, "days"); } return dates; } function organizeByMonth(dates) { let datesByMonth = {}; dates.forEach((date) => { let month = date.format("MMMM"); // Obtenir le mois if (!datesByMonth[month]) { datesByMonth[month] = []; } datesByMonth[month].push(date); }); return datesByMonth; } function organizeAssiduitiesByDay(datesByMonth, assiduities, justificatifs) { let assiduitiesByDay = {}; Object.keys(datesByMonth).forEach((month) => { assiduitiesByDay[month] = {}; datesByMonth[month].forEach((date) => { let dayAssiduities = assiduities.filter((assiduity) => { return moment.tz(date, TIMEZONE).isBetween( moment.tz(assiduity.date_debut, TIMEZONE), moment.tz(assiduity.date_fin, TIMEZONE), "day", "[]" ) }); let dayJustificatifs = justificatifs.filter((justif) => { return moment.tz(date, TIMEZONE).isBetween( moment.tz(justif.date_debut, TIMEZONE), moment.tz(justif.date_fin, TIMEZONE), "day", "[]" ) }); assiduitiesByDay[month][date.format("YYYY-MM-DD")] = { assiduites: dayAssiduities, justificatifs: dayJustificatifs }; }); }); return assiduitiesByDay; } function getDayColor(etat) { let color; switch (etat.toUpperCase()) { case "PRESENT": color = "#6bdb83"; break; case "ABSENT": color = "#F1A69C"; break; case "RETARD": color = "#f0c865"; break; case "NONWORK": color = "#bd81ca" break; default: color = "#FFF"; break; } return color; } function generateCalendar(assiduitiesByDay, nonWorkdays = []) { const calendar = document.querySelector('.calendrier') calendar.innerHTML = "" const days = { Mon: "Lun", Tue: "Mar", Wed: "Mer", Thu: "Jeu", Fri: "Ven", Sat: "Sam", Sun: "Dim", }; const months = { January: "Jan.", February: "Fev.", March: "Mar.", April: "Avr.", May: "Mai", June: "Juin", July: "Juil.", August: "Août", September: "Sep.", October: "Oct.", November: "Nov.", December: "Déc.", }; Object.keys(assiduitiesByDay).forEach((month) => { const monthEl = document.createElement('div') monthEl.classList.add("month") const title = document.createElement('h2'); title.textContent = `${months[month]}`; monthEl.appendChild(title) const daysEl = document.createElement('div') daysEl.classList.add('days'); Object.keys(assiduitiesByDay[month]).forEach((date) => { let dayAssiduities = assiduitiesByDay[month][date].assiduites; let dayJustificatifs = assiduitiesByDay[month][date].justificatifs; let color = "white"; if (dayAssiduities.some((a) => a.etat.toLowerCase() === "absent")) color = "absent"; else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "retard")) color = "retard"; else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "present")) color = "present"; let est_just = "" if (dayJustificatifs.some((j) => j.etat.toLowerCase() === "valide")) { est_just = "est_just"; } else if (dayJustificatifs.some((j) => j.etat.toLowerCase() !== "valide")) { est_just = "est_just invalide"; } const momentDate = moment.tz(date, TIMEZONE); let dayOfMonth = momentDate.format("D"); let dayOfWeek = momentDate.format("ddd"); dayOfWeek = days[dayOfWeek]; if (nonWorkdays.includes(dayOfWeek.toLowerCase())) color = "nonwork"; const day = document.createElement('div'); day.className = `day ${est_just}` day.style.backgroundColor = getDayColor(color); day.textContent = `${dayOfWeek} ${dayOfMonth}`; if (!nonWorkdays.includes(dayOfWeek.toLowerCase()) && dayAssiduities.length > 0) { const cache = document.createElement('div') cache.classList.add('dayline'); cache.appendChild( createMiniTimeline(dayAssiduities, date) ) day.appendChild(cache) } daysEl.appendChild(day); }); monthEl.appendChild(daysEl) calendar.appendChild(monthEl) }); } function getEtudAssiduites(deb, fin, callback = () => { }) { const url_api = getUrl() + `/api/assiduites/${etudid}/query?date_debut=${deb}&date_fin=${fin}`; async_get(url_api, (data, status) => { if (status === "success") { callback(data); } }); } function getEtudJustificatifs(deb, fin) { let list = []; const url_api = getUrl() + `/api/justificatifs/${etudid}/query?date_debut=${deb}&date_fin=${fin}`; sync_get(url_api, (data, status) => { if (status === "success") { list = data; } }); return list } function generate(annee) { if (annee < 1999 || annee > 2999) { openAlertModal("Année impossible", document.createTextNode("L'année demandé n'existe pas.")); return; } const bornes = { deb: `${annee}-09-01T00:00`, fin: `${annee + 1}-08-31T23:59` } let assiduities = getEtudAssiduites(bornes.deb, bornes.fin, (data) => { let dates = getDaysBetweenDates(bornes.deb, bornes.fin); let datesByMonth = organizeByMonth(dates); const justifs = getEtudJustificatifs(bornes.deb, bornes.fin); let assiduitiesByDay = organizeAssiduitiesByDay(datesByMonth, data, justifs); generateCalendar(assiduitiesByDay, nonwork); }); } function setterAnnee(annee) { annee = parseInt(annee); document.querySelector('.annee span').textContent = `Année scolaire ${annee}-${annee + 1} Changer année: ` generate(annee) } const defAnnee = {{ annee }} let annees = {{ annees | safe }} annees = annees.filter((x, i) => annees.indexOf(x) === i) const etudid = {{ sco.etud.id }}; const nonwork = [{{ nonworkdays | safe }}]; window.onload = () => { const select = document.querySelector('#annee'); annees.forEach((a) => { const opt = document.createElement("option"); opt.value = a + "", opt.textContent = `${a} - ${a + 1}`; if (a === defAnnee) { opt.selected = true; } select.appendChild(opt) }) setterAnnee(defAnnee) }; </script> {% endblock pageContent %}