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
13 changed files with 655 additions and 65 deletions
Showing only changes of commit 4dc2b41402 - Show all commits

View File

@ -599,7 +599,7 @@ def assiduite_edit(assiduite_id: int):
moduleimpl: ModuleImpl = None moduleimpl: ModuleImpl = None
if moduleimpl_id is not False: if moduleimpl_id is not False:
if moduleimpl_id is not None: if moduleimpl_id is not None and moduleimpl_id != "":
moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first() moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first()
if moduleimpl is None: if moduleimpl is None:
errors.append("param 'moduleimpl_id': invalide") errors.append("param 'moduleimpl_id': invalide")
@ -611,7 +611,7 @@ def assiduite_edit(assiduite_id: int):
else: else:
assiduite_unique.moduleimpl_id = moduleimpl_id assiduite_unique.moduleimpl_id = moduleimpl_id
else: else:
assiduite_unique.moduleimpl_id = moduleimpl_id assiduite_unique.moduleimpl_id = None
# Cas 3 : desc # Cas 3 : desc
desc = data.get("desc", False) desc = data.get("desc", False)

View File

@ -272,9 +272,6 @@ def justif_edit(justif_id: int):
if deb is None: if deb is None:
errors.append("param 'date_debut': format invalide") errors.append("param 'date_debut': format invalide")
if justificatif_unique.date_fin >= deb:
errors.append("param 'date_debut': date de début située après date de fin ")
# cas 4 : date_fin # cas 4 : date_fin
date_fin = data.get("date_fin", False) date_fin = data.get("date_fin", False)
if date_fin is not False: if date_fin is not False:
@ -283,13 +280,14 @@ def justif_edit(justif_id: int):
fin = scu.is_iso_formated(date_fin.replace(" ", "+"), convert=True) fin = scu.is_iso_formated(date_fin.replace(" ", "+"), convert=True)
if fin is None: if fin is None:
errors.append("param 'date_fin': format invalide") errors.append("param 'date_fin': format invalide")
if justificatif_unique.date_debut <= fin:
errors.append("param 'date_fin': date de fin située avant date de début ")
# Mise à jour des dates # Mise à jour des dates
deb = deb if deb is not None else justificatif_unique.date_debut deb = deb if deb is not None else justificatif_unique.date_debut
fin = fin if fin is not None else justificatif_unique.date_fin fin = fin if fin is not None else justificatif_unique.date_fin
if fin <= deb:
errors.append("param 'dates' : Date de début après date de fin")
justificatif_unique.date_debut = deb justificatif_unique.date_debut = deb
justificatif_unique.date_fin = fin justificatif_unique.date_fin = fin

View File

@ -54,8 +54,8 @@ class Trace:
lines: list[str] = [] lines: list[str] = []
for fname, traced in self.content.items(): for fname, traced in self.content.items():
date_fin: datetime or None = traced[1].isoformat() if traced[1] else "None" date_fin: datetime or None = traced[1].isoformat() if traced[1] else "None"
if traced[0] is not None:
lines.append(f"{fname},{traced[0].isoformat()},{date_fin}") lines.append(f"{fname},{traced[0].isoformat()},{date_fin}")
with open(self.path, "w", encoding="utf-8") as file: with open(self.path, "w", encoding="utf-8") as file:
file.write("\n".join(lines)) file.write("\n".join(lines))

View File

@ -517,14 +517,25 @@
background-image: url(../icons/filter.svg); background-image: url(../icons/filter.svg);
} }
[name='destroyFile'] {
-webkit-appearance: none;
appearance: none;
cursor: pointer;
background-image: url(../icons/trash.svg);
}
[name='destroyFile']:checked {
background-image: url(../icons/remove_circle.svg);
}
.icon { .icon {
display: block; display: block;
width: 24px; width: 24px;
height: 24px; height: 24px;
outline: none; outline: none !important;
border: none; border: none !important;
cursor: pointer; cursor: pointer;
margin: 0 2px; margin: 0 2px !important;
} }
.icon:focus { .icon:focus {

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M9.172 14.828L12.001 12m2.828-2.828L12.001 12m0 0L9.172 9.172M12.001 12l2.828 2.828M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z" stroke="#fe4217" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 434 B

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M20 9l-1.995 11.346A2 2 0 0116.035 22h-8.07a2 2 0 01-1.97-1.654L4 9M21 6h-5.625M3 6h5.625m0 0V4a2 2 0 012-2h2.75a2 2 0 012 2v2m-6.75 0h6.75" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>

After

Width:  |  Height:  |  Size: 418 B

View File

@ -159,6 +159,7 @@
$.when( $.when(
requests requests
).done(() => { ).done(() => {
loadAll();
}) })
} }
@ -171,7 +172,6 @@
let couverture = null; let couverture = null;
createJustificatif(justificatif, (data) => { createJustificatif(justificatif, (data) => {
console.log(data);
if (Object.keys(data.errors).length > 0) { if (Object.keys(data.errors).length > 0) {
console.error(data.errors); console.error(data.errors);
} }
@ -179,7 +179,6 @@
couverture = data.success[0].couverture couverture = data.success[0].couverture
justif_id = data.success[0].justif_id; justif_id = data.success[0].justif_id;
importFiles(justif_id); importFiles(justif_id);
loadAll();
return; return;
} }
}) })

View File

@ -246,7 +246,7 @@
day.textContent = `${dayOfWeek} ${dayOfMonth}`; day.textContent = `${dayOfWeek} ${dayOfMonth}`;
if (!nonWorkdays.includes(dayOfWeek.toLowerCase())) { 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');
cache.appendChild( cache.appendChild(
@ -304,7 +304,6 @@
let dates = getDaysBetweenDates(bornes.deb, bornes.fin); let dates = getDaysBetweenDates(bornes.deb, bornes.fin);
let datesByMonth = organizeByMonth(dates); let datesByMonth = organizeByMonth(dates);
const justifs = getEtudJustificatifs(bornes.deb, bornes.fin); const justifs = getEtudJustificatifs(bornes.deb, bornes.fin);
console.log(justifs)
let assiduitiesByDay = organizeAssiduitiesByDay(datesByMonth, data, justifs); let assiduitiesByDay = organizeAssiduitiesByDay(datesByMonth, data, justifs);
generateCalendar(assiduitiesByDay, nonwork); generateCalendar(assiduitiesByDay, nonwork);
}); });

View File

@ -24,7 +24,7 @@
/* Hidden by default */ /* Hidden by default */
position: fixed; position: fixed;
/* Stay in place */ /* Stay in place */
z-index: 750; z-index: 850;
/* Sit on top */ /* Sit on top */
padding-top: 100px; padding-top: 100px;
/* Location of the box */ /* Location of the box */

View File

@ -26,7 +26,7 @@
/* Stay in place */ /* Stay in place */
z-index: 750; z-index: 750;
/* Sit on top */ /* Sit on top */
padding-top: 100px; padding-top: 3vh;
/* Location of the box */ /* Location of the box */
left: 0; left: 0;
top: 0; top: 0;
@ -181,8 +181,10 @@
succBtn.classList.add("btnPrompt") succBtn.classList.add("btnPrompt")
succBtn.textContent = "Valider" succBtn.textContent = "Valider"
succBtn.addEventListener('click', () => { succBtn.addEventListener('click', () => {
success(); const retour = success();
closePromptModal(); if (retour == null || retour == false || retour == undefined) {
closePromptModal();
}
}) })
const cancelBtn = document.createElement('button') const cancelBtn = document.createElement('button')
cancelBtn.classList.add("btnPrompt") cancelBtn.classList.add("btnPrompt")

View File

@ -39,6 +39,10 @@
<div id="paginationContainerAssiduites" class="pagination-container"> <div id="paginationContainerAssiduites" class="pagination-container">
</div> </div>
<div style="display: none;" id="cache-module">
{% include "assiduites/widgets/moduleimpl_dynamic_selector.j2" %}
</div>
<script> <script>
const paginationContainerAssiduites = document.getElementById("paginationContainerAssiduites"); const paginationContainerAssiduites = document.getElementById("paginationContainerAssiduites");
let currentPageAssiduites = 1; let currentPageAssiduites = 1;
@ -105,18 +109,160 @@
row.appendChild(td) row.appendChild(td)
}) })
row.addEventListener("contextmenu", (e) => { row.addEventListener("contextmenu", openContext);
e.preventDefault();
selectedRow = e.target.parentElement;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.display = "block";
console.log(selectedRow);
});
tableBodyAssiduites.appendChild(row); tableBodyAssiduites.appendChild(row);
}); });
updateActivePaginationButton(); updateActivePaginationButton();
} }
function detailAssiduites(assiduite_id) {
const path = getUrl() + `/api/assiduite/${assiduite_id}`;
async_get(
path,
(data) => {
const user = getUserFromId(data.user_id);
const module = getModuleImpl(data.moduleimpl_id);
const date_debut = moment.tz(data.date_debut, TIMEZONE).format("DD/MM/YYYY HH:mm");
const date_fin = moment.tz(data.date_fin, TIMEZONE).format("DD/MM/YYYY HH:mm");
const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm");
const etat = data.etat.capitalize();
const desc = data.desc == null ? "" : data.desc;
const id = data.assiduite_id;
const est_just = data.est_just ? "Oui" : "Non";
const html = `
<div class="obj-detail">
<div class="obj-dates">
<div id="date_debut" class="obj-part">
<span class="obj-title">Date de début</span>
<span class="obj-content">${date_debut}</span>
</div>
<div id="date_fin" class="obj-part">
<span class="obj-title">Date de fin</span>
<span class="obj-content">${date_fin}</span>
</div>
<div id="entry_date" class="obj-part">
<span class="obj-title">Date de saisie</span>
<span class="obj-content">${entry_date}</span>
</div>
</div>
<div class="obj-mod">
<div id="module" class="obj-part">
<span class="obj-title">Module</span>
<span class="obj-content">${module}</span>
</div>
<div id="etat" class="obj-part">
<span class="obj-title">Etat</span>
<span class="obj-content">${etat}</span>
</div>
<div id="user" class="obj-part">
<span class="obj-title">Créer par</span>
<span class="obj-content">${user}</span>
</div>
</div>
<div class="obj-rest">
<div id="est_just" class="obj-part">
<span class="obj-title">Justifié</span>
<span class="obj-content">${est_just}</span>
</div>
<div id="desc" class="obj-part">
<span class="obj-title">Description</span>
<p class="obj-content">${desc}</p>
</div>
<div id="id" class="obj-part">
<span class="obj-title">Identifiant de l'assiduité</span>
<span class="obj-content">${id}</span>
</div>
</div>
</div>
`
const el = document.createElement('div');
el.innerHTML = html;
openAlertModal("Détails", el.firstElementChild, null, "green")
}
)
}
function editionAssiduites(assiduite_id) {
const path = getUrl() + `/api/assiduite/${assiduite_id}`;
async_get(
path,
(data) => {
const module = data.moduleimpl_id;
const etat = data.etat;
const desc = data.desc;
const html = `
<div class="assi-edit">
<div class="assi-edit-part">
<legend>État de l'assiduité</legend>
<select name="etat" id="etat">
<option value="present">Présent</option>
<option value="retard">En Retard</option>
<option value="absent">Absent</option>
</select>
</div>
<div class="assi-edit-part">
<legend>Module</legend>
<select name="module" id="module">
</select>
</div>
<div class="assi-edit-part">
<legend>Description</legend>
<textarea name="desc" id="desc" cols="50" rows="10" maxlength="500"></textarea>
</div>
</div>
`
const el = document.createElement('div')
el.innerHTML = html;
const assiEdit = el.firstElementChild;
assiEdit.querySelector('#etat').value = etat.toLowerCase();
assiEdit.querySelector('#desc').value = desc != null ? desc : "";
updateSelect(module, '#moduleimpl_select', "2022-09-04")
assiEdit.querySelector('#module').replaceWith(document.querySelector('#moduleimpl_select').cloneNode(true));
openPromptModal("Modification de l'assiduité", assiEdit, () => {
const prompt = document.querySelector('.assi-edit');
const etat = prompt.querySelector('#etat').value;
const desc = prompt.querySelector('#desc').value;
const module = prompt.querySelector('#moduleimpl_select').value;
const edit = {
"etat": etat,
"desc": desc,
"moduleimpl_id": module,
}
fullEditAssiduites(data.assiduite_id, edit, () => {
try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { }
})
}, () => { }, "green");
}
);
}
function fullEditAssiduites(assiduite_id, obj, call = () => { }) {
const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
async_post(
path,
obj,
call,
(data, status) => {
//error
console.error(data, status);
}
);
}
</script> </script>

View File

@ -11,6 +11,7 @@
const itemsPerPage = 10; const itemsPerPage = 10;
const contextMenu = document.getElementById("contextMenu"); const contextMenu = document.getElementById("contextMenu");
const editOption = document.getElementById("editOption"); const editOption = document.getElementById("editOption");
const detailOption = document.getElementById("detailOption");
const deleteOption = document.getElementById("deleteOption"); const deleteOption = document.getElementById("deleteOption");
let selectedRow; let selectedRow;
@ -21,14 +22,32 @@
editOption.addEventListener("click", () => { editOption.addEventListener("click", () => {
if (selectedRow) { if (selectedRow) {
// Code pour éditer la ligne sélectionnée const type = selectedRow.getAttribute('type');
console.debug("Éditer :", selectedRow); const obj_id = selectedRow.getAttribute('obj_id');
if (type == "assiduite") {
editionAssiduites(obj_id);
} else {
editionJustificatifs(obj_id);
}
}
});
detailOption.addEventListener("click", () => {
if (selectedRow) {
const type = selectedRow.getAttribute('type');
const obj_id = selectedRow.getAttribute('obj_id');
if (type == "assiduite") {
detailAssiduites(obj_id);
} else {
detailJustificatifs(obj_id);
}
} }
}); });
deleteOption.addEventListener("click", () => { deleteOption.addEventListener("click", () => {
if (selectedRow) { if (selectedRow) {
// Code pour supprimer la ligne sélectionnée
const type = selectedRow.getAttribute('type'); const type = selectedRow.getAttribute('type');
const obj_id = selectedRow.getAttribute('obj_id'); const obj_id = selectedRow.getAttribute('obj_id');
if (type == "assiduite") { if (type == "assiduite") {
@ -112,33 +131,70 @@
function renderPaginationButtons(array, assi = true) { function renderPaginationButtons(array, assi = true) {
const totalPages = Math.ceil(array.length / itemsPerPage); const totalPages = Math.ceil(array.length / itemsPerPage);
if (totalPages <= 1) {
if (assi) {
paginationContainerAssiduites.innerHTML = ""
} else {
paginationContainerJustificatifs.innerHTML = ""
}
if (totalPages == 1) {
return; return;
} }
for (let i = 1; i <= totalPages; i++) { if (assi) {
const paginationButton = document.createElement("a"); paginationContainerAssiduites.innerHTML = "<span class='liste_pagination'><button class='pagination_moins'>&lt;</button><select id='paginationAssi'></select><button class='pagination_plus'>&gt;</button></span>"
paginationButton.textContent = i; paginationContainerAssiduites.querySelector('#paginationAssi')?.addEventListener('change', (e) => {
paginationButton.classList.add("pagination-button"); currentPageAssiduites = e.target.value;
if (assi) { renderTableAssiduites(currentPageAssiduites, array);
paginationButton.addEventListener("click", () => { })
currentPageAssiduites = i;
paginationContainerAssiduites.querySelector('.pagination_moins').addEventListener('click', () => {
if (currentPageAssiduites > 1) {
currentPageAssiduites--;
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
renderTableAssiduites(currentPageAssiduites, array); renderTableAssiduites(currentPageAssiduites, array);
}); }
paginationContainerAssiduites.appendChild(paginationButton); })
paginationContainerAssiduites.querySelector('.pagination_plus').addEventListener('click', () => {
if (currentPageAssiduites < totalPages) {
currentPageAssiduites++;
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
renderTableAssiduites(currentPageAssiduites, array);
}
})
} else {
paginationContainerJustificatifs.innerHTML = "<span class='liste_pagination'><button class='pagination_moins'>&lt;</button><select id='paginationJusti'></select><button class='pagination_plus'>&gt;</button></span>"
paginationContainerJustificatifs.querySelector('#paginationJusti')?.addEventListener('change', (e) => {
currentPageJustificatifs = e.target.value;
renderTableJustificatifs(currentPageJustificatifs, array);
})
paginationContainerJustificatifs.querySelector('.pagination_moins').addEventListener('click', () => {
if (currentPageJustificatifs > 1) {
currentPageJustificatifs--;
paginationContainerJustificatifs.querySelector('#paginationJusti').value = currentPageAssiduites
renderTableJustificatifs(currentPageJustificatifs, array);
}
})
paginationContainerJustificatifs.querySelector('.pagination_plus').addEventListener('click', () => {
if (currentPageJustificatifs < totalPages) {
currentPageJustificatifs++;
paginationContainerJustificatifs.querySelector('#paginationJusti').value = currentPageAssiduites
renderTableJustificatifs(currentPageJustificatifs, array);
}
})
}
for (let i = 1; i <= totalPages; i++) {
const paginationButton = document.createElement("option");
paginationButton.textContent = i;
paginationButton.value = i;
if (assi) {
paginationContainerAssiduites.querySelector('#paginationAssi').appendChild(paginationButton)
} else { } else {
paginationButton.addEventListener("click", () => { paginationContainerJustificatifs.querySelector('#paginationJusti').appendChild(paginationButton)
currentPageJustificatifs = i;
renderTableAssiduites(currentPageJustificatifs, array);
});
paginationContainerJustificatifs.appendChild(paginationButton);
} }
} }
updateActivePaginationButton(assi); updateActivePaginationButton(assi);
@ -571,7 +627,13 @@
} }
} }
function openContext(e) {
e.preventDefault();
selectedRow = e.target.parentElement;
contextMenu.style.top = `${e.clientY - contextMenu.offsetHeight}px`;
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.display = "block";
}
</script> </script>
@ -608,7 +670,7 @@
.context-menu { .context-menu {
display: none; display: none;
position: absolute; position: fixed;
list-style-type: none; list-style-type: none;
padding: 10px 0; padding: 10px 0;
background-color: #f9f9f9; background-color: #f9f9f9;
@ -721,4 +783,34 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
.obj-title {
text-decoration: underline #bbb;
font-weight: bold;
}
.obj-part {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 33%;
padding: 5px;
border: 1px solid #bbb;
}
.obj-dates,
.obj-mod,
.obj-rest {
display: flex;
justify-content: space-evenly;
margin: 2px;
}
.liste_pagination {
display: flex;
justify-content: space-evenly;
align-items: center;
gap: 5px;
}
</style> </style>

View File

@ -93,19 +93,360 @@
row.appendChild(td) row.appendChild(td)
}) })
row.addEventListener("contextmenu", (e) => { row.addEventListener("contextmenu", openContext);
e.preventDefault();
selectedRow = e.target.parentElement;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.display = "block";
console.log(selectedRow);
});
tableBodyJustificatifs.appendChild(row); tableBodyJustificatifs.appendChild(row);
}); });
updateActivePaginationButton(false); updateActivePaginationButton(false);
} }
function detailJustificatifs(justi_id) {
const path = getUrl() + `/api/justificatif/${justi_id}`;
async_get(
path,
(data) => {
const user = getUserFromId(data.user_id);
const date_debut = moment.tz(data.date_debut, TIMEZONE).format("DD/MM/YYYY HH:mm");
const date_fin = moment.tz(data.date_fin, TIMEZONE).format("DD/MM/YYYY HH:mm");
const entry_date = moment.tz(data.entry_date, TIMEZONE).format("DD/MM/YYYY HH:mm");
</script> const etat = data.etat.capitalize();
const desc = data.raison == null ? "" : data.raison;
const id = data.justif_id;
const fichier = data.fichier != null ? "Oui" : "Non";
let filenames = []
if (fichier) {
sync_get(path + "/list", (data2) => {
filenames = data2;
})
}
const html = `
<div class="obj-detail">
<div class="obj-dates">
<div id="date_debut" class="obj-part">
<span class="obj-title">Date de début</span>
<span class="obj-content">${date_debut}</span>
</div>
<div id="date_fin" class="obj-part">
<span class="obj-title">Date de fin</span>
<span class="obj-content">${date_fin}</span>
</div>
<div id="entry_date" class="obj-part">
<span class="obj-title">Date de saisie</span>
<span class="obj-content">${entry_date}</span>
</div>
</div>
<div class="obj-mod">
<div id="module" class="obj-part">
<span class="obj-title">Raison</span>
<span class="obj-content">${desc}</span>
</div>
<div id="etat" class="obj-part">
<span class="obj-title">Etat</span>
<span class="obj-content">${etat}</span>
</div>
<div id="user" class="obj-part">
<span class="obj-title">Créer par</span>
<span class="obj-content">${user}</span>
</div>
</div>
<div class="obj-rest">
<div id="est_just" class="obj-part obj-66">
<span class="obj-title">Fichier(s)</span>
<div class="obj-content" id="fich-content"></div>
</div>
<div id="id" class="obj-part">
<span class="obj-title">Identifiant du justificatif</span>
<span class="obj-content">${id}</span>
</div>
</div>
</div>
`
const el = document.createElement('div');
el.innerHTML = html;
const fichContent = el.querySelector('#fich-content');
filenames.forEach((name) => {
const a = document.createElement('a');
a.textContent = name
a.classList.add("fich-file")
a.onclick = () => { downloadFile(id, name) };
fichContent.appendChild(a);
})
openAlertModal("Détails", el.firstElementChild, null, "green")
}
)
}
function downloadFile(id, name) {
const path = getUrl() + `/api/justificatif/${id}/export/${name}`;
fetch(path, {
method: "POST"
})
// This returns a promise inside of which we are checking for errors from the server.
// The catch promise at the end of the call does not getting called when the server returns an error.
// More information about the error catching can be found here: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/.
.then((result) => {
if (!result.ok) {
throw Error(result.statusText);
}
// We are reading the *Content-Disposition* header for getting the original filename given from the server
const header = result.headers.get('Content-Disposition');
const parts = header.split(';');
filename = parts[1].split('=')[1].replaceAll("\"", "");
return result.blob();
})
// We use the download property for triggering the download of the file from our browser.
// More information about the following code can be found here: https://stackoverflow.com/questions/32545632/how-can-i-download-a-file-using-window-fetch.
// The filename from the first promise is used as name of the file.
.then((blob) => {
if (blob != null) {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
}
})
// The catch is getting called only for client-side errors.
// For example the throw in the first then-promise, which is the error that came from the server.
.catch((err) => {
console.log(err);
});
}
function editionJustificatifs(justif_id) {
const path = getUrl() + `/api/justificatif/${justif_id}`;
async_get(
path,
(data) => {
const html = `
<div class="assi-edit">
<div class="justi-row">
<div class="justi-label">
<legend for="justi_date_debut">Date de début</legend>
<input type="datetime-local" name="justi_date_debut" id="justi_date_debut">
</div>
<div class="justi-label">
<legend for="justi_date_fin">Date de fin</legend>
<input type="datetime-local" name="justi_date_fin" id="justi_date_fin">
</div>
</div>
<div class="justi-row">
<div class="justi-label">
<legend for="justi_etat">Etat du justificatif</legend>
<select name="justi_etat" id="justi_etat">
<option value="attente" selected>En Attente de validation</option>
<option value="non_valide">Non Valide</option>
<option value="modifie">Modifié</option>
<option value="valide">Valide</option>
</select>
</div>
</div>
<div class="justi-row">
<div class="justi-label">
<legend for="justi_raison">Raison</legend>
<textarea name="justi_raison" id="justi_raison" cols="50" rows="10" maxlength="500"></textarea>
</div>
</div>
<div class="justi-row">
<div class="justi-sect">
</div>
<div class="justi-label">
<legend for="justi_fich">Importer un fichier</legend>
<input type="file" name="justi_fich" id="justi_fich" multiple>
</div>
</div>
</div>
`
const desc = data.raison
const fichier = data.fichier != null ? "Oui" : "Non";
const el = document.createElement('div')
el.innerHTML = html;
const assiEdit = el.firstElementChild;
assiEdit.querySelector('#justi_etat').value = data.etat.toLowerCase();
assiEdit.querySelector('#justi_raison').value = desc != null ? desc : "";
assiEdit.querySelector('#justi_date_debut').value = moment.tz(data.date_debut, TIMEZONE).format("YYYY-MM-DDTHH:MM")
assiEdit.querySelector('#justi_date_fin').value = moment.tz(data.date_fin, TIMEZONE).format("YYYY-MM-DDTHH:MM")
const fichContent = assiEdit.querySelector('.justi-sect');
let filenames = []
if (data.fichier) {
sync_get(path + "/list", (data2) => {
filenames = data2;
})
fichContent.insertAdjacentHTML('beforeend', "<legend>Fichier(s)</legend>")
}
filenames.forEach((name) => {
const a = document.createElement('a');
a.textContent = name
a.classList.add("fich-file")
a.onclick = () => { downloadFile(id, name) };
const input = document.createElement('input')
input.type = "checkbox"
input.name = "destroyFile";
input.classList.add('icon')
const span = document.createElement('span');
span.classList.add('file-line')
span.appendChild(input)
span.appendChild(a)
fichContent.appendChild(span);
})
openPromptModal("Modification du justificatif", assiEdit, () => {
const prompt = document.querySelector('.assi-edit');
let date_debut = prompt.querySelector('#justi_date_debut').value;
let date_fin = prompt.querySelector('#justi_date_fin').value;
if (date_debut == "" || date_fin == "") {
openAlertModal("Dates erronées", document.createTextNode('Les dates sont invalides'));
return true
}
date_debut = moment.tz(date_debut, TIMEZONE)
date_fin = moment.tz(date_fin, TIMEZONE)
if (date_debut >= date_fin) {
openAlertModal("Dates erronées", document.createTextNode('La date de fin doit être après la date de début'));
return true
}
const edit = {
date_debut: date_debut.format(),
date_fin: date_fin.format(),
raison: prompt.querySelector('#justi_raison').value,
etat: prompt.querySelector('#justi_etat').value,
}
const toRemoveFiles = [...prompt.querySelectorAll('[name="destroyFile"]:checked')]
if (toRemoveFiles.length > 0) {
removeFiles(justif_id, toRemoveFiles);
}
const in_files = prompt.querySelector('#justi_fich');
if (in_files.files.length > 0) {
importNewFiles(justif_id, in_files);
}
fullEditJustificatifs(data.justif_id, edit, () => {
loadAll();
})
}, () => { }, "green");
}
);
}
function fullEditJustificatifs(justif_id, obj, call = () => { }) {
const path = getUrl() + `/api/justificatif/${justif_id}/edit`;
async_post(
path,
obj,
call,
(data, status) => {
//error
console.error(data, status);
}
);
}
function removeFiles(justif_id, files = []) {
const path = getUrl() + `/api/justificatif/${justif_id}/remove`;
files = files.map((el) => {
return el.parentElement.querySelector('a').textContent;
});
console.log(justif_id, files);
sync_post(
path,
{
"remove": "list",
"filenames": files,
},
);
}
function importNewFiles(justif_id, in_files) {
const path = getUrl() + `/api/justificatif/${justif_id}/import`;
const requests = []
Array.from(in_files.files).forEach((f) => {
const fd = new FormData();
fd.append('file', f);
requests.push(
$.ajax(
{
url: path,
type: 'POST',
data: fd,
dateType: 'json',
contentType: false,
processData: false,
success: () => { },
}
)
)
});
$.when(
requests
).done(() => {
})
}
</script>
<style>
.fich-file {
cursor: pointer;
margin: 2px;
}
#fich-content {
display: flex;
flex-wrap: wrap;
}
.obj-66 {
width: 66%;
}
.file-line {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 5px;
}
</style>