2023-06-12 17:54:30 +02:00
|
|
|
<div id="studentTable">
|
|
|
|
<div class="thead">
|
|
|
|
<div class="tr">
|
|
|
|
<div class="th sticky" order="ASC" onclick="reOrderEtudiants()">Noms</div>
|
|
|
|
<button id="addColumn" class="floating-button">+</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="tbody">
|
|
|
|
|
|
|
|
{% for etud in etudiants %}
|
|
|
|
<div class="tr" etudid="{{etud.etudid}}">
|
|
|
|
<div class="td sticky">
|
|
|
|
<span>{{etud.nomprenom}}</span>
|
2023-06-14 17:53:19 +02:00
|
|
|
<img class="pdp-hover" src="" alt="No Img" etudid="{{etud.etudid}}">
|
2023-06-12 17:54:30 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{% endfor %}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<style>
|
|
|
|
.td.sticky .pdp-hover {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.td.sticky:hover .pdp-hover {
|
|
|
|
position: absolute;
|
|
|
|
display: block;
|
|
|
|
width: 90px;
|
|
|
|
height: 90px;
|
|
|
|
border-radius: 15px;
|
|
|
|
transition: all 0.5s;
|
|
|
|
left: calc(80% - 45px);
|
|
|
|
top: -25%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.err-assi {
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-start;
|
|
|
|
align-items: center;
|
|
|
|
gap: 15px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.table-container {
|
|
|
|
overflow: auto;
|
|
|
|
position: relative;
|
|
|
|
max-width: 100%;
|
|
|
|
margin: 0 auto;
|
|
|
|
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, .2);
|
|
|
|
}
|
|
|
|
|
|
|
|
.table {
|
|
|
|
border-collapse: collapse;
|
|
|
|
}
|
|
|
|
|
|
|
|
.thead .tr {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.thead .tr .th {
|
|
|
|
height: 200px;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th.sticky {
|
|
|
|
z-index: 5;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th,
|
|
|
|
.td {
|
|
|
|
padding: 20px;
|
|
|
|
text-align: center;
|
|
|
|
width: 225px;
|
|
|
|
border: 1px solid #ddd;
|
2023-06-14 17:53:19 +02:00
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-content: center;
|
2023-06-12 17:54:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.tr {
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-start;
|
|
|
|
align-items: center;
|
|
|
|
width: max-content;
|
|
|
|
}
|
|
|
|
|
|
|
|
.sticky {
|
|
|
|
position: sticky;
|
|
|
|
left: 0;
|
|
|
|
background-color: #fafafa;
|
|
|
|
border-right: 1px solid #ddd;
|
|
|
|
}
|
|
|
|
|
|
|
|
.mini-form {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
flex-direction: column;
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.mini-form input,
|
|
|
|
.mini-form select {
|
|
|
|
display: block;
|
|
|
|
margin: 5px;
|
|
|
|
padding: 5px;
|
|
|
|
border-radius: 5px;
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
max-width: 195px;
|
|
|
|
}
|
|
|
|
|
|
|
|
#addColumn {
|
|
|
|
font-size: 24px;
|
|
|
|
width: 50px;
|
|
|
|
height: 50px;
|
|
|
|
border-radius: 50%;
|
|
|
|
right: -60px;
|
|
|
|
top: calc(50% - 50px /2);
|
|
|
|
background-color: #007BFF;
|
|
|
|
color: white;
|
|
|
|
border: none;
|
|
|
|
outline: none;
|
|
|
|
cursor: pointer;
|
|
|
|
transition: background-color 0.3s;
|
|
|
|
}
|
|
|
|
|
|
|
|
#addColumn:hover {
|
|
|
|
background-color: #0056b3;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th {
|
|
|
|
background-color: #007BFF;
|
|
|
|
color: white;
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th.error {
|
|
|
|
background-color: crimson;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tbody .tr:nth-child(even) {
|
|
|
|
background-color: #f2f2f2;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tbody .tr:hover {
|
|
|
|
background-color: #ddd;
|
|
|
|
}
|
|
|
|
|
|
|
|
.td .etat {
|
|
|
|
display: grid;
|
|
|
|
grid-template-columns: 33% 33% 33%;
|
|
|
|
grid-template-rows: 50% 50%;
|
|
|
|
|
|
|
|
font-size: small;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btngroup {
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
flex-direction: row;
|
|
|
|
}
|
|
|
|
|
|
|
|
.btngroup>button {
|
|
|
|
background-color: transparent;
|
|
|
|
outline: none;
|
|
|
|
border: none;
|
|
|
|
cursor: pointer;
|
|
|
|
color: whitesmoke;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th .closeCol {
|
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.activate {
|
|
|
|
font-size: 14px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th[activated] {
|
|
|
|
transition: all 0.5s;
|
|
|
|
}
|
|
|
|
|
|
|
|
.th[activated='false'] {
|
|
|
|
opacity: 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
.num {
|
|
|
|
border: 1px whitesmoke solid;
|
|
|
|
border-radius: 15px;
|
|
|
|
padding: 2px 4px;
|
|
|
|
width: 24px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.assi_rbtn,
|
|
|
|
.assi_lbl {
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-evenly;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.td[assiduite_id='conflit'] {
|
|
|
|
background-color: #ff0000c2;
|
|
|
|
}
|
|
|
|
|
|
|
|
input[type='radio']:disabled {
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
|
|
}
|
2023-06-14 17:53:19 +02:00
|
|
|
|
|
|
|
.th.error:hover .col-error {
|
|
|
|
display: block;
|
|
|
|
z-index: 2000;
|
|
|
|
background-color: crimson;
|
|
|
|
width: 100%;
|
|
|
|
min-height: 25%;
|
|
|
|
bottom: -25%;
|
|
|
|
transition: all 1s;
|
|
|
|
}
|
|
|
|
|
|
|
|
.col-error {
|
|
|
|
position: absolute;
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.mass {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-evenly;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.mass input {
|
|
|
|
outline: none;
|
|
|
|
border: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.rbtn:disabled {
|
|
|
|
opacity: 0.7;
|
|
|
|
}
|
2023-07-04 15:04:58 +02:00
|
|
|
|
|
|
|
.td.etat {
|
|
|
|
box-sizing: border-box;
|
|
|
|
-moz-box-sizing: border-box;
|
|
|
|
-webkit-box-sizing: border-box;
|
|
|
|
border: 10px solid white;
|
|
|
|
}
|
2023-06-12 17:54:30 +02:00
|
|
|
</style>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
const etatDef = "{{etat_def}}";
|
|
|
|
|
|
|
|
window.forceModule = "{{ forcer_module }}"
|
|
|
|
window.forceModule = window.forceModule == "True" ? true : false
|
|
|
|
|
|
|
|
let colCount = 1;
|
|
|
|
|
|
|
|
let currentDate = "{{date}}";
|
|
|
|
if (currentDate == "") {
|
|
|
|
currentDate = moment().tz(TIMEZONE).format("YYYY-MM-DDTHH:mm");
|
|
|
|
} else {
|
|
|
|
currentDate = moment(currentDate).tz(TIMEZONE).format("YYYY-MM-DDTHH:mm");
|
|
|
|
}
|
|
|
|
|
|
|
|
function createColumn() {
|
|
|
|
let table = document.getElementById("studentTable");
|
|
|
|
let th = document.createElement("div");
|
|
|
|
th.classList.add("th", "error");
|
|
|
|
const col_id = `${colCount++}`;
|
|
|
|
th.setAttribute("col", col_id);
|
|
|
|
th.setAttribute("activated", "true");
|
|
|
|
th.innerHTML = `
|
2023-06-14 17:53:19 +02:00
|
|
|
<div class="col-error">La période n'est pas valide</div>
|
2023-06-12 17:54:30 +02:00
|
|
|
<div class="mini-form">
|
2023-06-14 17:53:19 +02:00
|
|
|
<div class="btngroup" style="justify-content: flex-end;">
|
2023-06-12 17:54:30 +02:00
|
|
|
<button class="closeCol" onclick="removeColumn(this)">x</button>
|
|
|
|
</div>
|
|
|
|
<input type="datetime-local" id="dateStart">
|
|
|
|
<input type="datetime-local" id="dateEnd">
|
|
|
|
{{moduleimpl_select|safe}}
|
2023-06-14 17:53:19 +02:00
|
|
|
<div id="mass_action_${col_id}" class="mass">
|
|
|
|
<input disabled="" type="radio" class="rbtn present" name="mass_action_${col_id}" value="present" onclick="massCol(this)">
|
|
|
|
<input disabled="" type="radio" class="rbtn retard" name="mass_action_${col_id}" value="retard" onclick="massCol(this)">
|
|
|
|
<input disabled="" type="radio" class="rbtn absent" name="mass_action_${col_id}" value="absent" onclick="massCol(this)">
|
|
|
|
<input disabled="" type="radio" class="rbtn aucun" name="mass_action_${col_id}" value="remove" onclick="massCol(this)">
|
|
|
|
|
|
|
|
</div>
|
2023-06-12 17:54:30 +02:00
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
table
|
|
|
|
.querySelector(".thead .tr")
|
|
|
|
.insertBefore(th, document.querySelector("#addColumn"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const last = [...document.querySelectorAll("#dateStart")].pop();
|
|
|
|
defaultDate(last);
|
|
|
|
|
|
|
|
try {
|
|
|
|
const sl = th.querySelector('.dynaSelect');
|
|
|
|
sl.id = `dynaSelect_${col_id}`;
|
|
|
|
const miniform = sl.parentElement;
|
|
|
|
const dateIso = miniform.querySelector("#dateStart").value;
|
|
|
|
if (dateIso != "") {
|
|
|
|
updateSelect("", `#${sl.id}`, dateIso)
|
|
|
|
}
|
|
|
|
|
|
|
|
miniform.querySelector("#dateStart").addEventListener('focusout', (el) => {
|
|
|
|
updateSelect("", `#${sl.id}`, el.target.value);
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
|
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
} catch {
|
|
|
|
}
|
|
|
|
|
|
|
|
th.querySelectorAll('input, select.dynaSelect, select#moduleimpl_select').forEach((el) => {
|
|
|
|
el.addEventListener("change", () => {
|
|
|
|
getAndUpdateCol(col_id);
|
|
|
|
})
|
|
|
|
})
|
2023-06-14 17:53:19 +02:00
|
|
|
const sl = th.querySelector('select');
|
|
|
|
sl.addEventListener('change', () => {
|
|
|
|
editModuleImpl(sl);
|
|
|
|
})
|
2023-06-12 17:54:30 +02:00
|
|
|
|
|
|
|
let rows = table.querySelector(".tbody").querySelectorAll(".tr");
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
|
|
let td = document.createElement("div");
|
|
|
|
td.setAttribute("colid", col_id)
|
|
|
|
td.classList.add("td", "etat");
|
|
|
|
const etudid = rows[i].getAttribute("etudid");
|
|
|
|
td.innerHTML = `
|
2023-06-14 17:53:19 +02:00
|
|
|
<div class="assi_rbtn" etat="">
|
|
|
|
<input class="rbtn present" type="radio" name="etat_${col_id}_${etudid}" value="present" disabled onclick="updateEtudAssiduite(this)">
|
|
|
|
<input class="rbtn retard" type="radio" name="etat_${col_id}_${etudid}" value="retard" disabled onclick="updateEtudAssiduite(this)">
|
|
|
|
<input class="rbtn absent" type="radio" name="etat_${col_id}_${etudid}" value="absent" disabled onclick="updateEtudAssiduite(this)">
|
2023-06-12 17:54:30 +02:00
|
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
rows[i].appendChild(td);
|
|
|
|
if (etatDef != "" && etatDef != "aucun") {
|
|
|
|
const inp = td.querySelector(`[value='${etatDef}']`).checked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function reOrderEtudiants() {
|
|
|
|
const th = document.querySelector(".th.sticky");
|
|
|
|
let lines = [...document.querySelectorAll('.tr[etudid]')];
|
|
|
|
const tbody = document.querySelector('.tbody')
|
|
|
|
|
|
|
|
let order = (a, b) => { return a > b }
|
|
|
|
|
|
|
|
if (th.getAttribute("order") == "ASC") {
|
|
|
|
order = (a, b) => { return b > a }
|
|
|
|
th.setAttribute("order", "DESC")
|
|
|
|
} else {
|
|
|
|
th.setAttribute("order", "ASC")
|
|
|
|
}
|
|
|
|
|
|
|
|
lines = lines.sort((a, b) => {
|
|
|
|
const nameA = a.querySelector(".td.sticky").textContent.split(" ").pop();
|
|
|
|
const nameB = b.querySelector(".td.sticky").textContent.split(" ").pop();
|
|
|
|
return order(nameA, nameB)
|
|
|
|
})
|
|
|
|
|
|
|
|
tbody.innerHTML = "";
|
|
|
|
tbody.append(...lines)
|
|
|
|
}
|
|
|
|
|
|
|
|
function previousCol(column) {
|
|
|
|
const cols = [...document.querySelectorAll("[col]")]
|
|
|
|
let previousCol = null;
|
|
|
|
|
|
|
|
let i = 1;
|
|
|
|
|
|
|
|
while (i < cols.length) {
|
|
|
|
previousCol = cols[i - 1];
|
|
|
|
if (cols[i] == column)
|
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return previousCol
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeColumn(element) {
|
|
|
|
const col = element.parentElement.parentElement.parentElement;
|
|
|
|
const col_id = col.getAttribute("col");
|
|
|
|
document.querySelectorAll(`[col='${col_id}'],[colid='${col_id}']`).forEach((el) => { el.remove() })
|
|
|
|
}
|
|
|
|
|
|
|
|
function defaultDate(element) {
|
|
|
|
const num = previousCol(element.parentElement.parentElement)?.getAttribute("col");
|
|
|
|
const last = document.querySelector(`[col='${num}'] #dateEnd`);
|
|
|
|
let date = undefined;
|
|
|
|
if (last == undefined) {
|
|
|
|
date = currentDate;
|
|
|
|
} else {
|
|
|
|
date = last.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
element.value = date;
|
|
|
|
|
|
|
|
element.addEventListener(
|
|
|
|
"focusout",
|
|
|
|
() => {
|
|
|
|
const el = element.parentElement.querySelector("#dateEnd");
|
|
|
|
const el2 = element.parentElement.querySelector("#dateStart");
|
|
|
|
el.value = moment(el2.valueAsDate).tz(TIMEZONE).utc()
|
|
|
|
.add(2, "hours")
|
|
|
|
.format("YYYY-MM-DDTHH:mm");
|
|
|
|
const colid = element.parentElement.parentElement.getAttribute('col');
|
|
|
|
getAndUpdateCol(colid);
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function setEtatCol(colId, etatId) {
|
|
|
|
const tds = [...document.querySelectorAll(`.td[colid='${colId}']`)]
|
|
|
|
tds.forEach((td) => {
|
|
|
|
setEtatLine(td, etatId);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function setEtatLine(line, etatId) {
|
2023-06-14 17:53:19 +02:00
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
let inputs = [...line.querySelectorAll("input")]
|
|
|
|
inputs.forEach((el) => { el.checked = false })
|
|
|
|
if (etatId !== "") {
|
|
|
|
inputs[Number.parseInt(etatId)].checked = true;
|
2023-06-14 17:53:19 +02:00
|
|
|
inputs[Number.parseInt(etatId)].parentElement.setAttribute('etat', inputs[Number.parseInt(etatId)].value)
|
2023-06-12 17:54:30 +02:00
|
|
|
}
|
2023-07-04 15:04:58 +02:00
|
|
|
|
|
|
|
let color;
|
|
|
|
switch (etatId) {
|
|
|
|
case 0:
|
|
|
|
color = "#9CF1AF";
|
|
|
|
break
|
|
|
|
case 1:
|
|
|
|
color = "#F1D99C";
|
|
|
|
break
|
|
|
|
case 2:
|
|
|
|
color = "#F1A69C";
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
color = "white";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
line.style.borderColor = color;
|
2023-06-12 17:54:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function _createAssiduites(inputDeb, inputFin, moduleSelect, etudid, etat, colId) {
|
|
|
|
if (moduleSelect == "") {
|
|
|
|
return {
|
|
|
|
"date_debut": inputDeb,
|
|
|
|
"date_fin": inputFin,
|
|
|
|
"etudid": etudid,
|
|
|
|
"etat": etat,
|
|
|
|
"colid": colId,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
"date_debut": inputDeb,
|
|
|
|
"date_fin": inputFin,
|
|
|
|
"etudid": etudid,
|
|
|
|
"moduleimpl_id": moduleSelect,
|
|
|
|
"etat": etat,
|
|
|
|
"colid": colId,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAssiduitesCol(col_id, get = true) {
|
|
|
|
const col = document.querySelector(`[col='${col_id}']`);
|
2023-06-14 17:53:19 +02:00
|
|
|
const colErr = col.querySelector('.col-error')
|
2023-06-12 17:54:30 +02:00
|
|
|
|
|
|
|
const inputDeb = col.querySelector("#dateStart").value;
|
|
|
|
const inputFin = col.querySelector("#dateEnd").value;
|
|
|
|
const moduleSelect = col.querySelector("#moduleimpl_select,.dynaSelect").value;
|
|
|
|
const d_debut = moment(inputDeb).tz(TIMEZONE);
|
|
|
|
const d_fin = moment(inputFin).tz(TIMEZONE);
|
|
|
|
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
if (inputDeb == "" || inputFin == "" || d_debut >= d_fin) {
|
|
|
|
colErr.textContent = `La période de la colonne n'est pas valide`;
|
2023-06-12 17:54:30 +02:00
|
|
|
return 0x2;
|
|
|
|
}
|
|
|
|
|
|
|
|
{% if periode %}
|
|
|
|
const t_before = "{{periode.deb}}T00:00";
|
|
|
|
const t_after = "{{periode.fin}}T23:59";
|
|
|
|
const testPeriode = [
|
|
|
|
d_debut.isBefore(t_before),
|
|
|
|
d_debut.isAfter(t_after),
|
|
|
|
d_fin.isBefore(t_before),
|
|
|
|
d_fin.isAfter(t_after),
|
|
|
|
]
|
|
|
|
if (testPeriode.some((e) => e)) {
|
2023-06-14 17:53:19 +02:00
|
|
|
colErr.textContent = `La période de la colonne n'est pas dans le semestre`;
|
2023-06-12 17:54:30 +02:00
|
|
|
return 0x3;
|
|
|
|
}
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
if (window.forceModule && moduleSelect == "") {
|
2023-06-14 17:53:19 +02:00
|
|
|
colErr.textContent = `Le module de la colonne n'a pas été entré. (Préférence de semestre)`;
|
2023-06-12 17:54:30 +02:00
|
|
|
return 0x4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get) {
|
|
|
|
getAssiduitesFromEtuds(false, false, d_debut.format(), d_fin.format())
|
|
|
|
return 0x0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
moduleimpl: moduleSelect,
|
|
|
|
deb: d_debut.format(),
|
|
|
|
fin: d_fin.format(),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAssiduitesCol(col_id) {
|
|
|
|
const col = document.querySelector(`[col='${col_id}']`);
|
|
|
|
const tds = [...document.querySelectorAll(`.td[colid='${col_id}']`)]
|
|
|
|
|
|
|
|
const inputDeb = col.querySelector("#dateStart").value;
|
|
|
|
const inputFin = col.querySelector("#dateEnd").value;
|
|
|
|
const d_debut = moment(inputDeb).tz(TIMEZONE);
|
|
|
|
const d_fin = moment(inputFin).tz(TIMEZONE);
|
|
|
|
|
|
|
|
const periode = {
|
|
|
|
deb: d_debut,
|
|
|
|
fin: d_fin,
|
|
|
|
}
|
|
|
|
|
|
|
|
tds.forEach((td) => {
|
|
|
|
tds.forEach((el) => {
|
|
|
|
const inputs = [...el.querySelectorAll('input')];
|
|
|
|
inputs.forEach((i) => { i.disabled = false })
|
|
|
|
});
|
|
|
|
setEtatLine(td, "")
|
|
|
|
const etu = td.parentElement.getAttribute('etudid');
|
|
|
|
const conflits = getAssiduitesConflict(etu, periode);
|
2023-06-14 17:53:19 +02:00
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
if (conflits.length == 0) {
|
|
|
|
td.setAttribute('assiduite_id', "-1");
|
|
|
|
} else if (conflits.length == 1 && isConflictSameAsPeriod(conflits[0], periode)) {
|
|
|
|
const assi = conflits[0];
|
|
|
|
td.setAttribute('assiduite_id', assi.assiduite_id);
|
|
|
|
const ind = ["PRESENT", "RETARD", "ABSENT"].indexOf(assi.etat.toUpperCase());
|
|
|
|
setEtatLine(td, ind)
|
|
|
|
} else {
|
|
|
|
td.setAttribute('assiduite_id', "conflit");
|
|
|
|
const inputs = [...td.querySelectorAll('input')];
|
|
|
|
inputs.forEach((i) => {
|
|
|
|
i.disabled = true;
|
2023-06-14 17:53:19 +02:00
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function setEtuds() {
|
|
|
|
if (!isSingleEtud()) {
|
|
|
|
const etudids = [...document.querySelectorAll("[etudid]")]
|
|
|
|
.map((el) => Number.parseInt(el.getAttribute("etudid")))
|
|
|
|
.filter((value, index, array) => array.indexOf(value) === index);
|
|
|
|
|
|
|
|
etudids.forEach((etu) => {
|
|
|
|
etuds[etu] = { etudid: etu }
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAndUpdateCol(colid) {
|
2023-06-14 17:53:19 +02:00
|
|
|
const column = document.querySelector(`[col='${colid}']`);
|
2023-06-12 17:54:30 +02:00
|
|
|
if (getAssiduitesCol(colid) == 0) {
|
2023-06-14 17:53:19 +02:00
|
|
|
column.classList.remove('error');
|
|
|
|
document.getElementById(`mass_action_${colid}`).querySelectorAll('.rbtn').forEach((el) => {
|
|
|
|
el.removeAttribute('disabled');
|
|
|
|
})
|
2023-06-12 17:54:30 +02:00
|
|
|
updateAssiduitesCol(colid)
|
|
|
|
} else {
|
2023-06-14 17:53:19 +02:00
|
|
|
document.getElementById(`mass_action_${colid}`).querySelectorAll('.rbtn').forEach((el) => {
|
|
|
|
el.setAttribute('disabled', '');
|
|
|
|
})
|
2023-06-12 17:54:30 +02:00
|
|
|
column.classList.add('error');
|
|
|
|
const tds = [...document.querySelectorAll(`.td[colid='${colid}']`)]
|
|
|
|
tds.forEach((el) => {
|
|
|
|
const inputs = [...el.querySelectorAll('input')];
|
|
|
|
inputs.forEach((i) => { i.disabled = true })
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAllCol() {
|
|
|
|
const colIds = [...document.querySelectorAll("[col]")].map((col) => { return col.getAttribute('col') });
|
|
|
|
colIds.forEach((colid) => {
|
|
|
|
updateAssiduitesCol(colid);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-13 16:25:45 +02:00
|
|
|
function launchToast(etudid, etat) {
|
|
|
|
let etatAffiche;
|
|
|
|
|
|
|
|
switch (etat.toUpperCase()) {
|
|
|
|
case "PRESENT":
|
|
|
|
etatAffiche =
|
|
|
|
"%etud% a été noté(e) <u><strong>présent(e)</strong></u>";
|
|
|
|
break;
|
|
|
|
case "RETARD":
|
|
|
|
etatAffiche =
|
|
|
|
"%etud% a été noté(e) <u><strong>en retard</strong></u>";
|
|
|
|
break;
|
|
|
|
case "ABSENT":
|
|
|
|
etatAffiche =
|
|
|
|
"%etud% a été noté(e) <u><strong>absent(e)</strong></u>";
|
|
|
|
break;
|
|
|
|
case "REMOVE":
|
|
|
|
etatAffiche = "L'assiduité de %etud% a été retirée.";
|
|
|
|
}
|
|
|
|
|
|
|
|
let color;
|
|
|
|
|
|
|
|
switch (etat.toUpperCase()) {
|
|
|
|
case "PRESENT":
|
|
|
|
color = "#6bdb83";
|
|
|
|
break;
|
|
|
|
case "ABSENT":
|
|
|
|
color = "#F1A69C";
|
|
|
|
break;
|
|
|
|
case "RETARD":
|
|
|
|
color = "#f0c865";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
color = "#AAA";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
const nom_prenom = document.querySelector(`.tr[etudid="${etudid}"] .td span`).textContent
|
2023-06-13 16:25:45 +02:00
|
|
|
const span = document.createElement("span");
|
|
|
|
span.innerHTML = etatAffiche.replace("%etud%", nom_prenom);
|
|
|
|
|
|
|
|
pushToast(generateToast(span, color, 5));
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
function updateEtudAssiduite(rbtn) {
|
|
|
|
const [_, colid, etudid] = rbtn.name.split("_");
|
|
|
|
|
|
|
|
const etat = rbtn.value;
|
|
|
|
|
|
|
|
const etudLine = rbtn.parentElement.parentElement;
|
|
|
|
|
|
|
|
const assi = etudLine.getAttribute('assiduite_id');
|
|
|
|
|
|
|
|
const { moduleimpl, deb, fin } = getAssiduitesCol(colid, false);
|
|
|
|
|
|
|
|
switch (assi) {
|
|
|
|
case "-1":
|
|
|
|
// création d'une nouvelle assiduité
|
|
|
|
const assiduite = _createAssiduites(deb, fin, moduleimpl, etudid, etat, colid);
|
2023-06-14 17:53:19 +02:00
|
|
|
rbtn.parentElement.setAttribute('etat', etat);
|
2023-06-12 17:54:30 +02:00
|
|
|
asyncCreateAssiduite(assiduite, (data) => {
|
|
|
|
if (Object.keys(data.success).length > 0) {
|
|
|
|
const assi_id = data.success['0'].assiduite_id;
|
|
|
|
etudLine.setAttribute('assiduite_id', assi_id);
|
|
|
|
assiduite["assiduite_id"] = assi_id;
|
|
|
|
assiduites[etudid].push(assiduite);
|
|
|
|
updateAllCol()
|
2023-06-13 16:25:45 +02:00
|
|
|
launchToast(etudid, etat);
|
2023-06-12 17:54:30 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
break;
|
|
|
|
case "conflit":
|
|
|
|
// Conflit, afficher résolveur
|
|
|
|
const assiduitesList = assiduites[etudid];
|
|
|
|
const d_debut = new moment.tz(deb, TIMEZONE);
|
|
|
|
const d_fin = new moment.tz(fin, TIMEZONE);
|
|
|
|
|
|
|
|
const period = {
|
|
|
|
deb: deb,
|
|
|
|
fin: fin,
|
|
|
|
}
|
|
|
|
const conflitResolver = new ConflitResolver(
|
|
|
|
assiduites[etudid],
|
|
|
|
period,
|
|
|
|
{
|
|
|
|
deb: new moment.tz(d_debut.startOf('day'), TIMEZONE),
|
|
|
|
fin: new moment.tz(d_fin.endOf('day'), TIMEZONE),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
const update = () => {
|
|
|
|
assiduites = {}
|
|
|
|
const cols = [...document.querySelectorAll("[col]")].map((col) => col.getAttribute('col'));
|
|
|
|
|
|
|
|
cols.forEach((c) => getAndUpdateCol(c));
|
|
|
|
|
|
|
|
};
|
|
|
|
conflitResolver.callbacks = {
|
|
|
|
delete: update,
|
|
|
|
edit: update,
|
|
|
|
split: update,
|
|
|
|
};
|
|
|
|
|
|
|
|
conflitResolver.open();
|
2023-06-14 17:53:19 +02:00
|
|
|
rbtn.checked = false;
|
2023-06-12 17:54:30 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Une assiduité est déjà connue -> modifier assiduité
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
if (etat == rbtn.parentElement.getAttribute('etat')) {
|
|
|
|
asyncRemoveAssiduite(assi, () => {
|
|
|
|
etudLine.setAttribute('assiduite_id', "-1");
|
|
|
|
rbtn.parentElement.setAttribute('etat', "")
|
|
|
|
assiduites[etudid] = assiduites[etudid].filter((el) => { return el.assiduite_id != assi })
|
|
|
|
updateAllCol()
|
|
|
|
launchToast(etudid, "remove");
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
const edit = {
|
|
|
|
moduleimpl_id: moduleimpl == "" ? null : moduleimpl,
|
|
|
|
etat: etat,
|
|
|
|
assiduite_id: Number.parseInt(assi),
|
|
|
|
}
|
2023-06-13 16:25:45 +02:00
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
asyncEditAssiduite(edit, (data) => {
|
2023-07-04 15:04:58 +02:00
|
|
|
const obj = structuredClone(getAssiduite(etudid, assi)[0])
|
2023-06-14 17:53:19 +02:00
|
|
|
obj.moduleimpl = edit.moduleimpl_id;
|
|
|
|
obj.etat = edit.etat;
|
2023-07-04 15:04:58 +02:00
|
|
|
replaceAssiduite(etudid, assi, obj)
|
2023-06-14 17:53:19 +02:00
|
|
|
launchToast(etudid, etat);
|
2023-07-04 15:04:58 +02:00
|
|
|
updateAllCol()
|
2023-06-14 17:53:19 +02:00
|
|
|
})
|
|
|
|
}
|
2023-06-12 17:54:30 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-13 16:25:45 +02:00
|
|
|
function getAssiduite(etudid, id) {
|
|
|
|
return assiduites[etudid].filter((a) => a.assiduite_id == id)
|
|
|
|
}
|
|
|
|
|
2023-07-04 15:04:58 +02:00
|
|
|
function replaceAssiduite(etudid, id, obj) {
|
|
|
|
assiduites[etudid] = assiduites[etudid].filter((a) => a.assiduite_id != id);
|
|
|
|
assiduites[etudid].push(obj)
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
function asyncCreateAssiduite(assi, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduite/${assi.etudid}/create`;
|
|
|
|
async_post(
|
|
|
|
path,
|
|
|
|
[assi],
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
//error
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
function asyncRemoveAssiduite(assi, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduite/delete`;
|
|
|
|
async_post(
|
|
|
|
path,
|
|
|
|
[assi],
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
//error
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
function asyncEditAssiduite(assi, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduite/${assi.assiduite_id}/edit`;
|
|
|
|
async_post(
|
|
|
|
path,
|
|
|
|
assi,
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
//error
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-14 17:53:19 +02:00
|
|
|
function asyncCreateAssiduiteGroup(assis, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduites/create`;
|
|
|
|
return async_post(
|
|
|
|
path,
|
|
|
|
assis,
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
//error
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function asyncEditAssiduiteGroup(assis, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduites/edit`;
|
|
|
|
return async_post(
|
|
|
|
path,
|
|
|
|
assis,
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function asyncDeleteAssiduiteGroup(assis, callback = () => { }) {
|
|
|
|
const path = getUrl() + `/api/assiduite/delete`;
|
|
|
|
async_post(
|
|
|
|
path,
|
|
|
|
assis,
|
|
|
|
callback,
|
|
|
|
(data, status) => {
|
|
|
|
//error
|
|
|
|
console.error(data, status);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function massCol(rbtn) {
|
|
|
|
const colid = rbtn.name.replace("mass_action_", "");
|
|
|
|
const col = document.querySelector(`[col="${colid}"]`);
|
|
|
|
const etat = rbtn.value;
|
|
|
|
|
|
|
|
const { moduleimpl, deb, fin } = getAssiduitesCol(colid, false);
|
|
|
|
|
|
|
|
const lines = [...document.querySelectorAll(`[assiduite_id][colid='${colid}']`)].filter((el) => {
|
|
|
|
return el.getAttribute('assiduite_id') != "conflit";
|
|
|
|
})
|
|
|
|
|
|
|
|
const toCreate = lines.filter((el) => { return el.getAttribute('assiduite_id') == '-1' })
|
|
|
|
const toEdit = lines.filter((el) => { return el.getAttribute('assiduite_id') != '-1' })
|
|
|
|
|
|
|
|
if (etat == "remove") {
|
|
|
|
const removeList = []
|
|
|
|
toEdit.forEach((el) => {
|
|
|
|
removeList.push(Number.parseInt(el.getAttribute('assiduite_id')))
|
|
|
|
})
|
|
|
|
|
|
|
|
asyncDeleteAssiduiteGroup(removeList, () => {
|
|
|
|
const span = document.createElement("span");
|
|
|
|
if (removeList.length > 0) {
|
|
|
|
span.innerHTML = `${removeList.length} assiduités ont été supprimées.`;
|
|
|
|
} else {
|
|
|
|
span.innerHTML = `Aucune assiduité n'a été supprimée.`;
|
|
|
|
}
|
|
|
|
|
|
|
|
toEdit.forEach((el) => {
|
|
|
|
const assi_id = Number.parseInt(el.getAttribute("assiduite_id"));
|
|
|
|
const etudid = Number.parseInt(el.parentElement.getAttribute('etudid'));
|
|
|
|
assiduites[etudid] = assiduites[etudid].filter((a) => { return a.assiduite_id != assi_id });
|
|
|
|
})
|
|
|
|
|
|
|
|
updateAllCol()
|
|
|
|
|
|
|
|
pushToast(generateToast(span, getToastColorFromEtat("remove"), 5));
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
|
|
|
|
const createList = []
|
|
|
|
const editList = []
|
|
|
|
|
|
|
|
toCreate.forEach((el) => {
|
|
|
|
const etudid = Number.parseInt(el.parentElement.getAttribute('etudid'));
|
|
|
|
createList.push(_createAssiduites(deb, fin, moduleimpl, etudid, etat, colid))
|
|
|
|
})
|
|
|
|
toEdit.forEach((el) => {
|
|
|
|
const edit = {
|
|
|
|
moduleimpl_id: moduleimpl == "" ? null : moduleimpl,
|
|
|
|
etat: etat,
|
|
|
|
assiduite_id: Number.parseInt(el.getAttribute('assiduite_id')),
|
|
|
|
etudid: Number.parseInt(el.parentElement.getAttribute('etudid')),
|
|
|
|
}
|
|
|
|
|
|
|
|
editList.push(edit);
|
|
|
|
})
|
|
|
|
|
|
|
|
$.when(
|
|
|
|
asyncCreateAssiduiteGroup(createList, (data) => {
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
asyncEditAssiduiteGroup(editList, (data) => {
|
|
|
|
|
|
|
|
})
|
|
|
|
).done((c, e) => {
|
|
|
|
Object.keys(c[0].success).forEach((k) => {
|
|
|
|
const assiduite = createList[Number.parseInt(k)];
|
|
|
|
assiduite["assiduite_id"] = c[0].success[k].assiduite_id;
|
|
|
|
assiduites[assiduite.etudid].push(assiduite);
|
|
|
|
})
|
|
|
|
Object.keys(e[0].success).forEach((k) => {
|
|
|
|
const { etudid, assiduite_id, moduleimpl_id, etat } = editList[Number.parseInt(k)]
|
|
|
|
assiduites[etudid].map((a) => {
|
|
|
|
if (a.assiduite_id == assiduite_id) {
|
|
|
|
a.moduleimpl_id = moduleimpl_id
|
|
|
|
a.etat = etat.toUpperCase();
|
|
|
|
} else {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
updateAllCol();
|
|
|
|
|
|
|
|
|
|
|
|
const count = toCreate.length + toEdit.length;
|
|
|
|
const etatAffiche =
|
|
|
|
etat.toUpperCase() == "RETARD"
|
|
|
|
? "En retard"
|
|
|
|
: etat;
|
|
|
|
const span = document.createElement("span");
|
|
|
|
if (count > 0) {
|
|
|
|
span.innerHTML = `${count} étudiants ont été mis <u><strong>${etatAffiche
|
|
|
|
.capitalize()
|
|
|
|
.trim()}</strong></u>`;
|
|
|
|
} else {
|
|
|
|
span.innerHTML = `Aucun étudiant n'a été mis <u><strong>${etatAffiche
|
|
|
|
.capitalize()
|
|
|
|
.trim()}</strong></u>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
pushToast(
|
|
|
|
generateToast(
|
|
|
|
span,
|
|
|
|
getToastColorFromEtat(etat.toUpperCase()),
|
|
|
|
5
|
|
|
|
)
|
|
|
|
);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
rbtn.checked = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function editModuleImpl(select) {
|
|
|
|
const col = select.parentElement.parentElement
|
|
|
|
const colid = col.getAttribute('col')
|
|
|
|
const value = select.value;
|
|
|
|
|
|
|
|
const lines = [...document.querySelectorAll(`[assiduite_id][colid='${colid}']`)].filter((el) => {
|
|
|
|
return el.getAttribute('assiduite_id') != "conflit";
|
|
|
|
})
|
|
|
|
|
|
|
|
const toEdit = lines.filter((el) => { return el.getAttribute('assiduite_id') != '-1' })
|
|
|
|
|
|
|
|
let editList = toEdit.map((e) => {
|
|
|
|
return {
|
|
|
|
assiduite_id: Number.parseInt(e.getAttribute('assiduite_id')),
|
|
|
|
etudid: Number.parseInt(e.parentElement.getAttribute('etudid')),
|
|
|
|
moduleimpl_id: value,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
asyncEditAssiduiteGroup(editList, (data) => {
|
|
|
|
Object.keys(data.success).forEach((k) => {
|
|
|
|
const { etudid, assiduite_id, moduleimpl_id } = editList[Number.parseInt(k)]
|
|
|
|
assiduites[etudid].map((a) => {
|
|
|
|
if (a.assiduite_id == assiduite_id) {
|
|
|
|
a.moduleimpl_id = moduleimpl_id
|
|
|
|
} else {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
if (Object.keys(data.success).length > 0) {
|
|
|
|
const span = document.createElement("span");
|
|
|
|
span.innerHTML = `Le module a bien été changé.`;
|
|
|
|
|
|
|
|
pushToast(generateToast(span, getToastColorFromEtat("remove"), 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:54:30 +02:00
|
|
|
window.addEventListener('load', () => {
|
|
|
|
document.getElementById("addColumn").addEventListener("click", () => {
|
|
|
|
createColumn();
|
|
|
|
});
|
|
|
|
|
|
|
|
createColumn();
|
|
|
|
setEtuds();
|
2023-06-14 17:53:19 +02:00
|
|
|
|
|
|
|
document.querySelectorAll('.pdp-hover').forEach((el) => {
|
|
|
|
el.src = getUrl() + `/api/etudiant/etudid/${el.getAttribute('etudid')}/photo?size=small`;
|
|
|
|
})
|
2023-06-12 17:54:30 +02:00
|
|
|
}, { once: true });
|
|
|
|
|
|
|
|
</script>
|