chore: Amélioration du rendu de l'appel

This commit is contained in:
N3WT DE COMPET
2025-05-25 18:32:46 +02:00
parent 3e5cebef44
commit fd6348fd6b

View File

@ -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);
// 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,41 +529,74 @@ export default function Page() {
name: "Gestion de l'appel",
transform: (row) => (
<div className="flex flex-col gap-2 items-center">
{/* Case à cocher pour la présence */}
{/* Présence */}
<div className="flex items-center gap-2">
{attendance[row.id] ? (
<>
<CheckBox
item={{ id: row.id }}
formData={{
attendance: attendance[row.id] ? [row.id] : [],
}}
handleChange={() => handleAttendanceChange(row.id)}
handleChange={() =>
handleAttendanceChange(row.id)
}
fieldName="attendance"
/>
<span className="text-sm font-medium text-gray-700">
Présent
</span>
</>
) : (
<>
{/* Icône croix pour remettre l'élève en présent */}
<button
type="button"
onClick={() => handleAttendanceChange(row.id)}
className="text-red-500 hover:text-red-700 transition"
title="Annuler l'absence"
>
<XCircle className="w-6 h-6" />
</button>
<span className="text-sm font-medium text-red-600">
Effacer l&apos;absence
</span>
</>
)}
</div>
{/* Champs pour le motif et le moment */}
{/* Détails absence/retard */}
{!attendance[row.id] && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 mt-2">
<div className="w-full bg-emerald-50 border border-emerald-100 rounded-lg p-3 mt-2 shadow-sm">
<div className="flex items-center gap-2 mb-2">
<Clock className="w-4 h-4 text-emerald-500" />
<span className="font-semibold text-emerald-700 text-sm">
Motif d&apos;absence
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 items-center">
{/* Select Absence/Retard */}
<SelectChoice
name={`reason-${row.id}`}
name={`type-${row.id}`}
label=""
placeHolder="Motif"
selected={formAbsences[row.id]?.reason || ''}
placeHolder="Type"
selected={formAbsences[row.id]?.type || ''}
callback={(e) =>
setFormAbsences((prev) => ({
...prev,
[row.id]: {
...prev[row.id],
reason: parseInt(e.target.value, 10),
type: e.target.value,
},
}))
}
choices={Object.values(AbsenceReason).map(
(reason) => ({
value: reason.value,
label: reason.label,
})
)}
choices={[
{ value: 'absence', label: 'Absence' },
{ value: 'retard', label: 'Retard' },
]}
/>
{/* Select Moment */}
<SelectChoice
name={`moment-${row.id}`}
label=""
@ -475,6 +618,28 @@ export default function Page() {
})
)}
/>
{/* Checkbox Justifié */}
<div className="flex items-center gap-2 justify-center">
<CheckBox
item={{ id: `justified-${row.id}` }}
formData={{
justified: !!formAbsences[row.id]?.justified,
}}
handleChange={() =>
setFormAbsences((prev) => ({
...prev,
[row.id]: {
...prev[row.id],
justified: !prev[row.id]?.justified,
},
}))
}
fieldName="justified"
itemLabelFunc={() => 'Justifié'}
/>
</div>
</div>
</div>
)}
</div>