From fd6348fd6b70ad4cfa5ad9a5351fc76013fdbf9d Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 25 May 2025 18:32:46 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20Am=C3=A9lioration=20du=20rendu=20de=20?= =?UTF-8?q?l'appel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../structure/SchoolClassManagement/page.js | 303 ++++++++++++++---- 1 file changed, 234 insertions(+), 69 deletions(-) diff --git a/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js b/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js index d14c935..36315bc 100644 --- a/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js +++ b/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js @@ -1,7 +1,7 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import { Users, Layers, CheckCircle, Clock } from 'lucide-react'; +import { Users, Layers, CheckCircle, Clock, XCircle } from 'lucide-react'; import Table from '@/components/Table'; import Popup from '@/components/Popup'; import { fetchClasse } from '@/app/actions/schoolAction'; @@ -20,9 +20,11 @@ import { import { useCsrfToken } from '@/context/CsrfContext'; import { useEstablishment } from '@/context/EstablishmentContext'; +import { useNotification } from '@/context/NotificationContext'; export default function Page() { const searchParams = useSearchParams(); + const { showNotification } = useNotification(); const schoolClassId = searchParams.get('schoolClassId'); const [classe, setClasse] = useState([]); const { getNiveauxLabels, getNiveauLabel } = useClasses(); @@ -114,8 +116,37 @@ export default function Page() { if (existingAbsence) { // Si une absence existe pour aujourd'hui, décocher la case et pré-remplir les champs + // Conversion reason -> type/justified + let type = ''; + let justified = false; + switch (existingAbsence.reason) { + case AbsenceReason.JUSTIFIED_ABSENCE.value: + type = 'absence'; + justified = true; + break; + case AbsenceReason.UNJUSTIFIED_ABSENCE.value: + type = 'absence'; + justified = false; + break; + case AbsenceReason.JUSTIFIED_LATE.value: + type = 'retard'; + justified = true; + break; + case AbsenceReason.UNJUSTIFIED_LATE.value: + type = 'retard'; + justified = false; + break; + default: + type = ''; + justified = false; + } + initialAttendance[student.id] = false; - initialFormAbsences[student.id] = { ...existingAbsence }; + initialFormAbsences[student.id] = { + ...existingAbsence, + type, + justified, + }; } else { // Sinon, cocher la case par défaut initialAttendance[student.id] = true; @@ -141,21 +172,70 @@ export default function Page() { }; const handleValidateAttendance = () => { - // Filtrer les absences modifiées uniquement pour les étudiants décochés (absents) - const absencesToUpdate = Object.entries(formAbsences).filter( - ([studentId, absenceData]) => - !attendance[studentId] && // L'étudiant est décoché (absent) - JSON.stringify(absenceData) !== - JSON.stringify(fetchedAbsences[studentId]) // Les données ont été modifiées - ); + let hasError = false; - // Envoyer les absences modifiées à une API - absencesToUpdate.forEach(([studentId, absenceData]) => { - logger.debug('Modification absence élève : ', studentId); - saveAbsence(studentId, absenceData); + // Pour chaque élève filtré (présents et absents) + filteredStudents.forEach((student) => { + const studentId = student.id; + const isPresent = attendance[studentId]; + const existingAbsence = fetchedAbsences[studentId]; + + if (isPresent) { + // Si l'élève est présent et qu'une absence existe, la supprimer + if (existingAbsence) { + deleteAbsences(existingAbsence.id, csrfToken) + .then(() => { + logger.debug( + `Absence pour l'élève ${studentId} supprimée (présent).` + ); + setFetchedAbsences((prev) => { + const updatedAbsences = { ...prev }; + delete updatedAbsences[studentId]; + return updatedAbsences; + }); + setFormAbsences((prev) => { + const updatedAbsences = { ...prev }; + delete updatedAbsences[studentId]; + return updatedAbsences; + }); + }) + .catch((error) => { + logger.error( + `Erreur lors de la suppression de l'absence pour l'élève ${studentId}:`, + error + ); + showNotification( + `Erreur lors de la suppression de l'absence pour l'élève ${studentId}`, + 'error', + 'Erreur' + ); + }); + } + // Si tu veux garder une trace de la présence, tu peux ici appeler une API ou enregistrer un "Présent" + } else { + // Si l'élève est absent, créer ou modifier l'absence + const absenceData = formAbsences[studentId]; + if (!absenceData || !absenceData.type || !absenceData.moment) { + logger.error( + `Tous les champs requis doivent être fournis pour l'élève ${studentId}.` + ); + showNotification( + `Tous les champs requis doivent être fournis pour l'élève ${studentId}.`, + 'error', + 'Erreur' + ); + hasError = true; + // On ne fait pas de return ici, on continue la boucle pour les autres élèves + } else { + saveAbsence(studentId, absenceData); + } + } }); - setIsEditingAttendance(false); // Revenir au mode visualisation + // On ne quitte le mode édition que s'il n'y a pas d'erreur + if (!hasError) { + setIsEditingAttendance(false); + } }; const handleAttendanceChange = (studentId) => { @@ -233,16 +313,36 @@ export default function Page() { }); }; + const getAbsenceReason = (type, justified) => { + if (type === 'absence') { + return justified + ? AbsenceReason.JUSTIFIED_ABSENCE.value + : AbsenceReason.UNJUSTIFIED_ABSENCE.value; + } else if (type === 'retard') { + return justified + ? AbsenceReason.JUSTIFIED_LATE.value + : AbsenceReason.UNJUSTIFIED_LATE.value; + } + return null; + }; + const saveAbsence = (studentId, absenceData) => { - if (!absenceData.reason || !studentId || !absenceData.moment) { + if (!absenceData.type || !studentId || !absenceData.moment) { logger.error('Tous les champs requis doivent être fournis.'); + showNotification( + 'Tous les champs requis doivent être fournis.', + 'error', + 'Erreur' + ); return; } + const reason = getAbsenceReason(absenceData.type, absenceData.justified); + const payload = { student: studentId, day: absenceData.day, - reason: absenceData.reason, + reason: reason, moment: absenceData.moment, establishment: selectedEstablishmentId, }; @@ -254,6 +354,11 @@ export default function Page() { logger.debug( `Absence pour l'élève ${studentId} modifiée avec succès.` ); + showNotification( + 'Opération effectuée avec succès.', + 'success', + 'Succès' + ); // Mettre à jour fetchedAbsences et formAbsences localement setFetchedAbsences((prev) => ({ ...prev, @@ -275,6 +380,11 @@ export default function Page() { createAbsences(payload, csrfToken) .then((response) => { logger.debug(`Absence pour l'élève ${studentId} créée avec succès.`); + showNotification( + 'Opération effectuée avec succès.', + 'success', + 'Succès' + ); // Mettre à jour fetchedAbsences et formAbsences localement setFetchedAbsences((prev) => ({ ...prev, @@ -419,62 +529,117 @@ export default function Page() { name: "Gestion de l'appel", transform: (row) => (
- {/* Case à cocher pour la présence */} - handleAttendanceChange(row.id)} - fieldName="attendance" - /> + {/* Présence */} +
+ {attendance[row.id] ? ( + <> + + handleAttendanceChange(row.id) + } + fieldName="attendance" + /> + + Présent + + + ) : ( + <> + {/* Icône croix pour remettre l'élève en présent */} + + + Effacer l'absence + + + )} +
- {/* Champs pour le motif et le moment */} + {/* Détails absence/retard */} {!attendance[row.id] && ( -
- - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - reason: parseInt(e.target.value, 10), - }, - })) - } - choices={Object.values(AbsenceReason).map( - (reason) => ({ - value: reason.value, - label: reason.label, - }) - )} - /> +
+
+ + + Motif d'absence + +
+
+ {/* Select Absence/Retard */} + + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + type: e.target.value, + }, + })) + } + choices={[ + { value: 'absence', label: 'Absence' }, + { value: 'retard', label: 'Retard' }, + ]} + /> - - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - moment: parseInt(e.target.value, 10), - }, - })) - } - choices={Object.values(AbsenceMoment).map( - (moment) => ({ - value: moment.value, - label: moment.label, - }) - )} - /> + {/* Select Moment */} + + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + moment: parseInt(e.target.value, 10), + }, + })) + } + choices={Object.values(AbsenceMoment).map( + (moment) => ({ + value: moment.value, + label: moment.label, + }) + )} + /> + + {/* Checkbox Justifié */} +
+ + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + justified: !prev[row.id]?.justified, + }, + })) + } + fieldName="justified" + itemLabelFunc={() => 'Justifié'} + /> +
+
)}