forked from ScoDoc/ScoDoc
372 lines
11 KiB
Django/Jinja
372 lines
11 KiB
Django/Jinja
{% extends "sco_page.j2" %}
|
|
|
|
{% block styles %}
|
|
{{ super() }}
|
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/minitimeline.css">
|
|
|
|
<style>
|
|
.ui-timepicker-container,#ui-datepicker-div{
|
|
z-index: 5 !important;
|
|
}
|
|
#new_periode,
|
|
#actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: fit-content;
|
|
gap: 0.5em;
|
|
}
|
|
|
|
#fix {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 1em;
|
|
justify-content: space-between;
|
|
width: fit-content;
|
|
}
|
|
|
|
#fix>.box {
|
|
border: 1px solid #444;
|
|
border-radius: 0.5em;
|
|
padding: 1em;
|
|
}
|
|
|
|
.timepicker {
|
|
width: 5em;
|
|
text-align: center;
|
|
}
|
|
|
|
#moduleimpl_select {
|
|
width: 10em;
|
|
}
|
|
|
|
#gtrcontent .pdp {
|
|
display: none;
|
|
}
|
|
|
|
#gtrcontent[data-pdp="true"] .pdp {
|
|
display: block;
|
|
}
|
|
|
|
#tableau-periode {
|
|
overflow-x: scroll;
|
|
max-width: var(--sco-content-max-width);
|
|
}
|
|
|
|
#tableau-periode .pdp {
|
|
width: 2em;
|
|
height: 2em;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.grid-table {
|
|
display: grid;
|
|
grid-template-columns: 200px repeat({{ etudiants|length }}, 1fr);
|
|
width: var(--sco-content-max-width);
|
|
}
|
|
.cell, .header {
|
|
border: 1px solid #ddd;
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
background-color: #f9f9f9;
|
|
}
|
|
.header{
|
|
justify-content: space-between;
|
|
|
|
}
|
|
.cell{
|
|
justify-content: center;
|
|
|
|
}
|
|
|
|
.cell p{
|
|
text-align: center;
|
|
}
|
|
.sticky {
|
|
position: sticky;
|
|
left: 0;
|
|
background-color: #f9f9f9;
|
|
z-index: 2;
|
|
}
|
|
|
|
.cell .assiduite-bubble {
|
|
display: block;
|
|
top: 0;
|
|
z-index: 0;
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
{# Temporaire #}
|
|
<style>
|
|
.wip::before{
|
|
content: "WIP 🚧";
|
|
font-size: 1.5em;
|
|
margin: 2px;
|
|
color: red;
|
|
}
|
|
</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>
|
|
{% include "sco_timepicker.j2" %}
|
|
|
|
<script>
|
|
|
|
function afficherPDP(checked) {
|
|
if (checked) {
|
|
gtrcontent.setAttribute("data-pdp", "true");
|
|
} else {
|
|
gtrcontent.removeAttribute("data-pdp");
|
|
}
|
|
}
|
|
|
|
async function nouvellePeriode(){
|
|
periodeId++;
|
|
|
|
// On récupère les valeurs des inputs
|
|
let date = document.getElementById("date").value;
|
|
let debut = document.getElementById("debut").value;
|
|
let fin = document.getElementById("fin").value;
|
|
let moduleimpl_id = document.getElementById("moduleimpl_select").value;
|
|
const moduleimpl = await getModuleImpl({moduleimpl_id: moduleimpl_id});
|
|
|
|
// On ajoute la nouvelle période au tableau
|
|
let periodeDiv = document.createElement("div");
|
|
periodeDiv.classList.add("cell", "sticky");
|
|
const periodP = document.createElement("p");
|
|
periodP.textContent = `Période du ${date} de ${debut} à ${fin}`;
|
|
|
|
const modP = document.createElement("p");
|
|
modP.textContent = moduleimpl;
|
|
|
|
const close = document.createElement("button");
|
|
close.textContent = "❌";
|
|
close.addEventListener("click", (event)=>{
|
|
document.querySelectorAll(`.cell[data-periodeid="${periodeDiv.getAttribute('data-periodeid')}"]`).forEach((e)=>e.remove());
|
|
});
|
|
|
|
periodeDiv.appendChild(periodP);
|
|
periodeDiv.appendChild(modP);
|
|
periodeDiv.appendChild(close);
|
|
periodeDiv.setAttribute("data-periodeid", periodeId);
|
|
document.getElementById("tableau-periode").appendChild(periodeDiv);
|
|
|
|
let etudids = [...document
|
|
.querySelectorAll("#tableau-periode .header[data-etudid]")]
|
|
.map((e)=>e.getAttribute("data-etudid"));
|
|
|
|
const date_debut = new Date($("#date").datepicker("getDate").format("YYYY-MM-DD") + "T" + debut);
|
|
const date_fin = new Date($("#date").datepicker("getDate").format("YYYY-MM-DD") + "T" + fin);
|
|
date_debut.add(1, "seconds")
|
|
const url =
|
|
`../../api/assiduites/group/query?date_debut=${date_debut.toFakeIso()}` +
|
|
`&date_fin=${date_fin.toFakeIso()}&etudids=${etudids.join(',')}&with_justifs`;
|
|
|
|
|
|
await fetch(url)
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error("Network response was not ok");
|
|
}
|
|
return res.json();
|
|
})
|
|
.then((data)=>{
|
|
Object.entries(data).forEach(([etudid, assiduites]) => {
|
|
let cell = document.createElement("div");
|
|
cell.classList.add("cell");
|
|
cell.setAttribute("data-etudid", etudid);
|
|
cell.setAttribute("data-periodeid", periodeId);
|
|
|
|
if (assiduites.length == 0) {
|
|
["present", "retard", "absent"].forEach((value) => {
|
|
const cbox = document.createElement('input');
|
|
cbox.type = "checkbox";
|
|
cbox.value = value;
|
|
cbox.name = `rbtn_${etudid}_${periodeId}`
|
|
cbox.classList.add("rbtn", value);
|
|
|
|
cbox.addEventListener('click', (event)=>{
|
|
const parent = event.target.parentElement;
|
|
parent.querySelectorAll(".rbtn").forEach((ele)=>{
|
|
if(ele.value != value){
|
|
ele.checked = false;
|
|
}
|
|
})
|
|
})
|
|
|
|
cbox.checked = etatDef.value == value
|
|
|
|
cell.appendChild(cbox);
|
|
});
|
|
}else{
|
|
setupAssiduiteBubble(cell, assiduites[0])
|
|
}
|
|
|
|
document.getElementById("tableau-periode").appendChild(cell);
|
|
});
|
|
}).catch((error)=>{
|
|
console.error('Error:', error);
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let periodeId = 0;
|
|
const moduleimpls = new Map();
|
|
const nonWorkDays = [{{ nonworkdays| safe }}];
|
|
|
|
|
|
window.forceModule = "{{ forcer_module }}" == "True"
|
|
if (window.forceModule) {
|
|
if (moduleimpl_select.value == "") {
|
|
document.getElementById('forcemodule').style.display = "block";
|
|
add_periode.disabled = true;
|
|
|
|
}
|
|
|
|
moduleimpl_select?.addEventListener('change', (e) => {
|
|
if (e.target.value != "") {
|
|
document.getElementById('forcemodule').style.display = "none";
|
|
add_periode.disabled = false;
|
|
|
|
} else {
|
|
document.getElementById('forcemodule').style.display = "block";
|
|
add_periode.disabled = true;
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
async function main(){
|
|
afficherPDP(pdp.checked);
|
|
$('#date').on('change', async function(d) {
|
|
// On vérifie si la date est un jour travaillé
|
|
dateCouranteEstTravaillee();
|
|
});
|
|
}
|
|
|
|
|
|
main();
|
|
</script>
|
|
|
|
{% endblock scripts %}
|
|
|
|
{% block title %}
|
|
{{title}}
|
|
{% endblock title %}
|
|
|
|
{% block app_content %}
|
|
|
|
<h2>Signalement différé de l'assiduité {{gr |safe}}</h2>
|
|
|
|
|
|
<div id="fix">
|
|
<!-- Nouvelle période
|
|
Permet de créer une nouvelle ligne pour une nouvelle période
|
|
(
|
|
Jour, -> datepicker
|
|
Heure de début, -> timepicker
|
|
Heure de fin -> timepicker
|
|
ModuleImplId -> select (liste des modules tout semestre confondu)
|
|
)
|
|
--->
|
|
|
|
<div id="new_periode" class="box">
|
|
<h4>Nouvelle période</h4>
|
|
<label for="date">
|
|
Date :
|
|
<input type="text" name="date" id="date" class="datepicker">
|
|
</label>
|
|
<label for="debut">
|
|
Heure de début :
|
|
<input type="text" name="debut" id="debut" class="timepicker">
|
|
</label>
|
|
<label for="fin">
|
|
Heure de fin :
|
|
<input type="text" name="fin" id="fin" class="timepicker">
|
|
</label>
|
|
|
|
<label for="moduleimpl_select">
|
|
<div id="forcemodule" style="display: none; margin:10px 0px;">
|
|
Vous devez spécifier le module ! (voir réglage préférence du semestre)
|
|
</div>
|
|
Module :
|
|
{{moduleimpl_select | safe}}
|
|
</label>
|
|
|
|
<button id="add_periode" onclick="nouvellePeriode()">Ajouter une période</button>
|
|
</div>
|
|
|
|
|
|
<!-- Boutons d'actions
|
|
- Sauvegarder
|
|
- Afficher la photo de profil
|
|
- Assiduité par défaut (aucune, present, retard, absent)
|
|
? - Import Excel (fournie un fichier excel
|
|
avec les étudiants et les périodes préremplis)
|
|
--->
|
|
|
|
<div id="actions" class="box">
|
|
<h4>Actions</h4>
|
|
<label for="pdp">
|
|
Photo de profil :
|
|
<input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)">
|
|
</label>
|
|
|
|
<label for="etatDef">
|
|
Assiduité par défaut :
|
|
<select name="etatDef" id="etatDef">
|
|
<option value="">Aucune</option>
|
|
<option value="present">Présence</option>
|
|
<option value="retard">Retard</option>
|
|
<option value="absent">Absence</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label for="excel" class="wip">
|
|
Importer Excel :
|
|
<input type="file" name="excel" id="excel" accept=".xlsx, .xls, .csv">
|
|
</label>
|
|
<button class="wip" id="save wip">Sauvegarder l'assiduité</button>
|
|
</div>
|
|
</div>
|
|
|
|
<br>
|
|
|
|
<!-- Tableau à double entrée
|
|
Colonne : Etudiants (Header = Nom, Prénom, Photo (si actif))
|
|
Ligne : Période (Header = Jour, Heure de début, Heure de fin, ModuleImplId)
|
|
Contenu :
|
|
- bouton assiduité (présent, retard, absent)
|
|
- Bouton conflit si conflit de période
|
|
--->
|
|
|
|
<div id="tableau-periode" class="grid-table">
|
|
<!-- Header de la première colonne -->
|
|
<div class="header sticky">Période</div>
|
|
<!-- Headers des autres colonnes (noms des étudiants) -->
|
|
{% for etud in etudiants %}
|
|
<div class="header etudinfo" data-etudid="{{etud.etudid}}" id="head-{{etud.etudid}}">
|
|
<img src="../../api/etudiant/etudid/{{etud.etudid}}/photo?size=small" alt="{{etud.nomprenom}}" class="pdp">
|
|
<span>{{ etud.nomprenom }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<!-- Sera remplis avec les nouvelles périodes -->
|
|
|
|
</div>
|
|
|
|
|
|
{% include "assiduites/widgets/alert.j2" %}
|
|
{% endblock app_content %}
|