'use client'; import React, { useState, useEffect } from 'react'; import SelectChoice from '@/components/Form/SelectChoice'; import AcademicResults from '@/components/Grades/AcademicResults'; import Attendance from '@/components/Grades/Attendance'; import Remarks from '@/components/Grades/Remarks'; import WorkPlan from '@/components/Grades/WorkPlan'; import Homeworks from '@/components/Grades/Homeworks'; import SpecificEvaluations from '@/components/Grades/SpecificEvaluations'; import Orientation from '@/components/Grades/Orientation'; import GradesStatsCircle from '@/components/Grades/GradesStatsCircle'; import Button from '@/components/Form/Button'; import logger from '@/utils/logger'; import { FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL, BASE_URL, } from '@/utils/Url'; import { useRouter } from 'next/navigation'; import { fetchStudents, fetchStudentCompetencies, searchStudents, fetchAbsences, editAbsences, deleteAbsences, } from '@/app/actions/subscriptionAction'; import { useEstablishment } from '@/context/EstablishmentContext'; import { useClasses } from '@/context/ClassesContext'; import { Award, FileText } from 'lucide-react'; import SectionHeader from '@/components/SectionHeader'; import GradesDomainBarChart from '@/components/Grades/GradesDomainBarChart'; import InputText from '@/components/Form/InputText'; import dayjs from 'dayjs'; import { useCsrfToken } from '@/context/CsrfContext'; export default function Page() { const router = useRouter(); const csrfToken = useCsrfToken(); const { selectedEstablishmentId, selectedEstablishmentEvaluationFrequency } = useEstablishment(); const { getNiveauLabel } = useClasses(); const [formData, setFormData] = useState({ selectedStudent: null, }); const [students, setStudents] = useState([]); const [studentCompetencies, setStudentCompetencies] = useState(null); const [grades, setGrades] = useState({}); const [searchTerm, setSearchTerm] = useState(''); const [selectedPeriod, setSelectedPeriod] = useState(null); const [allAbsences, setAllAbsences] = useState([]); // Définir les périodes selon la fréquence const getPeriods = () => { if (selectedEstablishmentEvaluationFrequency === 1) { return [ { label: 'Trimestre 1', value: 1, start: '09-01', end: '12-31' }, { label: 'Trimestre 2', value: 2, start: '01-01', end: '03-31' }, { label: 'Trimestre 3', value: 3, start: '04-01', end: '07-15' }, ]; } if (selectedEstablishmentEvaluationFrequency === 2) { return [ { label: 'Semestre 1', value: 1, start: '09-01', end: '01-31' }, { label: 'Semestre 2', value: 2, start: '02-01', end: '07-15' }, ]; } if (selectedEstablishmentEvaluationFrequency === 3) { return [{ label: 'Année', value: 1, start: '09-01', end: '07-15' }]; } return []; }; // Sélection automatique de la période courante useEffect(() => { if (!formData.selectedStudent) { setSelectedPeriod(null); return; } const periods = getPeriods(); const today = dayjs(); const current = periods.find((p) => { const start = dayjs(`${today.year()}-${p.start}`); const end = dayjs(`${today.year()}-${p.end}`); return ( today.isAfter(start.subtract(1, 'day')) && today.isBefore(end.add(1, 'day')) ); }); setSelectedPeriod(current ? current.value : null); }, [formData.selectedStudent, selectedEstablishmentEvaluationFrequency]); const academicResults = [ { subject: 'Mathématiques', grade: 16, average: 14, appreciation: 'Très bon travail', }, { subject: 'Français', grade: 15, average: 13, appreciation: 'Bonne participation', }, ]; const remarks = [ { date: '2023-09-10', teacher: 'Mme Dupont', comment: 'Participation active en classe.', }, { date: '2023-09-20', teacher: 'M. Martin', comment: 'Doit améliorer la concentration.', }, ]; const workPlan = [ { objective: 'Renforcer la lecture', support: 'Exercices hebdomadaires', followUp: 'En cours', }, { objective: 'Maîtriser les tables de multiplication', support: 'Jeux éducatifs', followUp: 'À démarrer', }, ]; const homeworks = [ { title: 'Rédaction', dueDate: '2023-10-10', status: 'Rendu' }, { title: 'Exercices de maths', dueDate: '2023-10-12', status: 'À faire' }, ]; const specificEvaluations = [ { test: 'Bilan de compétences', date: '2023-09-25', result: 'Bon niveau général', }, ]; const orientation = [ { date: '2023-10-01', counselor: 'Mme Leroy', advice: 'Poursuivre en filière générale', }, ]; const handleChange = (field, value) => setFormData((prev) => ({ ...prev, [field]: value })); useEffect(() => { if (selectedEstablishmentId) { fetchStudents(selectedEstablishmentId, null, 5) .then((studentsData) => { setStudents(studentsData); }) .catch((error) => logger.error('Error fetching students:', error)); } }, [selectedEstablishmentId]); // Charger les compétences et générer les grades à chaque changement d'élève sélectionné useEffect(() => { if (formData.selectedStudent && selectedPeriod) { const periodString = getPeriodString( selectedPeriod, selectedEstablishmentEvaluationFrequency ); fetchStudentCompetencies(formData.selectedStudent, periodString) .then((data) => { setStudentCompetencies(data); // Générer les grades à partir du retour API if (data && data.data) { const initialGrades = {}; data.data.forEach((domaine) => { domaine.categories.forEach((cat) => { cat.competences.forEach((comp) => { initialGrades[comp.competence_id] = comp.score ?? 0; }); }); }); setGrades(initialGrades); } }) .catch((error) => logger.error('Error fetching studentCompetencies:', error) ); } else { setGrades({}); setStudentCompetencies(null); } }, [formData.selectedStudent, selectedPeriod]); useEffect(() => { if (selectedEstablishmentId) { fetchAbsences(selectedEstablishmentId) .then((data) => setAllAbsences(data)) .catch((error) => logger.error('Erreur lors du fetch des absences:', error) ); } }, [selectedEstablishmentId]); // Transforme les absences backend pour l'élève sélectionné const absences = React.useMemo(() => { if (!formData.selectedStudent) return []; return allAbsences .filter((a) => a.student === formData.selectedStudent) .map((a) => ({ id: a.id, date: a.day, type: [2, 1].includes(a.reason) ? 'Absence' : 'Retard', reason: a.reason, // tu peux mapper le code vers un label si besoin justified: [1, 3].includes(a.reason), // 1 et 3 = justifié moment: a.moment, commentaire: a.commentaire, })); }, [allAbsences, formData.selectedStudent]); // Fonction utilitaire pour convertir la période sélectionnée en string backend function getPeriodString(selectedPeriod, frequency) { const year = dayjs().month() >= 8 ? dayjs().year() : dayjs().year() - 1; // année scolaire commence en septembre const nextYear = (year + 1).toString(); const schoolYear = `${year}-${nextYear}`; if (frequency === 1) return `T${selectedPeriod}_${schoolYear}`; if (frequency === 2) return `S${selectedPeriod}_${schoolYear}`; if (frequency === 3) return `A_${schoolYear}`; return ''; } // Callback pour justifier/non justifier une absence const handleToggleJustify = (absence) => { // Inverser l'état justifié (1/3 = justifié, 2/4 = non justifié) const newReason = absence.type === 'Absence' ? absence.justified ? 2 // Absence non justifiée : 1 // Absence justifiée : absence.justified ? 4 // Retard non justifié : 3; // Retard justifié editAbsences(absence.id, { ...absence, reason: newReason }, csrfToken) .then(() => { setAllAbsences((prev) => prev.map((a) => a.id === absence.id ? { ...a, reason: newReason } : a ) ); }) .catch((e) => { logger.error('Erreur lors du changement de justification', e); }); }; // Callback pour supprimer une absence const handleDeleteAbsence = (absence) => { return deleteAbsences(absence.id, csrfToken) .then(() => { setAllAbsences((prev) => prev.filter((a) => a.id !== absence.id)); }) .catch((e) => { logger.error("Erreur lors de la suppression de l'absence", e); }); }; return (
{/* Section haute : filtre + bouton + photo élève */}
{/* Colonne gauche : InputText + bouton */}
setSearchTerm(e.target.value)} placeholder="Rechercher un élève" required={false} enable={true} />
{ const today = dayjs(); const start = dayjs(`${today.year()}-${period.start}`); const end = dayjs(`${today.year()}-${period.end}`); const isPast = today.isAfter(end); return { value: period.value, label: period.label, disabled: isPast, }; })} selected={selectedPeriod || ''} callback={(e) => setSelectedPeriod(Number(e.target.value))} disabled={!formData.selectedStudent} />
{/* Colonne droite : Photo élève */}
{formData.selectedStudent && (() => { const student = students.find( (s) => s.id === formData.selectedStudent ); if (!student) return null; return ( <> {student.photo ? ( {`${student.first_name} ) : (
{student.first_name?.[0]} {student.last_name?.[0]}
)} ); })()}
{/* Section basse : liste élèves + infos */}
{/* Colonne 1 : Liste des élèves */}

Liste des élèves

    {students .filter( (student) => !searchTerm || `${student.last_name} ${student.first_name}` .toLowerCase() .includes(searchTerm.toLowerCase()) ) .map((student) => (
  • handleChange('selectedStudent', student.id)} > {student.photo ? ( {`${student.first_name} ) : (
    {student.first_name?.[0]} {student.last_name?.[0]}
    )}
    {student.last_name} {student.first_name}
    Niveau :{' '} {getNiveauLabel(student.level)} {' | '} Classe :{' '} {student.associated_class_name}
    {/* Icône PDF si bilan dispo pour la période sélectionnée */} {selectedPeriod && student.bilans && Array.isArray(student.bilans) && (() => { // Génère la string de période attendue const periodString = getPeriodString( selectedPeriod, selectedEstablishmentEvaluationFrequency ); const bilan = student.bilans.find( (b) => b.period === periodString && b.file ); if (bilan) { return ( e.stopPropagation()} // Pour ne pas sélectionner à nouveau l'élève > ); } return null; })()}
  • ))}
{/* Colonne 2 : Reste des infos */}
{formData.selectedStudent && (
)}
); }