This commit is contained in:
Emmanuel Viennet 2023-11-16 16:22:45 +01:00
commit b0daccb32e
32 changed files with 1522 additions and 2100 deletions

View File

@ -53,7 +53,6 @@ import requests
from pytz import timezone from pytz import timezone
import dateutil.parser as dtparser
import flask import flask
from flask import g, request, Response from flask import g, request, Response
@ -230,9 +229,9 @@ def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or No
""" """
try: try:
date: datetime.datetime = dtparser.isoparse(date) date: datetime.datetime = datetime.datetime.fromisoformat(date)
return date if convert else True return date if convert else True
except (dtparser.ParserError, ValueError, TypeError): except (ValueError, TypeError):
return None if convert else False return None if convert else False

View File

@ -1,3 +1,33 @@
:root {
--color-present: #6bdb83;
--color-absent: #e62a11;
--color-retard: #f0c865;
--color-justi: #7059FF;
--color-justi-invalide: #a84476;
--color-nonwork: #badfff;
--color-absent-justi: #e65ab7;
--color-retard-justi: #ffef7a;
--color-error: #FF0000;
--color-warning: #eec660;
--color-information: #658ef0;
--color-def: #d61616;
--color-conflit: #ff00009c;
--color-bg-def: #c8c8c8;
--color-primary: #7059FF;
--color-secondary: #6f9fff;
--color-defaut: #FFF;
--color-defaut-dark: #444;
--motif-justi: repeating-linear-gradient(135deg, transparent, transparent 4px, var(--color-justi) 4px, var(--color-justi) 8px);
--motif-justi-invalide: repeating-linear-gradient(-135deg, transparent, transparent 4px, var(--color-justi-invalide) 4px, var(--color-justi-invalide) 8px);
}
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
@ -36,10 +66,10 @@
.infos { .infos {
position: relative; position: relative;
width: fit-content;
display: flex; display: flex;
justify-content: space-evenly; justify-content: start;
align-content: center; align-content: center;
gap: 10px;
} }
#datestr { #datestr {
@ -48,7 +78,7 @@
border: 1px #444 solid; border: 1px #444 solid;
border-radius: 5px; border-radius: 5px;
padding: 5px; padding: 5px;
min-width: 100px; min-width: 250px;
display: inline-block; display: inline-block;
min-height: 20px; min-height: 20px;
} }
@ -87,7 +117,7 @@
} }
.ui-slider-range.ui-widget-header.ui-corner-all { .ui-slider-range.ui-widget-header.ui-corner-all {
background-color: #F9C768; background-color: var(--color-warning);
background-image: none; background-image: none;
opacity: 0.50; opacity: 0.50;
visibility: visible; visibility: visible;
@ -122,7 +152,7 @@
.etud_row.def, .etud_row.def,
.etud_row.dem { .etud_row.dem {
background-color: #c8c8c8; background-color: var(--color-bg-def);
} }
/* --- Index --- */ /* --- Index --- */
@ -149,7 +179,7 @@
.tr.def .td.sticky span::after { .tr.def .td.sticky span::after {
display: block; display: block;
content: " (Déf.)"; content: " (Déf.)";
color: #d61616; color: var(--color-def);
margin-left: 2px; margin-left: 2px;
} }
@ -157,7 +187,7 @@
.tr.dem .td.sticky span::after { .tr.dem .td.sticky span::after {
display: block; display: block;
content: " (Dém.)"; content: " (Dém.)";
color: #d61616; color: var(--color-def);
margin-left: 2px; margin-left: 2px;
} }
@ -213,32 +243,36 @@
} }
.etud_row.conflit { .etud_row.conflit {
background-color: #ff0000c2; background-color: var(--color-conflit);
} }
.etud_row .assiduites_bar .absent, .etud_row .assiduites_bar .absent,
.demo.absent { .demo.absent {
background-color: #F1A69C !important; background-color: var(--color-absent) !important;
} }
.etud_row .assiduites_bar .present, .etud_row .assiduites_bar .present,
.demo.present { .demo.present {
background-color: #9CF1AF !important; background-color: var(--color-present) !important;
} }
.etud_row .assiduites_bar .retard, .etud_row .assiduites_bar .retard,
.demo.retard { .demo.retard {
background-color: #F1D99C !important; background-color: var(--color-retard) !important;
}
.demo.nonwork {
background-color: var(--color-nonwork) !important;
} }
.etud_row .assiduites_bar .justified, .etud_row .assiduites_bar .justified,
.demo.justified { .demo.justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px); background-image: var(--motif-justi);
} }
.etud_row .assiduites_bar .invalid_justified, .etud_row .assiduites_bar .invalid_justified,
.demo.invalid_justified { .demo.invalid_justified {
background-image: repeating-linear-gradient(225deg, transparent, transparent 4px, #d61616 4px, #d61616 8px); background-image: var(--motif-justi-invalide);
} }
@ -273,27 +307,35 @@
height: 35px; height: 35px;
background-position: center; background-position: center;
background-size: cover; background-size: cover;
border-radius: 5px;
border: 1px solid var(--color-defaut-dark);
} }
.rbtn.present::before { .rbtn.present::before {
background-image: url(../icons/present.svg); background-image: url(../icons/present.svg);
background-color: var(--color-present);
} }
.rbtn.absent::before { .rbtn.absent::before {
background-color: var(--color-absent);
background-image: url(../icons/absent.svg); background-image: url(../icons/absent.svg);
} }
.rbtn.aucun::before { .rbtn.aucun::before {
background-image: url(../icons/aucun.svg); background-image: url(../icons/aucun.svg);
background-color: var(--color-defaut-dark);
} }
.rbtn.retard::before { .rbtn.retard::before {
background-color: var(--color-retard);
background-image: url(../icons/retard.svg); background-image: url(../icons/retard.svg);
} }
.rbtn:checked:before { .rbtn:checked:before {
outline: 5px solid #7059FF; outline: 5px solid var(--color-primary);
border-radius: 50%; border-radius: 50%;
} }
@ -486,7 +528,7 @@
.loader { .loader {
border: 6px solid #f3f3f3; border: 6px solid #f3f3f3;
border-radius: 50%; border-radius: 50%;
border-top: 6px solid #3498db; border-top: 6px solid var(--color-primary);
width: 60px; width: 60px;
height: 60px; height: 60px;
position: absolute; position: absolute;
@ -532,7 +574,7 @@
} }
.rouge { .rouge {
color: crimson; color: var(--color-error);
} }
.legende { .legende {
@ -588,7 +630,7 @@
#forcemodule { #forcemodule {
border-radius: 8px; border-radius: 8px;
background: crimson; background: var(--color-error);
max-width: fit-content; max-width: fit-content;
padding: 5px; padding: 5px;
color: white; color: white;

16
app/static/icons/absent.svg Executable file → Normal file

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,11 @@
<svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="85" height="85" rx="15" fill=""/>
<g opacity="0.7" clip-path="url(#clip0_120_4425)">
<path d="M67.2116 70L43 45.707L18.7885 70L15.0809 66.3043L39.305 41.9995L15.0809 17.6939L18.7885 14L43 38.2922L67.2116 14L70.9191 17.6939L46.695 41.9995L70.9191 66.3043L67.2116 70Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_120_4425">
<rect width="56" height="56" fill="white" transform="matrix(1 0 0 -1 15 70)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@ -1,5 +1,5 @@
<svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="85" height="85" rx="15" fill="#BBB"/> <rect width="85" height="85" rx="15" fill=""/>
<defs> <defs>
<clipPath id="clip0_120_4425"> <clipPath id="clip0_120_4425">
<rect width="56" height="56" fill="white" transform="matrix(1 0 0 -1 15 70)"/> <rect width="56" height="56" fill="white" transform="matrix(1 0 0 -1 15 70)"/>

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 287 B

View File

@ -1,7 +1,7 @@
<svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="85" height="85" rx="15" fill="#9CF1AF"/> <rect width="85" height="85" rx="15" fill=""/>
<g clip-path="url(#clip0_120_4405)"> <g clip-path="url(#clip0_120_4405)">
<g opacity="0.5"> <g opacity="0.7">
<path d="M70.7713 27.5875L36.0497 62.3091C35.7438 62.6149 35.2487 62.6149 34.9435 62.3091L15.2286 42.5935C14.9235 42.2891 14.9235 41.7939 15.2286 41.488L20.0191 36.6976C20.3249 36.3924 20.8201 36.3924 21.1252 36.6976L35.4973 51.069L64.8754 21.6909C65.1819 21.3858 65.6757 21.3858 65.9815 21.6909L70.7713 26.4814C71.0771 26.7865 71.0771 27.281 70.7713 27.5875Z" fill="black"/> <path d="M70.7713 27.5875L36.0497 62.3091C35.7438 62.6149 35.2487 62.6149 34.9435 62.3091L15.2286 42.5935C14.9235 42.2891 14.9235 41.7939 15.2286 41.488L20.0191 36.6976C20.3249 36.3924 20.8201 36.3924 21.1252 36.6976L35.4973 51.069L64.8754 21.6909C65.1819 21.3858 65.6757 21.3858 65.9815 21.6909L70.7713 26.4814C71.0771 26.7865 71.0771 27.281 70.7713 27.5875Z" fill="black"/>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 722 B

View File

@ -1,6 +1,6 @@
<svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="85" height="85" viewBox="0 0 85 85" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="85" height="85" rx="15" fill="#F1D99C"/> <rect width="85" height="85" rx="15" fill=""/>
<g opacity="0.5" clip-path="url(#clip0_120_4407)"> <g opacity="0.7" clip-path="url(#clip0_120_4407)">
<path d="M55.2901 49.1836L44.1475 41.3918V28C44.1475 27.3688 43.6311 26.8524 43 26.8524C42.3688 26.8524 41.8524 27.3688 41.8524 28V42C41.8524 42.3787 42.036 42.7229 42.3459 42.941L53.9819 51.077C54.177 51.2147 54.4065 51.2836 54.636 51.2836C54.9918 51.2836 55.3475 51.1115 55.577 50.7787C55.9327 50.2623 55.8065 49.5508 55.2901 49.1836Z" fill="black"/> <path d="M55.2901 49.1836L44.1475 41.3918V28C44.1475 27.3688 43.6311 26.8524 43 26.8524C42.3688 26.8524 41.8524 27.3688 41.8524 28V42C41.8524 42.3787 42.036 42.7229 42.3459 42.941L53.9819 51.077C54.177 51.2147 54.4065 51.2836 54.636 51.2836C54.9918 51.2836 55.3475 51.1115 55.577 50.7787C55.9327 50.2623 55.8065 49.5508 55.2901 49.1836Z" fill="black"/>
<path d="M62.7836 22.2164C57.482 16.9148 50.459 14 43 14C35.541 14 28.518 16.9148 23.2164 22.2164C17.9148 27.518 15 34.541 15 42C15 49.459 17.9148 56.482 23.2164 61.7836C28.518 67.0852 35.541 70 43 70C50.459 70 57.482 67.0852 62.7836 61.7836C68.0852 56.482 71 49.459 71 42C71 34.541 68.0852 27.518 62.7836 22.2164ZM44.1475 67.682V63C44.1475 62.3689 43.6311 61.8525 43 61.8525C42.3689 61.8525 41.8525 62.3689 41.8525 63V67.682C28.5869 67.0967 17.9033 56.4131 17.318 43.1475H22C22.6311 43.1475 23.1475 42.6311 23.1475 42C23.1475 41.3689 22.6311 40.8525 22 40.8525H17.318C17.9033 27.5869 28.5869 16.9033 41.8525 16.318V21C41.8525 21.6311 42.3689 22.1475 43 22.1475C43.6311 22.1475 44.1475 21.6311 44.1475 21V16.318C57.4131 16.9033 68.0967 27.5869 68.682 40.8525H64C63.3689 40.8525 62.8525 41.3689 62.8525 42C62.8525 42.6311 63.3689 43.1475 64 43.1475H68.682C68.0967 56.4131 57.4131 67.0967 44.1475 67.682Z" fill="black"/> <path d="M62.7836 22.2164C57.482 16.9148 50.459 14 43 14C35.541 14 28.518 16.9148 23.2164 22.2164C17.9148 27.518 15 34.541 15 42C15 49.459 17.9148 56.482 23.2164 61.7836C28.518 67.0852 35.541 70 43 70C50.459 70 57.482 67.0852 62.7836 61.7836C68.0852 56.482 71 49.459 71 42C71 34.541 68.0852 27.518 62.7836 22.2164ZM44.1475 67.682V63C44.1475 62.3689 43.6311 61.8525 43 61.8525C42.3689 61.8525 41.8525 62.3689 41.8525 63V67.682C28.5869 67.0967 17.9033 56.4131 17.318 43.1475H22C22.6311 43.1475 23.1475 42.6311 23.1475 42C23.1475 41.3689 22.6311 40.8525 22 40.8525H17.318C17.9033 27.5869 28.5869 16.9033 41.8525 16.318V21C41.8525 21.6311 42.3689 22.1475 43 22.1475C43.6311 22.1475 44.1475 21.6311 44.1475 21V16.318C57.4131 16.9033 68.0967 27.5869 68.682 40.8525H64C63.3689 40.8525 62.8525 41.3689 62.8525 42C62.8525 42.6311 63.3689 43.1475 64 43.1475H68.682C68.0967 56.4131 57.4131 67.0967 44.1475 67.682Z" fill="black"/>
</g> </g>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -32,6 +32,17 @@ Object.defineProperty(String.prototype, "capitalize", {
}, },
enumerable: false, enumerable: false,
}); });
const DatePrecisions = [
"year",
"month",
"day",
"hour",
"minute",
"second",
"millisecond",
];
// <<== Outils ==>> // <<== Outils ==>>
Object.defineProperty(Array.prototype, "reversed", { Object.defineProperty(Array.prototype, "reversed", {
value: function () { value: function () {
@ -101,6 +112,7 @@ function validateSelectors(btn) {
getAssiduitesFromEtuds(true); getAssiduitesFromEtuds(true);
document.querySelector(".selectors").disabled = true; document.querySelector(".selectors").disabled = true;
$("#tl_date").datepicker("option", "disabled", true);
generateMassAssiduites(); generateMassAssiduites();
generateAllEtudRow(); generateAllEtudRow();
btn.remove(); btn.remove();
@ -126,7 +138,7 @@ function validateSelectors(btn) {
} }
function onlyAbs() { function onlyAbs() {
if (getDate() > moment()) { if (getDate() > Date.now()) {
document document
.querySelectorAll(".rbtn.present, .rbtn.retard") .querySelectorAll(".rbtn.present, .rbtn.retard")
.forEach((el) => el.remove()); .forEach((el) => el.remove());
@ -162,6 +174,7 @@ function uniqueCheckBox(box) {
* @param {CallableFunction} errors fonction à effectuer en cas d'échec * @param {CallableFunction} errors fonction à effectuer en cas d'échec
*/ */
function sync_get(path, success, errors) { function sync_get(path, success, errors) {
//TODO Optimiser : rendre asynchrone + sans jquery
console.log("sync_get " + path); console.log("sync_get " + path);
$.ajax({ $.ajax({
async: false, async: false,
@ -177,16 +190,22 @@ function sync_get(path, success, errors) {
* @param {CallableFunction} success fonction à effectuer en cas de succès * @param {CallableFunction} success fonction à effectuer en cas de succès
* @param {CallableFunction} errors fonction à effectuer en cas d'échec * @param {CallableFunction} errors fonction à effectuer en cas d'échec
*/ */
function async_get(path, success, errors) { async function async_get(path, success, errors) {
console.log("async_get " + path); console.log("async_get " + path);
$.ajax({ try {
async: true, const response = await fetch(path);
type: "GET", if (response.ok) {
url: path, const data = await response.json();
success: success, success(data);
error: errors, } else {
}); throw new Error("Network response was not ok.");
}
} catch (error) {
console.error(error);
if (errors) errors(error);
}
} }
/** /**
* Fait une requête POST de façon synchrone * Fait une requête POST de façon synchrone
* @param {String} path adresse distante * @param {String} path adresse distante
@ -195,6 +214,7 @@ function async_get(path, success, errors) {
* @param {CallableFunction} errors fonction à effectuer en cas d'échec * @param {CallableFunction} errors fonction à effectuer en cas d'échec
*/ */
function sync_post(path, data, success, errors) { function sync_post(path, data, success, errors) {
//TODO Optimiser : rendre asynchrone + sans jquery
console.log("sync_post " + path); console.log("sync_post " + path);
$.ajax({ $.ajax({
async: false, async: false,
@ -212,17 +232,29 @@ function sync_post(path, data, success, errors) {
* @param {CallableFunction} success fonction à effectuer en cas de succès * @param {CallableFunction} success fonction à effectuer en cas de succès
* @param {CallableFunction} errors fonction à effectuer en cas d'échec * @param {CallableFunction} errors fonction à effectuer en cas d'échec
*/ */
function async_post(path, data, success, errors) { async function async_post(path, data, success, errors) {
console.log("sync_post " + path); console.log("async_post " + path);
return $.ajax({ try {
async: true, const response = await fetch(path, {
type: "POST", method: "POST",
url: path, headers: {
data: JSON.stringify(data), "Content-Type": "application/json",
success: success, },
error: errors, body: JSON.stringify(data),
}); });
if (response.ok) {
const responseData = await response.json();
success(responseData);
} else {
throw new Error("Network response was not ok.");
}
} catch (error) {
console.error(error);
if (errors) errors(error);
}
} }
// <<== Gestion des actions de masse ==>> // <<== Gestion des actions de masse ==>>
const massActionQueue = new Map(); const massActionQueue = new Map();
@ -268,8 +300,8 @@ function executeMassActionQueue() {
*/ */
const tlTimes = getTimeLineTimes(); const tlTimes = getTimeLineTimes();
let assiduite = { let assiduite = {
date_debut: tlTimes.deb.format(), date_debut: tlTimes.deb.toIsoUtcString(),
date_fin: tlTimes.fin.format(), date_fin: tlTimes.fin.toIsoUtcString(),
}; };
assiduite = setModuleImplId(assiduite); assiduite = setModuleImplId(assiduite);
@ -572,10 +604,13 @@ function formatDate(date, styles = { dateStyle: "full" }) {
*/ */
function updateDate() { function updateDate() {
const dateInput = document.querySelector("#tl_date"); const dateInput = document.querySelector("#tl_date");
let date = $(dateInput).datepicker("getDate");
if (date == null) {
date = new Date(Date.fromFRA(dateInput.value));
}
const date = dateInput.valueAsDate ?? new Date();
let dateStr = ""; let dateStr = "";
if (!verifyNonWorkDays(date.getDay(), nonWorkDays)) { if (!verifyNonWorkDays(date.getDay(), nonWorkDays)) {
dateStr = formatDate(date).capitalize(); dateStr = formatDate(date).capitalize();
} else { } else {
@ -595,7 +630,11 @@ function updateDate() {
) )
); );
openAlertModal("Attention", div, "", "#eec660"); openAlertModal("Attention", div, "", "#eec660");
dateInput.value = lastWorkDay.toISOString().split("T")[0]; $(dateInput).datepicker(
"setDate",
Date.toFRA(lastWorkDay.toIsoUtcString().split("T")[0])
);
dateStr = formatDate(lastWorkDay).capitalize(); dateStr = formatDate(lastWorkDay).capitalize();
} }
document.querySelector("#datestr").textContent = dateStr; document.querySelector("#datestr").textContent = dateStr;
@ -613,19 +652,10 @@ function getNearestWorkDay(date) {
} }
function verifyDateInSemester() { function verifyDateInSemester() {
const date = new moment.tz( const date = getDate();
document.querySelector("#tl_date").value,
TIMEZONE
);
const periodSemester = getFormSemestreDates(); const periodSemester = getFormSemestreDates();
return date.isBetween(periodSemester.deb, periodSemester.fin, "[]");
return date.isBetween(
periodSemester.deb,
periodSemester.fin,
undefined,
"[]"
);
} }
/** /**
@ -637,15 +667,15 @@ function setupDate(onchange = null) {
const input = document.querySelector("#tl_date"); const input = document.querySelector("#tl_date");
datestr.addEventListener("click", () => { datestr.addEventListener("click", () => {
if (!input.disabled) { if (!document.querySelector(".selectors").disabled) {
try { try {
input.showPicker(); document.querySelector(".infos .ui-datepicker-trigger").click();
} catch {} } catch {}
} }
}); });
if (onchange != null) { if (onchange != null) {
input.addEventListener("change", onchange); $(input).change(onchange);
} }
} }
@ -664,8 +694,8 @@ function getAssiduitesOnDateChange() {
* @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00) * @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00)
* @returns {String} la date intelligible * @returns {String} la date intelligible
*/ */
function formatDateModal(str, separator = "·") { function formatDateModal(str, separator = " ") {
return new moment.tz(str, TIMEZONE).format(`DD/MM/Y ${separator} HH:mm`); return new Date(str).format("DD/MM/Y HH:mm").replace(" ", separator);
} }
/** /**
@ -705,8 +735,8 @@ function verifyNonWorkDays(day, nonWorkdays) {
* Fonction qui vérifie si une période est dans un interval * Fonction qui vérifie si une période est dans un interval
* Objet période / interval * Objet période / interval
* { * {
* deb: moment.tz(<Date>), * deb: Date,
* fin: moment.tz(<Date>), * fin: Date,
* } * }
* @param {object} period * @param {object} period
* @param {object} interval * @param {object} interval
@ -718,19 +748,19 @@ function hasTimeConflict(period, interval) {
/** /**
* On récupère la période de la timeline * On récupère la période de la timeline
* @returns {deb : moment.tz(), fin: moment.tz()} * @returns {deb : Date, fin: Date)}
*/ */
function getTimeLineTimes() { function getTimeLineTimes() {
//getPeriodValues() -> retourne la position de la timeline [a,b] avec a et b des number //getPeriodValues() -> retourne la position de la timeline [a,b] avec a et b des number
let values = getPeriodValues(); let values = getPeriodValues();
//On récupère la date //On récupère la date
const dateiso = document.querySelector("#tl_date").value; const dateiso = getDate().format("YYYY-MM-DD");
//On génère des objets temps (moment.tz) //On génère des objets temps
values = values.map((el) => { values = values.map((el) => {
el = toTime(el).replace("h", ":"); el = toTime(el).replace("h", ":");
el = `${dateiso}T${el}`; el = `${dateiso}T${el}`;
return moment.tz(el, TIMEZONE); return new Date(el);
}); });
return { deb: values[0], fin: values[1] }; return { deb: values[0], fin: values[1] };
@ -744,8 +774,8 @@ function getTimeLineTimes() {
function isConflictSameAsPeriod(conflict, period = undefined) { function isConflictSameAsPeriod(conflict, period = undefined) {
const tlTimes = period == undefined ? getTimeLineTimes() : period; const tlTimes = period == undefined ? getTimeLineTimes() : period;
const clTimes = { const clTimes = {
deb: moment.tz(conflict.date_debut, TIMEZONE), deb: new Date(conflict.date_debut),
fin: moment.tz(conflict.date_fin, TIMEZONE), fin: new Date(conflict.date_fin),
}; };
return tlTimes.deb.isSame(clTimes.deb) && tlTimes.fin.isSame(clTimes.fin); return tlTimes.deb.isSame(clTimes.deb) && tlTimes.fin.isSame(clTimes.fin);
} }
@ -755,9 +785,10 @@ function isConflictSameAsPeriod(conflict, period = undefined) {
* @returns {Date} la date sélectionnée * @returns {Date} la date sélectionnée
*/ */
function getDate() { function getDate() {
const date = new Date(document.querySelector("#tl_date").value); const date =
date.setHours(0, 0, 0, 0); $("#tl_date").datepicker("getDate") ??
return date; new Date(Date.fromFRA(document.querySelector("#tl_date").value));
return date.startOf("day");
} }
/** /**
@ -766,10 +797,7 @@ function getDate() {
*/ */
function getNextDate() { function getNextDate() {
const date = getDate(); const date = getDate();
const next = new Date(date.valueOf()); return date.clone().add(1, "days");
next.setDate(date.getDate() + 1);
next.setHours(0, 0, 0, 0);
return next;
} }
/** /**
* Retourne un objet date représentant le jour précédent * Retourne un objet date représentant le jour précédent
@ -777,10 +805,7 @@ function getNextDate() {
*/ */
function getPrevDate() { function getPrevDate() {
const date = getDate(); const date = getDate();
const next = new Date(date.valueOf()); return date.clone().add(-1, "days");
next.setDate(date.getDate() - 1);
next.setHours(0, 0, 0, 0);
return next;
} }
/** /**
@ -788,44 +813,19 @@ function getPrevDate() {
* @param {Date} date * @param {Date} date
* @returns {string} la date iso avec le timezone * @returns {string} la date iso avec le timezone
*/ */
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function (num) {
return (num < 10 ? "0" : "") + num;
};
return (
date.getFullYear() +
"-" +
pad(date.getMonth() + 1) +
"-" +
pad(date.getDate()) +
"T" +
pad(date.getHours()) +
":" +
pad(date.getMinutes()) +
":" +
pad(date.getSeconds()) +
dif +
pad(Math.floor(Math.abs(tzo) / 60)) +
":" +
pad(Math.abs(tzo) % 60)
);
}
/** /**
* Transforme un temps numérique en une date moment.tz * Transforme un temps numérique en une date
* @param {number} nb * @param {number} nb
* @returns {moment.tz} Une date formée du temps donné et de la date courante * @returns {Date} Une date formée du temps donné et de la date courante
*/ */
function numberTimeToDate(nb) { function numberTimeToDate(nb) {
time = toTime(nb).replace("h", ":"); time = toTime(nb).replace("h", ":");
date = document.querySelector("#tl_date").value; date = getDate().format("YYYY-MM-DD");
datetime = `${date}T${time}`; datetime = `${date}T${time}`;
return moment.tz(datetime, TIMEZONE); return new Date(datetime);
} }
// <<== Gestion des assiduités ==>> // <<== Gestion des assiduités ==>>
@ -841,8 +841,8 @@ function numberTimeToDate(nb) {
function getAssiduitesFromEtuds(clear, deb, fin) { function getAssiduitesFromEtuds(clear, deb, fin) {
const etudIds = Object.keys(etuds).join(","); const etudIds = Object.keys(etuds).join(",");
const date_debut = deb ? deb : toIsoString(getPrevDate()); const date_debut = deb ? deb : getPrevDate().toIsoUtcString();
const date_fin = fin ? fin : toIsoString(getNextDate()); const date_fin = fin ? fin : getNextDate().toIsoUtcString();
if (clear) { if (clear) {
assiduites = {}; assiduites = {};
@ -885,8 +885,8 @@ function getAssiduitesFromEtuds(clear, deb, fin) {
function createAssiduite(etat, etudid) { function createAssiduite(etat, etudid) {
const tlTimes = getTimeLineTimes(); const tlTimes = getTimeLineTimes();
let assiduite = { let assiduite = {
date_debut: tlTimes.deb.format(), date_debut: tlTimes.deb.toIsoUtcString(),
date_fin: tlTimes.fin.format(), date_fin: tlTimes.fin.toIsoUtcString(),
etat: etat, etat: etat,
}; };
@ -1067,10 +1067,11 @@ function getAssiduitesConflict(etudid, periode) {
return etudAssiduites.filter((assi) => { return etudAssiduites.filter((assi) => {
const interval = { const interval = {
deb: moment.tz(assi.date_debut, TIMEZONE), deb: new Date(assi.date_debut),
fin: moment.tz(assi.date_fin, TIMEZONE), fin: new Date(assi.date_fin),
}; };
return hasTimeConflict(periode, interval); const test = hasTimeConflict(periode, interval);
return test;
}); });
} }
@ -1085,21 +1086,21 @@ function getLastAssiduiteOfPrevDate(etudid) {
return ""; return "";
} }
const period = { const period = {
deb: moment.tz(getPrevDate(), TIMEZONE), deb: getPrevDate(),
fin: moment.tz(getDate(), TIMEZONE), fin: getDate(),
}; };
const prevAssiduites = etudAssiduites const prevAssiduites = etudAssiduites
.filter((assi) => { .filter((assi) => {
const interval = { const interval = {
deb: moment.tz(assi.date_debut, TIMEZONE), deb: new Date(assi.date_debut),
fin: moment.tz(assi.date_fin, TIMEZONE), fin: new Date(assi.date_fin),
}; };
return hasTimeConflict(period, interval); return hasTimeConflict(period, interval);
}) })
.sort((a, b) => { .sort((a, b) => {
const a_fin = moment.tz(a.date_fin, TIMEZONE); const a_fin = new Date(a.date_fin);
const b_fin = moment.tz(b.date_fin, TIMEZONE); const b_fin = new Date(b.date_fin);
return b_fin < a_fin; return b_fin < a_fin;
}); });
@ -1134,8 +1135,8 @@ function getAssiduiteValue(field) {
* @param {String | Number} etudid identifiant de l'étudiant * @param {String | Number} etudid identifiant de l'étudiant
*/ */
function actualizeEtudAssiduite(etudid) { function actualizeEtudAssiduite(etudid) {
const date_debut = toIsoString(getPrevDate()); const date_debut = getPrevDate().toIsoUtcString();
const date_fin = toIsoString(getNextDate()); const date_fin = getNextDate().toIsoUtcString();
const url_api = const url_api =
getUrl() + getUrl() +
@ -1163,7 +1164,7 @@ function getAllAssiduitesFromEtud(
.replace("°", courant ? "&courant" : "") .replace("°", courant ? "&courant" : "")
: "" : ""
}`; }`;
//TODO Utiliser async_get au lieu de jquery
$.ajax({ $.ajax({
async: true, async: true,
type: "GET", type: "GET",
@ -1232,8 +1233,8 @@ function assiduiteAction(element) {
assiduites[etudid], assiduites[etudid],
getTimeLineTimes(), getTimeLineTimes(),
{ {
deb: new moment.tz(getDate(), TIMEZONE), deb: getDate(),
fin: new moment.tz(getNextDate(), TIMEZONE), fin: getNextDate(),
} }
); );
const update = (assi) => { const update = (assi) => {
@ -1377,7 +1378,6 @@ function insertEtudRow(etud, index, output = false) {
date_fin: null, date_fin: null,
prevAssiduites: prevAssiduite, prevAssiduites: prevAssiduite,
}; };
if (conflict.length > 0) { if (conflict.length > 0) {
assiduite.etatAssiduite = conflict[0].etat; assiduite.etatAssiduite = conflict[0].etat;
@ -1545,8 +1545,8 @@ function getFormSemestreDates() {
const dateFin = document.getElementById("formsemestre_date_fin").textContent; const dateFin = document.getElementById("formsemestre_date_fin").textContent;
return { return {
deb: dateDeb, deb: new Date(dateDeb),
fin: dateFin, fin: new Date(dateFin),
}; };
} }
@ -1613,8 +1613,10 @@ function getJustificatifFromPeriod(date, etudid, update) {
url: url:
getUrl() + getUrl() +
`/api/justificatifs/${etudid}/query?date_debut=${date.deb `/api/justificatifs/${etudid}/query?date_debut=${date.deb
.add(1, "s") .add(1, "seconds")
.format()}&date_fin=${date.fin.subtract(1, "s").format()}`, .toIsoUtcString()}&date_fin=${date.fin
.add(-1, "seconds")
.toIsoUtcString()}`,
success: (data) => { success: (data) => {
update(data); update(data);
}, },
@ -1646,8 +1648,8 @@ function fastJustify(assiduite) {
} }
const period = { const period = {
deb: new moment.tz(assiduite.date_debut, TIMEZONE), deb: new Date(assiduite.date_debut),
fin: new moment.tz(assiduite.date_fin, TIMEZONE), fin: new Date(assiduite.date_fin),
}; };
const action = (justifs) => { const action = (justifs) => {
//créer un nouveau justificatif //créer un nouveau justificatif
@ -1660,8 +1662,8 @@ function fastJustify(assiduite) {
//créer justificatif //créer justificatif
const justif = { const justif = {
date_debut: new moment.tz(assiduite.date_debut, TIMEZONE).format(), date_debut: new Date(assiduite.date_debut).toIsoUtcString(),
date_fin: new moment.tz(assiduite.date_fin, TIMEZONE).format(), date_fin: new Date(assiduite.date_fin).toIsoUtcString(),
raison: raison, raison: raison,
etat: etat, etat: etat,
}; };
@ -1694,7 +1696,7 @@ function fastJustify(assiduite) {
content, content,
success, success,
() => {}, () => {},
"#7059FF" "var(--color-primary)"
); );
}; };
if (assiduite.etudid) { if (assiduite.etudid) {
@ -1744,6 +1746,8 @@ function getAllJustificatifsFromEtud(
`/api/justificatifs/${etudid}${ `/api/justificatifs/${etudid}${
order ? "/query?order°".replace("°", courant ? "&courant" : "") : "" order ? "/query?order°".replace("°", courant ? "&courant" : "") : ""
}`; }`;
//TODO Utiliser async_get au lieu de jquery
$.ajax({ $.ajax({
async: true, async: true,
type: "GET", type: "GET",

576
app/static/js/date_utils.js Normal file
View File

@ -0,0 +1,576 @@
/**
* Transforme une date du format français (DD/MM/YYYY) au format iso (YYYY-MM-DD)
* Exemple d'utilisation :
* new Date(Date.fromFRA("30/06/2024")) -> new Date("2024-06-30")
* @param {string} dateFra
* @returns {string} dateIso
*/
Date.fromFRA = function (dateFra) {
if (dateFra == "") return "";
// Expression régulière pour valider le format de date ISO (YYYY-MM-DD)
const regexDateFra = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/\d{4}$/;
// Vérification du format de la date ISO
if (!regexDateFra.test(dateFra)) {
throw new Error(
`La date (format français) passée en paramètre [${dateFra}] n'est pas valide.`
);
}
// Conversion du format français (DD/MM/YYYY) au format ISO (YYYY-MM-DD)
return `${dateFra.substring(6, 10)}-${dateFra.substring(
3,
5
)}-${dateFra.substring(0, 2)}`;
};
/**
* Transforme une date du format iso (YYYY-MM-DD) au format français (DD/MM/YYYY)
* Exemple d'utilisation :
* Date.toFRA("2024-06-30") -> "30/06/2024"
* @param {string} dateIso
* @returns {string} dateFra
*/
Date.toFRA = function (dateIso) {
if (dateIso == "") return "";
// Expression régulière pour valider le format de date ISO (YYYY-MM-DD)
const regexDateIso = /^\d{4}-(0\d|1[0-2])-([0-2]\d|3[01])$/;
// Vérification du format de la date ISO
if (!regexDateIso.test(dateIso)) {
throw new Error(
`La date ISO passée en paramètre [${dateIso}] n'est pas valide.`
);
}
// Conversion du format ISO (YYYY-MM-DD) en format français (DD/MM/YYYY)
return `${dateIso.substring(8, 10)}/${dateIso.substring(
5,
7
)}/${dateIso.substring(0, 4)}`;
};
/**
* Vérifie si le début de l'une des périodes est avant la fin de l'autre
* et si la fin de cette période est après le début de l'autre.
* @param {Object} period {deb:Object, fin:Object}
* @param {Object} interval {deb:Object, fin:Object}
* @returns vrai si la periode et l'interval ont une intersection commune
*/
Date.intersect = function (period, interval) {
return period.deb <= interval.fin && period.fin >= interval.deb;
};
Object.defineProperty(Date.prototype, "isValid", {
value: function () {
return !Number.isNaN(this.getTime());
},
});
Object.defineProperty(Date.prototype, "startOf", {
/**
* Génère u la date à la plus petite valeur pour la précision donnée.
* @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds).
* @returns {Date} - Une nouvelle date ajustée.
*/
value: function (precision) {
const newDate = this.clone();
switch (precision) {
case "year":
newDate.setMonth(0);
case "month":
newDate.setDate(1);
case "day":
newDate.setHours(0);
case "hours":
newDate.setMinutes(0);
case "minutes":
newDate.setSeconds(0);
case "seconds":
newDate.setMilliseconds(0);
break;
case "milliseconds":
break;
default:
throw new Error(
`Invalid precision for startOf function [${precision}]`
);
}
return newDate;
},
});
Object.defineProperty(Date.prototype, "endOf", {
/**
* Ajuste la date à la plus grande valeur pour la précision donnée.
* @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds).
* @returns {Date} - Une nouvelle date ajustée.
*/
value: function (precision) {
const newDate = this.clone();
switch (precision) {
case "year":
newDate.setMonth(11); // Décembre est le 11ème mois (0-indexé)
case "month":
newDate.setDate(0); // Le jour 0 du mois suivant est le dernier jour du mois courant
newDate.setMonth(newDate.getMonth() + 1);
case "day":
newDate.setHours(23); // 23 heures est la dernière heure de la journée
case "hours":
newDate.setMinutes(59); // 59 minutes est la dernière minute de l'heure
case "minutes":
newDate.setSeconds(59); // 59 secondes est la dernière seconde de la minute
case "seconds":
newDate.setMilliseconds(999); // 999 millisecondes est la dernière milliseconde de la seconde
break;
case "milliseconds":
// Rien à faire pour les millisecondes
break;
default:
throw new Error("Invalid precision for endOf function");
}
return newDate;
},
});
Object.defineProperty(Date.prototype, "isBefore", {
/**
* Retourne vrai si la date est située avant la date fournie
* @param {Date} date
* @returns {boolean}
*/
value: function (date) {
return this.valueOf() < date.valueOf();
},
});
Object.defineProperty(Date.prototype, "isAfter", {
/**
* Retourne vrai si la date est située après la date fournie
* @param {Date} date
* @returns {boolean}
*/
value: function (date) {
return this.valueOf() > date.valueOf();
},
});
Object.defineProperty(Date.prototype, "isSame", {
/**
* Retourne vrai si les dates sont les mêmes
* @param {Date} date
* @param {string} precision default : "milliseconds"
* @returns boolean
*/
value: function (date, precision = "milliseconds") {
return (
this.startOf(precision).valueOf() == date.startOf(precision).valueOf()
);
},
});
Object.defineProperty(Date.prototype, "isBetween", {
/**
* Vérifie si la date est comprise dans une période avec une précision et une inclusivité optionnelles
* @param {Date} deb - La date de début de la période
* @param {Date} fin - La date de fin de la période
* @param {String} bornes - L'inclusivité/exclusivité de la comparaison ("[]", "()", "[)", "(]")
* - bornes incluses : []
* - bornes excluses : ()
* - borne gauche incluse et borne droit excluse : [)
* - borne gauche excluse et borne droit incluse : (]
*/
value: function (deb, fin, bornes = "[]") {
// Ajuste la date actuelle, la date de début et la date de fin à la précision spécifiée
// Vérifie les bornes en fonction de l'inclusivité/exclusivité spécifiée dans 'bornes'
const check_deb =
bornes[0] === "("
? this.valueOf() > deb.valueOf()
: this.valueOf() >= deb.valueOf();
const check_fin =
bornes[1] === ")"
? fin.valueOf() > this.valueOf()
: fin.valueOf() >= this.valueOf();
return check_deb && check_fin;
},
});
Object.defineProperty(Date.prototype, "toIsoUtcString", {
/**
* @returns date au format iso utc (yyyy-mm-ddThh:MM±oo:oo:oo)
*/
value: function () {
const date = this;
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? "+" : "-",
pad = function (num) {
return (num < 10 ? "0" : "") + num;
};
return (
date.getFullYear() +
"-" +
pad(date.getMonth() + 1) +
"-" +
pad(date.getDate()) +
"T" +
pad(date.getHours()) +
":" +
pad(date.getMinutes()) +
":" +
pad(date.getSeconds()) +
dif +
pad(Math.floor(Math.abs(tzo) / 60)) +
":" +
pad(Math.abs(tzo) % 60)
);
},
});
Object.defineProperty(Date.prototype, "clone", {
/**
* @returns Retourne une copie de la date (copie non liée)
*/
value: function () {
return structuredClone(this);
},
});
Object.defineProperty(Date.prototype, "format", {
value: function (formatString) {
let iso = this.toIsoUtcString();
switch (formatString) {
case "DD/MM/Y HH:mm":
return this.toLocaleString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
case "DD/MM/YYYY HH:mm":
return this.toLocaleString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
case "YYYY-MM-DDTHH:mm":
// slice : YYYY-MM-DDTHH
// slice + 3 : YYYY-MM-DDTHH:mm
return iso.slice(0, iso.indexOf(":") + 3);
case "YYYY-MM-DD":
return iso.slice(0, iso.indexOf("T"));
default:
return this.toIsoUtcString();
}
},
});
Object.defineProperty(Date.prototype, "add", {
/**
* Ajoute une valeur spécifiée à un élément de la date.
* @param {number} value - La valeur à ajouter.
* @param {string} type - Le type de la valeur (year, month, day, hours, minutes, seconds).
*/
value: function (value, type) {
switch (type) {
case "years":
this.setFullYear(this.getFullYear() + value);
break;
case "months":
this.setMonth(this.getMonth() + value);
break;
case "days":
this.setDate(this.getDate() + value);
break;
case "hours":
this.setHours(this.getHours() + value);
break;
case "minutes":
this.setMinutes(this.getMinutes() + value);
break;
case "seconds":
this.setSeconds(this.getSeconds() + value);
break;
default:
throw new Error(
`Invalid type for adding to date | type : ${type} value : ${value}`
);
}
return this; // Return the modified date
},
});
class Duration {
/**
* Constructeur de la classe Duration.
* @param {Date} start - La date de début de la période.
* @param {Date} end - La date de fin de la période.
*/
constructor(start, end) {
this.start = start; // Stocke la date de début.
this.end = end; // Stocke la date de fin.
this.duration = end - start; // Calcule la durée en millisecondes entre les deux dates.
}
/**
* Calcule le nombre d'années entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre d'années arrondi à quatre décimales.
*/
get years() {
const startYear = this.start.getFullYear(); // Obtient l'année de la date de début.
const endYear = this.end.getFullYear(); // Obtient l'année de la date de fin.
// Calcule la différence en années et arrondit à quatre décimales.
return parseFloat((endYear - startYear).toFixed(4));
}
/**
* Calcule le nombre de mois entre les deux dates, en tenant compte des années et des jours, et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de mois arrondi à quatre décimales.
*/
get months() {
const years = this.years; // Nombre d'années complètes.
// Calcule la différence en mois, en ajoutant la différence en jours divisée par 30 pour une approximation.
const months =
years * 12 +
(this.end.getMonth() - this.start.getMonth()) +
(this.end.getDate() - this.start.getDate()) / 30;
// Arrondit à quatre décimales.
return parseFloat(months.toFixed(4));
}
/**
* Calcule le nombre de jours entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de jours arrondi à quatre décimales.
*/
get days() {
// Convertit la durée en millisecondes en jours et arrondit à quatre décimales.
return parseFloat((this.duration / (24 * 60 * 60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre d'heures entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre d'heures arrondi à quatre décimales.
*/
get hours() {
// Convertit la durée en millisecondes en heures et arrondit à quatre décimales.
return parseFloat((this.duration / (60 * 60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre de minutes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de minutes arrondi à quatre décimales.
*/
get minutes() {
// Convertit la durée en millisecondes en minutes et arrondit à quatre décimales.
return parseFloat((this.duration / (60 * 1000)).toFixed(4));
}
/**
* Calcule le nombre de secondes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de secondes arrondi à quatre décimales.
*/
get seconds() {
// Convertit la durée en millisecondes en secondes et arrondit à quatre décimales.
return parseFloat((this.duration / 1000).toFixed(4));
}
/**
* Obtient le nombre de millisecondes entre les deux dates et arrondit le résultat à quatre décimales.
* @return {number} Le nombre de millisecondes arrondi à quatre décimales.
*/
get milliseconds() {
// Arrondit la durée totale en millisecondes à quatre décimales.
return parseFloat(this.duration.toFixed(4));
}
}
class ScoDocDateTimePicker extends HTMLElement {
constructor() {
super();
// Définir si le champ est requis
this.required = this.hasAttribute("required");
// Initialiser le shadow DOM
const shadow = this.attachShadow({ mode: "open" });
// Créer l'input pour la date
const dateInput = document.createElement("input");
dateInput.type = "date";
dateInput.id = "date";
// Créer l'input pour l'heure
const timeInput = document.createElement("input");
timeInput.type = "time";
timeInput.id = "time";
timeInput.step = 60;
// Ajouter les inputs dans le shadow DOM
shadow.appendChild(dateInput);
shadow.appendChild(timeInput);
// Gestionnaires d'événements pour la mise à jour de la valeur
dateInput.addEventListener("change", () => this.updateValue());
timeInput.addEventListener("change", () => this.updateValue());
// Style CSS pour les inputs
const style = document.createElement("style");
style.textContent = `
input {
display: inline-block;
}
input:invalid {
border: 1px solid red;
}
`;
// Ajouter le style au shadow DOM
shadow.appendChild(style);
}
static get observedAttributes() {
return ["show"]; // Ajoute 'show' à la liste des attributs observés
}
connectedCallback() {
// Récupérer l'attribut 'name'
this.name = this.getAttribute("name");
// Créer un input caché pour la valeur datetime
this.hiddenInput = document.createElement("input");
this.hiddenInput.type = "hidden";
this.hiddenInput.name = this.name;
this.appendChild(this.hiddenInput);
// Gérer la soumission du formulaire
this.closest("form")?.addEventListener("submit", (e) => {
if (!this.validate()) {
e.preventDefault(); // Empêcher la soumission si non valide
this.dispatchEvent(
new Event("invalid", { bubbles: true, cancelable: true })
);
} else {
// Mettre à jour la valeur de l'input caché avant la soumission
this.hiddenInput.value = this.isValid()
? this.valueAsDate.toIsoUtcString()
: "";
}
});
this.updateDisplay();
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "show") {
this.updateDisplay(); // Met à jour l'affichage si l'attribut 'show' change
}
}
updateDisplay() {
const mode = this.getAttribute("show") || "both";
const dateInput = this.shadowRoot.querySelector("#date");
const timeInput = this.shadowRoot.querySelector("#time");
switch (mode) {
case "date":
dateInput.style.display = "inline-block";
timeInput.style.display = "none";
break;
case "time":
dateInput.style.display = "none";
timeInput.style.display = "inline-block";
break;
case "both":
default:
dateInput.style.display = "inline-block";
timeInput.style.display = "inline-block";
}
}
// Vérifier si la valeur forme une date valide
isValid() {
return !Number.isNaN(this.valueAsDate.getTime());
}
// Valider l'élément
validate() {
if (this.required && !this.isValid()) {
return false;
}
return true;
}
// Mettre à jour la valeur interne
updateValue() {
const dateInput = this.shadowRoot.querySelector("#date");
const timeInput = this.shadowRoot.querySelector("#time");
this._value = `${dateInput.value}T${timeInput.value}`;
this.dispatchEvent(new Event("change", { bubbles: true }));
// Appliquer le style 'invalid' si nécessaire
dateInput.classList.toggle("invalid", this.required && !this.isValid());
timeInput.classList.toggle("invalid", this.required && !this.isValid());
}
// Getter pour obtenir la valeur actuelle.
get value() {
return this._value;
}
get valueAsObject() {
const dateInput = this.shadowRoot.querySelector("#date");
const timeInput = this.shadowRoot.querySelector("#time");
return {
date: dateInput.value,
time: timeInput.value,
};
}
// Getter pour obtenir la valeur en tant qu'objet Date.
get valueAsDate() {
return new Date(this._value);
}
// Setter pour définir la valeur. Sépare la valeur en date et heure et les définit individuellement.
set value(val) {
let [date, time] = val.split("T");
this.shadowRoot.querySelector("#date").value = date;
time = time.substring(0, 5);
this.shadowRoot.querySelector("#time").value = time;
this._value = val;
}
// Setter pour définir la valeur à partir d'un objet avec les propriétés 'date' et 'time'.
set valueAsObject(obj) {
const dateInput = this.shadowRoot.querySelector("#date");
const timeInput = this.shadowRoot.querySelector("#time");
if (obj.hasOwnProperty("date")) {
dateInput.value = obj.date || ""; // Définit la valeur de l'input de date si elle est fournie
}
if (obj.hasOwnProperty("time")) {
timeInput.value = obj.time.substring(0, 5) || ""; // Définit la valeur de l'input d'heure si elle est fournie
}
// Met à jour la valeur interne en fonction des nouvelles valeurs des inputs
this.updateValue();
}
// Setter pour définir la valeur à partir d'un objet Date.
set valueAsDate(dateVal) {
// Formatage de l'objet Date en string et mise à jour de la valeur.
this.value = `${dateVal.getFullYear()}-${String(
dateVal.getMonth() + 1
).padStart(2, "0")}-${String(dateVal.getDate()).padStart(2, "0")}T${String(
dateVal.getHours()
).padStart(2, "0")}:${String(dateVal.getMinutes()).padStart(2, "0")}`;
}
}
// Définition du nouvel élément personnalisé 'scodoc-datetime'.
customElements.define("scodoc-datetime", ScoDocDateTimePicker);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -11,19 +11,15 @@
<section class="justi-form page"> <section class="justi-form page">
<fieldset> <fieldset>
<div class="justi-row">
<button onclick="validerFormulaire(this)">Créer le justificatif</button>
<button onclick="effacerFormulaire()">Remettre à zero</button>
</div>
<div class="justi-row"> <div class="justi-row">
<div class="justi-label"> <div class="justi-label">
<legend for="justi_date_debut" required>Date de début</legend> <legend for="justi_date_debut" required>Date de début</legend>
<input type="datetime-local" name="justi_date_debut" id="justi_date_debut"> <scodoc-datetime name="justi_date_debut" id="justi_date_debut"> </scodoc-datetime>
<span>Journée(s) entière(s)</span> <input type="checkbox" name="justi_journee" id="justi_journee"> <span>Journée entière</span> <input type="checkbox" name="justi_journee" id="justi_journee">
</div> </div>
<div class="justi-label" id="date_fin"> <div class="justi-label" id="date_fin">
<legend for="justi_date_fin" required>Date de fin</legend> <legend for="justi_date_fin" required>Date de fin</legend>
<input type="datetime-local" name="justi_date_fin" id="justi_date_fin"> <scodoc-datetime name="justi_date_fin" id="justi_date_fin"></scodoc-datetime>
</div> </div>
</div> </div>
@ -55,6 +51,10 @@
</div> </div>
</div> </div>
<div class="justi-row">
<button onclick="validerFormulaire(this)">Créer le justificatif</button>
<button onclick="effacerFormulaire()">Remettre à zero</button>
</div>
</fieldset> </fieldset>
@ -112,15 +112,14 @@
function validateFields() { function validateFields() {
const field = document.querySelector('.justi-form') const field = document.querySelector('.justi-form')
const { deb, fin } = getDates() const { deb, fin } = getDates()
const date_debut = new Date(deb);
const date_fin = new Date(fin);
if (deb == "" || fin == "") { if (deb == "" || fin == "" || !date_debut.isValid() || !date_fin.isValid()) {
openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin valide."), "", color = "crimson"); openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin valide."), "", color = "crimson");
return false; return false;
} }
const date_debut = moment.tz(deb, TIMEZONE);
const date_fin = moment.tz(fin, TIMEZONE);
if (date_fin.isBefore(date_debut)) { if (date_fin.isBefore(date_debut)) {
openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson"); openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson");
return false; return false;
@ -138,8 +137,8 @@
const raison = field.querySelector('#justi_raison').value; const raison = field.querySelector('#justi_raison').value;
return { return {
date_debut: moment.tz(deb, TIMEZONE).format(), date_debut: new Date(deb).toIsoUtcString(),
date_fin: moment.tz(fin, TIMEZONE).format(), date_fin: new Date(fin).toIsoUtcString(),
etat: etat, etat: etat,
raison: raison, raison: raison,
} }
@ -153,7 +152,7 @@
const requests = [] const requests = []
Array.from(in_files.files).forEach((f) => { Array.from(in_files.files).forEach((f) => {
pushToast(generateToast(document.createTextNode(`Importation du fichier : ${f.name} commencée`), color = "#f0c865")); pushToast(generateToast(document.createTextNode(`Importation du fichier : ${f.name} commencée`), color = "var(--color-information)"));
const fd = new FormData(); const fd = new FormData();
fd.append('file', f); fd.append('file', f);
requests.push( requests.push(
@ -219,39 +218,31 @@
} }
function dayOnly() { function dayOnly() {
const { deb, fin } = getDates(); const date_deb = document.getElementById("justi_date_debut");
const date_fin = document.getElementById("justi_date_fin");
if (document.getElementById('justi_journee').checked) { if (document.getElementById('justi_journee').checked) {
document.getElementById("justi_date_debut").type = "date" date_deb.setAttribute("show", "date")
document.getElementById("justi_date_debut").value = deb.slice(0, deb.indexOf('T')) date_fin.setAttribute("show", "date")
document.getElementById("date_fin").classList.add("hidden");
document.getElementById("justi_date_fin").type = "date"
document.getElementById("justi_date_fin").value = fin.slice(0, fin.indexOf('T'))
} else { } else {
document.getElementById("justi_date_debut").type = "datetime-local" date_deb.removeAttribute("show")
document.getElementById("justi_date_debut").value = `${deb}T${assi_morning}` date_fin.removeAttribute("show")
document.getElementById("date_fin").classList.remove("hidden");
document.getElementById("justi_date_fin").type = "datetime-local"
document.getElementById("justi_date_fin").value = `${fin}T${assi_evening}`
} }
} }
function getDates() { function getDates() {
if (document.querySelector('.page #justi_journee').checked) { const date_deb = document.querySelector(".page #justi_date_debut")
const date_str_deb = document.querySelector(".page #justi_date_debut").value const date_fin = document.querySelector(".page #justi_date_fin")
const date_str_fin = document.querySelector(".page #justi_date_fin").value const journee = document.querySelector('.page #justi_journee').checked
const deb = date_deb.valueAsObject.date + "T" + (journee ? assi_morning : date_deb.valueAsObject.time)
const fin = (journee ? date_deb.valueAsObject.date : date_fin.valueAsObject.date) + "T" + (journee ? assi_evening : date_fin.valueAsObject.time)
return { return {
"deb": date_str_deb ? `${date_str_deb}T${assi_morning}` : "", "deb": deb,
"fin": date_str_fin ? `${date_str_fin}T${assi_evening}` : "", "fin": fin,
}
}
return {
"deb": document.querySelector(".page #justi_date_debut").value,
"fin": document.querySelector(".page #justi_date_fin").value,
} }
} }
@ -265,6 +256,9 @@
loadAll(); loadAll();
document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() }); document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() });
dayOnly() dayOnly()
document.getElementById("justi_date_debut").valueAsObject = { time: assi_morning }
document.getElementById("justi_date_fin").valueAsObject = { time: assi_evening }
} }
</script> </script>
{% endblock pageContent %} {% endblock pageContent %}

View File

@ -12,10 +12,10 @@
<!-- Statistiques d'assiduité (nb pres, nb retard, nb absence) + nb justifié --> <!-- Statistiques d'assiduité (nb pres, nb retard, nb absence) + nb justifié -->
<h4>Statistiques d'assiduité</h4> <h4>Statistiques d'assiduité</h4>
<div class="stats-inputs"> <div class="stats-inputs">
<label class="stats-label"> Date de début<input type="date" name="stats_date_debut" id="stats_date_debut" <label class="stats-label"> Date de début<input type="text" class="datepicker" name="stats_date_debut"
value="{{date_debut}}"></label> id="stats_date_debut" value="{{date_debut}}"></label>
<label class="stats-label"> Date de fin<input type="date" name="stats_date_fin" id="stats_date_fin" <label class="stats-label"> Date de fin<input type="text" class="datepicker" name="stats_date_fin"
value="{{date_fin}}"></label> id="stats_date_fin" value="{{date_fin}}"></label>
<button onclick="stats()">Actualiser</button> <button onclick="stats()">Actualiser</button>
</div> </div>
@ -87,27 +87,27 @@
function stats() { function stats() {
const dd_val = document.getElementById('stats_date_debut').value; const dd_val = document.getElementById('stats_date_debut').value;
const df_val = document.getElementById('stats_date_fin').value; const df_val = document.getElementById('stats_date_fin').value;
let date_debut = new Date(Date.fromFRA(dd_val));
if (dd_val == "" || df_val == "") { let date_fin = new Date(Date.fromFRA(df_val));
if (dd_val == "" || df_val == "" || !date_debut.isValid() || !date_debut.isValid()) {
openAlertModal("Dates invalides", document.createTextNode('Les dates sélectionnées sont invalides')); openAlertModal("Dates invalides", document.createTextNode('Les dates sélectionnées sont invalides'));
return; return;
} }
const date_debut = new moment.tz(dd_val + "T00:00", TIMEZONE); date_debut = date_debut.startOf("day")
const date_fin = new moment.tz(df_val + "T23:59", TIMEZONE); date_fin = date_fin.endOf("day")
if (date_debut.valueOf() > date_fin.valueOf()) { if (date_debut.isAfter(date_fin)) {
openAlertModal("Dates invalides", document.createTextNode('La date de début se situe après la date de fin.')); openAlertModal("Dates invalides", document.createTextNode('La date de début se situe après la date de fin.'));
return; return;
} }
countAssiduites(date_debut.toIsoUtcString(), date_fin.toIsoUtcString())
countAssiduites(date_debut.format(), date_fin.format())
} }
function getAssiduitesCount(dateDeb, dateFin, query) { function getAssiduitesCount(dateDeb, dateFin, query) {
const url_api = getUrl() + `/api/assiduites/${etudid}/count/query?date_debut=${dateDeb}&date_fin=${dateFin}&${query}`; const url_api = getUrl() + `/api/assiduites/${etudid}/count/query?date_debut=${dateDeb}&date_fin=${dateFin}&${query}`;
//Utiliser async_get au lieu de Jquery
return $.ajax({ return $.ajax({
async: true, async: true,
type: "GET", type: "GET",
@ -121,6 +121,7 @@
} }
function countAssiduites(dateDeb, dateFin) { function countAssiduites(dateDeb, dateFin) {
//TODO Utiliser Fetch when plutot que jquery
$.when( $.when(
getAssiduitesCount(dateDeb, dateFin, `etat=present`), getAssiduitesCount(dateDeb, dateFin, `etat=present`),
getAssiduitesCount(dateDeb, dateFin, `etat=retard`), getAssiduitesCount(dateDeb, dateFin, `etat=retard`),

View File

@ -4,37 +4,121 @@
<div class="pageContent"> <div class="pageContent">
{{minitimeline | safe }} {{minitimeline | safe }}
<h2>Assiduité de {{sco.etud.nomprenom}}</h2> <h2>Assiduité de {{sco.etud.nomprenom}}</h2>
<div class="options">
<input type="checkbox" id="show_pres" name="show_pres"><label for="show_pres">afficher les présences</label>
<input type="checkbox" name="show_reta" id="show_reta"><label for="show_reta">afficher les retards</label>
<input type="checkbox" name="mode_demi" id="mode_demi" checked><label for="mode_demi">mode demi journée</label>
</div>
<div class="calendrier"> <div class="calendrier">
</div> </div>
<div class="annee"> <div class="annee">
<span>Année scolaire 2022-2023 Changer année: </span> <span id="label-annee">Année scolaire 2022-2023</span><span id="label-changer" style="margin-left: 5px;">Changer
année: </span>
<select name="" id="annee" onchange="setterAnnee(this.value)"> <select name="" id="annee" onchange="setterAnnee(this.value)">
</select> </select>
<span id="label-nom">Assiduité de {{sco.etud.nomprenom}}</span>
</div> </div>
<div class="help"> <div class="help">
<h3>Calendrier</h3> <h3>Calendrier</h3>
<p>Les jours non travaillés sont affiché en violet</p> <p>Code couleur</p>
<p>Les jours possèdant une bordure "bleu" sont des jours où des absences/retards ont été justifiées par un <ul class="couleurs">
justificatif valide</p> <li><span title="Vert" class="present demo"></span> &rightarrow; présence de l'étudiant lors de la période
<p>Les jours possèdant une bordure "rouge" sont des jours où des absences/retards ont été justifiées par un </li>
justificatif non valide</p> <li><span title="Bleu clair" class="nonwork demo"></span> &rightarrow; la période n'est pas travaillée
<p>Le jour sera affiché en : </p> </li>
<ul> <li><span title="Rouge" class="absent demo"></span> &rightarrow; absence de l'étudiant lors de la période
<li>Rouge : s'il y a une absence enregistrée</li> </li>
<li>Orange : s'il y a un retard et pas d'absence</li> <li><span title="Rose" class="demo color absent est_just"></span> &rightarrow; absence justifiée
<li>Vert : s'il y a une présence enregistrée mais pas d'absence ni de retard</li> </li>
<li>Blanc : s'il n'y a rien d'enregistré</li> <li><span title="Orange" class="retard demo"></span> &rightarrow; retard de l'étudiant lors de la période
</li>
<li><span title="Jaune clair" class="demo color retard est_just"></span> &rightarrow; retard justifié
</li>
<li><span title="Quart Bleu" class="est_just demo"></span> &rightarrow; la période est justifiée par un
justificatif valide</li>
<li><span title="Quart Violet" class="invalide demo"></span> &rightarrow; la période est
justifiée par un justificatif non valide / en attente de validation
</li>
</ul> </ul>
<p>Vous pouvez passer le curseur sur les jours colorés afin de voir les informations de cette journée.</p>
<p>Vous pouvez passer le curseur sur les jours colorés afin de voir les informations supplémentaires</p>
</div> </div>
<ul class="couleurs print">
<li><span title="Vert" class="present demo"></span> présence
</li>
<li><span title="Bleu clair" class="nonwork demo"></span> non travaillé
</li>
<li><span title="Rouge" class="absent demo"></span> absence
</li>
<li><span title="Rose" class="demo color absent est_just"></span> absence justifiée
</li>
<li><span title="Orange" class="retard demo"></span> retard
</li>
<li><span title="Jaune clair" class="demo color retard est_just"></span>retard justifié
</li>
<li><span title="Quart Bleu" class="est_just demo"></span>
justificatif valide</li>
<li><span title="Quart Violet" class="invalide demo"></span> justificatif non valide
</li>
</ul>
</div> </div>
<style> <style>
.help .couleurs {
grid-template-columns: 2;
grid-template-rows: auto;
display: grid;
}
.couleurs.print {
display: none;
}
.help .couleurs li:nth-child(odd) {
grid-column: 1;
list-style-type: none;
}
.help .couleurs li:nth-child(even) {
grid-column: 2;
list-style-type: none;
}
.color.present {
background-color: var(--color-present) !important;
}
.color.absent {
background-color: var(--color-absent) !important;
}
.color.absent.est_just {
background-color: var(--color-absent-justi) !important;
}
.color.retard {
background-color: var(--color-retard) !important;
}
.color.retard.est_just {
background-color: var(--color-retard-justi) !important;
}
.color.nonwork {
background-color: var(--color-nonwork) !important;
}
.color {
background-color: var(--color-defaut) !important;
}
.pageContent { .pageContent {
margin-top: 1vh; margin-top: 1vh;
max-width: var(--sco-content-max-width); max-width: var(--sco-content-max-width);
@ -45,38 +129,71 @@
justify-content: space-evenly; justify-content: space-evenly;
flex-wrap: wrap; flex-wrap: wrap;
border: 1px solid #444; border: 1px solid #444;
border-radius: 12px;
margin-bottom: 12px;
} }
.month h2 { .month h3 {
text-align: center; text-align: center;
} }
.day { .day,
border: 1px solid #444; .demi .day.color.nonwork {
border-radius: 8px; text-align: left;
padding: 0 5px;
text-align: center;
margin: 2px; margin: 2px;
cursor: default; cursor: default;
font-size: 12px; font-size: 13px;
position: relative; position: relative;
font-weight: normal;
min-width: 6em;
display: flex;
justify-content: start;
} }
.day.est_just { .color.est_just.sans_etat::before {
border-left: 10px solid #7059FF; content: "";
position: absolute;
width: 25%;
height: 100%;
background-color: var(--color-justi) !important;
right: 0;
} }
.day.est_just.invalide { .color.invalide::before {
border-left: 10px solid #f64e4e; content: "";
position: absolute;
width: 25%;
height: 100%;
right: 0;
background-color: var(--color-justi-invalide) !important;
} }
.demo.invalide {
background-color: var(--color-justi-invalide) !important;
}
.demo.est_just {
background-color: var(--color-justi) !important;
}
.demi .day.nonwork>span {
flex: none;
border: none;
}
.demi .day {
border-radius: 0;
}
.day .dayline { .day .dayline {
position: absolute; position: absolute;
display: none; display: none;
left: -237%; bottom: -390%;
bottom: -420%;
z-index: 50; z-index: 50;
width: 250px; width: max-content;
height: 75px; height: 75px;
background-color: #dedede; background-color: #dedede;
border-radius: 15px; border-radius: 15px;
@ -88,7 +205,11 @@
} }
.dayline .mini-timeline { .dayline .mini-timeline {
margin-top: 14%; margin-top: 10%;
}
.dayline-title {
margin: 0;
} }
.dayline .mini_tick { .dayline .mini_tick {
@ -107,14 +228,127 @@
z-index: 2; z-index: 2;
transform: translateX(200%); transform: translateX(200%);
} }
#label-nom,
#label-justi {
display: none;
}
.demi .day {
display: flex;
justify-content: space-evenly;
}
.demi .day>span {
display: block;
flex: 1;
text-align: center;
z-index: 1;
width: 100%;
border: 1px solid #d5d5d5;
position: relative;
}
.demi .day>span:first-of-type {
width: 3em;
min-width: 3em;
}
.options>* {
margin-right: 5px;
}
.options input {
margin-right: 6px;
}
.options label {
font-weight: normal;
margin-right: 16px;
}
@media print {
.couleurs.print {
display: flex;
justify-content: space-evenly;
align-items: center;
}
.couleurs.print li {
list-style-type: none !important;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
.day,
.demi .day.color.color.nonwork {
min-width: 5em;
font-size: 11px;
}
.demi .day>span:first-of-type {
width: 2.5em;
min-width: 2.5em;
}
.color {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
.day.est_just,
.demi .day span.est_just {
background-image: none;
}
.day.invalide,
.demi .day span.invalide {
background-image: none;
}
.demi .day span.est_just::before {
content: "J";
}
.demi .day span.invalide::before {
content: "JI";
}
#sidebar,
.help,
h2,
#annee,
#label-changer,
.options {
display: none;
}
#label-nom,
#label-justi {
display: inline;
}
#gtrcontent {
margin: 5px;
}
.annee {
display: flex;
justify-content: space-evenly;
align-items: center;
}
}
</style> </style>
<script> <script>
function getDaysBetweenDates(start, end) { function getDaysBetweenDates(start, end) {
let now = moment(start); let now = new Date(start);
end = new Date(end);
let dates = []; let dates = [];
while (now.isSameOrBefore(end)) { while (now.isBefore(end) || now.isSame(end)) {
dates.push(now.clone()); dates.push(now.clone());
now.add(1, "days"); now.add(1, "days");
} }
@ -126,7 +360,7 @@
let datesByMonth = {}; let datesByMonth = {};
dates.forEach((date) => { dates.forEach((date) => {
let month = date.format("MMMM"); // Obtenir le mois let month = date.toLocaleString('fr-FR', { month: "short" }); // Obtenir le mois
if (!datesByMonth[month]) { if (!datesByMonth[month]) {
datesByMonth[month] = []; datesByMonth[month] = [];
@ -146,24 +380,22 @@
datesByMonth[month].forEach((date) => { datesByMonth[month].forEach((date) => {
let dayAssiduities = assiduities.filter((assiduity) => { let dayAssiduities = assiduities.filter((assiduity) => {
return moment.tz(date, TIMEZONE).isBetween( return new Date(date).isBetween(
moment.tz(assiduity.date_debut, TIMEZONE), new Date(assiduity.date_debut).startOf("day"),
moment.tz(assiduity.date_fin, TIMEZONE), new Date(assiduity.date_fin).startOf("day"),
"day",
"[]" "[]"
) )
}); });
let dayJustificatifs = justificatifs.filter((justif) => { let dayJustificatifs = justificatifs.filter((justif) => {
return moment.tz(date, TIMEZONE).isBetween( return new Date(date).isBetween(
moment.tz(justif.date_debut, TIMEZONE), new Date(justif.date_debut).startOf("day"),
moment.tz(justif.date_fin, TIMEZONE), new Date(justif.date_fin).startOf("day"),
"day",
"[]" "[]"
) )
}); });
assiduitiesByDay[month][date.format("YYYY-MM-DD")] = { assiduitiesByDay[month][date.toLocaleDateString("en-US")] = {
assiduites: dayAssiduities, assiduites: dayAssiduities,
justificatifs: dayJustificatifs justificatifs: dayJustificatifs
}; };
@ -173,108 +405,190 @@
return assiduitiesByDay; return assiduitiesByDay;
} }
function getDayColor(etat) {
let color;
switch (etat.toUpperCase()) {
case "PRESENT":
color = "#6bdb83";
break;
case "ABSENT":
color = "#F1A69C";
break;
case "RETARD":
color = "#f0c865";
break;
case "NONWORK":
color = "#bd81ca"
break;
default:
color = "#FFF";
break;
}
return color;
}
function generateCalendar(assiduitiesByDay, nonWorkdays = []) { function generateCalendar(assiduitiesByDay, nonWorkdays = []) {
const calendar = document.querySelector('.calendrier') const calendar = document.querySelector('.calendrier')
const options = getOptions();
calendar.innerHTML = "" calendar.innerHTML = ""
const days = { const days = {
Mon: "Lun", 1: "Lun",
Tue: "Mar", 2: "Mar",
Wed: "Mer", 3: "Mer",
Thu: "Jeu", 4: "Jeu",
Fri: "Ven", 5: "Ven",
Sat: "Sam", 6: "Sam",
Sun: "Dim", 0: "Dim",
};
const months = {
January: "Jan.",
February: "Fev.",
March: "Mar.",
April: "Avr.",
May: "Mai",
June: "Juin",
July: "Juil.",
August: "Août",
September: "Sep.",
October: "Oct.",
November: "Nov.",
December: "Déc.",
}; };
Object.keys(assiduitiesByDay).forEach((month) => { Object.keys(assiduitiesByDay).forEach((month) => {
const monthEl = document.createElement('div') const monthEl = document.createElement('div')
monthEl.classList.add("month") monthEl.classList.add("month")
const title = document.createElement('h2'); const title = document.createElement('h3');
title.textContent = `${months[month]}`; title.textContent = `${month.capitalize()}`;
monthEl.appendChild(title) monthEl.appendChild(title)
const daysEl = document.createElement('div') const daysEl = document.createElement('div')
daysEl.classList.add('days'); daysEl.classList.add('days');
if (options.mode_demi) daysEl.classList.add("demi");
Object.keys(assiduitiesByDay[month]).forEach((date) => { Object.keys(assiduitiesByDay[month]).forEach((date) => {
let dayAssiduities = assiduitiesByDay[month][date].assiduites; let dayAssiduities = assiduitiesByDay[month][date].assiduites;
let dayJustificatifs = assiduitiesByDay[month][date].justificatifs; let dayJustificatifs = assiduitiesByDay[month][date].justificatifs;
let color = "white"; let color = "sans_etat";
if (dayAssiduities.some((a) => a.etat.toLowerCase() === "absent")) color = "absent"; if (dayAssiduities.some((a) => a.etat.toLowerCase() === "absent")) color = "absent";
else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "retard")) else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "retard") && options.show_reta)
color = "retard"; color = "retard";
else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "present")) else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "present") && options.show_pres)
color = "present"; color = "present";
let est_just = "" let est_just = ""
if (dayJustificatifs.some((j) => j.etat.toLowerCase() === "valide")) { if (dayJustificatifs.some((j) => j.etat.toLowerCase() === "valide")) {
est_just = "est_just"; est_just = "est_just";
} else if (dayJustificatifs.some((j) => j.etat.toLowerCase() !== "valide")) { } else if (dayJustificatifs.some((j) => j.etat.toLowerCase() !== "valide")) {
est_just = "est_just invalide"; est_just = "invalide";
} }
const momentDate = moment.tz(date, TIMEZONE); const momentDate = new Date(date);
let dayOfMonth = momentDate.format("D"); let dayOfMonth = momentDate.getDate();
let dayOfWeek = momentDate.format("ddd"); let dayOfWeek = momentDate.getDay();
dayOfWeek = days[dayOfWeek]; dayOfWeek = days[dayOfWeek];
if (nonWorkdays.includes(dayOfWeek.toLowerCase())) color = "nonwork"; let isNonWorkDay = nonWorkdays.includes(dayOfWeek.toLowerCase());
const day = document.createElement('div'); const day = document.createElement('div');
day.className = `day ${est_just}` day.className = `day`;
if (isNonWorkDay) {
color = "nonwork";
} else if (!options.mode_demi) {
day.className = `day ${est_just}`;
}
day.style.backgroundColor = getDayColor(color);
if (options.mode_demi && !isNonWorkDay) {
est_just = []
// affichage n° jour + matin + aprem
const span_jour = document.createElement("span")
span_jour.textContent = dayOfWeek[0] + dayOfMonth;
const span_matin = document.createElement("span");
span_matin.classList.add("color");
const matin = [new Date(date), new Date(date)]
color = "sans_etat"
matin[0].setHours(0, 0, 0, 0)
matin[1].setHours(12, 59, 59)
const assiduitesMatin = dayAssiduities.filter((el) => {
const deb = new Date(el.date_debut);
const fin = new Date(el.date_fin);
return Date.intersect({ deb: deb, fin: fin }, { deb: matin[0], fin: matin[1] })
})
const justificatifsMatin = dayJustificatifs.filter((el) => {
const deb = new Date(el.date_debut);
const fin = new Date(el.date_fin);
return Date.intersect({ deb: deb, fin: fin }, { deb: matin[0], fin: matin[1] })
})
if (assiduitesMatin.some((a) => a.etat.toLowerCase() === "absent")) color = "absent";
else if (assiduitesMatin.some((a) => a.etat.toLowerCase() === "retard") && options.show_reta)
color = "retard";
else if (assiduitesMatin.some((a) => a.etat.toLowerCase() === "present") && options.show_pres)
color = "present";
if (color != "") {
span_matin.classList.add(color);
}
if (justificatifsMatin.some((j) => j.etat.toLowerCase() === "valide")) {
est_just = ["est_just"];
} else if (justificatifsMatin.some((j) => j.etat.toLowerCase() !== "valide")) {
est_just = ["invalide"];
}
span_matin.classList.add(...est_just)
est_just = []
const span_aprem = document.createElement("span");
span_aprem.classList.add("color");
const aprem = [new Date(date), new Date(date)]
color = "sans_etat"
aprem[0].setHours(13, 0, 0, 0)
aprem[1].setHours(23, 59, 59)
const assiduitesAprem = dayAssiduities.filter((el) => {
const deb = new Date(el.date_debut);
const fin = new Date(el.date_fin);
return Date.intersect({ deb: deb, fin: fin }, { deb: aprem[0], fin: aprem[1] })
})
const justificatifsAprem = dayJustificatifs.filter((el) => {
const deb = new Date(el.date_debut);
const fin = new Date(el.date_fin);
return Date.intersect({ deb: deb, fin: fin }, { deb: aprem[0], fin: aprem[1] })
})
if (assiduitesAprem.some((a) => a.etat.toLowerCase() === "absent")) color = "absent";
else if (assiduitesAprem.some((a) => a.etat.toLowerCase() === "retard") && options.show_reta)
color = "retard";
else if (assiduitesAprem.some((a) => a.etat.toLowerCase() === "present") && options.show_pres)
color = "present";
if (color != "") {
span_aprem.classList.add(color);
}
if (justificatifsAprem.some((j) => j.etat.toLowerCase() === "valide")) {
est_just = ["est_just"];
} else if (justificatifsAprem.some((j) => j.etat.toLowerCase() !== "valide")) {
est_just = ["invalide"];
}
span_aprem.classList.add(...est_just)
day.appendChild(span_jour)
day.appendChild(span_matin)
day.appendChild(span_aprem)
} else {
day.classList.add("color")
if (color != "") {
day.classList.add(color);
}
if (isNonWorkDay) {
const span_jour = document.createElement("span")
span_jour.textContent = dayOfWeek[0] + dayOfMonth;
day.appendChild(span_jour);
} else {
day.textContent = `${dayOfWeek} ${dayOfMonth}`; day.textContent = `${dayOfWeek} ${dayOfMonth}`;
}
}
console.warn(day.classList, day.classList.length)
if (!nonWorkdays.includes(dayOfWeek.toLowerCase()) && dayAssiduities.length > 0) { if (!nonWorkdays.includes(dayOfWeek.toLowerCase()) && dayAssiduities.length > 0) {
const cache = document.createElement('div') const cache = document.createElement('div')
cache.classList.add('dayline'); cache.classList.add('dayline');
const title = document.createElement('div')
title.className = "dayline-title";
title.innerHTML = "<span>Assiduité du </span><br>" + `<span>${formatDate(momentDate)}</span>`;
cache.appendChild(title)
cache.appendChild( cache.appendChild(
createMiniTimeline(dayAssiduities, date) createMiniTimeline(dayAssiduities, date)
) )
day.appendChild(cache) day.appendChild(cache)
} }
daysEl.appendChild(day); daysEl.appendChild(day);
}); });
monthEl.appendChild(daysEl) monthEl.appendChild(daysEl)
@ -287,13 +601,19 @@
const url_api = const url_api =
getUrl() + getUrl() +
`/api/assiduites/${etudid}/query?date_debut=${deb}&date_fin=${fin}`; `/api/assiduites/${etudid}/query?date_debut=${deb}&date_fin=${fin}`;
async_get(url_api, (data, status) => { async_get(url_api, (data) => {
if (status === "success") {
callback(data); callback(data);
}
}); });
} }
function getOptions() {
return {
"show_pres": document.getElementById("show_pres").checked,
"show_reta": document.getElementById("show_reta").checked,
"mode_demi": document.getElementById("mode_demi").checked,
}
}
function getEtudJustificatifs(deb, fin) { function getEtudJustificatifs(deb, fin) {
let list = []; let list = [];
const url_api = const url_api =
@ -309,7 +629,6 @@
} }
function generate(annee) { function generate(annee) {
if (annee < 1999 || annee > 2999) { if (annee < 1999 || annee > 2999) {
openAlertModal("Année impossible", document.createTextNode("L'année demandé n'existe pas.")); openAlertModal("Année impossible", document.createTextNode("L'année demandé n'existe pas."));
return; return;
@ -331,10 +650,11 @@
function setterAnnee(annee) { function setterAnnee(annee) {
annee = parseInt(annee); annee = parseInt(annee);
document.querySelector('.annee span').textContent = `Année scolaire ${annee}-${annee + 1} Changer année: ` document.querySelector('.annee #label-annee').textContent = `Année scolaire ${annee}-${annee + 1}`
generate(annee) generate(annee)
} }
const defAnnee = {{ annee }} const defAnnee = {{ annee }}
let annees = {{ annees | safe }} let annees = {{ annees | safe }}
annees = annees.filter((x, i) => annees.indexOf(x) === i) annees = annees.filter((x, i) => annees.indexOf(x) === i)
@ -351,6 +671,12 @@
} }
select.appendChild(opt) select.appendChild(opt)
}) })
document.querySelectorAll(".options input").forEach((e) => {
e.addEventListener("click", () => {
setterAnnee(select.value)
})
})
setterAnnee(defAnnee) setterAnnee(defAnnee)
}; };

View File

@ -41,7 +41,7 @@
if (defaultDates != null) { if (defaultDates != null) {
defaultDates.forEach((dateString) => { defaultDates.forEach((dateString) => {
d = moment(dateString).weekday(); d = new Date(dateString).getDay();
if (verifyNonWorkDays(d, nonWorkDays)) return; if (verifyNonWorkDays(d, nonWorkDays)) return;

View File

@ -9,7 +9,7 @@
<div class="infos"> <div class="infos">
Date: <span id="datestr"></span> Date: <span id="datestr"></span>
<input type="date" name="tl_date" id="tl_date" value="{{ date }}"> <input type="text" class="datepicker" name="tl_date" id="tl_date" value="{{ date }}">
</div> </div>
{{timeline|safe}} {{timeline|safe}}
@ -59,18 +59,9 @@
Correspondance des couleurs : Correspondance des couleurs :
</p> </p>
<ul> <ul>
<li><span title="Vert" class="present demo"></span> &rightarrow; présence de l'étudiant lors de la période {% include "assiduites/widgets/legende_couleur.j2" %}
</li>
<li><span title="Orange" class="retard demo"></span> &rightarrow; retard de l'étudiant lors de la période
</li>
<li><span title="Rouge" class="absent demo"></span> &rightarrow; absence de l'étudiant lors de la période
</li>
<li><span title="Hachure Bleue" class="justified demo"></span> &rightarrow; l'assiduité est justifiée par un
justificatif valide</li>
<li><span title="Hachure Rouge" class="invalid_justified demo"></span> &rightarrow; l'assiduité est
justifiée par un justificatif non valide / en attente de validation
</li>
</ul> </ul>
<p>Vous pouvez justifier rapidement une assiduité en saisisant l'assiduité puis en appuyant sur "Justifier"</p> <p>Vous pouvez justifier rapidement une assiduité en saisisant l'assiduité puis en appuyant sur "Justifier"</p>
<h3>Explication de la saisie différée</h3> <h3>Explication de la saisie différée</h3>

View File

@ -20,7 +20,8 @@
<div class="infos-button">Groupes&nbsp;: {{grp|safe}}</div> <div class="infos-button">Groupes&nbsp;: {{grp|safe}}</div>
<div class="infos-button" style="margin-left: 24px;">Date&nbsp;: <span style="margin-left: 8px;" <div class="infos-button" style="margin-left: 24px;">Date&nbsp;: <span style="margin-left: 8px;"
id="datestr"></span> id="datestr"></span>
<input type="date" name="tl_date" id="tl_date" value="{{ date }}" onchange="updateDate()"> <input type="text" class="datepicker" name="tl_date" id="tl_date" value="{{ date }}"
onchange="updateDate()">
</div> </div>
</div> </div>
</fieldset> </fieldset>
@ -69,17 +70,7 @@
Correspondance des couleurs : Correspondance des couleurs :
</p> </p>
<ul> <ul>
<li><span title="Vert" class="present demo"></span> &rightarrow; présence de l'étudiant lors de la période {% include "assiduites/widgets/legende_couleur.j2" %}
</li>
<li><span title="Orange" class="retard demo"></span> &rightarrow; retard de l'étudiant lors de la période
</li>
<li><span title="Rouge" class="absent demo"></span> &rightarrow; absence de l'étudiant lors de la période
</li>
<li><span title="Hachure Bleue" class="justified demo"></span> &rightarrow; l'assiduité est justifiée par un
justificatif valide</li>
<li><span title="Hachure Rouge" class="invalid_justified demo"></span> &rightarrow; l'assiduité est
justifiée par un justificatif non valide / en attente de validation
</li>
</ul> </ul>
</div> </div>
<!-- Ajout d'un conteneur pour le loader --> <!-- Ajout d'un conteneur pour le loader -->

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test DateUtils</title>
<script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script>
</head>
<body>
<form action="" method="post">
<scodoc-datetime name="scodoc-datetime"></scodoc-datetime>
<input type="submit" value="valider">
</form>
</body>
</html>

View File

@ -1,8 +1,8 @@
{% extends "sco_page.j2" %} {% extends "sco_page.j2" %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script> <script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script>
{% endblock %} {% endblock %}
{% block app_content %} {% block app_content %}
@ -10,10 +10,10 @@
<h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2> <h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2>
<div class="stats-inputs"> <div class="stats-inputs">
<label class="stats-label"> Date de début <input type="date" name="stats_date_debut" id="stats_date_debut" <label class="stats-label"> Date de début <input type="text" class="datepicker" name="stats_date_debut"
value="{{date_debut}}"></label> id="stats_date_debut" value="{{date_debut}}"></label>
<label class="stats-label"> Date de fin <input type="date" name="stats_date_fin" id="stats_date_fin" <label class="stats-label"> Date de fin <input type="text" class="datepicker" name="stats_date_fin"
value="{{date_fin}}"></label> id="stats_date_fin" value="{{date_fin}}"></label>
<button onclick="stats()">Changer</button> <button onclick="stats()">Changer</button>
<a style="margin-left:32px;" href="{{request.url}}&fmt=xlsx">{{scu.ICON_XLS|safe}}</a> <a style="margin-left:32px;" href="{{request.url}}&fmt=xlsx">{{scu.ICON_XLS|safe}}</a>
@ -21,8 +21,8 @@
{{tableau | safe}} {{tableau | safe}}
<div class=""help"> <div class="" help">
Les comptes sont exprimés en {{ assi_metric | lower}}s. Les comptes sont exprimés en {{ assi_metric | lower}}s.
</div> </div>
<script> <script>

View File

@ -127,7 +127,7 @@
<script> <script>
const alertmodal = document.getElementById("alertModal"); const alertmodal = document.getElementById("alertModal");
function openAlertModal(titre, contenu, footer, color = "crimson") { function openAlertModal(titre, contenu, footer, color = "var(--color-error)") {
alertmodal.classList.add('is-active'); alertmodal.classList.add('is-active');
alertmodal.querySelector('.alertmodal-title').textContent = titre; alertmodal.querySelector('.alertmodal-title').textContent = titre;

View File

@ -5,8 +5,8 @@
* @returns {String} un déplacement par rapport à la gauche en % * @returns {String} un déplacement par rapport à la gauche en %
*/ */
function getLeftPosition(start) { function getLeftPosition(start) {
const startTime = new moment.tz(start, TIMEZONE); const startTime = new Date(start);
const startMins = (startTime.hours() - 8) * 60 + startTime.minutes(); const startMins = (startTime.getHours() - 8) * 60 + startTime.getMinutes();
return (startMins / (18 * 60 - 8 * 60)) * 100 + "%"; return (startMins / (18 * 60 - 8 * 60)) * 100 + "%";
} }
/** /**
@ -21,8 +21,8 @@
return a.start < b.end && a.end > b.start; return a.start < b.end && a.end > b.start;
}; };
const startTime = new moment.tz(start, TIMEZONE); const startTime = new Date(start);
const endTime = new moment.tz(end, TIMEZONE); const endTime = new Date(end);
const assiduiteDuration = { start: startTime, end: endTime }; const assiduiteDuration = { start: startTime, end: endTime };
let position = 0; let position = 0;
@ -31,8 +31,8 @@
while (hasOverlap) { while (hasOverlap) {
hasOverlap = false; hasOverlap = false;
Array.from(container.children).some((el) => { Array.from(container.children).some((el) => {
const elStart = new moment.tz(el.getAttribute("data-start")); const elStart = new Date(el.getAttribute("data-start"));
const elEnd = new moment.tz(el.getAttribute("data-end")); const elEnd = new Date(el.getAttribute("data-end"));
const elDuration = { start: elStart, end: elEnd }; const elDuration = { start: elStart, end: elEnd };
if (overlaps(assiduiteDuration, elDuration)) { if (overlaps(assiduiteDuration, elDuration)) {
@ -54,13 +54,13 @@
function getColor(state) { function getColor(state) {
switch (state) { switch (state) {
case "PRESENT": case "PRESENT":
return "#9CF1AF"; return "var(--color-present)";
case "ABSENT": case "ABSENT":
return "#F1A69C"; return "var(--color-absent)";
case "RETARD": case "RETARD":
return "#F1D99C"; return "var(--color-retard)";
default: default:
return "gray"; return "var(--color-defaut-dark)";
} }
} }
@ -71,8 +71,8 @@
* @returns {String} la taille en % * @returns {String} la taille en %
*/ */
function getWidth(start, end) { function getWidth(start, end) {
const startTime = new moment.tz(start, TIMEZONE); const startTime = new Date(start);
const endTime = new moment.tz(end, TIMEZONE); const endTime = new Date(end);
const duration = (endTime - startTime) / 1000 / 60; const duration = (endTime - startTime) / 1000 / 60;
@ -258,11 +258,11 @@
const success = () => { const success = () => {
const separatorTime = document.getElementById("promptTime").value; const separatorTime = document.getElementById("promptTime").value;
const dateString = const dateString =
document.querySelector("#tl_date").value + `T${separatorTime}`; getDate().format("YYYY-MM-DD") + `T${separatorTime}`;
const separtorDate = new moment.tz(dateString, TIMEZONE); const separtorDate = new Date(dateString);
const assiduite_debut = new moment.tz(this.selectedAssiduite.date_debut, TIMEZONE); const assiduite_debut = new Date(this.selectedAssiduite.date_debut);
const assiduite_fin = new moment.tz(this.selectedAssiduite.date_fin, TIMEZONE); const assiduite_fin = new Date(this.selectedAssiduite.date_fin);
if ( if (
separtorDate.isAfter(assiduite_debut) && separtorDate.isAfter(assiduite_debut) &&
@ -270,14 +270,14 @@
) { ) {
const assiduite_avant = { const assiduite_avant = {
etat: this.selectedAssiduite.etat, etat: this.selectedAssiduite.etat,
date_debut: assiduite_debut.format(), date_debut: assiduite_debut.toIsoUtcString()(),
date_fin: separtorDate.format(), date_fin: separtorDate.toIsoUtcString()(),
}; };
const assiduite_apres = { const assiduite_apres = {
etat: this.selectedAssiduite.etat, etat: this.selectedAssiduite.etat,
date_debut: separtorDate.format(), date_debut: separtorDate.toIsoUtcString()(),
date_fin: assiduite_fin.format(), date_fin: assiduite_fin.toIsoUtcString()(),
}; };
if (this.selectedAssiduite.moduleimpl_id) { if (this.selectedAssiduite.moduleimpl_id) {
@ -309,11 +309,11 @@
"L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée." "L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée."
); );
openAlertModal("Attention", att, "", "#ecb52a"); openAlertModal("Attention", att, "", "var(--color-warning))");
} }
}; };
openPromptModal("Séparation de l'assiduité sélectionnée", fieldSet, success, () => { }, "#23aa40"); openPromptModal("Séparation de l'assiduité sélectionnée", fieldSet, success, () => { }, "var(--color-present)");
} }
/** /**
@ -342,7 +342,7 @@
const att = document.createTextNode( const att = document.createTextNode(
"L'état doit être 'present', 'absent' ou 'retard'." "L'état doit être 'present', 'absent' ou 'retard'."
); );
openAlertModal("Attention", att, "", "#ecb52a"); openAlertModal("Attention", att, "", "var(--color-warning)");
return; return;
} }
@ -357,7 +357,7 @@
}; };
//Affichage du prompt //Affichage du prompt
openPromptModal("Modification de l'état de l'assiduité sélectionnée", fieldSet, success, () => { }, "#23aa40"); openPromptModal("Modification de l'état de l'assiduité sélectionnée", fieldSet, success, () => { }, "var(--color-present)");
} }
/** /**
@ -396,8 +396,8 @@
//Placement des assiduités sur la timeline //Placement des assiduités sur la timeline
this.list.forEach((assiduite) => { this.list.forEach((assiduite) => {
const period = { const period = {
deb: new moment.tz(assiduite.date_debut, TIMEZONE), deb: new Date(assiduite.date_debut),
fin: new moment.tz(assiduite.date_fin, TIMEZONE), fin: new Date(assiduite.date_fin),
}; };
if (!hasTimeConflict(period, this.interval)) { if (!hasTimeConflict(period, this.interval)) {
return; return;

View File

@ -147,7 +147,7 @@
} }
#addColumn:hover { #addColumn:hover {
background-color: #0056b3; background-color: var(--color-primary);
} }
.th { .th {
@ -157,7 +157,7 @@
} }
.th.error { .th.error {
background-color: #d71111; background-color: var(--color-error);
} }
.tbody .tr:nth-child(even) { .tbody .tr:nth-child(even) {
@ -226,7 +226,7 @@
} }
.td[assiduite_id='conflit'] { .td[assiduite_id='conflit'] {
background-color: #ff0000c2; background-color: var(--color-error);
} }
input[type='radio']:disabled { input[type='radio']:disabled {
@ -237,7 +237,7 @@
.th.error:hover .col-error { .th.error:hover .col-error {
display: block; display: block;
z-index: 2000; z-index: 2000;
background-color: #d71111; background-color: var(--color-error);
width: 100%; width: 100%;
min-height: 25%; min-height: 25%;
bottom: -25%; bottom: -25%;
@ -274,17 +274,21 @@
<script> <script>
const assi_etat_defaut = "{{assi_etat_defaut}}"; const assi_etat_defaut = "{{assi_etat_defaut}}";
{% if scu.is_assiduites_module_forced(request.args.get('formsemestre_id', None)) %}
window.forceModule = "{{ forcer_module }}" window.forceModule = true;
window.forceModule = window.forceModule == "True" ? true : false {% else %}
window.forceModule = false;
{% endif %}
let colCount = 1; let colCount = 1;
let currentDate = "{{date}}"; let currentDate = "{{date}}";
if (currentDate == "") { if (currentDate == "") {
currentDate = moment().tz(TIMEZONE).format("YYYY-MM-DDTHH:mm"); currentDate = new Date();
currentDate = currentDate.format("YYYY-MM-DDTHH:mm");
} else { } else {
currentDate = moment(currentDate).tz(TIMEZONE).format("YYYY-MM-DDTHH:mm"); currentDate = new Date(currentDate);
currentDate = currentDate.format("YYYY-MM-DDTHH:mm");
} }
@ -432,7 +436,10 @@
const last = document.querySelector(`[col='${num}'] #dateEnd`); const last = document.querySelector(`[col='${num}'] #dateEnd`);
let date = undefined; let date = undefined;
if (last == undefined) { if (last == undefined) {
date = currentDate; date = new Date(currentDate);
const start = typeof mt_start !== 'undefined' ? mt_start : 0
date.setHours(start);
date = date.format("YYYY-MM-DDTHH:mm")
} else { } else {
date = last.value; date = last.value;
} }
@ -444,9 +451,10 @@
() => { () => {
const el = element.parentElement.querySelector("#dateEnd"); const el = element.parentElement.querySelector("#dateEnd");
const el2 = element.parentElement.querySelector("#dateStart"); const el2 = element.parentElement.querySelector("#dateStart");
el.value = moment(el2.valueAsDate).tz(TIMEZONE).utc() const newDate = new Date(el2.valueAsDate)
.add(2, "hours") .add(2, "hours");
.format("YYYY-MM-DDTHH:mm"); el.valueAsDate = newDate;
const colid = element.parentElement.parentElement.getAttribute('col'); const colid = element.parentElement.parentElement.getAttribute('col');
getAndUpdateCol(colid); getAndUpdateCol(colid);
}, },
@ -473,16 +481,16 @@
let color; let color;
switch (etatId) { switch (etatId) {
case 0: case 0:
color = "#9CF1AF"; color = "var(--color-present)";
break break
case 1: case 1:
color = "#F1D99C"; color = "var(--color-retard)";
break break
case 2: case 2:
color = "#F1A69C"; color = "var(--color-absent)";
break break
default: default:
color = "white"; color = "var(--color-defaut)";
break; break;
} }
@ -517,8 +525,8 @@
const inputDeb = col.querySelector("#dateStart").value; const inputDeb = col.querySelector("#dateStart").value;
const inputFin = col.querySelector("#dateEnd").value; const inputFin = col.querySelector("#dateEnd").value;
const moduleSelect = col.querySelector("#moduleimpl_select,.dynaSelect").value; const moduleSelect = col.querySelector("#moduleimpl_select,.dynaSelect").value;
const d_debut = moment(inputDeb).tz(TIMEZONE); const d_debut = new Date(inputDeb);
const d_fin = moment(inputFin).tz(TIMEZONE); const d_fin = new Date(inputFin);
if (inputDeb == "" || inputFin == "" || d_debut >= d_fin) { if (inputDeb == "" || inputFin == "" || d_debut >= d_fin) {
@ -547,14 +555,14 @@
} }
if (get) { if (get) {
getAssiduitesFromEtuds(false, d_debut.format(), d_fin.format()) getAssiduitesFromEtuds(false, d_debut.toIsoUtcString(), d_fin.toIsoUtcString())
return 0x0; return 0x0;
} }
return { return {
moduleimpl: moduleSelect, moduleimpl: moduleSelect,
deb: d_debut.format(), deb: d_debut.toIsoUtcString(),
fin: d_fin.format(), fin: d_fin.toIsoUtcString(),
} }
@ -568,8 +576,8 @@
const inputDeb = col.querySelector("#dateStart").value; const inputDeb = col.querySelector("#dateStart").value;
const inputFin = col.querySelector("#dateEnd").value; const inputFin = col.querySelector("#dateEnd").value;
const d_debut = moment(inputDeb).tz(TIMEZONE); const d_debut = new Date(inputDeb);
const d_fin = moment(inputFin).tz(TIMEZONE); const d_fin = new Date(inputFin);
const moduleimpl_id = col.querySelector("#moduleimpl_select,.dynaSelect").value; const moduleimpl_id = col.querySelector("#moduleimpl_select,.dynaSelect").value;
@ -671,13 +679,13 @@
switch (etat.toUpperCase()) { switch (etat.toUpperCase()) {
case "PRESENT": case "PRESENT":
color = "#6bdb83"; color = "var(--color-present)";
break; break;
case "ABSENT": case "ABSENT":
color = "#F1A69C"; color = "var(--color-absent)";
break; break;
case "RETARD": case "RETARD":
color = "#f0c865"; color = "var(--color-retard)";
break; break;
default: default:
color = "#AAA"; color = "#AAA";
@ -715,15 +723,27 @@
assiduites[etudid].push(assiduite); assiduites[etudid].push(assiduite);
updateAllCol() updateAllCol()
launchToast(etudid, etat); launchToast(etudid, etat);
} else {
let error = data.errors[Object.keys(data.errors)[0]];
if (error.message.indexOf("Module") != -1) {
const html = `
<h3>Aucun module n'a été spécifié (préférence du semestre concerné)</h3>
`;
const div = document.createElement("div");
div.innerHTML = html;
openAlertModal("Erreur Module", div);
rbtn.checked = false;
} }
}
}) })
break; break;
case "conflit": case "conflit":
// Conflit, afficher résolveur // Conflit, afficher résolveur
const assiduitesList = assiduites[etudid]; const assiduitesList = assiduites[etudid];
const d_debut = new moment.tz(deb, TIMEZONE); const d_debut = new Date(deb);
const d_fin = new moment.tz(fin, TIMEZONE); const d_fin = new Date(fin);
const period = { const period = {
deb: deb, deb: deb,
@ -733,8 +753,8 @@
assiduites[etudid], assiduites[etudid],
period, period,
{ {
deb: new moment.tz(d_debut.startOf('day'), TIMEZONE), deb: d_debut.startOf('day'),
fin: new moment.tz(d_fin.endOf('day'), TIMEZONE), fin: d_fin.endOf('day'),
} }
); );
const update = () => { const update = () => {
@ -944,11 +964,24 @@
}) })
).done((c, e) => { ).done((c, e) => {
let error;
Object.keys(c[0].success).forEach((k) => { Object.keys(c[0].success).forEach((k) => {
const assiduite = createList[Number.parseInt(k)]; const assiduite = createList[Number.parseInt(k)];
assiduite["assiduite_id"] = c[0].success[k].message.assiduite_id; assiduite["assiduite_id"] = c[0].success[k].message.assiduite_id;
assiduites[assiduite.etudid].push(assiduite); assiduites[assiduite.etudid].push(assiduite);
}) })
if (c[0].errors.length > 0) {
error = c[0].errors[Object.keys(c[0].errors)[0]];
if (error.message.indexOf("Module") != -1) {
const html = `
<h3>Aucun module n'a été spécifié (préférence du semestre concerné)</h3>
`;
const div = document.createElement("div");
div.innerHTML = html;
openAlertModal("Erreur Module", div);
toCreate.length = 0
}
}
Object.keys(e[0].success).forEach((k) => { Object.keys(e[0].success).forEach((k) => {
const { etudid, assiduite_id, moduleimpl_id, etat } = editList[Number.parseInt(k)] const { etudid, assiduite_id, moduleimpl_id, etat } = editList[Number.parseInt(k)]
assiduites[etudid].map((a) => { assiduites[etudid].map((a) => {

View File

@ -0,0 +1,12 @@
<li><span title="Vert" class="present demo"></span> &rightarrow; présence de l'étudiant lors de la période
</li>
<li><span title="Orange" class="retard demo"></span> &rightarrow; retard de l'étudiant lors de la période
</li>
<li><span title="Rouge" class="absent demo"></span> &rightarrow; absence de l'étudiant lors de la période
</li>
<li><span title="Hachure Bleue" class="justified demo"></span> &rightarrow; l'assiduité est justifiée par un
justificatif valide</li>
<li><span title="Hachure Rouge" class="invalid_justified demo"></span> &rightarrow; l'assiduité est
justifiée par un justificatif non valide / en attente de validation
</li>

View File

@ -13,16 +13,17 @@
*/ */
function createMiniTimeline(assiduitesArray, day = null) { function createMiniTimeline(assiduitesArray, day = null) {
const array = [...assiduitesArray]; const array = [...assiduitesArray];
const dateiso = day == null ? document.getElementById("tl_date").value : day;
const dateiso = day == null ? getDate().format("YYYY-MM-DD") : day;
const timeline = document.createElement("div"); const timeline = document.createElement("div");
timeline.className = "mini-timeline"; timeline.className = "mini-timeline";
if (isSingleEtud()) { if (isSingleEtud()) {
timeline.classList.add("single"); timeline.classList.add("single");
} }
const timelineDate = moment(dateiso).startOf("day"); const timelineDate = new Date(dateiso).startOf("day");
const dayStart = timelineDate.clone().add(mt_start, "hours"); const dayStart = timelineDate.clone().add(mt_start, "hours");
const dayEnd = timelineDate.clone().add(mt_end, "hours"); const dayEnd = timelineDate.clone().add(mt_end, "hours");
const dayDuration = moment.duration(dayEnd.diff(dayStart)).asMinutes(); const dayDuration = new Duration(dayStart, dayEnd).minutes;
timeline.appendChild(setMiniTick(timelineDate, dayStart, dayDuration)); timeline.appendChild(setMiniTick(timelineDate, dayStart, dayDuration));
@ -38,8 +39,8 @@
} }
array.forEach((assiduité) => { array.forEach((assiduité) => {
let startDate = moment(assiduité.date_debut); let startDate = new Date(assiduité.date_debut);
let endDate = moment(assiduité.date_fin); let endDate = new Date(assiduité.date_fin);
if (startDate.isBefore(dayStart)) { if (startDate.isBefore(dayStart)) {
startDate = dayEnd.clone().startOf("day").add(mt_start, "hours"); startDate = dayEnd.clone().startOf("day").add(mt_start, "hours");
@ -51,8 +52,8 @@
const block = document.createElement("div"); const block = document.createElement("div");
block.className = "mini-timeline-block"; block.className = "mini-timeline-block";
const duration = moment.duration(endDate.diff(startDate)).asMinutes(); const duration = new Duration(startDate, endDate).minutes;
const startOffset = moment.duration(startDate.diff(dayStart)).asMinutes(); const startOffset = new Duration(dayStart, startDate).minutes;
const leftPercentage = (startOffset / dayDuration) * 100; const leftPercentage = (startOffset / dayDuration) * 100;
const widthPercentage = (duration / dayDuration) * 100; const widthPercentage = (duration / dayDuration) * 100;
@ -61,8 +62,8 @@
if (assiduité.etat != "CRENEAU") { if (assiduité.etat != "CRENEAU") {
block.addEventListener("click", () => { block.addEventListener("click", () => {
let deb = startDate.hours() + startDate.minutes() / 60; let deb = startDate.getHours() + startDate.getMinutes() / 60;
let fin = endDate.hours() + endDate.minutes() / 60; let fin = endDate.getHours() + endDate.getMinutes() / 60;
deb = Math.max(mt_start, deb); deb = Math.max(mt_start, deb);
fin = Math.min(mt_end, fin); fin = Math.min(mt_end, fin);
@ -98,8 +99,8 @@
if (assiduité.etudid) { if (assiduité.etudid) {
getJustificatifFromPeriod( getJustificatifFromPeriod(
{ {
deb: new moment.tz(assiduité.date_debut, TIMEZONE), deb: new Date(assiduité.date_debut),
fin: new moment.tz(assiduité.date_fin, TIMEZONE), fin: new Date(assiduité.date_fin),
}, },
assiduité.etudid, assiduité.etudid,
action action
@ -166,7 +167,7 @@
userIdDiv.className = "assiduite-user_id"; userIdDiv.className = "assiduite-user_id";
userIdDiv.textContent = `saisie le ${formatDateModal( userIdDiv.textContent = `saisie le ${formatDateModal(
assiduite.entry_date, assiduite.entry_date,
"à" " à "
)}`; )}`;
if (assiduite.user_id != null) { if (assiduite.user_id != null) {
@ -184,8 +185,9 @@
} }
function setMiniTick(timelineDate, dayStart, dayDuration) { function setMiniTick(timelineDate, dayStart, dayDuration) {
const endDate = timelineDate.clone().set({ 'hour': 13, 'minute': 0 }); const endDate = timelineDate.clone().startOf("day");
const duration = moment.duration(endDate.diff(dayStart)).asMinutes(); endDate.setHours(13, 0);
const duration = new Duration(dayStart, endDate).minutes;
const widthPercentage = (duration / dayDuration) * 100; const widthPercentage = (duration / dayDuration) * 100;
const tick = document.createElement('span'); const tick = document.createElement('span');
tick.className = "mini_tick" tick.className = "mini_tick"
@ -245,15 +247,15 @@
} }
.assiduite-bubble.absent { .assiduite-bubble.absent {
border-color: #F1A69C !important; border-color: var(--color-absent) !important;
} }
.assiduite-bubble.present { .assiduite-bubble.present {
border-color: #9CF1AF !important; border-color: var(--color-present) !important;
} }
.assiduite-bubble.retard { .assiduite-bubble.retard {
border-color: #F1D99C !important; border-color: var(--color-retard) !important;
} }
.mini-timeline { .mini-timeline {
@ -295,27 +297,27 @@
} }
.mini-timeline-block.creneau { .mini-timeline-block.creneau {
outline: 3px solid #7059FF; outline: 3px solid var(--color-primary);
pointer-events: none; pointer-events: none;
} }
.mini-timeline-block.absent { .mini-timeline-block.absent {
background-color: #F1A69C !important; background-color: var(--color-absent) !important;
} }
.mini-timeline-block.present { .mini-timeline-block.present {
background-color: #9CF1AF !important; background-color: var(--color-present) !important;
} }
.mini-timeline-block.retard { .mini-timeline-block.retard {
background-color: #F1D99C !important; background-color: var(--color-retard) !important;
} }
.mini-timeline-block.justified { .mini-timeline-block.justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px); background-image: var(--motif-justi);
} }
.mini-timeline-block.invalid_justified { .mini-timeline-block.invalid_justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #d61616 4px, #d61616 8px); background-image: var(--motif-justi-invalide);
} }
</style> </style>

View File

@ -21,13 +21,9 @@
} }
function filterFormSemestres(semestres, dateIso) { function filterFormSemestres(semestres, dateIso) {
const date = new moment.tz( const date = new Date(dateIso);
dateIso,
TIMEZONE
);
semestres = semestres.filter((fm) => { semestres = semestres.filter((fm) => {
return date.isBetween(fm.date_debut_iso, fm.date_fin_iso, null, '[]') return date.isBetween(new Date(fm.date_debut_iso), new Date(fm.date_fin_iso), '[]');
}) })
return semestres; return semestres;
@ -97,9 +93,10 @@
function updateSelect(moduleimpl_id, query = "#moduleimpl_select", dateIso = null) { function updateSelect(moduleimpl_id, query = "#moduleimpl_select", dateIso = null) {
let sem = getEtudFormSemestres() let sem = getEtudFormSemestres()
if (dateIso == null) { if (!dateIso) {
dateIso = document.querySelector("#tl_date").value dateIso = getDate().format("YYYY-MM-DD")
} }
sem = filterFormSemestres(sem, dateIso) sem = filterFormSemestres(sem, dateIso)
const mod = getModulesImplByFormsemestre(sem) const mod = getModulesImplByFormsemestre(sem)
populateSelect(mod, moduleimpl_id, query); populateSelect(mod, moduleimpl_id, query);

View File

@ -158,7 +158,7 @@
<script> <script>
const promptModal = document.getElementById("promptModal"); const promptModal = document.getElementById("promptModal");
function openPromptModal(titre, contenu, success, cancel = () => { }, color = "crimson") { function openPromptModal(titre, contenu, success, cancel = () => { }, color = "var(--color-error)") {
promptModal.classList.add('is-active'); promptModal.classList.add('is-active');
promptModal.querySelector('.promptModal-title').textContent = titre; promptModal.querySelector('.promptModal-title').textContent = titre;

View File

@ -83,7 +83,7 @@
filterAssiduites.columns.forEach((k) => { filterAssiduites.columns.forEach((k) => {
const td = document.createElement('td'); const td = document.createElement('td');
if (k.indexOf('date') != -1) { if (k.indexOf('date') != -1) {
td.textContent = moment.tz(assiduite[k], TIMEZONE).format(`DD/MM/Y HH:mm`) td.textContent = new Date(assiduite[k]).format(`DD/MM/Y HH:mm`)
} else if (k.indexOf("module") != -1) { } else if (k.indexOf("module") != -1) {
td.textContent = getModuleImpl(assiduite); td.textContent = getModuleImpl(assiduite);
} else if (k.indexOf('est_just') != -1) { } else if (k.indexOf('est_just') != -1) {
@ -115,9 +115,9 @@
const user = getUser(data); const user = getUser(data);
const module = getModuleImpl(data); const module = getModuleImpl(data);
const date_debut = moment.tz(data.date_debut, TIMEZONE).format("DD/MM/YYYY HH:mm"); const date_debut = new Date(data.date_debut).format("DD/MM/YYYY HH:mm");
const date_fin = moment.tz(data.date_fin, TIMEZONE).format("DD/MM/YYYY HH:mm"); const date_fin = new Date(data.date_fin).format("DD/MM/YYYY HH:mm");
const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm"); const entry_date = new Date(data.entry_date).format("DD/MM/YYYY HH:mm");
const etat = data.etat.capitalize(); const etat = data.etat.capitalize();
const desc = data.desc == null ? "" : data.desc; const desc = data.desc == null ? "" : data.desc;
@ -174,7 +174,7 @@
const el = document.createElement('div'); const el = document.createElement('div');
el.innerHTML = html; el.innerHTML = html;
openAlertModal("Détails", el.firstElementChild, null, "green") openAlertModal("Détails", el.firstElementChild, null, "var(--color-information)")
} }
) )
} }
@ -245,7 +245,7 @@
}) })
}, () => { }, "green"); }, () => { }, "var(--color-information)");
} }
); );
} }
@ -433,7 +433,7 @@
if (l.querySelector(`#${key}_time`).value != "") { if (l.querySelector(`#${key}_time`).value != "") {
filterAssiduites.filters[key] = { filterAssiduites.filters[key] = {
pref: pref, pref: pref,
time: new moment.tz(time, TIMEZONE) time: new Date(time)
} }
} }
} else if (key.indexOf('etat') != -1) { } else if (key.indexOf('etat') != -1) {
@ -449,7 +449,7 @@
getAssi(assiduiteCallBack) getAssi(assiduiteCallBack)
}, () => { }, "#7059FF"); }, () => { }, "var(--color-primary)");
} }

View File

@ -75,7 +75,7 @@
} }
} }
if (k.indexOf('date') != -1) { if (k.indexOf('date') != -1) {
const assi_time = moment.tz(el[k], TIMEZONE); const assi_time = new Date(el[k]);
const filter_time = f[k].time; const filter_time = f[k].time;
switch (f[k].pref) { switch (f[k].pref) {
@ -262,8 +262,8 @@
let keyValueB = b[keyword]; let keyValueB = b[keyword];
if (keyword.indexOf("date") != -1) { if (keyword.indexOf("date") != -1) {
keyValueA = moment.tz(keyValueA, TIMEZONE) keyValueA = new Date(keyValueA)
keyValueB = moment.tz(keyValueB, TIMEZONE) keyValueB = new Date(keyValueB)
} }
if (keyword.indexOf("module") != -1) { if (keyword.indexOf("module") != -1) {
@ -385,7 +385,7 @@
downloadStr(data, input.value ? input.value : "download.csv") downloadStr(data, input.value ? input.value : "download.csv")
}, () => { }, "green"); }, () => { }, "var(--color-present)");
} }
function toCSV(array, filters) { function toCSV(array, filters) {
@ -489,24 +489,24 @@
} }
#deleteOption { #deleteOption {
background-color: #F1A69C; background-color: var(--color-absent);
} }
.l-present { .l-present {
background-color: #9CF1AF; background-color: var(--color-present)
} }
.l-absent, .l-absent,
.l-invalid { .l-invalid {
background-color: #F1A69C; background-color: var(--color-absent);
} }
.l-valid { .l-valid {
background-color: #8f7eff; background-color: var(--color-primary);
} }
.l-retard { .l-retard {
background-color: #F1D99C; background-color: var(--color-retard);
} }
/* Ajoutez des styles pour le conteneur de pagination et les boutons */ /* Ajoutez des styles pour le conteneur de pagination et les boutons */
@ -531,9 +531,9 @@
} }
.pagination-button.active { .pagination-button.active {
background-color: #007bff; background-color: var(--color-primary);
color: #fff; color: #fff;
border-color: #007bff; border-color: var(--color-primary);
} }
th>div { th>div {

View File

@ -82,7 +82,7 @@
filterJustificatifs.columns.forEach((k) => { filterJustificatifs.columns.forEach((k) => {
const td = document.createElement('td'); const td = document.createElement('td');
if (k.indexOf('date') != -1) { if (k.indexOf('date') != -1) {
td.textContent = moment.tz(justificatif[k], TIMEZONE).format(`DD/MM/Y HH:mm`) td.textContent = new Date(justificatif[k]).format(`DD/MM/Y HH:mm`)
} else if (k.indexOf('fichier') != -1) { } else if (k.indexOf('fichier') != -1) {
td.textContent = justificatif.fichier ? "Oui" : "Non"; td.textContent = justificatif.fichier ? "Oui" : "Non";
} else if (k.indexOf('etudid') != -1) { } else if (k.indexOf('etudid') != -1) {
@ -120,9 +120,9 @@
path, path,
(data) => { (data) => {
const user = getUser(data); const user = getUser(data);
const date_debut = moment.tz(data.date_debut, TIMEZONE).format("DD/MM/YYYY HH:mm"); const date_debut = new Date(data.date_debut).format("DD/MM/YYYY HH:mm");
const date_fin = moment.tz(data.date_fin, TIMEZONE).format("DD/MM/YYYY HH:mm"); const date_fin = new Date(data.date_fin).format("DD/MM/YYYY HH:mm");
const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm"); const entry_date = new Date(data.entry_date).format("DD/MM/YYYY HH:mm");
const etat = data.etat.capitalize(); const etat = data.etat.capitalize();
const desc = data.raison == null ? "" : data.raison; const desc = data.raison == null ? "" : data.raison;
@ -201,7 +201,7 @@
fichContent.appendChild(a); fichContent.appendChild(a);
}) })
openAlertModal("Détails", el.firstElementChild, null, "green") openAlertModal("Détails", el.firstElementChild, null, "var(--color-information)")
} }
) )
} }
@ -305,9 +305,8 @@
assiEdit.querySelector('#justi_etat').value = data.etat.toLowerCase(); assiEdit.querySelector('#justi_etat').value = data.etat.toLowerCase();
assiEdit.querySelector('#justi_raison').value = desc; assiEdit.querySelector('#justi_raison').value = desc;
const d_deb = moment.tz(data.date_debut, TIMEZONE).format("YYYY-MM-DDTHH:mm") const d_deb = new Date(data.date_debut).format("YYYY-MM-DDTHH:mm")
const d_fin = moment.tz(data.date_fin, TIMEZONE).format("YYYY-MM-DDTHH:mm") const d_fin = new Date(data.date_fin).format("YYYY-MM-DDTHH:mm")
console.warn(d_deb, d_fin, data.date_debut, data.date_fin)
assiEdit.querySelector('#justi_date_debut').value = d_deb assiEdit.querySelector('#justi_date_debut').value = d_deb
assiEdit.querySelector('#justi_date_fin').value = d_fin assiEdit.querySelector('#justi_date_fin').value = d_fin
@ -357,8 +356,8 @@
openAlertModal("Dates erronées", document.createTextNode('Les dates sont invalides')); openAlertModal("Dates erronées", document.createTextNode('Les dates sont invalides'));
return true return true
} }
date_debut = moment.tz(date_debut, TIMEZONE) date_debut = new Date(date_debut)
date_fin = moment.tz(date_fin, TIMEZONE) date_fin = new Date(date_fin)
if (date_debut >= date_fin) { if (date_debut >= date_fin) {
openAlertModal("Dates erronées", document.createTextNode('La date de fin doit être après la date de début')); openAlertModal("Dates erronées", document.createTextNode('La date de fin doit être après la date de début'));
@ -389,7 +388,7 @@
}) })
}, () => { }, "green"); }, () => { }, "var(--color-information)");
} }
); );
} }
@ -626,7 +625,7 @@
if (l.querySelector(`#${key}_time`).value != "") { if (l.querySelector(`#${key}_time`).value != "") {
filterJustificatifs.filters[key] = { filterJustificatifs.filters[key] = {
pref: pref, pref: pref,
time: new moment.tz(time, TIMEZONE) time: new Date(time)
} }
} }
} else if (key.indexOf('etat') != -1) { } else if (key.indexOf('etat') != -1) {
@ -643,7 +642,7 @@
loadAll(); loadAll();
}, () => { }, "#7059FF"); }, () => { }, "var(--color-primary)");
} }
function downloadJusti() { function downloadJusti() {

View File

@ -144,7 +144,6 @@
const startLeft = parseFloat(periodTimeLine.style.left); const startLeft = parseFloat(periodTimeLine.style.left);
const onMouseMove = (moveEvent) => { const onMouseMove = (moveEvent) => {
console.warn("move Period")
if (handleMoving) return; if (handleMoving) return;
const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX; const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX;
const containerWidth = timelineContainer.clientWidth; const containerWidth = timelineContainer.clientWidth;
@ -320,7 +319,7 @@
.period { .period {
position: absolute; position: absolute;
height: 100%; height: 100%;
background-color: rgba(0, 183, 255, 0.5); background-color: var(--color-secondary);
border-radius: 15px; border-radius: 15px;
} }
@ -352,7 +351,7 @@
.period:hover .period-time { .period:hover .period-time {
display: flex; display: flex;
background-color: rgba(0, 183, 255, 1); background-color: var(--color-secondary);
border-radius: 15px; border-radius: 15px;
padding: 5px; padding: 5px;
} }

View File

@ -68,7 +68,7 @@
<script> <script>
function generateToast(content, color = "#12d3a5", ttl = 5) { function generateToast(content, color = "var(--color-present)", ttl = 5) {
const toast = document.createElement('div') const toast = document.createElement('div')
toast.classList.add('toast', 'fadeIn') toast.classList.add('toast', 'fadeIn')
@ -97,13 +97,13 @@
let color; let color;
switch (etat.toUpperCase()) { switch (etat.toUpperCase()) {
case "PRESENT": case "PRESENT":
color = "#6bdb83"; color = "var(--color-present)";
break; break;
case "ABSENT": case "ABSENT":
color = "#F1A69C"; color = "var(--color-absent)";
break; break;
case "RETARD": case "RETARD":
color = "#f0c865"; color = "var(--color-retard)";
break; break;
default: default:
color = "#AAA"; color = "#AAA";

View File

@ -25,6 +25,7 @@
############################################################################## ##############################################################################
import datetime import datetime
import re
from flask import g, request, render_template, flash from flask import g, request, render_template, flash
from flask import abort, url_for, redirect from flask import abort, url_for, redirect
@ -167,8 +168,7 @@ def bilan_dept():
page_title="Saisie de l'assiduité", page_title="Saisie de l'assiduité",
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=[ cssstyles=[
"css/assiduites.css", "css/assiduites.css",
@ -290,8 +290,7 @@ def signal_assiduites_etud():
init_qtip=True, init_qtip=True,
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
"js/etud_info.js", "js/etud_info.js",
], ],
cssstyles=[ cssstyles=[
@ -321,7 +320,7 @@ def signal_assiduites_etud():
render_template( render_template(
"assiduites/pages/signal_assiduites_etud.j2", "assiduites/pages/signal_assiduites_etud.j2",
sco=ScoData(etud), sco=ScoData(etud),
date=date, date=_dateiso_to_datefr(date),
morning=morning, morning=morning,
lunch=lunch, lunch=lunch,
timeline=_timeline(heures=",".join([f"'{s}'" for s in heures])), timeline=_timeline(heures=",".join([f"'{s}'" for s in heures])),
@ -371,8 +370,7 @@ def liste_assiduites_etud():
init_qtip=True, init_qtip=True,
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -419,8 +417,7 @@ def bilan_etud():
init_qtip=True, init_qtip=True,
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -429,8 +426,8 @@ def bilan_etud():
) )
# Gestion des dates du bilan (par défaut l'année scolaire) # Gestion des dates du bilan (par défaut l'année scolaire)
date_debut: str = f"{scu.annee_scolaire()}-09-01" date_debut: str = f"01/09/{scu.annee_scolaire()}"
date_fin: str = f"{scu.annee_scolaire()+1}-06-30" date_fin: str = f"30/06/{scu.annee_scolaire()+1}"
# Récupération de la métrique d'assiduité # Récupération de la métrique d'assiduité
assi_metric = scu.translate_assiduites_metric( assi_metric = scu.translate_assiduites_metric(
@ -480,8 +477,7 @@ def ajout_justificatif_etud():
init_qtip=True, init_qtip=True,
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -530,8 +526,7 @@ def calendrier_etud():
init_qtip=True, init_qtip=True,
javascripts=[ javascripts=[
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -540,10 +535,12 @@ def calendrier_etud():
) )
# Récupération des années d'étude de l'étudiant # Récupération des années d'étude de l'étudiant
annees: list[int] = sorted( annees: list[int] = []
[ins.formsemestre.date_debut.year for ins in etud.formsemestre_inscriptions], for ins in etud.formsemestre_inscriptions:
reverse=True, annees.extend(
(ins.formsemestre.date_debut.year, ins.formsemestre.date_fin.year)
) )
annees = sorted(annees, reverse=True)
# Transformation en une liste "json" # Transformation en une liste "json"
# (sera utilisé pour générer le selecteur d'année) # (sera utilisé pour générer le selecteur d'année)
@ -681,12 +678,10 @@ def signal_assiduites_group():
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
+ [ + [
# Voir fonctionnement JS # Voir fonctionnement JS
# XXX Retirer moment
"js/etud_info.js", "js/etud_info.js",
"js/groups_view.js", "js/groups_view.js",
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -705,7 +700,7 @@ def signal_assiduites_group():
"assiduites/pages/signal_assiduites_group.j2", "assiduites/pages/signal_assiduites_group.j2",
gr_tit=gr_tit, gr_tit=gr_tit,
sem=sem["titre_num"], sem=sem["titre_num"],
date=date, date=_dateiso_to_datefr(date),
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
grp=sco_groups_view.menu_groups_choice(groups_infos), grp=sco_groups_view.menu_groups_choice(groups_infos),
moduleimpl_select=_module_selector(formsemestre, moduleimpl_id), moduleimpl_select=_module_selector(formsemestre, moduleimpl_id),
@ -833,8 +828,7 @@ def visu_assiduites_group():
"js/etud_info.js", "js/etud_info.js",
"js/groups_view.js", "js/groups_view.js",
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
], ],
cssstyles=CSSSTYLES cssstyles=CSSSTYLES
+ [ + [
@ -852,7 +846,7 @@ def visu_assiduites_group():
"assiduites/pages/signal_assiduites_group.j2", "assiduites/pages/signal_assiduites_group.j2",
gr_tit=gr_tit, gr_tit=gr_tit,
sem=sem["titre_num"], sem=sem["titre_num"],
date=date, date=_dateiso_to_datefr(date),
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
grp=sco_groups_view.menu_groups_choice(groups_infos), grp=sco_groups_view.menu_groups_choice(groups_infos),
moduleimpl_select=_module_selector(formsemestre, moduleimpl_id), moduleimpl_select=_module_selector(formsemestre, moduleimpl_id),
@ -1024,8 +1018,8 @@ def visu_assi_group():
inverse=False, inverse=False,
short=False, short=False,
), ),
date_debut=dates["debut"], date_debut=_dateiso_to_datefr(dates["debut"]),
date_fin=dates["fin"], date_fin=_dateiso_to_datefr(dates["debut"]),
gr_tit=gr_tit, gr_tit=gr_tit,
group_ids=request.args.get("group_ids", None), group_ids=request.args.get("group_ids", None),
sco=ScoData(formsemestre=groups_infos.get_formsemestre()), sco=ScoData(formsemestre=groups_infos.get_formsemestre()),
@ -1113,8 +1107,7 @@ def signal_assiduites_diff():
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
+ [ + [
"js/assiduites.js", "js/assiduites.js",
"libjs/moment-2.29.4.min.js", "js/date_utils.js",
"libjs/moment-timezone.js",
"js/etud_info.js", "js/etud_info.js",
], ],
) )
@ -1315,9 +1308,44 @@ def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
) )
@bp.route("/test", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
def test():
"""XXX fonction de test a retirer"""
if request.method == "POST":
print("test date_utils : ", request.form)
return render_template("assiduites/pages/test.j2")
# --- Fonctions internes --- # --- Fonctions internes ---
def _dateiso_to_datefr(date_iso: str) -> str:
"""
_dateiso_to_datefr Transforme une date iso en date format français
Args:
date_iso (str): date au format iso (YYYY-MM-DD)
Raises:
ValueError: Si l'argument `date_iso` n'est pas au bon format
Returns:
str: date au format français (DD/MM/YYYY)
"""
regex_date_iso: str = r"^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$"
# Vérification de la date_iso
if not re.match(regex_date_iso, date_iso):
raise ValueError(
f"La dateiso passée en paramètre [{date_iso}] n'est pas valide."
)
return f"{date_iso[8:10]}/{date_iso[5:7]}/{date_iso[0:4]}"
def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str: def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str:
""" """
_get_date_str transforme une période en chaîne lisible _get_date_str transforme une période en chaîne lisible