Assiduites : Gestion des justificatifs (rapide) WIP

Assiduites : ajout style justifié (minitimeline)
This commit is contained in:
iziram 2023-04-19 20:58:15 +02:00
parent dca7abe064
commit 560dc63823
7 changed files with 246 additions and 106 deletions

View File

@ -316,10 +316,10 @@ def compute_assiduites_justified(
for justi in justificatifs:
assiduites: Assiduite = (
Assiduite.query.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
.filter(Assiduite.etat != EtatAssiduite.PRESENT)
.filter(justi.etat == EtatJustificatif.VALIDE)
.filter(
Assiduite.date_debut <= justi.date_fin,
Assiduite.date_fin >= justi.date_debut,
Assiduite.date_debut < justi.date_fin,
Assiduite.date_fin > justi.date_debut,
)
)
@ -329,11 +329,9 @@ def compute_assiduites_justified(
db.session.add(assi)
if reset:
un_justified: Assiduite = (
Assiduite.query.filter(Assiduite.id.not_in(list_assiduites_id))
.filter(Assiduite.etat != EtatAssiduite.PRESENT)
.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
)
un_justified: Assiduite = Assiduite.query.filter(
Assiduite.id.not_in(list_assiduites_id)
).join(Justificatif, Justificatif.etudid == Assiduite.etudid)
for assi in un_justified:
assi.est_just = False

View File

@ -20,7 +20,6 @@ class CountCalculator:
evening: time = time(18, 0),
skip_saturday: bool = True,
) -> None:
self.morning: time = morning
self.noon: time = noon
self.after_noon: time = after_noon
@ -318,13 +317,11 @@ def justifies(justi: Justificatif, obj: bool = False) -> list[int]:
if justi.etat != scu.EtatJustificatif.VALIDE:
return []
assiduites_query: Assiduite = (
Assiduite.query.join(Justificatif, Assiduite.etudid == Justificatif.etudid)
.filter(Assiduite.etat != scu.EtatAssiduite.PRESENT)
.filter(
Assiduite.date_debut <= justi.date_fin,
Assiduite.date_fin >= justi.date_debut,
)
assiduites_query: Assiduite = Assiduite.query.join(
Justificatif, Assiduite.etudid == Justificatif.etudid
).filter(
Assiduite.date_debut <= justi.date_fin,
Assiduite.date_fin >= justi.date_debut,
)
if not obj:

View File

@ -192,6 +192,14 @@
background-color: #F1D99C !important;
}
.etud_row .assiduites_bar .justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px);
}
.etud_row .assiduites_bar .invalid_justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #d61616 4px, #d61616 8px);
}
/* --- Boutons assiduités --- */
.etud_row .btns_field {

View File

@ -16,6 +16,7 @@ let currentValues = [8.0, 10.0];
//Objet stockant les étudiants et les assiduités
let etuds = {};
let assiduites = {};
let justificatifs = {};
// Variable qui définit si le processus d'action de masse est lancé
let currentMassAction = false;
@ -1094,12 +1095,40 @@ function createMiniTimeline(assiduitesArray) {
setPeriodValues(deb, fin);
updateSelectedSelect(getCurrentAssiduiteModuleImplId());
updateJustifyBtn();
});
}
//ajouter affichage assiduites on over
setupAssiduiteBuble(block, assiduité);
if (assiduité.est_just) {
block.classList.add("justified");
}
}
const action = (justificatifs) => {
if (justificatifs.length > 0) {
let j = "invalid_justified";
justificatifs.forEach((ju) => {
if (ju.etat == "VALIDE") {
j = "justified";
}
});
block.classList.add(j);
}
};
getJustificatifFromPeriod(
{
deb: new moment.tz(assiduité.date_debut, TIMEZONE),
fin: new moment.tz(assiduité.date_fin, TIMEZONE),
},
assiduité.etudid,
action
);
switch (assiduité.etat) {
case "PRESENT":
block.classList.add("present");
@ -1773,31 +1802,33 @@ function getCurrentAssiduite(etudid) {
// <<== Gestion de la justification ==>>
function getJustificatifFromPeriod(date) {
let justifs = [];
sync_get(
getUrl() +
`/api/justificatifs/${etudid}/query?date_debut=${date.deb.format()}&date_fin=${date.fin.format()}`,
(data) => {
justifs = data;
}
);
return justifs;
function getJustificatifFromPeriod(date, etudid, update) {
$.ajax({
async: true,
type: "GET",
url:
getUrl() +
`/api/justificatifs/${etudid}/query?date_debut=${date.deb
.add(1, "s")
.format()}&date_fin=${date.fin.subtract(1, "s").format()}`,
success: (data) => {
update(data);
},
error: () => {},
});
}
function updateJustifieButton(isJustified, isDisabled = true) {
const btn = document.getElementById("justif-rapide");
if (isJustified) {
btn.classList.add("justifie");
} else {
btn.classList.remove("justifie");
}
function updateJustifyBtn() {
if (isSingleEtud()) {
const assi = getCurrentAssiduite(etudid);
if (isDisabled) {
btn.setAttribute("disabled", "true");
} else {
btn.removeAttribute("disabled");
const just = assi ? !assi.est_just : false;
const btn = document.getElementById("justif-rapide");
if (!just) {
btn.setAttribute("disabled", "true");
} else {
btn.removeAttribute("disabled");
}
}
}
@ -1806,12 +1837,100 @@ function fastJustify(assiduite) {
deb: new moment.tz(assiduite.date_debut, TIMEZONE),
fin: new moment.tz(assiduite.date_fin, TIMEZONE),
};
const justifs = getJustificatifFromPeriod(period);
const action = (justifs) => {
if (justifs.length > 0) {
justifyAssiduite(assiduite.assiduite_id, !assiduite.est_just);
} else {
console.debug("WIP");
//créer un nouveau justificatif
// Afficher prompt -> demander raison et état
if (justifs.length > 0) {
//modifier l'assiduité
} else {
//créer un nouveau justificatif
// Afficher prompt -> demander raison et état
}
const success = () => {
const raison = document.getElementById("promptText").value;
const etat = document.getElementById("promptSelect").value;
//créer justificatif
const justif = {
date_debut: new moment.tz(assiduite.date_debut, TIMEZONE)
.add(1, "s")
.format(),
date_fin: new moment.tz(assiduite.date_fin, TIMEZONE)
.subtract(1, "s")
.format(),
raison: raison,
etat: etat,
};
createJustificatif(justif);
// justifyAssiduite(assiduite.assiduite_id, true);
generateAllEtudRow();
};
const content = document.createElement("fieldset");
const htmlPrompt = `<legend>Entrez l'état du justificatif :</legend>
<select name="promptSelect" id="promptSelect" required>
<option value="valide">Valide</option>
<option value="attente">En Attente de validation</option>
<option value="non_valide">Non Valide</option>
<option value="modifie">Modifié</option>
</select>
<legend>Raison:</legend>
<textarea type="text" placeholder="Explication du justificatif (non obligatoire)" id="promptText" style="width:100%;"></textarea>
`;
content.innerHTML = htmlPrompt;
openPromptModal(
"Nouveau justificatif (Rapide)",
content,
success,
() => {},
"#7059FF"
);
}
};
getJustificatifFromPeriod(period, assiduite.etudid, action);
}
function justifyAssiduite(assiduite_id, justified) {
const assiduite = {
est_just: justified,
};
const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
let bool = false;
sync_post(
path,
assiduite,
(data, status) => {
bool = true;
},
(data, status) => {
//error
console.error(data, status);
}
);
return bool;
}
function createJustificatif(justif) {
const path = getUrl() + `/api/justificatif/${etudid}/create`;
sync_post(
path,
[justif],
(data, status) => {
//success
if (data.success.length > 0) {
console.table(data[0]);
}
console.warn(data);
},
(data, status) => {
//error
console.error(data, status);
}
);
}

View File

@ -29,6 +29,8 @@
</div>
</div>
{% include "assiduites/toast.j2" %}
{% include "assiduites/alert.j2" %}
{% include "assiduites/prompt.j2" %}
<div id="page-assiduite-content">
{% block content %}
<h2>Signalement de l'assiduité de <span class="rouge">{{sco.etud.nomprenom}}</span></h2>
@ -43,13 +45,13 @@
<div>
{% include "assiduites/moduleimpl_dynamic_selector.j2" %}
<button class="btn" onclick="justify()" disabled id="justif-rapide">Justifier</button>
<button class="btn" onclick="fastJustify(getCurrentAssiduite(etudid))" id="justif-rapide">Justifier</button>
</div>
<div class="btn_group">
<button class="btn" onclick="setPeriodValues(8,18)">Journée</button>
<button class="btn" onclick="setPeriodValues(8,13)">Matin</button>
<button class="btn" onclick="setPeriodValues(13,18)">Après-midi</button>
<button class="btn" onclick="setTimeLineTimes(8,18)">Journée</button>
<button class="btn" onclick="setTimeLineTimes(8,13)">Matin</button>
<button class="btn" onclick="setTimeLineTimes(13,18)">Après-midi</button>
</div>
<div class="etud_holder">
@ -64,8 +66,7 @@
<div class="loader"></div>
</div>
{% include "assiduites/alert.j2" %}
{% include "assiduites/prompt.j2" %}
<script>
const etudid = {{ sco.etud.id }};
@ -74,20 +75,29 @@
updateSelect()
});
setupTimeLine(() => {
updateJustifyBtn();
});
updateDate();
getSingleEtud({{ sco.etud.id }});
actualizeEtud({{ sco.etud.id }});
updateSelect()
updateJustifyBtn();
function setTimeLineTimes(a, b) {
setPeriodValues(a, b);
updateJustifyBtn();
}
</script>
<style>
.rouge {
color: crimson;
}
.justifie {
background-color: rgb(104, 104, 252);
color: whitesmoke;

View File

@ -72,5 +72,6 @@
<script>
updateDate();
setupDate();
setupTimeLine();
</script>
</section>

View File

@ -38,62 +38,69 @@
return Math.round(value * 4) / 4;
}
timelineContainer.addEventListener("mousedown", (event) => {
const startX = event.clientX;
function setupTimeLine(callback) {
const func_call = callback ? callback : () => { }
timelineContainer.addEventListener("mousedown", (event) => {
const startX = event.clientX;
if (event.target === periodTimeLine) {
const startLeft = parseFloat(periodTimeLine.style.left);
if (event.target === periodTimeLine) {
const startLeft = parseFloat(periodTimeLine.style.left);
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
generateAllEtudRow();
snapHandlesToQuarters()
document.removeEventListener("mousemove", onMouseMove);
},
{ once: true }
);
} else if (event.target.classList.contains("period-handle")) {
const startWidth = parseFloat(periodTimeLine.style.width);
const startLeft = parseFloat(periodTimeLine.style.left);
const isLeftHandle = event.target.classList.contains("left");
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newWidth =
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
if (isLeftHandle) {
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, newWidth);
} else {
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
}
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
snapHandlesToQuarters()
generateAllEtudRow();
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
};
document.removeEventListener("mousemove", onMouseMove);
},
{ once: true }
);
}
});
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
generateAllEtudRow();
snapHandlesToQuarters()
document.removeEventListener("mousemove", onMouseMove);
func_call()
},
{ once: true }
);
} else if (event.target.classList.contains("period-handle")) {
const startWidth = parseFloat(periodTimeLine.style.width);
const startLeft = parseFloat(periodTimeLine.style.left);
const isLeftHandle = event.target.classList.contains("left");
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newWidth =
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
if (isLeftHandle) {
const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, newWidth);
} else {
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
}
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
snapHandlesToQuarters()
generateAllEtudRow();
document.removeEventListener("mousemove", onMouseMove);
func_call()
},
{ once: true }
);
}
});
}
function adjustPeriodPosition(newLeft, newWidth) {