feat: Gestion du planning [3]

This commit is contained in:
Luc SORIGNET
2025-05-03 15:12:17 +02:00
parent cb4fe74a9e
commit 58144ba0d0
39 changed files with 939 additions and 1864 deletions

View File

@ -1,211 +1,74 @@
'use client';
import React, { useState, useEffect } from 'react';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import { AnimatePresence, motion } from 'framer-motion';
import PlanningClassView from '@/components/Structure/Planning/PlanningClassView';
import SpecialitiesList from '@/components/Structure/Planning/SpecialitiesList';
import { BE_SCHOOL_PLANNINGS_URL } from '@/utils/Url';
import { useClasses } from '@/context/ClassesContext';
import { ClasseFormProvider } from '@/context/ClasseFormContext';
import TabsStructure from '@/components/Structure/Configuration/TabsStructure';
import { Bookmark, Users, BookOpen, Newspaper } from 'lucide-react';
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';
import ScheduleNavigation from '@/components/Calendar/ScheduleNavigation';
const ScheduleManagement = ({ handleUpdatePlanning, classes }) => {
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const currentSchoolYearStart =
currentMonth >= 8 ? currentYear : currentYear - 1;
const [selectedClass, setSelectedClass] = useState(null);
const [selectedLevel, setSelectedLevel] = useState('');
const [schedule, setSchedule] = useState(null);
const { getNiveauxTabs } = useClasses();
const niveauxLabels = Array.isArray(selectedClass?.levels)
? getNiveauxTabs(selectedClass.levels)
: [];
export default function ScheduleManagement({classes,specialities,teachers}) {
const [isModalOpen, setIsModalOpen] = useState(false);
const handleOpenModal = () => setIsModalOpen(true);
const handleCloseModal = () => setIsModalOpen(false);
const [eventData, setEventData] = useState({
title: '',
description: '',
start: '',
end: '',
location: '',
planning: '', // Enlever la valeur par défaut ici
recursionType: RecurrenceType.NONE,
selectedDays: [],
recursionEnd: '',
customInterval: 1,
customUnit: 'days',
viewType: 'week', // Ajouter la vue semaine par défaut
});
useEffect(() => {
if (selectedClass) {
const defaultLevel = niveauxLabels.length > 0 ? niveauxLabels[0].id : '';
const niveau = selectedLevel || defaultLevel;
const initializeNewEvent = (date = new Date()) => {
// S'assurer que date est un objet Date valide
const eventDate = date instanceof Date ? date : new Date();
setSelectedLevel(niveau);
const currentPlanning = selectedClass.plannings_read?.find(
(planning) => planning.niveau === niveau
);
setSchedule(currentPlanning ? currentPlanning.planning : {});
}
}, [selectedClass, niveauxLabels]);
useEffect(() => {
if (selectedClass && selectedLevel) {
const currentPlanning = selectedClass.plannings_read?.find(
(planning) => planning.niveau === selectedLevel
);
setSchedule(currentPlanning ? currentPlanning.planning : {});
}
}, [selectedClass, selectedLevel]);
const handleLevelSelect = (niveau) => {
setSelectedLevel(niveau);
setEventData({
title: '',
description: '',
start: eventDate.toISOString(),
end: new Date(eventDate.getTime() + 2 * 60 * 60 * 1000).toISOString(),
location: '',
planning: '', // Ne pas définir de valeur par défaut ici non plus
recursionType: RecurrenceType.NONE,
selectedDays: [],
recursionEnd: new Date(
eventDate.getTime() + 2 * 60 * 60 * 1000
).toISOString(),
customInterval: 1,
customUnit: 'days',
});
setIsModalOpen(true);
};
const handleClassSelect = (classId) => {
const selectedClasse = categorizedClasses['Actives'].find(
(classe) => classe.id === classId
);
setSelectedClass(selectedClasse);
setSelectedLevel('');
};
const onDrop = (item, hour, day) => {
const { id, name, color, teachers } = item;
const newSchedule = {
...schedule,
emploiDuTemps: schedule.emploiDuTemps || {},
};
if (!newSchedule.emploiDuTemps[day]) {
newSchedule.emploiDuTemps[day] = [];
}
const courseTime = `${hour.toString().padStart(2, '0')}:00`;
const existingCourseIndex = newSchedule.emploiDuTemps[day].findIndex(
(course) => course.heure === courseTime
);
const newCourse = {
duree: '1',
heure: courseTime,
matiere: name,
teachers: teachers,
color: color,
};
if (existingCourseIndex !== -1) {
newSchedule.emploiDuTemps[day][existingCourseIndex] = newCourse;
} else {
newSchedule.emploiDuTemps[day].push(newCourse);
}
// Mettre à jour scheduleRef
setSchedule(newSchedule);
// Utiliser `handleUpdatePlanning` pour mettre à jour le planning du niveau de la classe
const planningId = selectedClass.plannings_read.find(
(planning) => planning.niveau === selectedLevel
)?.planning.id;
if (planningId) {
logger.debug('newSchedule : ', newSchedule);
handleUpdatePlanning(BE_SCHOOL_PLANNINGS_URL, planningId, newSchedule);
}
};
const categorizedClasses = classes.reduce((acc, classe) => {
const { school_year } = classe;
const [startYear] = school_year.split('-').map(Number);
const category =
startYear >= currentSchoolYearStart ? 'Actives' : 'Anciennes';
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(classe);
return acc;
}, {});
return (
<div className="flex flex-col h-full">
<DndProvider backend={HTML5Backend}>
<div className="p-4 bg-gray-100 border-b">
<div className="grid grid-cols-3 gap-4">
{/* Colonne Classes */}
<div className="p-4 bg-gray-50 rounded-lg shadow-inner">
<div className="flex justify-between items-center mb-4">
<h2 className="text-3xl text-gray-800 flex items-center">
<Users className="w-8 h-8 mr-2" />
Classes
</h2>
</div>
{categorizedClasses['Actives'] && (
<TabsStructure
activeTab={selectedClass?.id}
setActiveTab={handleClassSelect}
tabs={categorizedClasses['Actives'].map((classe) => ({
id: classe.id,
title: classe.atmosphere_name,
icon: Users,
}))}
/>
)}
</div>
{/* Colonne Niveaux */}
<div className="p-4 bg-gray-50 rounded-lg shadow-inner">
<div className="flex justify-between items-center mb-4">
<h2 className="text-3xl text-gray-800 flex items-center">
<Bookmark className="w-8 h-8 mr-2" />
Niveaux
</h2>
</div>
{niveauxLabels && (
<TabsStructure
activeTab={selectedLevel}
setActiveTab={handleLevelSelect}
tabs={niveauxLabels}
/>
)}
</div>
{/* Colonne Spécialités */}
<div className="p-4 bg-gray-50 rounded-lg shadow-inner">
<div className="flex justify-between items-center mb-4">
<h2 className="text-3xl text-gray-800 flex items-center">
<BookOpen className="w-8 h-8 mr-2" />
Spécialités
</h2>
</div>
<SpecialitiesList
teachers={selectedClass ? selectedClass.teachers : []}
/>
</div>
</div>
</div>
<div className="flex-1 p-4 overflow-y-auto">
<AnimatePresence mode="wait">
<motion.div
key="year"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.2 }}
className="flex-1 relative"
>
<ClasseFormProvider initialClasse={selectedClass || {}}>
<PlanningClassView
schedule={schedule}
onDrop={onDrop}
selectedLevel={selectedLevel}
handleUpdatePlanning={handleUpdatePlanning}
classe={selectedClass}
/>
</ClasseFormProvider>
</motion.div>
</AnimatePresence>
</div>
</DndProvider>
</div>
<div className="flex h-full overflow-hidden">
<ScheduleNavigation classes={classes} />
<Calendar
onDateClick={initializeNewEvent}
onEventClick={(event) => {
setEventData(event);
setIsModalOpen(true);
}}
/>
<ScheduleEventModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
eventData={eventData}
setEventData={setEventData}
specialities={specialities}
teachers={teachers}
classes={classes}
/>
</div>
);
};
}
export default ScheduleManagement;