{% extends "sco_page.j2" %} {% block styles %} {{ super() }} <link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css"> <style> .ligne.valide, .ligne.non_valide{ opacity: 0.5; } .ligne { display: grid; grid-template-columns: 1fr 0.5fr 1.5fr 2fr 1fr 1fr; gap: 4px; padding-bottom: 4px; border-bottom: 1px solid #ccc; margin-bottom: 4px; } .ligne.head { font-weight: bold; border-bottom: 2px solid #ccc; } .ligne>div { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%; } .ligne>div:not(:last-of-type) { border-right: 1px solid #ccc; } .pdp { width: 50px; border-radius: 8px; margin-right: 4px; } .etud { text-align: center; } .validation{ flex-direction: row !important; justify-content: space-evenly !important; } .ligne button[etat]{ border: 1px solid #444; color: #444; padding: 2px 6px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 2px 1px; cursor: pointer; border-radius: 8px; } .ligne.attente button[etat="attente"]{ color: whitesmoke; background-color: var(--color-retard); } .ligne.valide button[etat="valide"]{ color: whitesmoke; background-color: var(--color-present); } .ligne.non_valide button[etat="non_valide"]{ color: whitesmoke; background-color: var(--color-absent); } .hint{ font-size: 0.8em; color: #666; } .entry_date::before{ content: "Saisie le "; } .sco-drop { border: 1px solid #e1e1e1; /* Couleur de bordure plus douce */ border-radius: 8px; /* Coins plus arrondis */ background-color: #fafafa; /* Couleur de fond légère */ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* Ombre douce pour de la profondeur */ width: 100%; /* Adaptation à la largeur de son conteneur */ max-width: 600px; /* Largeur maximale pour une meilleure apparence sur grands écrans */ margin: 10px auto; /* Centrage avec une marge */ position: relative; z-index: 1; } .sco-drop[open] { z-index: 2; /* Empilement au-dessus des autres détails */ } .sco-drop summary { font-weight: 600; /* Texte plus épais */ color: #333; /* Couleur de texte plus foncée pour le contraste */ padding: 7px 10px; /* Plus de padding pour une meilleure ergonomie */ cursor: pointer; list-style: none; /* Enlève les puces */ outline: none; /* Supprime la bordure de focus par défaut pour un look plus net */ user-select: none; /* Empêche la sélection du texte */ text-align: center; } .sco-drop summary::-webkit-details-marker { display: none; /* Cache le triangle par défaut sur Chrome/Safari */ } .sco-drop summary:focus { outline: none; /* Plus propre sans contour lors du focus */ } .sco-drop ul { list-style: none; /* Enlève les puces */ margin: 5px 0; padding: 0; background-color: #fff; /* Arrière-plan blanc pour le contenu */ position: absolute; border-radius: 8px; z-index: 1000; border: 1px solid #e1e1e1; /* Bordure plus douce */ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); /* Ombre douce pour de la profondeur */ overflow-y: scroll; max-height: 150px; /* Hauteur maximale pour une meilleure apparence sur grands écrans */ } .sco-drop li { padding: 10px 20px; /* Espacement intérieur pour les éléments de liste */ border-top: 1px solid #e1e1e1; /* Séparateur subtil entre les éléments */ } .sco-drop li:first-child { border-top: none; /* Pas de bordure en haut du premier élément */ } </style> {% endblock styles %} {% block scripts %} {{ super() }} <script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script> <script src="{{scu.STATIC_DIR}}/js/assiduites.js"></script> <script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script> <script> function changerEtatJustificatif(justifId, etat) { const ligne = document.getElementById("justi-" + justifId); // Mettre à jour le justificatif async_post( "../../api/justificatif/" + justifId + "/edit", { etat: etat }, () => { // Mettre à jour la ligne ligne.classList.remove("attente", "modifie", "valide", "non_valide"); ligne.classList.add(etat); // Afficher le toast const p = document.createElement("span"); let color = ""; switch (etat) { case "attente": color = "var(--color-retard)"; p.textContent = "Justificatif mis en attente"; break; case "valide": color = "var(--color-present)"; p.textContent = "Justificatif validé"; break; case "non_valide": color = "var(--color-absent)"; p.textContent = "Justificatif invalidé"; break; default: color = "gray"; break; } const toast = generateToast(p, color, 3); pushToast(toast); }, (e) => { console.error(e); } ); } /** * Filtre les lignes en fonction des états demandés * @param {Array} etats */ function filtrerLignes() { const etats = [ att.checked ? "attente" : null, modif.checked ? "modifie" : null, ]; // Sauvegarde des paramètres localStorage.setItem("scodoc-etud-att", `${att.checked}`); localStorage.setItem("scodoc-etud-modif", `${modif.checked}`); document.querySelectorAll(".ligne").forEach((el) => { if ((el.id == "")) return; // Si au moins un état se trouve dans la classe de l'élément // Alors on laisse affiché cet élément if ( etats.some((e) => { return el.classList.contains(e); }) ) { el.classList.remove("hidden"); } else { el.classList.add("hidden"); } }); } function main() { const checked = localStorage.getItem("scodoc-etud-pdp") == "true"; afficherPDP(checked); // Gestion des filtres att.checked = localStorage.getItem("scodoc-etud-att") == "true"; modif.checked = localStorage.getItem("scodoc-etud-modif") == "true"; att.addEventListener("change", filtrerLignes); modif.addEventListener("change", filtrerLignes); filtrerLignes() // Gestion des dropdowns document.body.addEventListener("click", (e) => { if (!e.target.matches(".sco-drop, .sco-drop *")) { document.querySelectorAll(".sco-drop").forEach((drop) => { drop.removeAttribute("open"); }); } }); } main(); </script> {% endblock %} {% block app_content %} <h2>Traitement des justificatifs <span class="rouge">{{formsemestre.titre_num()}}</span></h2> <div class="scobox"> <label for="att"> Justificatifs en attente <input type="checkbox" id="att" checked> </label> <label for="modif"> Justificatifs modifié <input type="checkbox" id="modif" checked> </label> <label for="pdp"> Photo des étudiants : <input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)"> </label> </div> <div class="scobox"> <div class="ligne head"> <div>Etudiant</div> <div>Abs</div> <div>Plage</div> <div>Description</div> <div>Fichiers</div> <div>Validation</div> </div> {% for ligne in lignes %} <div class="ligne {{ligne.etat}}" id="justi-{{ligne.justif.justif_id}}"> <div class="etud"> <img src="../../api/etudiant/etudid/{{ligne.etud.id}}/photo?size=small" alt="{{ligne.etud.nomprenom}}" class="pdp"> <span class="etudinfo" id="{{ligne.justif.justif_id}}-{{ligne.etud.id}}">{{ ligne.etud.nomprenom }}</span> </div> <div class="stats"> <span> NJ : {{ligne.etud.stats[0]}} </span> <span> J : {{ligne.etud.stats[1]}} </span> </div> <div class="plage"> {% if ligne.justif.date_debut.date() == ligne.justif.date_fin.date() %} <span class="date"> {{ligne.justif.date_debut.strftime(scu.DATE_FMT)}} de {{ligne.justif.date_debut.strftime(scu.TIME_FMT)}} à {{ligne.justif.date_fin.strftime(scu.TIME_FMT)}} </span> {% else %} <span class="date_debut"> du {{ligne.justif.date_debut.strftime(scu.DATE_FMT)}} </span> <span class="date_fin"> au {{ligne.justif.date_fin.strftime(scu.DATE_FMT)}} </span> {% endif %} <span class="entry_date hint"> {{ligne.justif.entry_date.strftime("%d/%m/%y %Hh%M")}} </span> {% if ligne.assiduites.__len__() == 0 %} <p>Aucune assiduité concernée</p> {% else %} <details class="sco-drop"> <summary> Assiduités concernées </summary> <ul> {% for assi in ligne.assiduites %} <li> {{scu.EtatAssiduite(assi.etat).version_lisible()}} {% if assi.date_debut.date() == assi.date_fin.date() %} du {{assi.date_debut.strftime(scu.DATE_FMT)}} de {{assi.date_debut.strftime(scu.TIME_FMT)}} à {{assi.date_fin.strftime(scu.TIME_FMT)}} {% else %} du {{assi.date_debut.strftime("%d/%m/%y %Hh%M")}} au {{assi.date_fin.strftime("%d/%m/%y %Hh%M")}} {% endif %} </li> {% endfor %} </ul> </details> {% endif %} </div> <div class="desc"> <p>{{ligne.justif.raison}}</p> {% if ligne.etat == "modifie" %} <p class="hint">le justificatif a été modifié</p> {% endif %} </div> <div class="fichiers"> {% if ligne.fichiers.total == 0 %} <p>Aucun fichier joint</p> {% else %} <details class="sco-drop"> <summary>Fichiers joints</summary> <ul> {% for filename in ligne.fichiers.filenames %} <li> <a href="{{url_for('apiweb.justif_export',justif_id=ligne.justif.justif_id, filename=filename, scodoc_dept=g.scodoc_dept)}}">{{filename}}</a> </li> {% endfor %} </ul> </details> {% endif %} </div> <div class="validation"> <button type="button" etat="valide" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'valide')">OUI</button> <button type="button" etat="non_valide" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'non_valide')">NON</button> <button type="button" etat="attente" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'attente')">ATT</button> </div> </div> {% endfor %} </div> {% include "assiduites/widgets/toast.j2" %} {% endblock %}