1
0
forked from ScoDoc/ScoDoc
ScoDoc/app/templates/scolar/partition_editor.j2

1070 lines
35 KiB
Django/Jinja

{# -*- mode: jinja-html -*- #}
{% extends "sco_page.j2" %}
{% block title %}
Partitions de {{ formsemestre.titre_annee() }}
{% endblock title %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/partition_editor.css">
{% if not is_edt_configured %}
<style>
span.calendarEdit {
display: none;
}
</style>
{% endif %}
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{scu.STATIC_DIR}}/js/partition_editor.js"></script>
{% endblock %}
{% block app_content %}
<h1>{% if not read_only %}Édition des p{% else %}P{%endif%}artitions</h1>
<h2></h2>
<main>
<div class="wait"></div>
<section id="zonePartitions">
<h2>Filtres</h2>
<div>
<label class="edition">
<input type="checkbox" autocomplete="off" id="inputModif"
{% if edit_partition %}checked{% endif %}>
Modifier les partitions et groupes
</label>
<div class="filtres"></div>
<div style="display: flex; justify-content: space-between;">
<div class="editing ajoutPartition">Ajouter une partition</div>
<label class="editing valider" for="inputModif">Fini</label>
</div>
</div>
</section>
<section id="zoneChoix">
<h2>Étudiants</h2>
<details>
<summary>Outils d'affectation</summary>
<div class="autoAffectation">
<a href="students_groups_auto_assignment?formsemestre_id={{formsemestre.id}}"><svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M13.8 12H3" />
</svg> Aide à l'affectation dans les parcours</a>
<div>Importer les résultats :
<form class=dropZone>
<div>
Déposez le fichier .xlsx ou <br>
<label>
<input type=file accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
</label>
</div>
</form>
</div>
</div>
<div class="autoAffectation">
Affecter automatiquement les étudiants du groupe<br>
<select name="affectationFrom" id="affectationFrom"></select>
vers le groupe
<select name="affectationTo" id="affectationTo"></select>
<div class="affectationGo">Valider</div>
<div class="progress">
<div></div>
</div>
</div>
</details>
<div class="etudiants"></div>
</section>
<section id="zoneGroupes">
<h2>Groupes</h2>
<div class="groupes"></div>
</section>
</main>
{% if not read_only %}
<div class="scobox space-before-24">
<ul>
<li><a class="stdlink" href="{{
url_for('scolar.edit_partition_form',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id
)
}}">Ancienne page édition partitions</a>
</li>
<li>Ancienne page modification groupes:
<ul>
{% for partition in formsemestre.get_partitions_list(with_default=False) %}
<li><a class="stdlink" href="{{
url_for('scolar.affect_groups',
scodoc_dept=g.scodoc_dept, partition_id=partition.id
)
}}">{{ partition.partition_name }}</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
</div>
{% endif %}
<script src="{{scu.STATIC_DIR}}/libjs/xlsx-populate-1.21.0.min.js"></script>
<script>
go();
async function go() {
document.querySelector('.wait').style.display = "";
let params = (new URL(document.location)).searchParams;
let formsemestre_id = params.get('formsemestre_id');
let partitions = await fetchData("/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/" + formsemestre_id + "/partitions");
let etudiants = await fetchData("/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/" + formsemestre_id + "/resultats");
etudiants.sort((a, b) => {
return a.sort_key.localeCompare(b.sort_key)
})
processDatas(partitions, etudiants);
processEvents();
listeGroupesAutoaffectation();
document.querySelector("body").classList.add("loaded");
document.querySelector('.wait').style.display = "none";
{% if edit_partition %}
setEditMode();
{% endif %}
}
function fetchData(request) {
return fetch(request)
.then(r => { return r.json() })
.then(data => {
return data;
}).catch(error => {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors du transfert des données.</h2>";
throw 'Fin du script - données invalides';
})
}
function processDatas(partitions, etudiants) {
/* Filtres et groupes */
let divFiltres = document.querySelector(".filtres");
let outputGroupes = "";
let arrayPartitions = Object.values(partitions).sort((a, b) => {
return a.numero - b.numero;
})
arrayPartitions.forEach((partition) => {
let divPartition = templateFiltres_partition(partition);
divFiltres.append(divPartition);
let arrayGroups = Object.values(partition.groups).sort((a, b) => {
return a.numero - b.numero;
})
arrayGroups.forEach((groupe) => {
let divPlus = divPartition.querySelector(".ajoutGroupe");
divPlus.parentElement.insertBefore(templateFiltres_groupe(groupe), divPlus);
})
// Groupes
outputGroupes += `
<div class=partition data-idpartition="${partition.id}">
<h3>${partition.partition_name}</h3>
<div class=groupe data-idgroupe=aucun>
<div>Non affecté(s)</div>
<div class=etudiants></div>
</div>
${(() => {
let arrayGroups = Object.values(partition.groups).sort((a, b) => {
return a.numero - b.numero;
})
let output = "";
arrayGroups.forEach((groupe) => {
output += templateGroupe_zoneGroupes(groupe.id, groupe.group_name);
})
return output;
})()}
</div>`;
})
let hiden = document.createElement("div");
hiden.className = "hidenDropZone";
divFiltres.append(hiden);
document.querySelector("#zoneGroupes>.groupes").innerHTML = outputGroupes;
/* Etudiants */
output = "";
etudiants.forEach(etudiant => {
output += `
<div data-etudid="${etudiant.etudid}" >
<div class=nom data-etudid="${etudiant.etudid}" data-nom="${etudiant.nom_disp}" data-prenom="${etudiant.prenom}"><a href="fiche_etud?etudid=${etudiant.etudid}">${etudiant.nom_disp} ${etudiant.prenom}</a><div class=small>${etudiant.bac}</div></div>
${(() => {
let output = "<div class=grpPartitions>";
arrayPartitions.forEach((partition) => {
output += `
<div class=partition data-idpartition="${partition.id}">
<div>${partition.partition_name}</div>
${(() => {
let output = "";
let affected = false;
let arrayGroups = Object.values(partition.groups).sort((a, b) => {
return a.numero - b.numero;
})
arrayGroups.forEach((groupe) => {
output += `
<label><input type=radio name="${partition.id}-${etudiant.etudid}" value="${groupe.id}" ${(etudiant.partitions[partition.id] == groupe.id) ? "checked" : ""}><span data-grpname="${groupe.group_name}">${groupe.group_name}</span></label>`;
if (etudiant.partitions[partition.id] == groupe.id) {
affected = true;
document.querySelector(`#zoneGroupes [data-idgroupe="${groupe.id}"]>.etudiants`).innerHTML += templateEtudiant_zoneGroupes(etudiant);
}
})
if (!affected) {
document.querySelector(`#zoneGroupes [data-idpartition="${partition.id}"]>[data-idgroupe="aucun"]>.etudiants`).innerHTML += templateEtudiant_zoneGroupes(etudiant);
}
return `<label title="Aucun groupe"><input type=radio name="${partition.id}-${etudiant.etudid}" value="aucun" ${(!affected) ? "checked" : ""}><span class=aucun> - </span></label>` + output;
})()}
</div>`;
})
return output + "</div>";
})()}
</div>`;
})
document.querySelector("#zoneChoix>.etudiants").innerHTML = output;
}
function templateFiltres_partition(partition) {
let div = document.createElement("div");
div.dataset.idpartition = partition.id;
if (partition.groups_editable == false) {
div.classList.add("nonEditable");
}
div.innerHTML = `
<!-- Partition -->
<h3 data-idpartition="${partition.id}">
<span class="editing move">||</span>
<span>${partition.partition_name}</span>
<span class="editing modif">✏️</span>
<span class="editing suppr">❌</span>
<div class=onoff>Masquer<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0b0b0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg></div>
</h3>
<!-- Groupes -->
<div class=groupes>
<button class="dt-button" data-idgroupe=aucun>
Non affectés
</button>
<div class="editing ajoutGroupe">+</div>
</div>
<!-- Config -->
<div class=config>
Configuration
<hr>
<label title="Calculer et afficher les rangs dans les groupes de cette partition ?">
<input class=rang type=checkbox ${partition.bul_show_rank ? "checked" : ""} data-attr=bul_show_rank> Rang bulletins
</label>
<label title="Doit-on afficher les groupes de cette partition dans les listes ?">
<input class=groupe type=checkbox ${partition.show_in_lists ? "checked" : ""} data-attr=show_in_lists> Afficher sur bulletins et tableaux
</label>
<label>
<a class="stdlink" href="{{url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
}}/groups_auto_repartition/${partition.id}">Répartir les étudiants</a>
</label>
</div>
`;
div.querySelector(".move").addEventListener("mousedown", moveStart);
div.querySelector(".modif").addEventListener("click", editText);
div.querySelector(".suppr").addEventListener("click", suppr);
div.querySelector(".onoff").addEventListener("click", masquerPartitions);
div.querySelector("[data-idgroupe]").addEventListener("click", filtre);
div.querySelector(".ajoutGroupe").addEventListener("click", addGroupe);
div.querySelector(".rang").addEventListener("input", setAttribute);
div.querySelector(".groupe").addEventListener("input", setAttribute);
return div;
}
function templateFiltres_groupe(groupe) {
let div = document.createElement("button");
div.classList.add("dt-button");
div.dataset.idgroupe = groupe.id;
div.dataset.idedt = groupe.edt_id || "";
let title_EDT = `Identifiant EDT: ${groupe.edt_id || groupe.group_name}`;
div.innerHTML = `
<span class="editing move">||</span>
<span>${groupe.group_name}</span>
<span class="editing rename">✏️</span>
<span class="editing calendarEdit" title="${title_EDT}">📅</span>
<span class="editing suppr">❌</span>`;
if (groupe.edt_id) {
div.querySelector(".calendarEdit").classList.add("actif");
}
div.addEventListener("click", filtre);
div.querySelector(".move").addEventListener("mousedown", moveStart);
div.querySelector(".rename").addEventListener("click", editText);
div.querySelector(".calendarEdit").addEventListener("click", editCalendar);
div.querySelector(".suppr").addEventListener("click", suppr);
return div;
}
function templateGroupe_zoneGroupes(idGroupe, name) {
return `<div class=groupe data-idgroupe="${idGroupe}">
<div>${name}</div>
<div class=etudiants></div>
</div>`;
}
function templateEtudiant_zoneGroupes(etudiant) {
return `<div data-etudid="${etudiant.etudid}" data-nom="${etudiant.nom_disp}" data-prenom="${etudiant.prenom}">${etudiant.nom_disp} ${etudiant.prenom}</div>`
}
function listeGroupesAutoaffectation() {
let output = '<option value disabled selected hidden>Choisir</option>';
document.querySelectorAll('#zonePartitions .filtres>[data-idpartition]').forEach(partition => {
output += `
<optgroup label="${partition.children[0].children[1].innerText}">
<option value="non-${partition.dataset.idpartition}">Non affectés ${partition.children[0].children[1].innerText}</option>`;
partition.querySelectorAll('[data-idgroupe]:not([data-idgroupe="aucun"])').forEach(groupe => {
output += `<option value=${groupe.dataset.idgroupe}>${groupe.children[1].innerText}</option>`;
})
output += "</optgroup>";
})
document.querySelector("#affectationFrom").innerHTML = output;
document.querySelector("#affectationTo").innerHTML = output;
}
/******************************/
/* Gestionnaire d'événements */
/******************************/
function setEditMode() {
let editing = document.querySelector("[contentEditable=true]");
if (!editing) {
document.querySelector("body").classList.toggle("editionActivated");
return;
}
this.checked = true;
if (!editing.classList.contains("highlight")) {
editing.classList.add("highlight");
setTimeout(() => { editing.classList.remove("highlight") }, 1000);
}
}
function processEvents() {
/*--------------------*/
/* Edition partitions */
/*--------------------*/
document.querySelector(".edition>input").addEventListener("input", setEditMode);
document.querySelectorAll(".ajoutPartition").forEach(btnPlus => { btnPlus.addEventListener("click", addPartition) })
/*--------------------*/
/* Changement groupe */
/*--------------------*/
document.querySelectorAll("label").forEach(btn => { btn.addEventListener("mousedown", (event) => { event.preventDefault() }) });
document.querySelectorAll(".etudiants input").forEach(input => { input.addEventListener("input", assignment) })
document.querySelector(".affectationGo").addEventListener("click", affectationGo);
}
/**********************/
/* Filtrage */
/**********************/
function masquerPartitions() {
let idPartition = this.closest("[data-idpartition]").dataset.idpartition;
document.querySelectorAll(`[data-idpartition="${idPartition}"]`).forEach(e => {
e.classList.toggle("hide");
})
}
function filtre() {
if (document.querySelector("body").classList.contains("editionActivated")) {
return;
}
let nbUnselected = this.parentElement.querySelectorAll(".unselect").length;
let nbBtn = this.parentElement.children.length;
if (nbUnselected == 0) {
Array.from(this.parentElement.children).forEach(e => {
e.classList.toggle("unselect");
})
}
this.classList.toggle("unselect");
nbUnselected = this.parentElement.querySelectorAll(".unselect").length;
if (nbUnselected == nbBtn) {
Array.from(this.parentElement.children).forEach(e => {
e.classList.toggle("unselect");
})
}
// Groupes
let groupesSelected = {};
document.querySelectorAll(".filtres [data-idgroupe]:not(.unselect)").forEach(e => {
let idpartition = e.closest("[data-idpartition]").dataset.idpartition;
if (!groupesSelected[idpartition]) {
groupesSelected[idpartition] = [];
}
groupesSelected[idpartition].push(e.dataset.idgroupe)
})
document.querySelectorAll("#zoneChoix .etudiants>div").forEach(e => {
let found = true;
Object.entries(groupesSelected).forEach(([idpartition, tabGroupes]) => {
if (!tabGroupes.includes(
e.querySelector(`[data-idpartition="${idpartition}"] input:checked`).value
)
) {
found = false
}
})
if (found) {
e.classList.remove("hide")
} else {
e.classList.add("hide")
}
})
}
/*****************************************/
/* Import des résultats auto affectation */
/*****************************************/
document.querySelector(".dropZone").addEventListener("drop", dropFile);
document.querySelector(".dropZone input").addEventListener("change", dropFile);
document.querySelector(".dropZone").addEventListener("dragover", dragOver);
document.querySelector(".dropZone").addEventListener("dragleave", dragLeave);
function dropFile(event) {
event.preventDefault();
this.classList.remove("fileOver");
if (event.target.files?.[0] || event.dataTransfer.items[0].type.match('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
let file = event.target.files?.[0] || event.dataTransfer.items[0].getAsFile();
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onloadend = () => {
manageFile(reader.result)
}
}
}
function manageFile(file) {
XlsxPopulate.fromDataAsync(file)
.then(function (workbook) {
const sheet = workbook.sheet(0);
let ligne = 1;
let colonne = 1;
let parcours;
let etudid;
while (parcours = sheet.row(ligne).cell(colonne).value()) {
ligne += 2;
while (etudid = sheet.row(ligne).cell(colonne).value()) {
console.log(parcours, etudid);
document.querySelector(`#zoneChoix .etudiants [data-etudid="${etudid}"] [data-grpname="${parcours}"]`)?.click();
ligne++;
}
ligne = 1;
colonne += 3;
}
})
}
function dragOver(event) {
event.preventDefault();
this.classList.add("fileOver")
}
function dragLeave() {
this.classList.remove("fileOver")
}
/****************************/
/* Affectation à un groupe */
/****************************/
var progressNb = 0;
var progressRef = 0;
function affectationGo() {
let from = document.querySelector("#affectationFrom").value;
let to = document.querySelector("#affectationTo").value;
if (!from || !to) {
return;
}
let elements = [];
if (from[0] != "n") {
elements = document.querySelectorAll(`#zoneChoix .etudiants [value="${from}"]:checked`)
} else {
document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${from.split("-")[1]}"]`).forEach(element => {
if (!element.querySelector('input:not([value="aucun"]):checked')) {
elements.push(element);
}
})
}
let progress = document.querySelector("#zoneChoix .autoAffectation .progress");
if (elements.length > 1) {
progress.style.setProperty('--reference', elements.length);
progress.style.setProperty('--nombre', 0);
progressRef = elements.length;
progressNb = 0;
}
elements.forEach(groupeSelected => {
if (to[0] != "n") {
groupeSelected.closest(".grpPartitions").querySelector(`[value="${to}"]`).click();
} else {
let toNumber = to.split("-")[1];
groupeSelected.closest(".grpPartitions").querySelector(`[data-idpartition="${toNumber}"] [value="aucun"]`).click();
}
})
}
function assignment() {
let groupe = this.parentElement.parentElement.parentElement.parentElement;
let nom = groupe.children[0].dataset.nom;
let prenom = groupe.children[0].dataset.prenom;
let etudid = groupe.children[0].dataset.etudid;
let idPartition = this.parentElement.parentElement.dataset.idpartition;
let idGroupe = this.value;
document.querySelector(`#zoneGroupes [data-idPartition="${idPartition}"] [data-etudid="${etudid}"]`).remove();
let etudiant = {
etudid: etudid,
nom_disp: nom,
prenom: prenom
}
let results = document.querySelector(`#zoneGroupes [data-idPartition="${idPartition}"] [data-idgroupe="${idGroupe}"]>.etudiants`);
results.innerHTML += templateEtudiant_zoneGroupes(etudiant);
/* Tri */
let results2 = [...results.children];
results2.sort((a, b) => {
return (a.dataset.nom + a.dataset.prenom).localeCompare(b.dataset.nom + b.dataset.prenom)
})
results.innerHTML = "";
results.append(...results2);
/* Save */
this.classList.add("saving");
if (idGroupe == "aucun") {
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${idPartition}/remove_etudiant/${etudid}`;
} else {
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/group/${idGroupe}/set_etudiant/${etudid}`
}
fetch(url, { method: "POST" })
.then(r => { return r.json() })
.then(r => {
if (r.etudid == etudid) {
this.classList.remove("saving");
this.classList.add("saved");
setTimeout(() => { this.classList.remove("saved") }, 800);
let progress = document.querySelector("#zoneChoix .autoAffectation .progress");
progress.style.setProperty('--nombre', ++progressNb);
if (progressNb == progressRef) {
sco_message("Tous les étudiants sont affectés");
}
return;
}
throw ('Les données retournées ne sont pas valides : ' + r.status + ' ' + r.message);
})
.catch(error => {
document.querySelector("main").innerHTML = "<h2>" + error + "</h2>";
})
}
/****************************/
/* Ajout partition / groupe */
/****************************/
function addPartition() {
let date = new Date;
var name = "Nouvelle " + date.getSeconds();
let params = (new URL(document.location)).searchParams;
let formsemestre_id = params.get('formsemestre_id');
var url = "/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/" + formsemestre_id + "/partition/create";
var payload = { partition_name: name, show_in_lists: true };
// Save
fetch(url,
{
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(r => { return r.json() })
.then(r => {
if (r.message == "invalid partition_name" || r.message == "invalid group_name") {
sco_message("Le nom " + name + " existe déjà");
div.remove();
return;
}
// Ajout dans la zone filtres
let partition = {
id: r.id,
partition_name: name,
show_in_lists: true
}
let divPartition = templateFiltres_partition(partition);
document.querySelector("#zonePartitions .filtres").appendChild(divPartition);
divPartition.querySelector(".modif").click();
// Ajout de la zone pour chaque étudiant
let outputGroupes = "";
document.querySelectorAll("#zoneChoix .grpPartitions").forEach(e => {
let etudid = e.previousElementSibling.dataset.etudid;
// Préparation pour la section suivante
let etudiant = {
etudid: etudid,
nom_disp: e.previousElementSibling.dataset.nom,
prenom: e.previousElementSibling.dataset.prenom
}
outputGroupes += templateEtudiant_zoneGroupes(etudiant);
////////////////////////
let div = document.createElement("div");
div.className = "partition";
div.dataset.idpartition = r.id;
div.innerHTML = `
<div>${name}</div>
<label title="Aucun groupe">
<input type="radio" name="${r.id}-${etudid}" value="aucun" checked>
<span class="aucun">-</span>
</label>
`;
div.querySelector("input").addEventListener("input", assignment);
e.appendChild(div);
});
// Ajout de la zone groupes
document.querySelector("#zoneGroupes>.groupes").innerHTML += `
<div class=partition data-idpartition="${r.id}">
<h3>${name}</h3>
<div class=groupe data-idgroupe=aucun>
<div>Non affecté(s)</div>
<div class=etudiants>${outputGroupes}</div>
</div>
</div>`;
listeGroupesAutoaffectation();
})
.catch(error => {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (1).</h2>";
})
}
function addGroupe() {
let date = new Date;
// Groupe
var name = "Nouveau " + date.getSeconds();
let idPartition = this.parentElement.previousElementSibling.dataset.idpartition;
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${idPartition}/group/create`;
var payload = { group_name: name };
fetch(url,
{
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(r => { return r.json() })
.then(r => {
if (r.message == "invalid partition_name" || r.message == "invalid group_name") {
sco_message("Le nom " + name + " existe déjà");
return;
}
let groupe = {
id: r.id,
group_name: name
}
let divGroupe = templateFiltres_groupe(groupe);
this.parentElement.insertBefore(divGroupe, this);
// Ajout du bouton pour chaque étudiant
document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${idPartition}"]`).forEach(e => {
let etudid = e.parentElement.previousElementSibling.dataset.etudid;
let label = document.createElement("label");
label.innerHTML = `<input type=radio name="${idPartition}-${etudid}" value="${r.id}"><span>${name}</span>`;
label.querySelector("input").addEventListener("input", assignment);
e.appendChild(label);
})
// Ajout du groupe dans la zone Groupes
document.querySelector(`#zoneGroupes .partition[data-idpartition="${idPartition}"]`).innerHTML += templateGroupe_zoneGroupes(r.id, name);
// Lancement de l'édition du nom
// divGroupe.querySelector(".modif").click();
listeGroupesAutoaffectation();
})
.catch(error => {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (4).</h2>";
});
}
/********************/
/* Edition du texte */
/********************/
function editText() {
let e = this.previousElementSibling;
e.classList.add("editingText");
e.setAttribute("contenteditable", "true");
e.addEventListener("keydown", writing);
e.addEventListener("focusout", () => { saveEditing(this.previousElementSibling) });
// On sélectionne la zone
const range = document.createRange();
const selection = window.getSelection();
selection.removeAllRanges();
range.selectNodeContents(e);
selection.addRange(range);
}
function writing(event) {
switch (event.key) {
case 'Enter':
saveEditing(this);
event.preventDefault();
break;
case 'Escape':
saveEditing(this);
event.preventDefault();
break;
}
}
function saveEditing(obj) {
// Vérification que le champ est non vide
if (obj.innerText == "") {
event.preventDefault();
sco_message("Ce champ ne peut rester vide.");
obj.focus();
return;
}
// Fin de l'édition
obj.classList.remove("editingText");
obj.setAttribute("contenteditable", "false");
obj.removeEventListener("keydown", writing);
// Save
if (obj.parentElement.dataset.idpartition) {
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${obj.parentElement.dataset.idpartition}/edit`;
var payload = { partition_name: obj.innerText }
document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${obj.parentElement.dataset.idpartition}"]>div`).forEach(e => { e.innerText = obj.innerText });
document.querySelector(`#zoneGroupes [data-idpartition="${obj.parentElement.dataset.idpartition}"]>h3`).innerText = obj.innerText;
} else {
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/group/${obj.parentElement.dataset.idgroupe}/edit`;
var payload = { group_name: obj.innerText }
document.querySelectorAll(`#zoneChoix .etudiants [value="${obj.parentElement.dataset.idgroupe}"]+span`).forEach(e => { e.innerText = obj.innerText });
document.querySelector(`#zoneGroupes [data-idgroupe="${obj.parentElement.dataset.idgroupe}"]>div`).innerText = obj.innerText;
}
fetch(url,
{
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(r => { return r.json() })
.then(r => {
if (!r) {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (2).</h2>";
}
listeGroupesAutoaffectation();
})
.catch(error => {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (3).</h2>";
})
}
/***********************/
/* Edition de l'id_EDT */
/***********************/
function editCalendar() {
let data = `data-idgroupe="${this.parentElement.dataset.idgroupe}"`;
let nom = this.previousElementSibling.previousElementSibling.innerText;
let id_EDT = this.parentElement.dataset.idedt;
let div = document.createElement("div");
div.className = "confirm";
div.innerHTML = `
<div>
<h1>Modifier l'id EDT du groupe <span>${nom}</span></h1>
<input value="${id_EDT}">
<p>Optionnel : identifiant du groupe dans le logiciel d'emploi du temps, pour le cas où les noms de groupes ne seraient pas les mêmes dans ScoDoc et dans l'emploi du temps (si plusieurs ids de groupes EDT doivent correspondre au même groupe ScoDoc, les séparer par des virgules).</p>
<div>
<div class="ok" ${data}>Valider</div>
<div class="nok">Annuler</div>
</div>
</div>
`;
document.body.append(div);
document.querySelector(".ok").addEventListener("click", editCalConfirm);
document.querySelector(".nok").addEventListener("click", closeConfirm);
}
function editCalConfirm() {
let idGroupe = this.dataset.idgroupe;
let id_EDT = this.parentElement.parentElement.querySelector("input").value;
let btnGroupe = document.querySelector(`#zonePartitions .groupes [data-idgroupe="${idGroupe}"]`);
btnGroupe.dataset.idedt = id_EDT;
btnGroupe.querySelector(".calendarEdit").title = `Identifiant EDT: ${id_EDT || this.parentElement.parentElement.querySelector("span").innerText}`;
if (id_EDT) {
btnGroupe.querySelector(".calendarEdit").classList.add("actif");
} else {
btnGroupe.querySelector(".calendarEdit").classList.remove("actif");
}
//Save
let url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/group/${idGroupe}/set_edt_id/${id_EDT}`;
fetch(url, { method: "POST" })
.then(r => { return r.json() })
.then(r => {
if (r.id != idGroupe) {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (5).</h2>";
}
});
closeConfirm();
}
/*********************************/
/* Suppression parcours / groupe */
/*********************************/
function suppr() {
if (this.parentElement.dataset.idpartition) {
var data = `data-idpartition="${this.parentElement.dataset.idpartition}"`;
} else {
var data = `data-idgroupe="${this.parentElement.dataset.idgroupe}"`;
}
let div = document.createElement("div");
div.className = "confirm";
div.innerHTML = `
<div>
<h1>Vous être sur le point de supprimer <span>${this.previousElementSibling.previousElementSibling.innerText}</span>,<br>cette opération est irréversible</h1>
<div>
<div class="ok" ${data}>Supprimer</div>
<div class="nok">Annuler</div>
</div>
</div>
`;
document.body.append(div);
document.querySelector(".ok").addEventListener("click", supprConfirmed);
document.querySelector(".nok").addEventListener("click", closeConfirm);
}
function supprConfirmed() {
closeConfirm();
/* Suppression des éléments dans la page */
if (this.dataset.idpartition) {
document.querySelectorAll(`[data-idpartition="${this.dataset.idpartition}"]`).forEach(e => { e.remove() })
var url = "/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/" + this.dataset.idpartition + "/delete";
} else {
document.querySelectorAll(`[value="${this.dataset.idgroupe}"]`).forEach(e => {
if (e.checked == true) {
e.parentElement.parentElement.querySelector("label").click()
}
e.parentElement.remove()
})
document.querySelectorAll(`[data-idgroupe="${this.dataset.idgroupe}"]`).forEach(e => { e.remove() })
var url = "/ScoDoc/{{formsemestre.departement.acronym}}/api/group/" + this.dataset.idgroupe + "/delete";
}
//Save
fetch(url, { method: "POST" })
.then(r => { return r.json() })
.then(r => {
if (r.OK != true) {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>";
}
listeGroupesAutoaffectation();
})
}
function closeConfirm() {
document.querySelector(".confirm").remove();
}
/*************************/
/* Changement de l'ordre */
/*************************/
let moveData = {};
function moveStart(event) {
moveData.x = event.pageX;
moveData.y = event.pageY;
if (this.parentElement.dataset.idpartition) {
moveData.element = this.parentElement.parentElement;
} else {
moveData.element = this.parentElement;
}
moveData.element.classList.add("moving");
moveData.element.parentElement.classList.add('grabbing');
document.body.addEventListener("mousemove", move);
document.body.addEventListener("mouseup", moveEnd);
Array.from(moveData.element.parentElement.children).forEach(e => {
if ((e.dataset.idpartition && e.classname != "nonEditable") ||
(e.dataset.idgroupe != "aucun")) {
e.addEventListener("mouseup", newPosition)
}
})
}
function move(event) {
event.preventDefault();
moveData.element.style.transform = `translate(${event.pageX - moveData.x}px, ${event.pageY - moveData.y}px)`
}
function moveEnd() {
document.body.removeEventListener("mousemove", move);
document.body.removeEventListener("mouseup", moveEnd);
moveData.element.parentElement.classList.remove('grabbing');
moveData.element.style.transform = "";
moveData.element.classList.remove("moving");
Array.from(moveData.element.parentElement.children).forEach(e => {
if ((e.dataset.idpartition && e.classname != "nonEditable") ||
(e.dataset.idgroupe && e.dataset.idgroupe != "aucun")) {
e.removeEventListener("mouseup", newPosition)
}
})
moveData = {};
}
function newPosition(event) {
moveData.element.parentElement.insertBefore(moveData.element, this);
let positions = [];
Array.from(moveData.element.parentElement.children).forEach(e => {
if ((e.dataset.idpartition && e.classname != "nonEditable") ||
(e.dataset.idgroupe && e.dataset.idgroupe != "aucun")) {
positions.push(parseInt(e.dataset.idpartition || e.dataset.idgroupe))
}
})
// Save positions
if (this.dataset.idpartition || this.classList.contains("hidenDropZone")) {
let params = (new URL(document.location)).searchParams;
let formsemestre_id = params.get('formsemestre_id');
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/${formsemestre_id}/partitions/order`;
document.querySelectorAll(`#zonePartitions .masques>div`).forEach(parent => {
positions.forEach(position => {
parent.append(parent.querySelector(`[data-idpartition="${position}"]`))
})
})
document.querySelectorAll(`#zoneChoix .grpPartitions`).forEach(parent => {
positions.forEach(position => {
parent.append(parent.querySelector(`[data-idpartition="${position}"]`))
})
})
document.querySelectorAll(`#zoneGroupes>.groupes`).forEach(parent => {
positions.forEach(position => {
parent.append(parent.querySelector(`[data-idpartition="${position}"]`))
})
})
} else {
let idPartition = this.closest("[data-idpartition]").dataset.idpartition;
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${idPartition}/groups/order`;
document.querySelectorAll(`#zoneChoix .etudiants .partition[data-idpartition="${idPartition}"]`).forEach(partition => {
positions.forEach(position => {
partition.append(partition.querySelector(`[value="${position}"]`).parentElement)
})
})
document.querySelectorAll(`#zoneGroupes .partition[data-idpartition="${idPartition}"]`).forEach(partition => {
positions.forEach(position => {
partition.append(partition.querySelector(`[data-idgroupe="${position}"]`))
})
})
}
fetch(url,
{
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(positions)
})
.then(r => { return r.json() })
.then(r => {
if (!r) {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>";
}
listeGroupesAutoaffectation();
})
.catch(error => {
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (7).</h2>";
})
}
/*****************/
/* Config */
/*****************/
function setAttribute() {
fetch("partition_set_attr",
{
method: "post",
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: `partition_id=${this.closest("[data-idpartition]").dataset.idpartition}&attr=${this.dataset.attr}&value=${this.checked ? 1 : 0}`
}
).then(function (response) {
return response.text();
})
.then(function (txt) {
sco_message(txt);
});
}
</script>
{% endblock %}