Assiduité : signal_assiduites_hebdo : v2 sans mobile

This commit is contained in:
Iziram 2024-05-29 15:59:19 +02:00
parent fac36fa11c
commit f4f6c13d79
3 changed files with 197 additions and 32 deletions

View File

@ -68,7 +68,13 @@ async function async_post(path, data, success, errors) {
const responseData = await response.json();
success(responseData);
} else {
throw new Error("Network response was not ok.");
if (response.status == 404) {
response.json().then((data) => {
if (errors) errors(data);
});
} else {
throw new Error("Network response was not ok.");
}
}
} catch (error) {
console.error(error);
@ -615,7 +621,10 @@ function erreurModuleImpl(message) {
openAlertModal("Sélection du module", content);
}
if (message == "L'étudiant n'est pas inscrit au module") {
if (
message == "L'étudiant n'est pas inscrit au module" ||
message == "param 'moduleimpl_id': etud non inscrit"
) {
const HTML = `
<p>Attention, l'étudiant n'est pas inscrit à ce module.</p>
<p>Si c'est une erreur, veuillez voir avec le ou les responsables de votre scodoc.</p>
@ -822,7 +831,7 @@ function dateCouranteEstTravaillee() {
const nouvelleDate = retourJourTravail(date);
$("#date").datepicker("setDate", nouvelleDate);
let msg = "Le jour sélectionné";
if ((new Date()).format("YYYY-MM-DD") == date.format("YYYY-MM-DD")) {
if (new Date().format("YYYY-MM-DD") == date.format("YYYY-MM-DD")) {
msg = "Aujourd'hui";
}
const att = document.createTextNode(

View File

@ -108,6 +108,11 @@
background-color: var(--color-conflit);
}
.conflit_calendar{
font-size: 1.5em;
cursor: pointer;
}
</style>
@ -185,6 +190,9 @@
<script>
const readonly = "{{readonly | safe}}" == "True";
const non_present = "{{non_present | safe}}" == "True";
const etuds = [
{% for etud in etudiants %}
{
@ -231,6 +239,8 @@
date_fin: fin.toFakeIso(),
}
let cancelEvent = false;
if (assiduite_id != "") {
if (same) {
// Suppression
@ -243,13 +253,13 @@
td.setAttribute("assiduite_id", "");
} else {
console.error(data.errors["0"].message);
cancelEvent = true;
erreurModuleImpl(data.errors["0"].message);
}
},
(error) => {
console.error("Erreur lors de la suppression de l'assiduité", error);
cancelEvent = true;
}
);
} else {
@ -262,6 +272,8 @@
},
(error) => {
console.error("Erreur lors de la modification de l'assiduité", error);
cancelEvent = true;
erreurModuleImpl(error.message);
}
);
}
@ -278,7 +290,7 @@
} else {
console.error(data.errors["0"].message);
erreurModuleImpl(data.errors["0"].message);
cancelEvent = true;
}
},
(error) => {
@ -288,6 +300,8 @@
);
}
return cancelEvent;
}
async function recupAssiduitesHebdo(callback) {
@ -324,6 +338,14 @@
function updateTable(assiduites) {
const img_conflit = `
<a
class="conflit_calendar"
title="Des assiduités existent déjà pour cette période. Cliquez ici pour voir le calendrier de l'assiduité de l'étudiant"
data-tooltip
target="_blank"
>📅</a>`
// Suppression existant
document.querySelectorAll("td.btns").forEach((el) => {
el.remove();
@ -395,14 +417,17 @@
// Peuplement des boutons en fonction des assiduités
boutons = `
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn present" value="present">
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn retard" value="retard">
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn absent" value="absent">
`
let boutons = `
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn retard" value="retard">
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn absent" value="absent">
`
if (!non_present) {
boutons = `<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
class="rbtn present" value="present">`+boutons;
}
// matin
tdMatin.innerHTML = boutons
@ -417,13 +442,15 @@
if (deb.isSame(morningPeriod.deb, "minutes") && fin.isSame(morningPeriod.fin, "minutes")) {
let etat = assi.etat.toLowerCase();
tdMatin.querySelector(`[value="${etat}"]`).checked = true;
const input = tdMatin.querySelector(`[value="${etat}"]`)
if (input) {
input.checked = true;
}
tdMatin.setAttribute("assiduite_id", assi.assiduite_id);
} else {
tdMatin.innerHTML = ""
tdMatin.innerHTML = img_conflit;
tdMatin.querySelector(".conflit_calendar").href = `calendrier_assi_etud?etudid=${etudid}`;
tdMatin.classList.add("conflit");
tdMatin.title = "Des assiduités existent déjà pour cette période"
tdMatin.setAttribute("data-tooltip", "");
}
}
@ -440,13 +467,15 @@
if (deb.isSame(afternoonPeriod.deb, "minutes") && fin.isSame(afternoonPeriod.fin, "minutes")) {
let etat = assi.etat.toLowerCase();
tdApresmidi.querySelector(`[value="${etat}"]`).checked = true;
const input = tdApresmidi.querySelector(`[value="${etat}"]`)
if (input) {
input.checked = true;
}
tdApresmidi.setAttribute("assiduite_id", assi.assiduite_id);
} else {
tdApresmidi.innerHTML = ""
tdApresmidi.innerHTML = img_conflit;
tdApresmidi.querySelector(".conflit_calendar").href = `calendrier_assi_etud?etudid=${etudid}`;
tdApresmidi.classList.add("conflit");
tdApresmidi.title = "Des assiduités existent déjà pour cette période"
tdApresmidi.setAttribute("data-tooltip", "");
}
}
@ -454,16 +483,29 @@
}
document.querySelectorAll("td .rbtn").forEach((el) => {
el.addEventListener("click", (e) => {
el.addEventListener("click", async (e) => {
if (readonly) {
e.preventDefault();
return;
}
let target = e.target;
let parent = target.parentElement;
let isCancelled = await actionButton(target, !target.checked);
if (isCancelled) {
e.preventDefault();
target.checked = !target.checked;
return;
}
let inputs = parent.querySelectorAll(".rbtn");
inputs.forEach((input) => {
if (input != target) {
input.checked = false;
}
});
actionButton(target, !target.checked);
});
});
@ -507,10 +549,109 @@
function allPresent(day, time) {
// Version naive : coche tous les boutons de la colonne
// TODO - Optimiser avec une seule requête API
let inputs = document.querySelectorAll(`td[day="${day}"][time="${time}"] .rbtn[value="present"]`);
inputs.forEach((input) => {
input.click();
let tds = document.querySelectorAll(`td[day="${day}"][time="${time}"]`);
const real_time = time == "am" ? "matin" : "apresmidi";
const assi = {
etat: "present",
moduleimpl_id: document.getElementById("moduleimpl_select").value,
date_debut: new Date(days[day].date.format('YYYY-MM-DD') + "T" + temps[real_time].debut).toFakeIso(),
date_fin: new Date(days[day].date.format('YYYY-MM-DD') + "T" + temps[real_time].fin).toFakeIso(),
}
let toCreate = []; // [{etudid:<int>}]
let toEdit = [];// [{etudid:<int>, assiduite_id:<int>}]
tds.forEach((td) => {
// on ne touche pas aux conflits
if (td.classList.contains("conflit")) {
return;
}
const tr = td.parentElement;
const etudid = Number(tr.getAttribute("etudid"));
const assiduite_id = td.getAttribute("assiduite_id");
if (assiduite_id == "") {
toCreate.push({ etudid: etudid });
} else {
toEdit.push({ etudid: etudid, assiduite_id: Number(assiduite_id) });
}
})
// Création
toCreate = toCreate.map((el) => {
return {
...assi,
etudid: el.etudid,
}
});
// Modification
toEdit = toEdit.map((el) => {
return {
...assi,
etudid: el.etudid,
assiduite_id: el.assiduite_id,
}
});
// Appel API
let counts = {
create: toCreate.length,
edit: toEdit.length
}
const promiseCreate = async_post(
`../../api/assiduites/create`,
toCreate,
async (data) => {
if (data.errors.length > 0) {
console.error(data.errors);
data.errors.forEach((err) => {
let obj = toCreate[err.indice];
let etu = etuds.find((el) => el.id == obj.etudid);
const text = document.createTextNode(`Erreur pour ${etu.nom} ${etu.prenom} : ${err.message}`);
const toast = generateToast(text, "var(--color-error)", 10);
pushToast(toast);
});
}
counts.create = data.success.length;
},
(error) => {
console.error("Erreur lors de la création de l'assiduité", error);
}
);
const promiseEdit = async_post(
`../../api/assiduites/edit`,
toEdit,
async (data) => {
if (data.errors.length > 0) {
console.error(data.errors);
data.errors.forEach((err) => {
let obj = toEdit[err.indice];
let etu = etuds.find((el) => el.id == obj.etudid);
const text = document.createTextNode(`Erreur pour ${etu.nom} ${etu.prenom} : ${err.message}`);
const toast = generateToast(text, "var(--color-error)");
pushToast(toast);
});
}
counts.edit = data.success.length;
},
(error) => {
console.error("Erreur lors de l'édition de l'assiduité", error);
}
);
// Affiche un loader
afficheLoader();
Promise.all([promiseCreate, promiseEdit]).then(async () => {
retirerLoader();
await recupAssiduitesHebdo(updateTable);
envoiToastTous("present", counts.create + counts.edit);
});
}
</script>
@ -654,6 +795,15 @@ document.addEventListener("DOMContentLoaded", ()=>{
Le matin <a href="#" id="text-matin" title="Cliquer pour modifier les horaires">9h à 12h</a> et l'après-midi de <a href="#" id="text-apresmidi" title="Cliquer pour modifier les horaires">13h à 17h</a>
</h3>
{% if readonly %}
<h4
title="Vous n'avez pas les permissions nécessaires afin de modifier les assiduités"
data-tooltip
>
Ouvert en mode <span class="rouge">lecture seule</span>.
</h4>
{% endif %}
<table id="table">
<thead>
<tr class="premier">
@ -676,8 +826,9 @@ document.addEventListener("DOMContentLoaded", ()=>{
{% endif %}
{% endfor %}
</tr>
{% if not readonly and not non_present %}
<tr>
{# Ne pas afficher si preference "non presences" #}
{# Ne pas afficher si preference "non presences" / "readonly" #}
<th></th>
{% for jour in hebdo_jours %}
{% if not jour[0] or jour[1][0] not in ['Samedi', 'Dimanche'] %}
@ -689,13 +840,13 @@ document.addEventListener("DOMContentLoaded", ()=>{
</th>
{% endif %}
{% endfor %}
</tr>
{% endif %}
</thead>
<tbody>
{% for etud in etudiants %}
<tr etudid="{{etud.etudid}}" id="row-{{etud.etudid}}">
<td class="etudinfo" id="etud-{{etud.etudid}}">{{ etud.nomprenom }}</td>
<td class="etudinfo" id="etud-{{etud.etudid}}">{{ etud.nom_prenom() }}</td>
{# Sera rempli en JS #}
{# Ne pas afficher bouton présent si pref "non présences" #}
{# <td>

View File

@ -2070,8 +2070,6 @@ def signal_assiduites_hebdo():
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
)
# TODO vérif perm AbsChange -> readonly
# Gestion des jours
jours: dict[str, list[str]] = {
"lun": [
@ -2113,12 +2111,19 @@ def signal_assiduites_hebdo():
return render_template(
"assiduites/pages/signal_assiduites_hebdo.j2",
title="Assiduité: saisie hebdomadaire",
gr=gr_tit,
etudiants=etudiants,
moduleimpl_select=_module_selector(
formsemestre=formsemestre, moduleimpl_id=moduleimpl_id
),
hebdo_jours=hebdo_jours,
readonly=not current_user.has_permission(Permission.AbsChange),
non_present=sco_preferences.get_preference(
"non_present",
formsemestre_id=formsemestre_id,
dept_id=g.scodoc_dept_id,
),
)