From 78d96f82f91ed777073250b960eee8f326cccb43 Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sat, 31 May 2025 02:00:00 +0200 Subject: [PATCH] feat: Ajout de l'emploi du temps sur la page parent --- Back-End/Subscriptions/serializers.py | 6 +- Front-End/src/app/[locale]/parents/page.js | 142 ++++++++++++------ Front-End/src/components/Calendar/Calendar.js | 49 +++--- Front-End/src/components/Calendar/WeekView.js | 31 ++-- .../src/components/ParentPlanningSection.js | 30 ++++ .../Structure/Planning/ScheduleManagement.js | 2 - Front-End/src/context/PlanningContext.js | 6 +- 7 files changed, 187 insertions(+), 79 deletions(-) create mode 100644 Front-End/src/components/ParentPlanningSection.js diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index f506981..e3aeabd 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -381,16 +381,20 @@ class RegistrationFormSerializer(serializers.ModelSerializer): class StudentByParentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) + associated_class_name = serializers.SerializerMethodField() class Meta: model = Student - fields = ['id', 'last_name', 'first_name', 'level', 'photo'] + fields = ['id', 'last_name', 'first_name', 'level', 'photo', 'associated_class_name'] def __init__(self, *args, **kwargs): super(StudentByParentSerializer, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].required = False + def get_associated_class_name(self, obj): + return obj.associated_class.atmosphere_name if obj.associated_class else None + class RegistrationFormByParentSerializer(serializers.ModelSerializer): student = StudentByParentSerializer(many=False, required=True) diff --git a/Front-End/src/app/[locale]/parents/page.js b/Front-End/src/app/[locale]/parents/page.js index 428d08f..ac61d05 100644 --- a/Front-End/src/app/[locale]/parents/page.js +++ b/Front-End/src/app/[locale]/parents/page.js @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import Table from '@/components/Table'; -import { Edit3, Users, Download, Eye, Upload } from 'lucide-react'; +import { Edit3, Users, Download, Eye, Upload, CalendarDays } from 'lucide-react'; import StatusLabel from '@/components/StatusLabel'; import FileUpload from '@/components/FileUpload'; import { FE_PARENTS_EDIT_SUBSCRIPTION_URL } from '@/utils/Url'; @@ -15,6 +15,12 @@ import { BASE_URL } from '@/utils/Url'; import { useEstablishment } from '@/context/EstablishmentContext'; import { useCsrfToken } from '@/context/CsrfContext'; import { useClasses } from '@/context/ClassesContext'; +import { + PlanningProvider, + PlanningModes +} from '@/context/PlanningContext'; +import SectionHeader from '@/components/SectionHeader'; +import ParentPlanningSection from '@/components/ParentPlanningSection'; export default function ParentHomePage() { const [children, setChildren] = useState([]); @@ -22,6 +28,8 @@ export default function ParentHomePage() { const [uploadingStudentId, setUploadingStudentId] = useState(null); // ID de l'étudiant pour l'upload const [uploadedFile, setUploadedFile] = useState(null); // Fichier uploadé const [uploadState, setUploadState] = useState('off'); // État "on" ou "off" pour l'affichage du composant + const [showPlanning, setShowPlanning] = useState(false); + const [planningClassName, setPlanningClassName] = useState(null); const router = useRouter(); const csrfToken = useCsrfToken(); const [reloadFetch, setReloadFetch] = useState(false); @@ -97,6 +105,11 @@ export default function ParentHomePage() { } }; + const showClassPlanning = (student) => { + setPlanningClassName(`${student.associated_class_name} - ${getNiveauLabel(student.level)}`); + setShowPlanning(true); + }; + const childrenColumns = [ { name: 'photo', @@ -127,6 +140,12 @@ export default function ParentHomePage() { }, { name: 'Nom', transform: (row) => `${row.student.last_name}` }, { name: 'Prénom', transform: (row) => `${row.student.first_name}` }, + { + name: 'Classe', + transform: (row) => ( +
{(row.student.associated_class_name)}
+ ), + }, { name: 'Niveau', transform: (row) => ( @@ -192,7 +211,6 @@ export default function ParentHomePage() { > - {/* Nouvelle action Upload */} + <> + + + )} ), @@ -228,40 +258,66 @@ export default function ParentHomePage() { ]; return ( -
-
-

- - Enfants -

-
- - - {/* Composant FileUpload et bouton Valider en dessous du tableau */} - {uploadState === 'on' && uploadingStudentId && ( -
- +
+ {showPlanning && planningClassName ? ( + // Affichage grand format mais respectant la sidebar + <> +
- )} -
+
+ + + +
+ + ) : ( + // Affichage classique avec le tableau des enfants +
+ +
+
+ + {/* Composant FileUpload et bouton Valider en dessous du tableau */} + {uploadState === 'on' && uploadingStudentId && ( +
+ + +
+ )} + + )} ); } diff --git a/Front-End/src/components/Calendar/Calendar.js b/Front-End/src/components/Calendar/Calendar.js index ffd7765..548aa33 100644 --- a/Front-End/src/components/Calendar/Calendar.js +++ b/Front-End/src/components/Calendar/Calendar.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { usePlanning } from '@/context/PlanningContext'; +import { usePlanning, PlanningModes } from '@/context/PlanningContext'; import WeekView from '@/components/Calendar/WeekView'; import MonthView from '@/components/Calendar/MonthView'; import YearView from '@/components/Calendar/YearView'; @@ -22,14 +22,16 @@ import { fr } from 'date-fns/locale'; import { AnimatePresence, motion } from 'framer-motion'; // Ajouter cet import import logger from '@/utils/logger'; -const Calendar = ({ modeSet, onDateClick, onEventClick, schoolClassMode=false }) => { +const Calendar = ({ modeSet, onDateClick, onEventClick, planningClassName='' }) => { const { currentDate, setCurrentDate, viewType, setViewType, events, - hiddenSchedules + hiddenSchedules, + planningMode, + parentView } = usePlanning(); const [visibleEvents, setVisibleEvents] = useState([]); const [showDatePicker, setShowDatePicker] = useState(false); @@ -90,9 +92,9 @@ const Calendar = ({ modeSet, onDateClick, onEventClick, schoolClassMode=false }) return (
- {!schoolClassMode && ( + {/* Navigation à gauche */} + {planningMode === PlanningModes.PLANNING && (
- {/* Navigation à gauche */} - - {/* Menu déroulant pour le mois/année */}
- - {/* Menu de sélection du mois/année */} {showDatePicker && (
{viewType !== 'year' && ( @@ -156,7 +154,6 @@ const Calendar = ({ modeSet, onDateClick, onEventClick, schoolClassMode=false })
)}
-
)} - {/* Numéro de semaine au centre */} - {viewType === 'week' && ( -
- Semaine - - {getWeek(currentDate, { weekStartsOn: 1 })} + {/* Centre : numéro de semaine ou classe/niveau */} +
+ {((planningMode === PlanningModes.PLANNING || planningMode === PlanningModes.CLASS_SCHEDULE) && viewType === 'week' && !parentView) && ( +
+ Semaine + + {getWeek(currentDate, { weekStartsOn: 1 })} + +
+ )} + {parentView && ( + + {/* À adapter selon les props disponibles */} + {planningClassName} -
- )} + )} +
{/* Contrôles à droite */}
- {!schoolClassMode && ( + {planningMode === PlanningModes.PLANNING && ( )} + {(planningMode === PlanningModes.PLANNING || (planningMode === PlanningModes.CLASS_SCHEDULE && !parentView)) && ( + + + )}
diff --git a/Front-End/src/components/Calendar/WeekView.js b/Front-End/src/components/Calendar/WeekView.js index 18d2072..5f3d1d2 100644 --- a/Front-End/src/components/Calendar/WeekView.js +++ b/Front-End/src/components/Calendar/WeekView.js @@ -1,12 +1,13 @@ import React, { useEffect, useState, useRef } from 'react'; -import { usePlanning } from '@/context/PlanningContext'; +import { usePlanning, PlanningModes } from '@/context/PlanningContext'; import { format, startOfWeek, addDays, isSameDay } from 'date-fns'; import { fr } from 'date-fns/locale'; import { getWeekEvents } from '@/utils/events'; import { isToday } from 'date-fns'; + const WeekView = ({ onDateClick, onEventClick, events }) => { - const { currentDate } = usePlanning(); + const { currentDate, planningMode, parentView } = usePlanning(); const [currentTime, setCurrentTime] = useState(new Date()); const scrollContainerRef = useRef(null); // Ajouter cette référence @@ -106,10 +107,14 @@ const WeekView = ({ onDateClick, onEventClick, events }) => { key={event.id} className="rounded-sm overflow-hidden cursor-pointer hover:shadow-lg" style={eventStyle} - onClick={(e) => { - e.stopPropagation(); - onEventClick(event); - }} + onClick={ + parentView + ? undefined + : (e) => { + e.stopPropagation(); + onEventClick(event); + } + } >
{ className={`h-20 relative border-b border-gray-100 ${isWeekend(day) ? 'bg-gray-50' : 'bg-white'} ${isToday(day) ? 'bg-emerald-100/50 border-x border-emerald-600' : ''}`} - onClick={() => { - const date = new Date(day); - date.setHours(hour); - onDateClick(date); - }} + onClick={ + parentView + ? undefined + : () => { + const date = new Date(day); + date.setHours(hour); + onDateClick(date); + } + } >
{dayEvents diff --git a/Front-End/src/components/ParentPlanningSection.js b/Front-End/src/components/ParentPlanningSection.js new file mode 100644 index 0000000..6a3faca --- /dev/null +++ b/Front-End/src/components/ParentPlanningSection.js @@ -0,0 +1,30 @@ +import React, { useEffect } from 'react'; +import Calendar from '@/components/Calendar/Calendar'; +import { usePlanning } from '@/context/PlanningContext'; + +const ParentPlanningSection = ({ planningClassName }) => { + const { setHiddenSchedules, schedules } = usePlanning(); + + useEffect(() => { + if (schedules && schedules.length > 0 && planningClassName) { + const visibleSchedule = schedules.find(s => s.name === planningClassName); + const hidden = schedules + .filter(s => s.name !== planningClassName) + .map(s => s.id); + setHiddenSchedules(hidden); + + // Optionnel : si aucun planning ne correspond, tout masquer + if (!visibleSchedule) { + setHiddenSchedules(schedules.map(s => s.id)); + } + } + }, [schedules, planningClassName, setHiddenSchedules]); + + return ( +
+ +
+ ); +}; + +export default ParentPlanningSection; \ No newline at end of file diff --git a/Front-End/src/components/Structure/Planning/ScheduleManagement.js b/Front-End/src/components/Structure/Planning/ScheduleManagement.js index 41113e1..2e31485 100644 --- a/Front-End/src/components/Structure/Planning/ScheduleManagement.js +++ b/Front-End/src/components/Structure/Planning/ScheduleManagement.js @@ -2,7 +2,6 @@ import React, { useState } from 'react'; -import logger from '@/utils/logger'; import { RecurrenceType } from '@/context/PlanningContext'; import Calendar from '@/components/Calendar/Calendar'; import ScheduleEventModal from '@/components/Structure/Planning/ScheduleEventModal'; @@ -60,7 +59,6 @@ export default function ScheduleManagement({ setEventData(event); setIsModalOpen(true); }} - schoolClassMode={true} />