ScoDoc-PE/app/templates/assiduites/widgets/minitimeline.j2

325 lines
10 KiB
Django/Jinja

<div class="assiduite-bubble">
</div>
<script>
const mt_start = {{ t_start }};
const mt_end = {{ t_end }};
/**
* Création de la minitiline d'un étudiant
* @param {Array[Assiduité]} assiduitesArray
* @returns {HTMLElement} l'élément correspondant à la mini timeline
*/
function createMiniTimeline(assiduitesArray, day = null) {
const array = [...assiduitesArray];
const date = day == null ? getDate() : new Date(day);
const timeline = document.createElement("div");
timeline.className = "mini-timeline";
if (isSingleEtud()) {
timeline.classList.add("single");
}
const timelineDate = date.startOf("day");
const dayStart = timelineDate.clone().add(mt_start, "hours");
const dayEnd = timelineDate.clone().add(mt_end, "hours");
const dayDuration = new Duration(dayStart, dayEnd).minutes;
timeline.appendChild(setMiniTick(timelineDate, dayStart, dayDuration));
if (day == null) {
const tlTimes = getTimeLineTimes();
array.push({
date_debut: tlTimes.deb.format(),
date_fin: tlTimes.fin.format(),
etat: "CRENEAU",
});
}
array.forEach((assiduité) => {
if(assiduité.etat == "CRENEAU" && readOnly) return;
let startDate = new Date(Date.removeUTC(assiduité.date_debut));
let endDate = new Date(Date.removeUTC(assiduité.date_fin));
if (startDate.isBefore(dayStart)) {
startDate = dayEnd.clone().startOf("day").add(mt_start, "hours");
}
if (endDate.isAfter(dayEnd)) {
endDate = dayEnd.clone().startOf("day").add(mt_end, "hours");
}
const block = document.createElement("div");
block.className = "mini-timeline-block";
const duration = (new Duration(startDate, endDate)).minutes;
const startOffset = (new Duration(dayStart, startDate)).minutes;
const leftPercentage = (startOffset / dayDuration) * 100;
const widthPercentage = (duration / dayDuration) * 100;
block.style.left = `${leftPercentage}%`;
block.style.width = `${widthPercentage}%`;
if (assiduité.etat != "CRENEAU") {
block.addEventListener("click", () => {
let deb = startDate.getHours() + startDate.getMinutes() / 60;
let fin = endDate.getHours() + endDate.getMinutes() / 60;
deb = Math.max(mt_start, deb);
fin = Math.min(mt_end, fin);
if (day == null) setPeriodValues(deb, fin);
if (isSingleEtud()) {
updateSelectedSelect(getCurrentAssiduiteModuleImplId());
updateJustifyBtn();
}
try {
if (isCalendrier()) {
window.location = `liste_assiduites_etud?etudid=${etudid}&assiduite_id=${assiduité.assiduite_id}`
}
} catch { }
});
//ajouter affichage assiduites on over
setupAssiduiteBuble(block, assiduité);
}
const action = (justificatifs) => {
if (justificatifs.length > 0) {
let j = "invalid_justified";
justificatifs.forEach((ju) => {
if (ju.etat == "VALIDE") {
j = "justified";
}
});
block.classList.add(j);
}
};
if (assiduité.etudid) {
getJustificatifFromPeriod(
{
deb: new Date(assiduité.date_debut),
fin: new Date(assiduité.date_fin),
},
assiduité.etudid,
action
);
}
switch (assiduité.etat) {
case "PRESENT":
block.classList.add("present");
break;
case "RETARD":
block.classList.add("retard");
break;
case "ABSENT":
block.classList.add("absent");
break;
case "CRENEAU":
block.classList.add("creneau");
break;
default:
block.style.backgroundColor = "white";
}
timeline.appendChild(block);
});
return timeline;
}
/**
* Ajout de la visualisation des assiduités de la mini timeline
* @param {HTMLElement} el l'élément survollé
* @param {Assiduité} assiduite l'assiduité représentée par l'élément
*/
function setupAssiduiteBuble(el, assiduite) {
if (!assiduite) return;
el.addEventListener("mouseenter", (event) => {
const bubble = document.querySelector(".assiduite-bubble");
bubble.className = "assiduite-bubble";
bubble.classList.add("is-active", assiduite.etat.toLowerCase());
bubble.innerHTML = "";
const idDiv = document.createElement("div");
idDiv.className = "assiduite-id";
idDiv.textContent = `${getModuleImpl(assiduite)}`;
bubble.appendChild(idDiv);
const periodDivDeb = document.createElement("div");
periodDivDeb.className = "assiduite-period";
periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`;
bubble.appendChild(periodDivDeb);
const periodDivFin = document.createElement("div");
periodDivFin.className = "assiduite-period";
periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`;
bubble.appendChild(periodDivFin);
const stateDiv = document.createElement("div");
stateDiv.className = "assiduite-state";
stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
bubble.appendChild(stateDiv);
const userIdDiv = document.createElement("div");
userIdDiv.className = "assiduite-user_id";
userIdDiv.textContent = `saisie le ${formatDateModal(
assiduite.entry_date,
" à "
)}`;
if (assiduite.user_id != null) {
userIdDiv.textContent += `\npar ${assiduite.user_nom_complet}`
}
bubble.appendChild(userIdDiv);
bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`;
bubble.style.top = `${event.clientY + 20}px`;
});
el.addEventListener("mouseout", () => {
const bubble = document.querySelector(".assiduite-bubble");
bubble.classList.remove("is-active");
});
}
function setMiniTick(timelineDate, dayStart, dayDuration) {
const endDate = timelineDate.clone().startOf("day");
endDate.setHours(13, 0);
const duration = new Duration(dayStart, endDate).minutes;
const widthPercentage = (duration / dayDuration) * 100;
const tick = document.createElement('span');
tick.className = "mini_tick"
tick.textContent = "13h"
tick.style.left = `${widthPercentage}%`
return tick
}
</script>
<style>
.assiduite-bubble {
position: fixed;
display: none;
background-color: #f9f9f9;
border-radius: 5px;
padding: 8px;
border: 3px solid #ccc;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
font-size: 12px;
line-height: 1.4;
z-index: 500;
}
.assiduite-bubble.is-active {
display: block;
}
.assiduite-bubble::before {
content: "";
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 6px;
border-style: solid;
border-color: transparent transparent #f9f9f9 transparent;
}
.assiduite-bubble::after {
content: "";
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 5px;
border-style: solid;
border-color: transparent transparent #ccc transparent;
}
.assiduite-id,
.assiduite-period,
.assiduite-state,
.assiduite-user_id {
margin-bottom: 4px;
}
.assiduite-bubble.absent {
border-color: var(--color-absent) !important;
}
.assiduite-bubble.present {
border-color: var(--color-present) !important;
}
.assiduite-bubble.retard {
border-color: var(--color-retard) !important;
}
.mini-timeline {
height: 7px;
border: 1px solid black;
position: relative;
background-color: white;
}
.mini-timeline.single {
height: 9px;
}
.mini-timeline-block {
position: absolute;
height: 100%;
z-index: 1;
}
#page-assiduite-content .mini-timeline-block {
cursor: pointer;
}
.mini_tick {
position: absolute;
text-align: start;
top: -40px;
transform: translateX(-50%);
z-index: 1;
}
.mini_tick::after {
display: block;
content: "|";
position: absolute;
bottom: -2px;
z-index: 2;
}
.mini-timeline-block.creneau {
outline: 3px solid var(--color-primary);
pointer-events: none;
}
.mini-timeline-block.absent {
background-color: var(--color-absent) !important;
}
.mini-timeline-block.present {
background-color: var(--color-present) !important;
}
.mini-timeline-block.retard {
background-color: var(--color-retard) !important;
}
.mini-timeline-block.justified {
background-image: var(--motif-justi);
}
.mini-timeline-block.invalid_justified {
background-image: var(--motif-justi-invalide);
}
</style>