forked from ScoDoc/ScoDoc
Assiduité : signal_assiduites_hebdo : v2 sans mobile
This commit is contained in:
parent
fac36fa11c
commit
f4f6c13d79
@ -68,7 +68,13 @@ async function async_post(path, data, success, errors) {
|
|||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
success(responseData);
|
success(responseData);
|
||||||
} else {
|
} 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) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -615,7 +621,10 @@ function erreurModuleImpl(message) {
|
|||||||
|
|
||||||
openAlertModal("Sélection du module", content);
|
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 = `
|
const HTML = `
|
||||||
<p>Attention, l'étudiant n'est pas inscrit à ce module.</p>
|
<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>
|
<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);
|
const nouvelleDate = retourJourTravail(date);
|
||||||
$("#date").datepicker("setDate", nouvelleDate);
|
$("#date").datepicker("setDate", nouvelleDate);
|
||||||
let msg = "Le jour sélectionné";
|
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";
|
msg = "Aujourd'hui";
|
||||||
}
|
}
|
||||||
const att = document.createTextNode(
|
const att = document.createTextNode(
|
||||||
|
@ -108,6 +108,11 @@
|
|||||||
background-color: var(--color-conflit);
|
background-color: var(--color-conflit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conflit_calendar{
|
||||||
|
font-size: 1.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -185,6 +190,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
const readonly = "{{readonly | safe}}" == "True";
|
||||||
|
const non_present = "{{non_present | safe}}" == "True";
|
||||||
|
|
||||||
const etuds = [
|
const etuds = [
|
||||||
{% for etud in etudiants %}
|
{% for etud in etudiants %}
|
||||||
{
|
{
|
||||||
@ -231,6 +239,8 @@
|
|||||||
date_fin: fin.toFakeIso(),
|
date_fin: fin.toFakeIso(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cancelEvent = false;
|
||||||
|
|
||||||
if (assiduite_id != "") {
|
if (assiduite_id != "") {
|
||||||
if (same) {
|
if (same) {
|
||||||
// Suppression
|
// Suppression
|
||||||
@ -243,13 +253,13 @@
|
|||||||
td.setAttribute("assiduite_id", "");
|
td.setAttribute("assiduite_id", "");
|
||||||
} else {
|
} else {
|
||||||
console.error(data.errors["0"].message);
|
console.error(data.errors["0"].message);
|
||||||
|
cancelEvent = true;
|
||||||
erreurModuleImpl(data.errors["0"].message);
|
erreurModuleImpl(data.errors["0"].message);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error("Erreur lors de la suppression de l'assiduité", error);
|
console.error("Erreur lors de la suppression de l'assiduité", error);
|
||||||
|
cancelEvent = true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -262,6 +272,8 @@
|
|||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error("Erreur lors de la modification de l'assiduité", error);
|
console.error("Erreur lors de la modification de l'assiduité", error);
|
||||||
|
cancelEvent = true;
|
||||||
|
erreurModuleImpl(error.message);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -278,7 +290,7 @@
|
|||||||
} else {
|
} else {
|
||||||
console.error(data.errors["0"].message);
|
console.error(data.errors["0"].message);
|
||||||
erreurModuleImpl(data.errors["0"].message);
|
erreurModuleImpl(data.errors["0"].message);
|
||||||
|
cancelEvent = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
@ -288,6 +300,8 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cancelEvent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function recupAssiduitesHebdo(callback) {
|
async function recupAssiduitesHebdo(callback) {
|
||||||
@ -324,6 +338,14 @@
|
|||||||
|
|
||||||
function updateTable(assiduites) {
|
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
|
// Suppression existant
|
||||||
document.querySelectorAll("td.btns").forEach((el) => {
|
document.querySelectorAll("td.btns").forEach((el) => {
|
||||||
el.remove();
|
el.remove();
|
||||||
@ -395,14 +417,17 @@
|
|||||||
|
|
||||||
|
|
||||||
// Peuplement des boutons en fonction des assiduités
|
// Peuplement des boutons en fonction des assiduités
|
||||||
boutons = `
|
let boutons = `
|
||||||
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
|
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
|
||||||
class="rbtn present" value="present">
|
class="rbtn retard" value="retard">
|
||||||
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
|
<input type="checkbox" name="matin-${etudid}" id="matin-${etudid}"
|
||||||
class="rbtn retard" value="retard">
|
class="rbtn absent" value="absent">
|
||||||
<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
|
// matin
|
||||||
tdMatin.innerHTML = boutons
|
tdMatin.innerHTML = boutons
|
||||||
@ -417,13 +442,15 @@
|
|||||||
|
|
||||||
if (deb.isSame(morningPeriod.deb, "minutes") && fin.isSame(morningPeriod.fin, "minutes")) {
|
if (deb.isSame(morningPeriod.deb, "minutes") && fin.isSame(morningPeriod.fin, "minutes")) {
|
||||||
let etat = assi.etat.toLowerCase();
|
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);
|
tdMatin.setAttribute("assiduite_id", assi.assiduite_id);
|
||||||
} else {
|
} else {
|
||||||
tdMatin.innerHTML = ""
|
tdMatin.innerHTML = img_conflit;
|
||||||
|
tdMatin.querySelector(".conflit_calendar").href = `calendrier_assi_etud?etudid=${etudid}`;
|
||||||
tdMatin.classList.add("conflit");
|
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")) {
|
if (deb.isSame(afternoonPeriod.deb, "minutes") && fin.isSame(afternoonPeriod.fin, "minutes")) {
|
||||||
let etat = assi.etat.toLowerCase();
|
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);
|
tdApresmidi.setAttribute("assiduite_id", assi.assiduite_id);
|
||||||
} else {
|
} else {
|
||||||
tdApresmidi.innerHTML = ""
|
tdApresmidi.innerHTML = img_conflit;
|
||||||
|
tdApresmidi.querySelector(".conflit_calendar").href = `calendrier_assi_etud?etudid=${etudid}`;
|
||||||
tdApresmidi.classList.add("conflit");
|
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) => {
|
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 target = e.target;
|
||||||
let parent = target.parentElement;
|
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");
|
let inputs = parent.querySelectorAll(".rbtn");
|
||||||
inputs.forEach((input) => {
|
inputs.forEach((input) => {
|
||||||
if (input != target) {
|
if (input != target) {
|
||||||
input.checked = false;
|
input.checked = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
actionButton(target, !target.checked);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -507,10 +549,109 @@
|
|||||||
function allPresent(day, time) {
|
function allPresent(day, time) {
|
||||||
// Version naive : coche tous les boutons de la colonne
|
// Version naive : coche tous les boutons de la colonne
|
||||||
// TODO - Optimiser avec une seule requête API
|
// TODO - Optimiser avec une seule requête API
|
||||||
let inputs = document.querySelectorAll(`td[day="${day}"][time="${time}"] .rbtn[value="present"]`);
|
let tds = document.querySelectorAll(`td[day="${day}"][time="${time}"]`);
|
||||||
inputs.forEach((input) => {
|
const real_time = time == "am" ? "matin" : "apresmidi";
|
||||||
input.click();
|
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>
|
</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>
|
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>
|
</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">
|
<table id="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="premier">
|
<tr class="premier">
|
||||||
@ -676,8 +826,9 @@ document.addEventListener("DOMContentLoaded", ()=>{
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if not readonly and not non_present %}
|
||||||
<tr>
|
<tr>
|
||||||
{# Ne pas afficher si preference "non presences" #}
|
{# Ne pas afficher si preference "non presences" / "readonly" #}
|
||||||
<th></th>
|
<th></th>
|
||||||
{% for jour in hebdo_jours %}
|
{% for jour in hebdo_jours %}
|
||||||
{% if not jour[0] or jour[1][0] not in ['Samedi', 'Dimanche'] %}
|
{% if not jour[0] or jour[1][0] not in ['Samedi', 'Dimanche'] %}
|
||||||
@ -689,13 +840,13 @@ document.addEventListener("DOMContentLoaded", ()=>{
|
|||||||
</th>
|
</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for etud in etudiants %}
|
{% for etud in etudiants %}
|
||||||
<tr etudid="{{etud.etudid}}" id="row-{{etud.etudid}}">
|
<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 #}
|
{# Sera rempli en JS #}
|
||||||
{# Ne pas afficher bouton présent si pref "non présences" #}
|
{# Ne pas afficher bouton présent si pref "non présences" #}
|
||||||
{# <td>
|
{# <td>
|
||||||
|
@ -2070,8 +2070,6 @@ def signal_assiduites_hebdo():
|
|||||||
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO vérif perm AbsChange -> readonly
|
|
||||||
|
|
||||||
# Gestion des jours
|
# Gestion des jours
|
||||||
jours: dict[str, list[str]] = {
|
jours: dict[str, list[str]] = {
|
||||||
"lun": [
|
"lun": [
|
||||||
@ -2113,12 +2111,19 @@ def signal_assiduites_hebdo():
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/pages/signal_assiduites_hebdo.j2",
|
"assiduites/pages/signal_assiduites_hebdo.j2",
|
||||||
|
title="Assiduité: saisie hebdomadaire",
|
||||||
gr=gr_tit,
|
gr=gr_tit,
|
||||||
etudiants=etudiants,
|
etudiants=etudiants,
|
||||||
moduleimpl_select=_module_selector(
|
moduleimpl_select=_module_selector(
|
||||||
formsemestre=formsemestre, moduleimpl_id=moduleimpl_id
|
formsemestre=formsemestre, moduleimpl_id=moduleimpl_id
|
||||||
),
|
),
|
||||||
hebdo_jours=hebdo_jours,
|
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,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user