Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
10 changed files with 413 additions and 71 deletions
Showing only changes of commit da8b416785 - Show all commits

View File

@ -10,6 +10,10 @@
opacity: 0.5; opacity: 0.5;
} }
#validate_selectors {
margin-top: 5vh;
}
.no-display { .no-display {
display: none !important; display: none !important;
} }
@ -246,6 +250,10 @@
background-image: url(../icons/absent.svg); background-image: url(../icons/absent.svg);
} }
.rbtn.aucun::before {
background-image: url(../icons/aucun.svg);
}
.rbtn.retard::before { .rbtn.retard::before {
background-image: url(../icons/retard.svg); background-image: url(../icons/retard.svg);
} }
@ -498,3 +506,18 @@
width: 75%; width: 75%;
padding: 20px; padding: 20px;
} }
.order {
display: block;
width: 24px;
height: 24px;
background-image: url(../icons/sort.svg);
outline: none;
border: none;
cursor: pointer;
}
.order:focus {
outline: none;
border: none;
}

8
app/static/icons/aucun.svg Executable file
View File

@ -0,0 +1,8 @@
<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"/>
<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: 291 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10 14H2m6-4H2m4-4H2m10 12H2m17 2V4m0 16l3-3m-3 3l-3-3m3-13l3 3m-3-3l-3 3"/></svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@ -20,17 +20,7 @@ let justificatifs = {};
// Variable qui définit si le processus d'action de masse est lancé // Variable qui définit si le processus d'action de masse est lancé
let currentMassAction = false; let currentMassAction = false;
let currentMassActionEtat = undefined;
/**
* Variable de gestion des conflits
*/
let modal;
let closeBtn;
let timeline;
let deleteBtn;
let splitBtn;
let editBtn;
let selectedAssiduite;
/** /**
* Ajout d'une fonction `capitalize` sur tous les strings * Ajout d'une fonction `capitalize` sur tous les strings
@ -74,7 +64,7 @@ function setupCheckBox(parent = document) {
* - Module impl * - Module impl
* - Date * - Date
*/ */
function validateSelectors() { function validateSelectors(btn) {
const action = () => { const action = () => {
const group_ids = getGroupIds(); const group_ids = getGroupIds();
@ -113,6 +103,8 @@ function validateSelectors() {
document.querySelector(".selectors").disabled = true; document.querySelector(".selectors").disabled = true;
generateMassAssiduites(); generateMassAssiduites();
generateAllEtudRow(); generateAllEtudRow();
btn.remove();
onlyAbs();
}; };
if (!verifyDateInSemester()) { if (!verifyDateInSemester()) {
@ -133,6 +125,14 @@ function validateSelectors() {
action(); action();
} }
function onlyAbs() {
if (getDate() > moment()) {
document
.querySelectorAll(".rbtn.present, .rbtn.retard")
.forEach((el) => el.remove());
}
}
/** /**
* Limite le nombre de checkbox marquée * Limite le nombre de checkbox marquée
* Vérifie aussi si le cliqué est fait sur des assiduités conflictuelles * Vérifie aussi si le cliqué est fait sur des assiduités conflictuelles
@ -287,6 +287,7 @@ function executeMassActionQueue() {
console.error(data, status); console.error(data, status);
} }
); );
return createQueue.length;
}; };
//Fonction qui modifie les assiduités de la queue 'edition' //Fonction qui modifie les assiduités de la queue 'edition'
@ -312,6 +313,7 @@ function executeMassActionQueue() {
console.error(data, status); console.error(data, status);
} }
); );
return editQueue.length;
}; };
//Fonction qui supprime les assiduités de la queue 'supprimer' //Fonction qui supprime les assiduités de la queue 'supprimer'
@ -328,12 +330,44 @@ function executeMassActionQueue() {
console.error(data, status); console.error(data, status);
} }
); );
return toDelete.length;
}; };
//On exécute les fonctions de queue //On exécute les fonctions de queue
create(); let color;
edit(); switch (currentMassActionEtat.toUpperCase()) {
supprimer(); case "PRESENT":
color = "#6bdb83";
break;
case "ABSENT":
color = "#F1A69C";
break;
case "RETARD":
color = "#f0c865";
break;
default:
color = "#AAA";
break;
}
let count = 0;
if (currentMassActionEtat == "remove") {
count += supprimer();
const span = document.createElement("span");
span.innerHTML = `${count} assiduités ont été supprimées.`;
pushToast(generateToast(span, color, 5));
} else {
count += create();
count += edit();
const etat =
currentMassActionEtat.toUpperCase() == "RETARD"
? "En retard"
: currentMassActionEtat;
const span = document.createElement("span");
span.innerHTML = `${count} étudiants ont été mis <u><strong>${etat
.capitalize()
.trim()}</strong></u>`;
pushToast(generateToast(span, color, 5));
}
//On récupère les assiduités puis on regénère les lignes d'étudiants //On récupère les assiduités puis on regénère les lignes d'étudiants
getAssiduitesFromEtuds(true); getAssiduitesFromEtuds(true);
generateAllEtudRow(); generateAllEtudRow();
@ -346,7 +380,10 @@ function massAction() {
//On récupère tous les boutons d'assiduités //On récupère tous les boutons d'assiduités
const fields = Array.from(document.querySelectorAll(".btns_field.single")); const fields = Array.from(document.querySelectorAll(".btns_field.single"));
//On récupère l'état de l'action de masse //On récupère l'état de l'action de masse
const action = getAssiduiteValue(document.querySelector(".btns_field.mass")); currentMassActionEtat = getAssiduiteValue(
document.querySelector(".btns_field.mass")
);
//On remet à 0 les queues //On remet à 0 les queues
resetMassActionQueue(); resetMassActionQueue();
@ -366,7 +403,11 @@ function massAction() {
*/ */
fields.forEach((field) => { fields.forEach((field) => {
if (field.getAttribute("type") != "conflit") { if (field.getAttribute("type") != "conflit") {
field.querySelector(`.rbtn.${action}`).click(); if (currentMassActionEtat != "remove") {
field.querySelector(`.rbtn.${currentMassActionEtat}`).click();
} else {
field.querySelector(".rbtn.absent").click();
}
} else { } else {
const etudid = field.getAttribute("etudid"); const etudid = field.getAttribute("etudid");
conflicts.push(etuds[parseInt(etudid)]); conflicts.push(etuds[parseInt(etudid)]);
@ -379,6 +420,7 @@ function massAction() {
//Fin du processus, on remet à false //Fin du processus, on remet à false
currentMassAction = false; currentMassAction = false;
currentMassActionEtat = undefined;
//On remet à zero les boutons d'assiduité de masse //On remet à zero les boutons d'assiduité de masse
const boxes = Array.from( const boxes = Array.from(
@ -423,6 +465,7 @@ function generateMassAssiduites() {
class="rbtn present"> class="rbtn present">
<input type="checkbox" value="retard" name="mass_btn_assiduites" id="mass_rbtn_retard" class="rbtn retard"> <input type="checkbox" value="retard" name="mass_btn_assiduites" id="mass_rbtn_retard" class="rbtn retard">
<input type="checkbox" value="absent" name="mass_btn_assiduites" id="mass_rbtn_absent" class="rbtn absent"> <input type="checkbox" value="absent" name="mass_btn_assiduites" id="mass_rbtn_absent" class="rbtn absent">
<input type="checkbox" value="remove" name="mass_btn_assiduites" id="mass_rbtn_aucun" class="rbtn aucun">
</fieldset>`; </fieldset>`;
content.insertBefore(mass, content.querySelector(".etud_holder")); content.insertBefore(mass, content.querySelector(".etud_holder"));
@ -953,7 +996,6 @@ function actualizeEtudAssiduite(etudid, has_formsemestre = true) {
getUrl() + getUrl() +
`/api/assiduites/${etudid}/query?${formsemestre_id}date_debut=${date_debut}&date_fin=${date_fin}`; `/api/assiduites/${etudid}/query?${formsemestre_id}date_debut=${date_debut}&date_fin=${date_fin}`;
sync_get(url_api, (data, status) => { sync_get(url_api, (data, status) => {
console.error(data, status);
if (status === "success") { if (status === "success") {
assiduites[etudid] = data; assiduites[etudid] = data;
} }
@ -991,20 +1033,22 @@ function assiduiteAction(element) {
// Cas de l'action de masse -> peuplement des queues // Cas de l'action de masse -> peuplement des queues
if (currentMassAction) { if (currentMassAction) {
switch (type) { if (currentMassActionEtat != "remove") {
case "création": switch (type) {
addToMassActionQueue("creer", { etat: etat, etudid: etudid }); case "création":
break; addToMassActionQueue("creer", { etat: etat, etudid: etudid });
case "édition": break;
if (etat === "remove") { case "édition":
addToMassActionQueue("supprimer", assiduite_id); if (etat != "remove") {
} else { addToMassActionQueue("editer", {
addToMassActionQueue("editer", { etat: etat,
etat: etat, assiduite_id: assiduite_id,
assiduite_id: assiduite_id, });
}); }
} break;
break; }
} else if (type == "édition") {
addToMassActionQueue("supprimer", assiduite_id);
} }
} else { } else {
// Cas normal -> mise à jour en base // Cas normal -> mise à jour en base
@ -1042,13 +1086,49 @@ function assiduiteAction(element) {
} }
if (type != "conflit") { if (type != "conflit") {
document let etatAffiche;
.querySelector(".toast-holder")
.appendChild( switch (etat.toUpperCase()) {
generateToast( case "PRESENT":
document.createTextNode("L'assiduité a bien été enregistrée.") etatAffiche =
) "%etud% a été noté(e) <u><strong>présent(e)</strong></u>";
); break;
case "RETARD":
etatAffiche =
"%etud% a été noté(e) <u><strong>en retard</strong></u>";
break;
case "ABSENT":
etatAffiche =
"%etud% a été noté(e) <u><strong>absent(e)</strong></u>";
break;
case "REMOVE":
etatAffiche = "L'assiduité de %etud% a été retirée.";
}
let color;
switch (etat.toUpperCase()) {
case "PRESENT":
color = "#6bdb83";
break;
case "ABSENT":
color = "#F1A69C";
break;
case "RETARD":
color = "#f0c865";
break;
default:
color = "#AAA";
break;
}
const nom_prenom = `${etuds[etudid].nom.toUpperCase()} ${etuds[
etudid
].prenom.capitalize()}`;
const span = document.createElement("span");
span.innerHTML = etatAffiche.replace("%etud%", nom_prenom);
pushToast(generateToast(span, color, 5));
} }
actualizeEtud(etudid, !isSingleEtud); actualizeEtud(etudid, !isSingleEtud);

View File

@ -692,6 +692,52 @@
}) })
} }
function launchToast(etudid, etat) {
let etatAffiche;
switch (etat.toUpperCase()) {
case "PRESENT":
etatAffiche =
"%etud% a été noté(e) <u><strong>présent(e)</strong></u>";
break;
case "RETARD":
etatAffiche =
"%etud% a été noté(e) <u><strong>en retard</strong></u>";
break;
case "ABSENT":
etatAffiche =
"%etud% a été noté(e) <u><strong>absent(e)</strong></u>";
break;
case "REMOVE":
etatAffiche = "L'assiduité de %etud% a été retirée.";
}
let color;
switch (etat.toUpperCase()) {
case "PRESENT":
color = "#6bdb83";
break;
case "ABSENT":
color = "#F1A69C";
break;
case "RETARD":
color = "#f0c865";
break;
default:
color = "#AAA";
break;
}
const nom_prenom = `${etuds[etudid].nom.toUpperCase()} ${etuds[
etudid
].prenom.capitalize()}`;
const span = document.createElement("span");
span.innerHTML = etatAffiche.replace("%etud%", nom_prenom);
pushToast(generateToast(span, color, 5));
}
function updateEtudAssiduite(rbtn) { function updateEtudAssiduite(rbtn) {
const [_, colid, etudid] = rbtn.name.split("_"); const [_, colid, etudid] = rbtn.name.split("_");
@ -714,8 +760,8 @@
assiduite["assiduite_id"] = assi_id; assiduite["assiduite_id"] = assi_id;
assiduites[etudid].push(assiduite); assiduites[etudid].push(assiduite);
updateAllCol() updateAllCol()
launchToast(etudid, etat);
// TODO Envoyer toast
} }
}) })
break; break;
@ -761,7 +807,12 @@
} }
asyncEditAssiduite(edit, (data) => { asyncEditAssiduite(edit, (data) => {
console.log(data) const obj = getAssiduite(etudid, assi);
obj.moduleimpl = edit.moduleimpl_id;
obj.etat = edit.etat;
launchToast(etudid, etat);
}) })
break; break;
@ -769,6 +820,10 @@
} }
function getAssiduite(etudid, id) {
return assiduites[etudid].filter((a) => a.assiduite_id == id)
}
function asyncCreateAssiduite(assi, callback = () => { }) { function asyncCreateAssiduite(assi, callback = () => { }) {
const path = getUrl() + `/api/assiduite/${assi.etudid}/create`; const path = getUrl() + `/api/assiduite/${assi.etudid}/create`;
async_post( async_post(

View File

@ -10,11 +10,36 @@
<table id="assiduiteTable"> <table id="assiduiteTable">
<thead> <thead>
<tr> <tr>
<th>Début</th> <th>
<th>Fin</th> <div>
<th>État</th> <span>Début</span>
<th>Module</th> <a class="order" onclick="order('date_debut', assiduiteCallBack, this)"></a>
<th>Justifiée</th> </div>
</th>
<th>
<div>
<span>Fin</span>
<a class="order" onclick="order('date_fin', assiduiteCallBack, this)"></a>
</div>
</th>
<th>
<div>
<span>État</span>
<a class="order" onclick="order('etat', assiduiteCallBack, this)"></a>
</div>
</th>
<th>
<div>
<span>Module</span>
<a class="order" onclick="order('moduleimpl_id', assiduiteCallBack, this)"></a>
</div>
</th>
<th>
<div>
<span>Justifiée</span>
<a class="order" onclick="order('est_just', assiduiteCallBack, this)"></a>
</div>
</th>
</tr> </tr>
</thead> </thead>
<tbody id="tableBodyAssiduites"> <tbody id="tableBodyAssiduites">
@ -26,10 +51,30 @@
<table id="justificatifTable"> <table id="justificatifTable">
<thead> <thead>
<tr> <tr>
<th>Début</th> <th>
<th>Fin</th> <div>
<th>État</th> <span>Début</span>
<th>Raison</th> <a class="order" onclick="order('date_debut', justificatifCallBack, this, false)"></a>
</div>
</th>
<th>
<div>
<span>Fin</span>
<a class="order" onclick="order('date_fin', justificatifCallBack, this, false)"></a>
</div>
</th>
<th>
<div>
<span>État</span>
<a class="order" onclick="order('etat', justificatifCallBack, this, false)"></a>
</div>
</th>
<th>
<div>
<span>Raison</span>
<a class="order" onclick="order('raison', justificatifCallBack, this, false)"></a>
</div>
</th>
</tr> </tr>
</thead> </thead>
<tbody id="tableBodyJustificatifs"> <tbody id="tableBodyJustificatifs">
@ -70,6 +115,7 @@
th { th {
background-color: #f2f2f2; background-color: #f2f2f2;
} }
tr:hover { tr:hover {
@ -138,6 +184,12 @@
color: #fff; color: #fff;
border-color: #007bff; border-color: #007bff;
} }
th>div {
display: flex;
justify-content: space-between;
align-items: center;
}
</style> </style>
<script> <script>
@ -147,6 +199,8 @@
const itemsPerPage = 10; const itemsPerPage = 10;
let currentPageAssiduites = 1; let currentPageAssiduites = 1;
let currentPageJustificatifs = 1; let currentPageJustificatifs = 1;
let orderAssiduites = true;
let orderJustificatifs = true;
const tableBodyAssiduites = document.getElementById("tableBodyAssiduites"); const tableBodyAssiduites = document.getElementById("tableBodyAssiduites");
const tableBodyJustificatifs = document.getElementById("tableBodyJustificatifs"); const tableBodyJustificatifs = document.getElementById("tableBodyJustificatifs");
@ -184,6 +238,33 @@
} }
}); });
function assiduiteCallBack(assi) {
renderTableAssiduites(currentPageAssiduites, assi);
renderPaginationButtons(assi);
}
function justificatifCallBack(justi) {
renderTableJustificatifs(currentPageJustificatifs, justi);
renderPaginationButtons(justi, false);
}
const moduleimpls = {}
function getModuleImpl(id) {
if (id == null || id == undefined) {
moduleimpls[id] = "Pas de module"
}
if (id in moduleimpls) {
return moduleimpls[id];
}
const url_api = getUrl() + `/api/moduleimpl/${id}`;
sync_get(url_api, (data) => {
moduleimpls[id] = `${data.module.code} ${data.module.abbrev}`;
}, (data) => { moduleimpls[id] = "Pas de module" });
return moduleimpls[id];
}
function renderTableAssiduites(page, assiduités) { function renderTableAssiduites(page, assiduités) {
tableBodyAssiduites.innerHTML = ""; tableBodyAssiduites.innerHTML = "";
const start = (page - 1) * itemsPerPage; const start = (page - 1) * itemsPerPage;
@ -198,10 +279,10 @@
row.classList.add(etat); row.classList.add(etat);
row.innerHTML = ` row.innerHTML = `
<td>${new Date(assiduite.date_debut).toLocaleString()}</td> <td>${moment.tz(assiduite.date_debut, TIMEZONE).format(`DD/MM/Y HH:mm`)}</td>
<td>${new Date(assiduite.date_fin).toLocaleString()}</td> <td>${moment.tz(assiduite.date_fin, TIMEZONE).format(`DD/MM/Y HH:mm`)}</td>
<td>${etat}</td> <td>${etat}</td>
<td>${assiduite.moduleimpl_id}</td> <td>${assiduite.est_just ? "Oui" : "Non" <td>${getModuleImpl(assiduite.moduleimpl_id)}</td> <td>${assiduite.est_just ? "Oui" : "Non"
}</td> }</td>
`; `;
@ -267,6 +348,10 @@
paginationContainerJustificatifs.innerHTML = "" paginationContainerJustificatifs.innerHTML = ""
} }
if (totalPages == 1) {
return;
}
for (let i = 1; i <= totalPages; i++) { for (let i = 1; i <= totalPages; i++) {
const paginationButton = document.createElement("a"); const paginationButton = document.createElement("a");
paginationButton.textContent = i; paginationButton.textContent = i;
@ -312,15 +397,47 @@
} }
function loadAll() { function loadAll() {
getAllAssiduitesFromEtud(etudid, (assi) => { getAllAssiduitesFromEtud(etudid, assiduiteCallBack)
renderTableAssiduites(currentPageAssiduites, assi);
renderPaginationButtons(assi), true; getAllJustificatifsFromEtud(etudid, justificatifCallBack)
}) }
function order(keyword, callback = () => { }, el, assi = true) {
const call = (array) => {
const sorted = array.sort((a, b) => {
let keyValueA = a[keyword];
let keyValueB = b[keyword];
if (keyword.indexOf("date") != -1) {
keyValueA = moment.tz(keyValueA, TIMEZONE)
keyValueB = moment.tz(keyValueB, TIMEZONE)
}
if (keyword.indexOf("module") != -1) {
keyValueA = getModuleImpl(keyValueA);
keyValueB = getModuleImpl(keyValueB);
}
let orderDertermined = keyValueA > keyValueB;
if (el.classList.contains("desc")) {
orderDertermined = keyValueA < keyValueB;
}
return orderDertermined
});
el.classList.toggle("desc");
callback(sorted);
};
if (assi) {
getAllAssiduitesFromEtud(etudid, call)
} else {
getAllJustificatifsFromEtud(etudid, call)
}
getAllJustificatifsFromEtud(etudid, (assi) => {
renderTableJustificatifs(currentPageJustificatifs, assi);
renderPaginationButtons(assi, false);
})
} }
window.onload = () => { window.onload = () => {

View File

@ -71,7 +71,8 @@
setupDate(() => { setupDate(() => {
if (updateDate()) { if (updateDate()) {
actualizeEtud(etudid); actualizeEtud(etudid);
updateSelect() updateSelect();
onlyAbs();
} }
}); });
@ -98,11 +99,6 @@
window.forceModule = "{{ forcer_module }}" window.forceModule = "{{ forcer_module }}"
window.forceModule = window.forceModule == "True" ? true : false window.forceModule = window.forceModule == "True" ? true : false
window.addEventListener('load', function () {
loading();
}, { once: true });
</script> </script>

View File

@ -22,14 +22,14 @@
Date: <span id="datestr"></span> Date: <span id="datestr"></span>
<input type="date" name="tl_date" id="tl_date" value="{{ date }}" onchange="updateDate()"> <input type="date" name="tl_date" id="tl_date" value="{{ date }}" onchange="updateDate()">
</div> </div>
<button id="validate_selectors" onclick="validateSelectors()">
Valider
</button>
</fieldset> </fieldset>
{{timeline|safe}} {{timeline|safe}}
<button id="validate_selectors" onclick="validateSelectors(this)">
Faire la saisie
</button>
<div class="etud_holder"> <div class="etud_holder">
<p class="placeholder"> <p class="placeholder">
Veillez à choisir le groupe concerné par la saisie ainsi que la date de la saisie. Veillez à choisir le groupe concerné par la saisie ainsi que la date de la saisie.
@ -77,5 +77,23 @@
window.forceModule = "{{ forcer_module }}" window.forceModule = "{{ forcer_module }}"
window.forceModule = window.forceModule == "True" ? true : false window.forceModule = window.forceModule == "True" ? true : false
if (window.forceModule) {
const btn = document.getElementById("validate_selectors");
const select = document.getElementById("moduleimpl_select");
if (select.value == "") {
btn.disabled = true;
}
select.addEventListener('change', (e) => {
if (e.target.value != "") {
btn.disabled = false;
} else {
btn.disabled = true;
}
});
}
</script> </script>
</section> </section>

View File

@ -2,6 +2,7 @@
<div class="period" style="left: 0%; width: 20%"> <div class="period" style="left: 0%; width: 20%">
<div class="period-handle left"></div> <div class="period-handle left"></div>
<div class="period-handle right"></div> <div class="period-handle right"></div>
<div class="period-time">Time</div>
</div> </div>
</div> </div>
<script> <script>
@ -77,6 +78,15 @@
return Math.round(value * tick_time) / tick_time; return Math.round(value * tick_time) / tick_time;
} }
function updatePeriodTimeLabel() {
const values = getPeriodValues();
const deb = numberToTime(values[0])
const fin = numberToTime(values[1])
const text = `${deb} - ${fin}`
periodTimeLine.querySelector('.period-time').textContent = text;
}
function setupTimeLine(callback) { function setupTimeLine(callback) {
const func_call = callback ? callback : () => { }; const func_call = callback ? callback : () => { };
timelineContainer.addEventListener("mousedown", (event) => { timelineContainer.addEventListener("mousedown", (event) => {
@ -91,6 +101,8 @@
const newLeft = startLeft + (deltaX / containerWidth) * 100; const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width)); adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
updatePeriodTimeLabel();
}; };
document.addEventListener("mousemove", onMouseMove); document.addEventListener("mousemove", onMouseMove);
@ -121,6 +133,8 @@
} else { } else {
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth); adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
} }
updatePeriodTimeLabel();
}; };
document.addEventListener("mousemove", onMouseMove); document.addEventListener("mousemove", onMouseMove);
@ -187,6 +201,7 @@
snapHandlesToQuarters(); snapHandlesToQuarters();
generateAllEtudRow(); generateAllEtudRow();
updatePeriodTimeLabel()
} }
function snapHandlesToQuarters() { function snapHandlesToQuarters() {
@ -204,6 +219,8 @@
const width = `${wid}%` const width = `${wid}%`
periodTimeLine.style.left = left; periodTimeLine.style.left = left;
periodTimeLine.style.width = width; periodTimeLine.style.width = width;
updatePeriodTimeLabel()
} }
function computePercentage(a, b) { function computePercentage(a, b) {
@ -273,4 +290,23 @@
right: 0; right: 0;
border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px;
} }
.period .period-time {
display: none;
position: absolute;
left: calc(50% - var(--w)/2 - 5px);
justify-content: center;
align-content: center;
top: calc(-60% - 10px);
--w: 10em;
width: var(--w);
}
.period:hover .period-time {
display: flex;
background-color: rgba(0, 183, 255, 1);
border-radius: 15px;
padding: 5px;
}
</style> </style>

View File

@ -83,6 +83,14 @@
return toast return toast
} }
function pushToast(toast) {
document
.querySelector(".toast-holder")
.appendChild(
toast
);
}