forked from ScoDoc/ScoDoc
1027 lines
34 KiB
Django/Jinja
1027 lines
34 KiB
Django/Jinja
{# -*- mode: jinja-html -*- #}
|
|
|
|
{% if not is_edt_configured %}
|
|
<style>
|
|
span.calendarEdit {
|
|
display: none;
|
|
}
|
|
</style>
|
|
{% endif %}
|
|
|
|
<h1>{% if not read_only %}Édition des p{% else %}P{%endif%}artitions</h1>
|
|
|
|
<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>
|
|
|
|
<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="{{scu.ScoURL()
|
|
}}/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>
|