Compare commits

...

9 Commits

2 changed files with 272 additions and 35 deletions

View File

@ -870,10 +870,19 @@ function setupAssiduiteBubble(el, assiduite) {
modifs.target = "_blank"; modifs.target = "_blank";
modifs.href = `tableau_assiduite_actions?type=assiduite&action=modifier&obj_id=${assiduite.assiduite_id}`; modifs.href = `tableau_assiduite_actions?type=assiduite&action=modifier&obj_id=${assiduite.assiduite_id}`;
// Ajout d'un lien pour supprimer l'assiduité
const supprs = document.createElement("a");
supprs.className = "";
supprs.textContent = ``;
supprs.title = "Cliquez pour supprimer l'assiduité";
supprs.target = "_blank";
supprs.href = `tableau_assiduite_actions?type=assiduite&action=supprimer&obj_id=${assiduite.assiduite_id}`;
const actionsDiv = document.createElement("div"); const actionsDiv = document.createElement("div");
actionsDiv.className = "assiduite-actions"; actionsDiv.className = "assiduite-actions";
actionsDiv.appendChild(modifs); actionsDiv.appendChild(modifs);
actionsDiv.appendChild(infos); actionsDiv.appendChild(infos);
actionsDiv.appendChild(supprs);
bubble.appendChild(actionsDiv); bubble.appendChild(actionsDiv);
const idDiv = document.createElement("div"); const idDiv = document.createElement("div");

View File

@ -132,8 +132,248 @@
<script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script> <script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script>
{% include "sco_timepicker.j2" %} {% include "sco_timepicker.j2" %}
<script>
{# Gestion de l'historique #}
<script>
/**
* Enregistre une action dans l'historique
* @param {string} action - L'action à enregistrer
* @param {Object} data - Les données associées à l'action
*/
function recordAction(action, data) {
historyStack.push({ action: action, data: data });
}
/**
* Permet de revenir en arrière sur la dernière action
*/
async function revertAction() {
const lastAction = historyStack.pop();
if (lastAction) {
switch (lastAction.action) {
case "create":
await deleteAssiduite(lastAction.data, false);
break;
case "delete":
await createAssiduite(lastAction.data, false);
break;
}
// On met à jour l'affichage
updateCell(
lastAction.data.periodId,
lastAction.data.etudid,
lastAction.data.assiduite_id
);
}
}
/**
* Permet de vider l'historique
*/
function clearHistory() {
historyStack = [];
}
window.addEventListener('keyup', (event) => {
if (event.ctrlKey && event.key === 'z') {
revertAction();
}
});
let historyStack = [];
</script>
{# Gestion des actions d'assiduité #}
<script>
/**
* Permet de générer un nouvel objet assiduité à partir du plageId, etudid et etat
* @param {int} plageId - L'id de la plage
* @param {int} etudid - L'id de l'étudiant
* @param {string} etat - L'état de l'assiduité (present, retard, absent)
*/
function generateNewAssiduite(plageId, etudid, etat) {
const periode = periodes.get(plageId);
if (!periode) return;
return {
etudid: etudid,
etat: etat,
date_debut: periode.date_debut.toFakeIso(),
date_fin: periode.date_fin.toFakeIso(),
moduleimpl_id: periode.moduleimpl_id,
periodId: plageId,
};
}
/**
* Permet de récuppérer une assiduité à partir de son assiduite_id
* @param {int} assiduite_id
* Retourne une promise
*/
async function getAssiduite(assiduite_id) {
return await fetch(`../../api/assiduite/${assiduite_id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (!res.ok) {
throw new Error("Network response was not ok");
}
return res.json();
})
.then((data) => {
return data;
})
}
/**
* Crée une nouvelle assiduité.
*
* @param {Object} newAssiduite - Les informations de la nouvelle assiduité.
* @param {boolean} [record=true] - Indique si l'action doit être enregistrée.
* @returns {Promise<void>} - Une promesse qui se résout lorsque la création de l'assiduité est terminée.
*/
async function createAssiduite(newAssiduite, record = true) {
if (!newAssiduite) return;
await async_post(
`../../api/assiduite/${newAssiduite.etudid}/create`,
[newAssiduite],
(data) => {
if (data.success.length > 0) {
newAssiduite.assiduite_id = data.success[0].message.assiduite_id;
// On enregistre l'action si elle est réussie
if (record) recordAction("create", newAssiduite);
} else {
console.error(data.errors["0"].message);
erreurModuleImpl(data.errors["0"].message);
}
},
(error) => {
console.error("Erreur lors de la création de l'assiduité", error);
}
);
}
/**
* Supprime une assiduité.
*
* @param {Object} assiduite - L'assiduité à supprimer.
* @param {boolean} [record=true] - Indique si l'action de suppression doit être enregistrée.
* @returns {Promise<void>} - Une promesse qui se résout lorsque la suppression est terminée.
*/
async function deleteAssiduite(assiduite, record = true) {
if (!assiduite) return;
await async_post(
`../../api/assiduite/delete`,
[assiduite.assiduite_id],
(data) => {
if (data.success.length > 0) {
// On enregistre l'action si elle est réussie
assiduite.assiduite_id = null;
if (record) recordAction("delete", assiduite);
} else {
console.error(data.errors["0"].message);
erreurModuleImpl(data.errors["0"].message);
}
},
(error) => {
console.error("Erreur lors de la suppression de l'assiduité", error);
}
);
}
/**
* Crée une action d'assiduité.
*
* @param {string} plageId - L'identifiant de la plage horaire.
* @param {string} etudid - L'identifiant de l'étudiant.
* @param {string} etat - L'état de l'assiduité.
* @returns {Promise<void>} Une promesse qui se résout lorsque l'assiduité est créée avec succès.
*/
async function createAssiduiteAction(plageId, etudid, etat) {
// création de l'assiduité
const newAssiduite = generateNewAssiduite(plageId, etudid, etat);
await createAssiduite(newAssiduite);
updateCell(plageId, etudid, newAssiduite.assiduite_id);
}
/**
* Met à jour une cellule du tableau
*
* @param {string} plageId - L'identifiant de la plage horaire.
* @param {string} etudid - L'identifiant de l'étudiant.
* @param {string|null} assiduite_id - L'identifiant de l'assiduité (facultatif).
*/
function updateCell(plageId, etudid, assiduite_id = null) {
// Vérifie si la plage horaire existe
if (!periodes.has(plageId)) return;
// Mise à jour de la cellule
const cell = document.querySelector(`#cell-${etudid}-${plageId}`);
if (!cell) return;
cell.innerHTML = "";
if (assiduite_id) {
// Récupère les informations de l'assiduité
getAssiduite(assiduite_id).then((data) => {
setupAssiduiteBubble(cell, data);
const suppr = cell.querySelector('.assiduite-actions a:last-child')
suppr?.addEventListener('click', (e) => {
e.preventDefault();
data.periodId = plageId;
deleteAssiduite(data);
updateCell(plageId, etudid);
});
}).catch(()=>{
setupButtons(cell, etudid, plageId);
});
} else {
setupButtons(cell, etudid, plageId);
}
}
function setupButtons(cell, etudid, plageId){
const etats = ["retard", "absent"];
const assi_btns = document.createElement("div");
assi_btns.classList.add("assi-btns");
if (!window.nonPresent) {
etats.splice(0, 0, "present");
}
etats.forEach((value) => {
const cbox = document.createElement("input");
cbox.type = "checkbox";
cbox.value = value;
cbox.name = `rbtn_${etudid}_${plageId}`;
cbox.classList.add("rbtn", value);
// Événement pour s'assurer qu'un seul bouton est coché à la fois
cbox.addEventListener("click", (event) => {
const parent = event.target.parentElement;
parent.querySelectorAll(".rbtn").forEach((ele) => {
if (ele.value != value) {
ele.checked = false;
}
});
createAssiduiteAction(plageId, etudid, value);
});
assi_btns.appendChild(cbox);
});
cell.appendChild(assi_btns);
}
</script>
{# Gestion des plages et MAIN #}
<script>
/** /**
* Permet d'ajouter une nouvelle période au tableau * Permet d'ajouter une nouvelle période au tableau
* Par défaut la période est générèe avec les valeurs des inputs * Par défaut la période est générèe avec les valeurs des inputs
@ -307,41 +547,18 @@ async function nouvellePeriode(period = null) {
const assiduites = data[etudid]; const assiduites = data[etudid];
// Si l'étudiant n'a pas d'assiduité, on crée les boutons assiduité // Si l'étudiant n'a pas d'assiduité, on crée les boutons assiduité
if (assiduites.length == 0) { if (assiduites.length == 0) {
setupButtons(cell, etudid, periodId);
const assi_btns = document.createElement('div');
assi_btns.classList.add('assi-btns');
const etats = ["retard", "absent"];
if(!window.nonPresent){
etats.splice(0,0,"present");
}
etats.forEach((value) => {
const cbox = document.createElement("input");
cbox.type = "checkbox";
cbox.value = value;
cbox.name = `rbtn_${etudid}_${periodId}`;
cbox.classList.add("rbtn", value);
// Event pour être sur qu'un seul bouton est coché à la fois
cbox.addEventListener("click", (event) => {
const parent = event.target.parentElement;
parent.querySelectorAll(".rbtn").forEach((ele) => {
if (ele.value != value) {
ele.checked = false;
}
});
});
// Si une valeur par défaut est donnée alors on l'applique
cbox.checked = etatDef.value == value;
assi_btns.appendChild(cbox);
});
cell.appendChild(assi_btns);
} else { } else {
// Si une (ou plus) assiduité sont trouvée pour la période // Si une (ou plus) assiduité sont trouvée pour la période
// alors on affiche les informations de la première assiduité // alors on affiche les informations de la première assiduité
setupAssiduiteBubble(cell, assiduites[0]); setupAssiduiteBubble(cell, assiduites[0]);
const suppr = cell.querySelector('.assiduite-actions a:last-child')
suppr?.addEventListener('click', (e) => {
e.preventDefault();
assiduites[0].periodId = periodId;
deleteAssiduite(assiduites[0]);
updateCell(periodId, etudid);
});
} }
} }
}) })
@ -606,6 +823,18 @@ window.addEventListener("load", main);
<button id="add_periode" onclick="nouvellePeriode()">Ajouter une plage</button> <button id="add_periode" onclick="nouvellePeriode()">Ajouter une plage</button>
</div> </div>
<div class="box">
<p>Cette page enregistre automatiquement la saisie effectuée.</p>
<p>La page enregistre un historique des dernières saisies.
<br>En cas d'erreur, appuyez sur le bouton "Annuler"
<br>ou appuyez sur "Ctrl + Z" pour annuler la dernière saisie.
</p>
<p>Vous pouvez également vider l'historique en appuyant sur le bouton "Vider l'historique".</p>
<button onclick="revertAction()">Annuler la dernière saisie</button>
<button onclick="clearHistory()">Vider l'historique</button>
</div>
</div> </div>
<!-- Boutons d'actions <!-- Boutons d'actions
- Sauvegarder - Sauvegarder
@ -614,13 +843,12 @@ window.addEventListener("load", main);
---> --->
<br> <br>
<div id="actions" class="flex"> <div id="actions" class="flex">
<button id="save" onclick="sauvegarderAssiduites()">ENREGISTRER</button>
<label for="pdp"> <label for="pdp">
Photo de profil : Photo de profil :
<input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)"> <input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)">
</label> </label>
<label for="etatDef"> {# <label for="etatDef">
Intialiser les étudiants comme : Intialiser les étudiants comme :
<select name="etatDef" id="etatDef"> <select name="etatDef" id="etatDef">
<option value="">-</option> <option value="">-</option>
@ -630,7 +858,7 @@ window.addEventListener("load", main);
<option value="retard">en retard</option> <option value="retard">en retard</option>
<option value="absent">absents</option> <option value="absent">absents</option>
</select> </select>
</label> </label> #}
</div> </div>