'use client'; import React, { useState, useEffect } from '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'; import { useSearchParams } from 'next/navigation'; import logger from '@/utils/logger'; import { useClasses } from '@/context/ClassesContext'; import Button from '@/components/Button'; import SelectChoice from '@/components/SelectChoice'; import CheckBox from '@/components/CheckBox'; import { fetchAbsences, createAbsences, editAbsences, deleteAbsences, } from '@/app/actions/subscriptionAction'; 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(); const [popupVisible, setPopupVisible] = useState(false); const [popupMessage, setPopupMessage] = useState(''); const [selectedLevels, setSelectedLevels] = useState([]); // Par défaut, tous les niveaux sont sélectionnés const [filteredStudents, setFilteredStudents] = useState([]); const [isEditingAttendance, setIsEditingAttendance] = useState(false); // État pour le mode édition const [attendance, setAttendance] = useState({}); // État pour les cases cochées const [fetchedAbsences, setFetchedAbsences] = useState({}); // Absences récupérées depuis le backend const [formAbsences, setFormAbsences] = useState({}); // Absences modifiées localement const csrfToken = useCsrfToken(); const { selectedEstablishmentId } = useEstablishment(); // AbsenceMoment constants const AbsenceMoment = { MORNING: { value: 1, label: 'Matinée' }, AFTERNOON: { value: 2, label: 'Après-midi' }, TOTAL: { value: 3, label: 'Journée' }, }; // AbsenceReason constants const AbsenceReason = { JUSTIFIED_ABSENCE: { value: 1, label: 'Absence justifiée' }, UNJUSTIFIED_ABSENCE: { value: 2, label: 'Absence non justifiée' }, JUSTIFIED_LATE: { value: 3, label: 'Retard justifié' }, UNJUSTIFIED_LATE: { value: 4, label: 'Retard non justifié' }, }; useEffect(() => { // Récupérer les données de la classe et initialiser les élèves filtrés if (schoolClassId) { fetchClasse(schoolClassId) .then((classeData) => { logger.debug('Classes récupérées :', classeData); setClasse(classeData); setFilteredStudents(classeData.students); // Initialiser les élèves filtrés setSelectedLevels(getNiveauxLabels(classeData.levels)); // Initialiser les niveaux sélectionnés }) .catch(requestErrorHandler); } }, [schoolClassId]); useEffect(() => { // Récupérer les absences pour l'établissement sélectionné if (selectedEstablishmentId) { fetchAbsences(selectedEstablishmentId) .then((data) => { const absencesById = data.reduce((acc, absence) => { acc[absence.student] = absence; return acc; }, {}); setFetchedAbsences(absencesById); }) .catch((error) => logger.error('Erreur lors de la récupération des absences :', error) ); } }, [selectedEstablishmentId]); useEffect(() => { // Filtrer les élèves en fonction des niveaux sélectionnés if (classe && selectedLevels.length > 0) { const filtered = classe.students.filter((student) => selectedLevels.includes(getNiveauLabel(student.level)) ); setFilteredStudents(filtered); } else { setFilteredStudents([]); // Aucun élève si aucun niveau n'est sélectionné } }, [selectedLevels, classe]); useEffect(() => { // Initialiser `attendance` et `formAbsences` en fonction des élèves filtrés et des absences if (filteredStudents.length > 0 && fetchedAbsences) { const today = new Date().toISOString().split('T')[0]; const initialAttendance = {}; const initialFormAbsences = {}; filteredStudents.forEach((student) => { const existingAbsence = fetchedAbsences[student.id] && fetchedAbsences[student.id].day === today ? fetchedAbsences[student.id] : null; 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, type, justified, }; } else { // Sinon, cocher la case par défaut initialAttendance[student.id] = true; } }); setAttendance(initialAttendance); setFormAbsences(initialFormAbsences); } }, [filteredStudents, fetchedAbsences]); const handleLevelClick = (label) => { setSelectedLevels( (prev) => prev.includes(label) ? prev.filter((level) => level !== label) // Retirer le niveau si déjà sélectionné : [...prev, label] // Ajouter le niveau si non sélectionné ); }; const handleToggleAttendanceMode = () => { setIsEditingAttendance((prev) => !prev); // Basculer entre mode édition et visualisation }; const handleValidateAttendance = () => { let hasError = false; // 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); } } }); // On ne quitte le mode édition que s'il n'y a pas d'erreur if (!hasError) { setIsEditingAttendance(false); } }; const handleAttendanceChange = (studentId) => { const today = new Date().toISOString().split('T')[0]; // Obtenir la date actuelle au format YYYY-MM-DD setAttendance((prev) => { const updatedAttendance = { ...prev, [studentId]: !prev[studentId], // Inverser l'état de présence }; // Si l'élève est décoché (absent) if (!updatedAttendance[studentId]) { // Vérifier s'il existe une absence pour le jour actuel const existingAbsence = Object.values(fetchedAbsences).find( (absence) => absence.student === studentId && absence.day === today ); if (existingAbsence) { // Afficher l'absence existante pour le jour actuel setFormAbsences((prev) => ({ ...prev, [studentId]: { ...existingAbsence, }, })); } else { // Initialiser des champs vides pour créer une nouvelle absence setFormAbsences((prev) => ({ ...prev, [studentId]: { day: today, reason: null, moment: null, }, })); } } else { // Si l'élève est recoché (présent), supprimer l'absence existante const existingAbsence = Object.values(fetchedAbsences).find( (absence) => absence.student === studentId && absence.day === today ); if (existingAbsence) { // Appeler la fonction pour supprimer l'absence deleteAbsences(existingAbsence.id, csrfToken) .then(() => { logger.debug( `Absence pour l'élève ${studentId} supprimée avec succès.` ); // Mettre à jour les absences récupérées setFetchedAbsences((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 ); }); } // Supprimer les données d'absence dans `formAbsences` setFormAbsences((prev) => { const updatedAbsences = { ...prev }; delete updatedAbsences[studentId]; return updatedAbsences; }); } return updatedAttendance; }); }; 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.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: reason, moment: absenceData.moment, establishment: selectedEstablishmentId, commentaire: absenceData.commentaire, }; if (absenceData.id) { // Modifier une absence existante editAbsences(absenceData.id, payload, csrfToken) .then(() => { 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, [studentId]: { ...prev[studentId], ...payload }, })); setFormAbsences((prev) => ({ ...prev, [studentId]: { ...prev[studentId], ...payload }, })); }) .catch((error) => { logger.error( `Erreur lors de la modification de l'absence pour l'élève ${studentId}:`, error ); }); } else { // Créer une nouvelle absence 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, [studentId]: { id: response.id, ...payload }, })); setFormAbsences((prev) => ({ ...prev, [studentId]: { id: response.id, ...payload }, })); }) .catch((error) => { logger.error( `Erreur lors de la création de l'absence pour l'élève ${studentId}:`, error ); }); } }; const requestErrorHandler = (err) => { logger.error('Error fetching data:', err); }; const today = new Date().toISOString().split('T')[0]; // Obtenez la date actuelle au format YYYY-MM-DD return (
Filtrer les élèves par niveau
Liste des enseignants