forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
1
app/static/icons/remove_circle.svg
Normal file
1
app/static/icons/remove_circle.svg
Normal 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 |
1
app/static/icons/trash.svg
Normal file
1
app/static/icons/trash.svg
Normal 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 |
@ -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;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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 */
|
||||||
|
@ -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")
|
||||||
|
@ -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>
|
@ -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'><</button><select id='paginationAssi'></select><button class='pagination_plus'>></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'><</button><select id='paginationJusti'></select><button class='pagination_plus'>></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>
|
@ -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>
|
Loading…
Reference in New Issue
Block a user