<ul id="contextMenu" class="context-menu"> <li id="detailOption">Detail</li> <li id="editOption">Editer</li> <li id="deleteOption">Supprimer</li> </ul> {% include "assiduites/widgets/alert.j2" %} {% include "assiduites/widgets/prompt.j2" %} <script> const itemsPerPage = 10; const contextMenu = document.getElementById("contextMenu"); const editOption = document.getElementById("editOption"); const deleteOption = document.getElementById("deleteOption"); let selectedRow; document.addEventListener("click", () => { contextMenu.style.display = "none"; }); editOption.addEventListener("click", () => { if (selectedRow) { // Code pour éditer la ligne sélectionnée console.debug("Éditer :", selectedRow); } }); deleteOption.addEventListener("click", () => { if (selectedRow) { // Code pour supprimer la ligne sélectionnée const type = selectedRow.getAttribute('type'); const obj_id = selectedRow.getAttribute('obj_id'); if (type == "assiduite") { deleteAssiduite(obj_id); } else { deleteJustificatif(obj_id); } loadAll(); } }); function filterArray(array, f) { return array.filter((el) => { let t = Object.keys(f).every((k) => { if (k == "etat") { return f.etat.includes(el.etat.toLowerCase()) }; if (k == "est_just") { if (f.est_just != "") { return `${el.est_just}` == f.est_just; } } if (k.indexOf('date') != -1) { const assi_time = moment.tz(el[k], TIMEZONE); const filter_time = f[k].time; switch (f[k].pref) { case "0": return assi_time.isSame(filter_time, 'minute'); case "-1": return assi_time.isBefore(filter_time, 'minutes'); case "1": return assi_time.isAfter(filter_time, 'minutes'); } } if (k == "moduleimpl_id") { const m = el[k] == undefined || el[k] == null ? "null" : el[k]; if (f.moduleimpl_id != '') { return m == f.moduleimpl_id; } } return true; }) return t; }) } function generateTableHead(columns, assi = true) { const table = assi ? "#assiduiteTable" : "#justificatifTable" const call = assi ? [assiduiteCallBack, true] : [justificatifCallBack, false] const tr = document.querySelector(`${table} thead tr`); tr.innerHTML = "" columns.forEach((c) => { const th = document.createElement('th'); const div = document.createElement('div'); const span = document.createElement('span'); span.textContent = columnTranslator(c); const a = document.createElement('a'); a.classList.add('icon', "order"); a.onclick = () => { order(c, call[0], a, call[1]) } div.appendChild(span) div.appendChild(a) th.appendChild(div); tr.appendChild(th); }) } function renderPaginationButtons(array, assi = true) { const totalPages = Math.ceil(array.length / itemsPerPage); if (assi) { paginationContainerAssiduites.innerHTML = "" } else { paginationContainerJustificatifs.innerHTML = "" } if (totalPages == 1) { return; } for (let i = 1; i <= totalPages; i++) { const paginationButton = document.createElement("a"); paginationButton.textContent = i; paginationButton.classList.add("pagination-button"); if (assi) { paginationButton.addEventListener("click", () => { currentPageAssiduites = i; renderTableAssiduites(currentPageAssiduites, array); }); paginationContainerAssiduites.appendChild(paginationButton); } else { paginationButton.addEventListener("click", () => { currentPageJustificatifs = i; renderTableAssiduites(currentPageJustificatifs, array); }); paginationContainerJustificatifs.appendChild(paginationButton); } } updateActivePaginationButton(assi); } function updateActivePaginationButton(assi = true) { if (assi) { const paginationButtons = paginationContainerAssiduites.querySelectorAll("#paginationContainerAssiduites .pagination-button"); paginationButtons.forEach((button) => { if (parseInt(button.textContent) === currentPageAssiduites) { button.classList.add("active"); } else { button.classList.remove("active"); } }); } else { const paginationButtons = paginationContainerJustificatifs.querySelectorAll("#paginationContainerJustificatifs .pagination-button"); paginationButtons.forEach((button) => { if (parseInt(button.textContent) === currentPageJustificatifs) { button.classList.add("active"); } else { button.classList.remove("active"); } }); } } function loadAll() { try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { } try { getAllJustificatifsFromEtud(etudid, justificatifCallBack) } catch (_) { } } function order(keyword, callback = () => { }, el, assi = true) { const call = (array, ordered) => { const sorted = array.sort((a, b) => { let keyValueA = a[keyword]; let keyValueB = b[keyword]; if (keyword.indexOf("date") != -1) { keyValueA = moment.tz(keyValueA, TIMEZONE) keyValueB = moment.tz(keyValueB, TIMEZONE) } if (keyword.indexOf("module") != -1) { keyValueA = getModuleImpl(keyValueA); keyValueB = getModuleImpl(keyValueB); } let orderDertermined = keyValueA > keyValueB; if (!ordered) { orderDertermined = keyValueA < keyValueB; } return orderDertermined }); callback(sorted); }; if (assi) { orderAssiduites = !orderAssiduites; getAllAssiduitesFromEtud(etudid, (a) => { call(a, orderAssiduites) }) } else { orderJustificatifs = !orderJustificatifs; getAllJustificatifsFromEtud(etudid, (a) => { call(a, orderJustificatifs) }) } } function filter(assi = true) { if (assi) { let html = ` <div class="filter-body"> <h3>Affichage des colonnes:</h3> <div class="filter-head"> <label> Date de saisie <input class="chk" type="checkbox" name="entry_date" id="entry_date"> </label> <label> Date de Début <input class="chk" type="checkbox" name="date_debut" id="date_debut" checked> </label> <label> Date de Fin <input class="chk" type="checkbox" name="date_fin" id="date_fin" checked> </label> <label> Etat <input class="chk" type="checkbox" name="etat" id="etat" checked> </label> <label> Module <input class="chk" type="checkbox" name="moduleimpl_id" id="moduleimpl_id" checked> </label> <label> Justifiée <input class="chk" type="checkbox" name="est_just" id="est_just" checked> </label> </div> <hr> <h3>Filtrage des colonnes:</h3> <span class="filter-line"> <span class="filter-title" for="entry_date">Date de saisie</span> <select name="entry_date_pref" id="entry_date_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="entry_date_time" id="entry_date_time"> </span> <span class="filter-line"> <span class="filter-title" for="date_debut">Date de début</span> <select name="date_debut_pref" id="date_debut_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="date_debut_time" id="date_debut_time"> </span> <span class="filter-line"> <span class="filter-title" for="date_fin">Date de fin</span> <select name="date_fin_pref" id="date_fin_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="date_fin_time" id="date_fin_time"> </span> <span class="filter-line"> <span class="filter-title" for="etat">Etat</span> <input checked type="checkbox" name="etat_present" id="etat_present" class="rbtn present" value="present"> <input checked type="checkbox" name="etat_retard" id="etat_retard" class="rbtn retard" value="retard"> <input checked type="checkbox" name="etat_absent" id="etat_absent" class="rbtn absent" value="absent"> </span> <span class="filter-line"> <span class="filter-title" for="moduleimpl_id">Module</span> <select id="moduleimpl_id"> <option value="">Pas de filtre</option> </select> </span> <span class="filter-line"> <span class="filter-title" for="est_just">Est Justifiée</span> <select id="est_just"> <option value="">Pas de filtre</option> <option value="true">Oui</option> <option value="false">Non</option> </select> </span> </div> `; const span = document.createElement('span'); span.innerHTML = html html = span.firstElementChild const filterHead = html.querySelector('.filter-head'); filterHead.innerHTML = "" let cols = ["entry_date", "date_debut", "date_fin", "etat", "moduleimpl_id", "est_just"]; cols.forEach((k) => { const label = document.createElement('label') label.classList.add('f-label') const s = document.createElement('span'); s.textContent = columnTranslator(k); const input = document.createElement('input'); input.classList.add('chk') input.type = "checkbox" input.name = k input.id = k; input.checked = filterAssiduites.columns.includes(k) label.appendChild(s) label.appendChild(input) filterHead.appendChild(label) }) const sl = html.querySelector('.filter-line #moduleimpl_id'); let opts = [] Object.keys(moduleimpls).forEach((k) => { const opt = document.createElement('option'); opt.value = k == null ? "null" : k; opt.textContent = moduleimpls[k]; opts.push(opt); }) opts = opts.sort((a, b) => { return a.value < b.value }) sl.append(...opts); // Mise à jour des filtres Object.keys(filterAssiduites.filters).forEach((key) => { const l = html.querySelector(`.filter-title[for="${key}"]`).parentElement; if (key.indexOf('date') != -1) { l.querySelector(`#${key}_pref`).value = filterAssiduites.filters[key].pref; l.querySelector(`#${key}_time`).value = filterAssiduites.filters[key].time.format("YYYY-MM-DDTHH:mm"); } else if (key.indexOf('etat') != -1) { l.querySelectorAll('input').forEach((e) => { e.checked = filterAssiduites.filters[key].includes(e.value) }) } else if (key.indexOf("module") != -1) { l.querySelector('#moduleimpl_id').value = filterAssiduites.filters[key]; } else if (key.indexOf("est_just") != -1) { l.querySelector('#est_just').value = filterAssiduites.filters[key]; } }) openPromptModal("Filtrage des assiduités", html, () => { const columns = [...document.querySelectorAll('.chk')] .map((el) => { if (el.checked) return el.id }) .filter((el) => el) filterAssiduites.columns = columns filterAssiduites.filters = {} //reste des filtres const lines = [...document.querySelectorAll('.filter-line')]; lines.forEach((l) => { const key = l.querySelector('.filter-title').getAttribute('for'); if (key.indexOf('date') != -1) { const pref = l.querySelector(`#${key}_pref`).value; const time = l.querySelector(`#${key}_time`).value; if (l.querySelector(`#${key}_time`).value != "") { filterAssiduites.filters[key] = { pref: pref, time: new moment.tz(time, TIMEZONE) } } } else if (key.indexOf('etat') != -1) { filterAssiduites.filters[key] = [...l.querySelectorAll("input:checked")].map((e) => e.value); } else if (key.indexOf("module") != -1) { filterAssiduites.filters[key] = l.querySelector('#moduleimpl_id').value; } else if (key.indexOf("est_just") != -1) { filterAssiduites.filters[key] = l.querySelector('#est_just').value; } }) getAllAssiduitesFromEtud(etudid, assiduiteCallBack) }, () => { }, "#7059FF"); } else { let html = ` <div class="filter-body"> <h3>Affichage des colonnes:</h3> <div class="filter-head"> <label> Date de saisie <input class="chk" type="checkbox" name="entry_date" id="entry_date"> </label> <label> Date de Début <input class="chk" type="checkbox" name="date_debut" id="date_debut" checked> </label> <label> Date de Fin <input class="chk" type="checkbox" name="date_fin" id="date_fin" checked> </label> <label> Etat <input class="chk" type="checkbox" name="etat" id="etat" checked> </label> <label> Raison <input class="chk" type="checkbox" name="raison" id="raison" checked> </label> <label> Fichier <input class="chk" type="checkbox" name="fichier" id="fichier" checked> </label> </div> <hr> <h3>Filtrage des colonnes:</h3> <span class="filter-line"> <span class="filter-title" for="entry_date">Date de saisie</span> <select name="entry_date_pref" id="entry_date_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="entry_date_time" id="entry_date_time"> </span> <span class="filter-line"> <span class="filter-title" for="date_debut">Date de début</span> <select name="date_debut_pref" id="date_debut_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="date_debut_time" id="date_debut_time"> </span> <span class="filter-line"> <span class="filter-title" for="date_fin">Date de fin</span> <select name="date_fin_pref" id="date_fin_pref"> <option value="-1">Avant</option> <option value="0">Égal</option> <option value="1">Après</option> </select> <input type="datetime-local" name="date_fin_time" id="date_fin_time"> </span> <span class="filter-line"> <span class="filter-title" for="etat">Etat</span> <label> Valide <input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="valide"> </label> <label> Non Valide <input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="non_valide"> </label> <label> En Attente <input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="attente"> </label> <label> Modifié <input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="modifie"> </label> </span> </div> `; const span = document.createElement('span'); span.innerHTML = html html = span.firstElementChild const filterHead = html.querySelector('.filter-head'); filterHead.innerHTML = "" let cols = ["entry_date", "date_debut", "date_fin", "etat", "raison", "fichier"]; cols.forEach((k) => { const label = document.createElement('label') label.classList.add('f-label') const s = document.createElement('span'); s.textContent = columnTranslator(k); const input = document.createElement('input'); input.classList.add('chk') input.type = "checkbox" input.name = k input.id = k; input.checked = filterJustificatifs.columns.includes(k) label.appendChild(s) label.appendChild(input) filterHead.appendChild(label) }) // Mise à jour des filtres Object.keys(filterJustificatifs.filters).forEach((key) => { const l = html.querySelector(`.filter-title[for="${key}"]`).parentElement; if (key.indexOf('date') != -1) { l.querySelector(`#${key}_pref`).value = filterJustificatifs.filters[key].pref; l.querySelector(`#${key}_time`).value = filterJustificatifs.filters[key].time.format("YYYY-MM-DDTHH:mm"); } else if (key.indexOf('etat') != -1) { l.querySelectorAll('input').forEach((e) => { e.checked = filterJustificatifs.filters[key].includes(e.value) }) } }) openPromptModal("Filtrage des Justificatifs", html, () => { const columns = [...document.querySelectorAll('.chk')] .map((el) => { if (el.checked) return el.id }) .filter((el) => el) filterJustificatifs.columns = columns filterJustificatifs.filters = {} //reste des filtres const lines = [...document.querySelectorAll('.filter-line')]; lines.forEach((l) => { const key = l.querySelector('.filter-title').getAttribute('for'); if (key.indexOf('date') != -1) { const pref = l.querySelector(`#${key}_pref`).value; const time = l.querySelector(`#${key}_time`).value; if (l.querySelector(`#${key}_time`).value != "") { filterJustificatifs.filters[key] = { pref: pref, time: new moment.tz(time, TIMEZONE) } } } else if (key.indexOf('etat') != -1) { filterJustificatifs.filters[key] = [...l.querySelectorAll("input:checked")].map((e) => e.value); } }) getAllJustificatifsFromEtud(etudid, justificatifCallBack) }, () => { }, "#7059FF"); } } function columnTranslator(colName) { switch (colName) { case "date_debut": return "Début"; case "entry_date": return "Saisie le"; case "date_fin": return "Fin"; case "etat": return "État"; case "moduleimpl_id": return "Module"; case "est_just": return "Justifiée"; case "raison": return "Raison"; case "fichier": return "Fichier"; } } </script> <style> .pageContent { width: 100%; max-width: var(--sco-content-max-width); display: flex; flex-direction: column; flex-wrap: wrap; } table { border-collapse: collapse; text-align: left; margin: 20px 0; } th, td { border: 1px solid #dddddd; padding: 8px; } th { background-color: #f2f2f2; } tr:hover { filter: brightness(1.2) } .context-menu { display: none; position: absolute; list-style-type: none; padding: 10px 0; background-color: #f9f9f9; border: 1px solid #ccc; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); cursor: pointer; z-index: 45; } .context-menu li { padding: 8px 16px; background-color: #f9f9f9; } .context-menu li:hover { filter: brightness(0.7); } #deleteOption { background-color: #F1A69C; } .l-present { background-color: #9CF1AF; } .l-absent, .l-invalid { background-color: #F1A69C; } .l-valid { background-color: #8f7eff; } .l-retard { background-color: #F1D99C; } /* Ajoutez des styles pour le conteneur de pagination et les boutons */ .pagination-container { display: flex; justify-content: center; margin: 20px 0; } .pagination-button { padding: 10px; border: 1px solid #ccc; cursor: pointer; background-color: #f9f9f9; margin: 0 5px; text-decoration: none; color: #000; } .pagination-button:hover { background-color: #ddd; } .pagination-button.active { background-color: #007bff; color: #fff; border-color: #007bff; } th>div { display: flex; justify-content: space-between; align-items: center; } .filter-head { display: flex; flex-wrap: wrap; gap: 5px; } .filter-line { display: flex; justify-content: start; align-items: center; margin: 15px; } .filter-line>* { margin-right: 5px; } .rbtn { width: 35px; height: 35px; margin: 0 5px !important; } .f-label { margin: 0 5px; } .chk { margin-left: 2px !important; } label { display: flex; justify-content: center; align-items: center; padding: 0; margin: 0; } </style>