diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 4699d00..1c73588 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -418,7 +418,7 @@ class AbsenceReason(models.IntegerChoices): UNJUSTIFIED_LATE = 4, 'Unjustified Late' class AbsenceManagement(models.Model): - day = models.DateField() + day = models.DateField(blank=True, null=True) moment = models.IntegerField( choices=AbsenceMoment.choices, default=AbsenceMoment.TOTAL @@ -430,9 +430,11 @@ class AbsenceManagement(models.Model): student = models.ForeignKey( Student, on_delete=models.CASCADE, - related_name='absences' + related_name='absences', + blank=True, null=True ) - establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='absences') + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='absences', blank=True, null=True) + commentaire = models.TextField(blank=True, null=True) def __str__(self): return f"{self.student} - {self.day} - {self.get_moment_display()} - {self.get_reason_display()}" \ No newline at end of file diff --git a/Front-End/src/app/[locale]/admin/grades/page.js b/Front-End/src/app/[locale]/admin/grades/page.js index edba3b3..3170d8b 100644 --- a/Front-End/src/app/[locale]/admin/grades/page.js +++ b/Front-End/src/app/[locale]/admin/grades/page.js @@ -20,6 +20,9 @@ import { fetchStudents, fetchStudentCompetencies, searchStudents, + fetchAbsences, + editAbsences, + deleteAbsences, } from '@/app/actions/subscriptionAction'; import { useEstablishment } from '@/context/EstablishmentContext'; import { useClasses } from '@/context/ClassesContext'; @@ -28,9 +31,11 @@ import SectionHeader from '@/components/SectionHeader'; import GradesDomainBarChart from '@/components/Grades/GradesDomainBarChart'; import InputText from '@/components/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(); @@ -43,6 +48,7 @@ export default function Page() { 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 = () => { @@ -99,11 +105,6 @@ export default function Page() { }, ]; - const absences = [ - { date: '2023-09-01', type: 'Absence', reason: 'Maladie', justified: true }, - { date: '2023-09-15', type: 'Retard', reason: 'Trafic', justified: false }, - ]; - const remarks = [ { date: '2023-09-10', @@ -196,6 +197,32 @@ export default function Page() { } }, [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 @@ -207,6 +234,42 @@ export default function Page() { 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 (