diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index c22e38e8a..d4f129a78 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -183,7 +183,7 @@ } .etud_row.conflit { - background-color: #ff000061; + background-color: #ff0000c2; } .etud_row .assiduites_bar .absent { @@ -261,7 +261,7 @@ /*<== Modal conflit ==>*/ .modal { - display: none; + display: block; position: fixed; z-index: 500; left: 0; @@ -388,7 +388,6 @@ padding: 20px; border: 1px solid #888; width: max-content; - height: 30%; position: relative; border-radius: 10px; display: none; @@ -492,4 +491,10 @@ .rouge { color: crimson; +} + +.legende { + border: 1px dashed #333; + width: 75%; + padding: 20px; } \ No newline at end of file diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index 015c52573..4f736c68d 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -43,6 +43,12 @@ Object.defineProperty(String.prototype, "capitalize", { enumerable: false, }); // <<== Outils ==>> +Object.defineProperty(Array.prototype, "reversed", { + value: function () { + return [...this].map(this.pop, this); + }, + enumerable: false, +}); /** * Ajout des évents sur les boutons d'assiduité @@ -88,7 +94,7 @@ function validateSelectors() { ); }); - if (getModuleImplId() == null && forceModule) { + if (getModuleImplId() == null && window.forceModule) { const HTML = `

Attention, le module doit obligatoirement être renseigné.

Cela vient de la configuration du semestre ou plus largement du département.

@@ -181,6 +187,23 @@ function sync_post(path, data, success, errors) { error: errors, }); } +/** + * Fait une requête POST de façon asynchrone + * @param {String} path adresse distante + * @param {object} data données à envoyer (objet js) + * @param {CallableFunction} success fonction à effectuer en cas de succès + * @param {CallableFunction} errors fonction à effectuer en cas d'échec + */ +function async_post(path, data, success, errors) { + $.ajax({ + async: true, + type: "POST", + url: path, + data: JSON.stringify(data), + success: success, + error: errors, + }); +} // <<== Gestion des actions de masse ==>> const massActionQueue = new Map(); @@ -489,7 +512,12 @@ function verifyDateInSemester() { const periodSemester = getFormSemestreDates(); - return date.isBetween(periodSemester.deb, periodSemester.fin); + return date.isBetween( + periodSemester.deb, + periodSemester.fin, + undefined, + "[]" + ); } /** @@ -603,8 +631,8 @@ function getTimeLineTimes() { * @param {object} conflict * @returns {boolean} Renvoie Vrai si la période de la timeline est égal au conflit */ -function isConflictSameAsTimeLine(conflict) { - const tlTimes = getTimeLineTimes(); +function isConflictSameAsPeriod(conflict, period = undefined) { + const tlTimes = period == undefined ? getTimeLineTimes() : period; const clTimes = { deb: moment.tz(conflict.date_debut, TIMEZONE), fin: moment.tz(conflict.date_fin, TIMEZONE), @@ -702,14 +730,14 @@ function numberTimeToDate(nb) { * @param {boolean} clear vidage de l'objet "assiduites" ou non * @returns {object} l'objets Assiduités { : [,]} */ -function getAssiduitesFromEtuds(clear, has_formsemestre = true) { +function getAssiduitesFromEtuds(clear, has_formsemestre = true, deb, fin) { const etudIds = Object.keys(etuds).join(","); const formsemestre_id = has_formsemestre ? `formsemestre_id=${getFormSemestreId()}&` : ""; - const date_debut = toIsoString(getPrevDate()); - const date_fin = toIsoString(getNextDate()); + const date_debut = deb ? deb : toIsoString(getPrevDate()); + const date_fin = fin ? fin : toIsoString(getNextDate()); if (clear) { assiduites = {}; @@ -717,12 +745,25 @@ function getAssiduitesFromEtuds(clear, has_formsemestre = true) { const url_api = getUrl() + - `/api/assiduites/group/query?date_debut=${formsemestre_id}${date_debut}&date_fin=${date_fin}&etudids=${etudIds}`; + `/api/assiduites/group/query?date_debut=${date_debut}&${formsemestre_id}&date_fin=${date_fin}&etudids=${etudIds}`; sync_get(url_api, (data, status) => { if (status === "success") { const dataKeys = Object.keys(data); dataKeys.forEach((key) => { - assiduites[key] = data[key]; + if (clear || !(key in assiduites)) { + assiduites[key] = data[key]; + } else { + assiduites[key] = assiduites[key].concat(data[key]); + } + let assi_ids = []; + assiduites[key] = assiduites[key].reversed().filter((value) => { + if (assi_ids.indexOf(value.assiduite_id) == -1) { + assi_ids.push(value.assiduite_id); + return true; + } + + return false; + }); }); } }); @@ -824,18 +865,21 @@ function editAssiduite(assiduite_id, etat) { * @param {String | Number} etudid identifiant de l'étudiant * @returns {Array[Assiduité]} un tableau d'assiduité */ -function getAssiduitesConflict(etudid) { +function getAssiduitesConflict(etudid, periode) { const etudAssiduites = assiduites[etudid]; if (!etudAssiduites) { return []; } - const period = getTimeLineTimes(); + if (!periode) { + periode = getTimeLineTimes(); + } + return etudAssiduites.filter((assi) => { const interval = { deb: moment.tz(assi.date_debut, TIMEZONE), fin: moment.tz(assi.date_fin, TIMEZONE), }; - return hasTimeConflict(period, interval); + return hasTimeConflict(periode, interval); }); } @@ -908,8 +952,8 @@ function actualizeEtudAssiduite(etudid, has_formsemestre = true) { const url_api = getUrl() + `/api/assiduites/${etudid}/query?${formsemestre_id}date_debut=${date_debut}&date_fin=${date_fin}`; - sync_get(url_api, (data, status) => { + console.error(data, status); if (status === "success") { assiduites[etudid] = data; } @@ -976,8 +1020,25 @@ function assiduiteAction(element) { } break; case "conflit": - openModal(assiduites[etudid]); - break; + const conflitResolver = new ConflitResolver( + assiduites[etudid], + getTimeLineTimes(), + { + deb: new moment.tz(getDate(), TIMEZONE), + fin: new moment.tz(getNextDate(), TIMEZONE), + } + ); + const update = (assi) => { + actualizeEtud(assi.etudid); + }; + conflitResolver.callbacks = { + delete: update, + edit: update, + split: update, + }; + + conflitResolver.open(); + return; } if (type != "conflit") { @@ -1085,7 +1146,7 @@ function insertEtudRow(etud, index, output = false) { assiduite.id = conflict[0].assiduite_id; assiduite.date_debut = conflict[0].date_debut; assiduite.date_fin = conflict[0].date_fin; - if (isConflictSameAsTimeLine(conflict[0])) { + if (isConflictSameAsPeriod(conflict[0])) { assiduite.type = "édition"; } else { assiduite.type = "conflit"; @@ -1166,433 +1227,6 @@ function generateAllEtudRow() { } // <== Gestion du modal de conflit ==> -/** - * Mise à jour du modal de conflit - * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant - */ -function refreshModal(assiduiteList) { - const tlTime = getTimeLineTimes(); - - renderTimeline(assiduiteList, { - date_debut: tlTime.deb, - date_fin: tlTime.fin, - }); -} -/** - * Ouverture du modal de conflit - * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant - */ -function openModal(assiduiteList) { - modal.style.display = "block"; - - const tlTime = getTimeLineTimes(); - - renderTimeline(assiduiteList, { - date_debut: tlTime.deb, - date_fin: tlTime.fin, - }); -} - -/** - * Fermeture du modal de conflit - */ -function closeModal() { - modal.style.display = "none"; -} - -/** - * Génération du modal - * @param {Array[Assiduité]} assiduites la liste des assiduités à afficher - * @param {Période} specialAssiduite Une assiduité représentant la période conflictuelle - */ -function renderTimeline(assiduites, specialAssiduite) { - const timeLabels = document.querySelector(".time-labels"); - const assiduitesContainer = document.querySelector(".assiduites-container"); - - timeLabels.innerHTML = ""; - assiduitesContainer.innerHTML = '
'; - - // Ajout des labels d'heure sur la frise chronologique - // TODO permettre la modification des bornes (8 et 18) - for (let i = 8; i <= 18; i++) { - const timeLabel = document.createElement("div"); - timeLabel.className = "time-label"; - timeLabel.textContent = i < 10 ? `0${i}:00` : `${i}:00`; - timeLabels.appendChild(timeLabel); - } - - //Placement de la période conflictuelle sur la timeline - const specialAssiduiteEl = document.querySelector(".assiduite-special"); - specialAssiduiteEl.style.width = getWidth( - specialAssiduite.date_debut, - specialAssiduite.date_fin - ); - specialAssiduiteEl.style.left = getLeftPosition(specialAssiduite.date_debut); - specialAssiduiteEl.style.top = "0"; - specialAssiduiteEl.style.zIndex = "0"; // Place l'assiduité spéciale en arrière-plan - assiduitesContainer.appendChild(specialAssiduiteEl); - const interval = { - deb: new moment.tz(getDate(), TIMEZONE), - fin: new moment.tz(getNextDate(), TIMEZONE), - }; - //Placement des assiduités sur la timeline - assiduites.forEach((assiduite) => { - const period = { - deb: new moment.tz(assiduite.date_debut, TIMEZONE), - fin: new moment.tz(assiduite.date_fin, TIMEZONE), - }; - if (!hasTimeConflict(period, interval)) { - return; - } - const el = document.createElement("div"); - el.className = "assiduite"; - el.style.backgroundColor = getColor(assiduite.etat); - el.style.width = getWidth(assiduite.date_debut, assiduite.date_fin); - el.style.left = getLeftPosition(assiduite.date_debut); - el.style.top = "10px"; - el.setAttribute("data-id", assiduite.assiduite_id); - el.addEventListener("click", () => selectAssiduite(assiduite)); - - // Ajout des informations dans la visualisation d'une assiduité - const infoContainer = document.createElement("div"); - infoContainer.className = "assiduite-info"; - - const idDiv = document.createElement("div"); - idDiv.className = "assiduite-id"; - idDiv.textContent = `ID: ${assiduite.assiduite_id}`; - infoContainer.appendChild(idDiv); - - const periodDivDeb = document.createElement("div"); - periodDivDeb.className = "assiduite-period"; - periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`; - infoContainer.appendChild(periodDivDeb); - const periodDivFin = document.createElement("div"); - periodDivFin.className = "assiduite-period"; - periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`; - infoContainer.appendChild(periodDivFin); - - const stateDiv = document.createElement("div"); - stateDiv.className = "assiduite-state"; - stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`; - infoContainer.appendChild(stateDiv); - - const userIdDiv = document.createElement("div"); - userIdDiv.className = "assiduite-user_id"; - userIdDiv.textContent = `saisi le ${formatDateModal( - assiduite.entry_date, - "à" - )} \npar ${getUserFromId(assiduite.user_id)}`; - infoContainer.appendChild(userIdDiv); - - el.appendChild(infoContainer); - assiduitesContainer.appendChild(el); - }); -} - -/** - * Transformation d'une date de début en position sur la timeline - * @param {String} start - * @returns {String} un déplacement par rapport à la gauche en % - */ -function getLeftPosition(start) { - const startTime = new moment.tz(start, TIMEZONE); - const startMins = (startTime.hours() - 8) * 60 + startTime.minutes(); - return (startMins / (18 * 60 - 8 * 60)) * 100 + "%"; -} - -/** - * Ajustement de l'espacement vertical entre les assiduités superposées - * @param {HTMLElement} container le conteneur des assiduités - * @param {String} start la date début de l'assiduité à placer - * @param {String} end la date de fin de l'assiduité à placer - * @returns {String} La position en px - */ -function getTopPosition(container, start, end) { - const overlaps = (a, b) => { - return a.start < b.end && a.end > b.start; - }; - - const startTime = new moment.tz(start, TIMEZONE); - const endTime = new moment.tz(end, TIMEZONE); - const assiduiteDuration = { start: startTime, end: endTime }; - - let position = 0; - let hasOverlap = true; - - while (hasOverlap) { - hasOverlap = false; - Array.from(container.children).some((el) => { - const elStart = new moment.tz(el.getAttribute("data-start")); - const elEnd = new moment.tz(el.getAttribute("data-end")); - const elDuration = { start: elStart, end: elEnd }; - - if (overlaps(assiduiteDuration, elDuration)) { - position += 25; // Pour ajuster l'espacement vertical entre les assiduités superposées - hasOverlap = true; - return true; - } - return false; - }); - } - return position + "px"; -} - -/** - * Transformation d'un état en couleur - * @param {String} state l'état - * @returns {String} la couleur correspondant à l'état - */ -function getColor(state) { - switch (state) { - case "PRESENT": - return "#9CF1AF"; - case "ABSENT": - return "#F1A69C"; - case "RETARD": - return "#F1D99C"; - default: - return "gray"; - } -} - -/** - * Calcule de la largeur de l'assiduité sur la timeline - * @param {String} start date iso de début - * @param {String} end date iso de fin - * @returns {String} la taille en % - */ -function getWidth(start, end) { - const startTime = new moment.tz(start, TIMEZONE); - const endTime = new moment.tz(end, TIMEZONE); - const duration = (endTime - startTime) / 1000 / 60; - return (duration / (18 * 60 - 8 * 60)) * 100 + "%"; -} - -/** - * Sélection d'une assiduité sur la timeline - * @param {Assiduité} assiduite l'assiduité sélectionnée - */ -function selectAssiduite(assiduite) { - // Désélectionner l'assiduité précédemment sélectionnée - if (selectedAssiduite) { - const prevSelectedEl = document.querySelector( - `.assiduite[data-id="${selectedAssiduite.assiduite_id}"]` - ); - if (prevSelectedEl) { - prevSelectedEl.classList.remove("selected"); - } - } - - // Sélectionner la nouvelle assiduité - selectedAssiduite = assiduite; - const selectedEl = document.querySelector( - `.assiduite[data-id="${assiduite.assiduite_id}"]` - ); - if (selectedEl) { - selectedEl.classList.add("selected"); - } - - //Mise à jour de la partie information du modal - const selectedModal = document.querySelector(".modal-assiduite-content"); - - selectedModal.classList.add("show"); - - document.getElementById("modal-assiduite-id").textContent = - assiduite.assiduite_id; - document.getElementById( - "modal-assiduite-user" - ).textContent = `saisi le ${formatDateModal( - assiduite.entry_date, - "à" - )} \npar ${getUserFromId(assiduite.user_id)}`; - document.getElementById("modal-assiduite-module").textContent = - assiduite.moduleimpl_id; - document.getElementById("modal-assiduite-deb").textContent = formatDateModal( - assiduite.date_debut - ); - document.getElementById("modal-assiduite-fin").textContent = formatDateModal( - assiduite.date_fin - ); - document.getElementById("modal-assiduite-etat").textContent = - assiduite.etat.capitalize(); - - //Activation des boutons d'actions de conflit - deleteBtn.disabled = false; - splitBtn.disabled = false; - editBtn.disabled = false; -} -/** - * Suppression de l'assiduité sélectionnée - */ -function deleteAssiduiteModal() { - if (!selectedAssiduite) return; - deleteAssiduite(selectedAssiduite.assiduite_id); - actualizeEtud(selectedAssiduite.etudid); - refreshModal(assiduites[selectedAssiduite.etudid]); - - // Désélection de l'assiduité - resetSelection(); -} - -/** - * Division d'une assiduité - * @param {Assiduité} assiduite l'assiduité sélectionnée - */ -function splitAssiduiteModal(assiduite) { - //Préparation du prompt - const htmlPrompt = `Entrez l'heure de séparation (HH:mm) : - `; - - const fieldSet = document.createElement("fieldset"); - fieldSet.classList.add("fieldsplit"); - fieldSet.innerHTML = htmlPrompt; - - //Callback de division - const success = () => { - const separatorTime = document.getElementById("promptTime").value; - const dateString = - document.querySelector("#tl_date").value + `T${separatorTime}`; - const separtorDate = new moment.tz(dateString, TIMEZONE); - - const assiduite_debut = new moment.tz(assiduite.date_debut, TIMEZONE); - const assiduite_fin = new moment.tz(assiduite.date_fin, TIMEZONE); - - if ( - separtorDate.isAfter(assiduite_debut) && - separtorDate.isBefore(assiduite_fin) - ) { - const assiduite_avant = { - etat: assiduite.etat, - date_debut: assiduite_debut.format(), - date_fin: separtorDate.format(), - }; - - const assiduite_apres = { - etat: assiduite.etat, - date_debut: separtorDate.format(), - date_fin: assiduite_fin.format(), - }; - - if (assiduite.moduleimpl_id) { - assiduite_apres["moduleimpl_id"] = assiduite.moduleimpl_id; - assiduite_avant["moduleimpl_id"] = assiduite.moduleimpl_id; - } - - deleteAssiduite(assiduite.assiduite_id); - - const path = getUrl() + `/api/assiduite/${assiduite.etudid}/create`; - sync_post( - path, - [assiduite_avant, assiduite_apres], - (data, status) => { - //success - }, - (data, status) => { - //error - console.error(data, status); - } - ); - - actualizeEtud(assiduite.etudid); - refreshModal(assiduites[assiduite.etudid]); - resetSelection(); - } else { - const att = document.createTextNode( - "L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée." - ); - - openAlertModal("Attention", att, "", "#ecb52a"); - } - }; - - openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f"); -} -/** - * Modification d'une assiduité conflictuelle - * @param {Assiduité} selectedAssiduite l'assiduité sélectionnée - */ -function editAssiduiteModal(selectedAssiduite) { - if (!selectedAssiduite) return; - - //Préparation du modal d'édition - const htmlPrompt = `Entrez l'état de l'assiduité : - `; - - const fieldSet = document.createElement("fieldset"); - fieldSet.classList.add("fieldsplit"); - fieldSet.innerHTML = htmlPrompt; - - //Callback d'action d'édition - const success = () => { - const newState = document.getElementById("promptSelect").value; - if (!["present", "absent", "retard"].includes(newState.toLowerCase())) { - const att = document.createTextNode( - "L'état doit être 'present', 'absent' ou 'retard'." - ); - openAlertModal("Attention", att, "", "#ecb52a"); - return; - } - - // Actualiser l'affichage - - editAssiduite(selectedAssiduite.assiduite_id, newState); - actualizeEtud(selectedAssiduite.etudid); - refreshModal(assiduites[selectedAssiduite.etudid]); - - // Désélection de l'assiduité - resetSelection(); - }; - - //Affichage du prompt - openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f"); -} - -/** - * Remise à zéro de la sélection - * Désactivation des boutons d'actions de conflit - */ -function resetSelection() { - selectedAssiduite = null; - deleteBtn.disabled = true; - splitBtn.disabled = true; - editBtn.disabled = true; - - document.querySelector(".modal-assiduite-content").classList.remove("show"); -} -/** - * Ajout des évents sur les boutons du modal - */ -window.onload = () => { - modal = document.getElementById("myModal"); - if (modal) { - closeBtn = document.querySelector(".close"); - timeline = document.getElementById("timeline"); - deleteBtn = document.getElementById("delete"); - splitBtn = document.getElementById("split"); - editBtn = document.getElementById("edit"); - selectedAssiduite = null; - - closeBtn?.addEventListener("click", closeModal); - - deleteBtn?.addEventListener("click", deleteAssiduiteModal); - splitBtn?.addEventListener("click", () => { - if (selectedAssiduite) { - splitAssiduiteModal(selectedAssiduite); - } - }); - editBtn?.addEventListener("click", () => { - if (selectedAssiduite) { - editAssiduiteModal(selectedAssiduite); - } - }); - } -}; // <<== Gestion de la récupération d'informations ==>> @@ -1840,7 +1474,6 @@ function createJustificatif(justif) { if (data.success.length > 0) { console.table(data[0]); } - console.warn(data); }, (data, status) => { //error diff --git a/app/templates/assiduites/conflict.j2 b/app/templates/assiduites/conflict.j2 new file mode 100644 index 000000000..814ee85e9 --- /dev/null +++ b/app/templates/assiduites/conflict.j2 @@ -0,0 +1,459 @@ + \ No newline at end of file diff --git a/app/templates/assiduites/differee.j2 b/app/templates/assiduites/differee.j2 new file mode 100644 index 000000000..54745d30d --- /dev/null +++ b/app/templates/assiduites/differee.j2 @@ -0,0 +1,807 @@ +
+
+
+
Noms
+ +
+
+
+ + {% for etud in etudiants %} +
+
+ {{etud.nomprenom}} + +
+
+ {% endfor %} +
+
+ + + \ No newline at end of file diff --git a/app/templates/assiduites/minitimeline.j2 b/app/templates/assiduites/minitimeline.j2 index 8de471d42..29c0c7c63 100644 --- a/app/templates/assiduites/minitimeline.j2 +++ b/app/templates/assiduites/minitimeline.j2 @@ -177,7 +177,6 @@ const endDate = timelineDate.clone().set({ 'hour': 13, 'minute': 0 }); const duration = moment.duration(endDate.diff(dayStart)).asMinutes(); const widthPercentage = (duration / dayDuration) * 100; - console.log(endDate, duration, widthPercentage) const tick = document.createElement('span'); tick.className = "mini_tick" tick.textContent = "13h" diff --git a/app/templates/assiduites/moduleimpl_dynamic_selector.j2 b/app/templates/assiduites/moduleimpl_dynamic_selector.j2 index dbbecc8a1..6d7bba62b 100644 --- a/app/templates/assiduites/moduleimpl_dynamic_selector.j2 +++ b/app/templates/assiduites/moduleimpl_dynamic_selector.j2 @@ -1,6 +1,6 @@ @@ -16,14 +16,14 @@ return semestre; } - function filterFormSemestres(semestres) { + function filterFormSemestres(semestres, dateIso) { const date = new moment.tz( - document.querySelector("#tl_date").value, + dateIso, TIMEZONE ); semestres = semestres.filter((fm) => { - return date.isBetween(fm.date_debut_iso, fm.date_fin_iso) + return date.isBetween(fm.date_debut_iso, fm.date_fin_iso, null, '[]') }) return semestres; @@ -67,8 +67,8 @@ } - function populateSelect(sems, selected) { - const select = document.getElementById('moduleimpl_select'); + function populateSelect(sems, selected, query) { + const select = document.querySelector(query); select.innerHTML = `` sems.forEach((mods, label) => { @@ -90,24 +90,25 @@ } - function updateSelect(moduleimpl_id) { + function updateSelect(moduleimpl_id, query = "#moduleimpl_select", dateIso = null) { let sem = getEtudFormSemestres() - sem = filterFormSemestres(sem) + if (dateIso == null) { + dateIso = document.querySelector("#tl_date").value + } + sem = filterFormSemestres(sem, dateIso) const mod = getModulesImplByFormsemestre(sem) - populateSelect(mod, moduleimpl_id); + populateSelect(mod, moduleimpl_id, query); } - function updateSelectedSelect(moduleimpl_id) { + function updateSelectedSelect(moduleimpl_id, query = "#moduleimpl_select") { const mod_id = moduleimpl_id != null ? moduleimpl_id : "" - document.getElementById('moduleimpl_select').value = mod_id; + document.querySelector(query).value = mod_id; } - window.onload = () => { - document.getElementById('moduleimpl_select').addEventListener('change', () => { - const mod_id = document.getElementById('moduleimpl_select').value; - + window.addEventListener("load", () => { + document.getElementById('moduleimpl_select').addEventListener('change', (el) => { const assi = getCurrentAssiduite(etudid); if (assi) { editAssiduite(assi.assiduite_id, assi.etat); @@ -118,7 +119,7 @@ if (conflicts.length > 0) { updateSelectedSelect(conflicts[0].moduleimpl_id); } - } + }, { once: true }); diff --git a/app/templates/assiduites/signal_assiduites_diff.j2 b/app/templates/assiduites/signal_assiduites_diff.j2 index cecfe1469..be5f4a40b 100644 --- a/app/templates/assiduites/signal_assiduites_diff.j2 +++ b/app/templates/assiduites/signal_assiduites_diff.j2 @@ -1,512 +1,8 @@

Signalement différé des assiduités {{gr |safe}}

{{sem | safe }}

- -
-
-
-
Noms
- -
-
-
- {% for etud in etudiants %} -
-
{{etud.nomprenom}}
-
- {% endfor %} -
-
+{{diff | safe}} {% include "assiduites/alert.j2" %} {% include "assiduites/prompt.j2" %} - - - - \ No newline at end of file +{% include "assiduites/conflict.j2" %} \ No newline at end of file diff --git a/app/templates/assiduites/signal_assiduites_etud.j2 b/app/templates/assiduites/signal_assiduites_etud.j2 index 7e77f7b49..5a884569e 100644 --- a/app/templates/assiduites/signal_assiduites_etud.j2 +++ b/app/templates/assiduites/signal_assiduites_etud.j2 @@ -1,36 +1,8 @@ {# -*- mode: jinja-html -*- #} - {% include "assiduites/toast.j2" %} {% include "assiduites/alert.j2" %} {% include "assiduites/prompt.j2" %} +{% include "assiduites/conflict.j2" %}
{% block content %}

Signalement de l'assiduité de {{sco.etud.nomprenom}}

@@ -44,7 +16,7 @@
- {% include "assiduites/moduleimpl_dynamic_selector.j2" %} + {{moduleimpl_select | safe }}
@@ -59,6 +31,30 @@
+
+ {{diff | safe}} + +
+

Explication diverses

+

+ Si la période indiquée par la timeline provoque un conflit d'assiduité pour un étudiant sa ligne deviendra + rouge. +
+ Dans ce cas il faut résoudre manuellement le conflit : cliquez sur un des boutons d'assiduités pour ouvrir + le + résolveur de conflit. +
+ Correspondance des couleurs : +

+
    +
  • Vert -> présence de l'étudiant lors de la période
  • +
  • Orange -> retard de l'étudiant lors de la période
  • +
  • Rouge -> absence de l'étudiant lors de la période
  • +
  • Hachure Bleu -> l'assiduité est justifiée par un justificatif valide
  • +
  • Hachure Rouge -> l'assiduité est justifiée par un justificatif non valide / en attente de validation +
  • +
+
@@ -99,8 +95,13 @@ } - let forceModule = "{{ forcer_module }}" - forceModule = forceModule == "True" ? true : false + window.forceModule = "{{ forcer_module }}" + window.forceModule = window.forceModule == "True" ? true : false + + window.addEventListener('load', function () { + loading(); + }, { once: true }); + @@ -110,6 +111,11 @@ background-color: rgb(104, 104, 252); color: whitesmoke; } + + fieldset { + outline: none; + border: none; + } {% endblock %} diff --git a/app/templates/assiduites/signal_assiduites_group.j2 b/app/templates/assiduites/signal_assiduites_group.j2 index 294499d96..0bacda8f2 100644 --- a/app/templates/assiduites/signal_assiduites_group.j2 +++ b/app/templates/assiduites/signal_assiduites_group.j2 @@ -16,7 +16,7 @@
Groupes : {{grp|safe}}
-
Modules :{{moduleimpl_select|safe}}
+
Module :{{moduleimpl_select|safe}}
Date: @@ -31,35 +31,31 @@ {{timeline|safe}}
+

+ Veillez à choisir le groupe concerné par la saisie ainsi que la date de la saisie. + Après validation, il faudra recharger la page pour changer les informations de la saisie. +

-