diff --git a/Back-End/Dockerfile b/Back-End/Dockerfile index afa5421..0aa05ac 100644 --- a/Back-End/Dockerfile +++ b/Back-End/Dockerfile @@ -3,6 +3,7 @@ # The first instruction is what image we want to base our container on # We Use an official Python runtime as a parent image FROM python:3.12.7 +WORKDIR /Back-End # Allows docker to cache installed dependencies between builds COPY requirements.txt requirements.txt @@ -11,7 +12,6 @@ RUN pip install pymupdf # Mounts the application code to the image COPY . . -WORKDIR /Back-End EXPOSE 8080 diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index 39a69a6..44b3571 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -68,11 +68,7 @@ class TeacherSerializer(serializers.ModelSerializer): def get_droit(self, obj): if obj.associated_profile: - droit_id = obj.associated_profile.droit - return { - "label": obj.associated_profile.get_droit_display(), - "id": droit_id - } + return obj.associated_profile.droit return None def get_specialities_details(self, obj): @@ -163,7 +159,7 @@ class SchoolClassSerializer(serializers.ModelSerializer): return instance def get_teachers_details(self, obj): - return [{'last_name': teacher.last_name, 'first_name': teacher.first_name} for teacher in obj.teachers.all()] + return [{'id': teacher.id, 'last_name': teacher.last_name, 'first_name': teacher.first_name} for teacher in obj.teachers.all()] def get_updated_date_formatted(self, obj): utc_time = timezone.localtime(obj.updated_date) diff --git a/Front-End/src/components/MultiSelect.js b/Front-End/src/components/MultiSelect.js new file mode 100644 index 0000000..3680d95 --- /dev/null +++ b/Front-End/src/components/MultiSelect.js @@ -0,0 +1,84 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Check, ChevronDown } from 'lucide-react'; + +const MultiSelect = ({ name, label, options, selectedOptions, onChange, errorMsg }) => { + const [isOpen, setIsOpen] = useState(false); + const containerRef = useRef(null); + + const handleSelect = (option) => { + const isSelected = selectedOptions.some(selected => selected.id === option.id); + let newSelectedOptions; + if (isSelected) { + newSelectedOptions = selectedOptions.filter(selected => selected.id !== option.id); + } else { + newSelectedOptions = [...selectedOptions, option]; + } + onChange(newSelectedOptions); + }; + + const handleClickOutside = (event) => { + if (containerRef.current && !containerRef.current.contains(event.target)) { + setIsOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ +
+ + {isOpen && ( + + )} +
+ {errorMsg &&

{errorMsg}

} +
+ ); +}; + +export default MultiSelect; \ No newline at end of file diff --git a/Front-End/src/components/SelectChoice.js b/Front-End/src/components/SelectChoice.js index 6e3fbd7..b935496 100644 --- a/Front-End/src/components/SelectChoice.js +++ b/Front-End/src/components/SelectChoice.js @@ -1,36 +1,34 @@ -export default function SelectChoice({ type, name, label,required, placeHolder, choices, callback, selected, errorMsg, IconItem, disabled = false }) { +export default function SelectChoice({ type, name, label, required, placeHolder, choices, callback, selected, errorMsg, IconItem, disabled = false }) { return ( <> -
- -
- {IconItem && - - {} - - } - -
- {errorMsg &&

{errorMsg}

} + +
+ {IconItem && + + {} + + } +
+ {errorMsg &&

{errorMsg}

} ); } \ No newline at end of file diff --git a/Front-End/src/components/Structure/Configuration/ClassForm.js b/Front-End/src/components/Structure/Configuration/ClassForm.js deleted file mode 100644 index 01cf73e..0000000 --- a/Front-End/src/components/Structure/Configuration/ClassForm.js +++ /dev/null @@ -1,253 +0,0 @@ -import React, { useState } from 'react'; -import InputTextIcon from '@/components/InputTextIcon'; -import Button from '@/components/Button'; -import SelectChoice from '@/components/SelectChoice'; -import CheckBoxList from '@/components/CheckBoxList'; -import PlanningConfiguration from '@/components/Structure/Configuration/PlanningConfiguration'; -import TeachersSelectionConfiguration from '@/components/Structure/Configuration/TeachersSelectionConfiguration'; -import { Users, Maximize2, Calendar, UserPlus } from 'lucide-react'; -import { useClasseForm } from '@/context/ClasseFormContext'; -import { useClasses } from '@/context/ClassesContext'; - -const ClassForm = ({ onSubmit, isNew, teachers }) => { - - const { formData, setFormData } = useClasseForm(); - const { getNiveauNameById, schoolYears, getNiveauxLabels, getNiveauxTabs, generateAgeToNiveaux, niveauxPremierCycle, niveauxSecondCycle, niveauxTroisiemeCycle, typeEmploiDuTemps, updatePlannings } = useClasses(); - const [selectedTeachers, setSelectedTeachers] = useState(formData.teachers); - - const handleTeacherSelection = (teacher) => { - setSelectedTeachers(prevState => - prevState.includes(teacher.id) - ? prevState.filter(id => id !== teacher.id) - : [...prevState, teacher.id] - ); - setFormData(prevState => ({ - ...prevState, - teachers: prevState.teachers.includes(teacher.id) - ? prevState.teachers.filter(id => id !== teacher.id) - : [...prevState.teachers, teacher.id] - })); - }; - - const handleTimeChange = (e, index) => { - const { value } = e.target; - setFormData(prevState => { - const updatedTimes = [...prevState.time_range]; - updatedTimes[index] = value; - - const updatedFormData = { - ...prevState, - time_range: updatedTimes, - }; - - const existingPlannings = prevState.plannings || []; - updatedFormData.plannings = updatePlannings(updatedFormData, existingPlannings); - - return updatedFormData; - }); - }; - - const handleJoursChange = (e) => { - const { value, checked } = e.target; - const dayId = parseInt(value, 10); - - setFormData((prevState) => { - const updatedJoursOuverture = checked - ? [...prevState.opening_days, dayId] - : prevState.opening_days.filter((id) => id !== dayId); - - const updatedFormData = { - ...prevState, - opening_days: updatedJoursOuverture, - }; - - const existingPlannings = prevState.plannings || []; - updatedFormData.plannings = updatePlannings(updatedFormData, existingPlannings); - - return updatedFormData; - }); - }; - - const handleChange = (e) => { - e.preventDefault(); - const { name, value, type, checked } = e.target; - - setFormData(prevState => { - - // Copier l'état précédent - let newState = { ...prevState }; - - if (type === 'checkbox') { - const newValues = checked - ? [...(prevState[name] || []), parseInt(value)] - : (prevState[name] || []).filter(v => v !== parseInt(value)); - newState[name] = newValues; - } else if (name === 'age_range') { - const [minAgeStr, maxAgeStr] = value.split('-'); - const minAge = minAgeStr ? parseInt(minAgeStr) : null; - const maxAge = minAgeStr ? parseInt(maxAgeStr) : null; - const selectedNiveaux = generateAgeToNiveaux(minAge, maxAge); - const niveauxLabels = getNiveauxLabels(selectedNiveaux); - - newState = { - ...prevState, - [name]: value, - levels: selectedNiveaux.length > 0 ? selectedNiveaux : [], - }; - } else if (type === 'radio') { - newState[name] = parseInt(value, 10); - } else { - newState[name] = value; - } - - const existingPlannings = prevState.plannings || []; - newState.plannings = updatePlannings(newState, existingPlannings); - - return newState; - }); -}; - - const handleSubmit = () => { - onSubmit(formData); - }; - - const [minAge, maxAge] = formData.age_range.length === 2 ? formData.age_range : [null, null]; - const selectedAgeGroup = generateAgeToNiveaux(minAge, maxAge); - - return ( -
-
- -
- {/* Section Ambiance */} -
- -
-
- -
-
- -
-
-
- - {/* Section Niveau */} -
- -
- !selectedAgeGroup.includes(parseInt(item.id))} - className="w-full" - /> - !selectedAgeGroup.includes(parseInt(item.id))} - className="w-full" - /> - !selectedAgeGroup.includes(parseInt(item.id))} - className="w-full" - /> -
-
-
- -
- {/* Section Capacité */} -
- -
- -
-
- - {/* Année scolaire */} -
- -
- -
-
-
- - {/* Section Enseignants */} - - - {/* Section Emploi du temps */} - - -
-
- -
- ); -}; - -export default ClassForm; diff --git a/Front-End/src/components/Structure/Configuration/ClassesSection.js b/Front-End/src/components/Structure/Configuration/ClassesSection.js index 31cd45d..f5ac6b2 100644 --- a/Front-End/src/components/Structure/Configuration/ClassesSection.js +++ b/Front-End/src/components/Structure/Configuration/ClassesSection.js @@ -1,165 +1,427 @@ -import { Trash2, MoreVertical, Edit3, Plus, ZoomIn } from 'lucide-react'; -import { useState } from 'react'; +import { Trash2, Edit3, Plus, ZoomIn, Users, Check, X, Hand } from 'lucide-react'; +import React, { useState, useEffect } from 'react'; import Table from '@/components/Table'; -import DropdownMenu from '@/components/DropdownMenu'; -import Modal from '@/components/Modal'; -import ClassForm from '@/components/Structure/Configuration/ClassForm'; -import ClasseDetails from '@/components/ClasseDetails'; +import Popup from '@/components/Popup'; +import InputText from '@/components/InputText'; +import SelectChoice from '@/components/SelectChoice'; +import TeacherItem from '@/components/Structure/Configuration/TeacherItem'; +import MultiSelect from '@/components/MultiSelect'; import LevelLabel from '@/components/CustomLabels/LevelLabel'; -import TeacherLabel from '@/components/CustomLabels/TeacherLabel'; -import { ClasseFormProvider } from '@/context/ClasseFormContext'; -import { useClasses } from '@/context/ClassesContext'; +import { DndProvider, HTML5Backend, useDrop } from 'react-dnd'; +const ItemTypes = { + TEACHER: 'teacher', +}; -const ClassesSection = ({ classes, teachers, handleCreate, handleEdit, handleDelete }) => { +const TeachersDropZone = ({ classe, handleTeachersChange, teachers, isEditing }) => { + const [localTeachers, setLocalTeachers] = useState(classe.teachers_details || []); - const { getNiveauxLabels } = useClasses(); - const [isOpen, setIsOpen] = useState(false); - const [isOpenDetails, setIsOpenDetails] = useState(false); - const [editingClass, setEditingClass] = useState(null); + useEffect(() => { + }, [teachers]); - const openEditModal = (classe) => { - setIsOpen(true); - setEditingClass(classe); - } + useEffect(() => { + setLocalTeachers(classe.teachers_details || []); + }, [classe.teachers_details]); - const openEditModalDetails = (classe) => { - setIsOpenDetails(true); - setEditingClass(classe); - } + useEffect(() => { + handleTeachersChange(localTeachers.map(teacher => teacher.id)); + }, [localTeachers]); - const closeEditModal = () => { - setIsOpen(false); - setEditingClass(null); - }; + const [{ isOver, canDrop }, drop] = useDrop({ + accept: ItemTypes.TEACHER, + drop: (item) => { + const teacherDetails = teachers.find(teacher => teacher.id === item.id); + const exists = localTeachers.some(teacher => teacher.id === item.id); + if (!exists) { + setLocalTeachers(prevTeachers => { + const updatedTeachers = [ + ...prevTeachers, + { id: item.id, last_name: teacherDetails.last_name, first_name: teacherDetails.first_name } + ]; + return updatedTeachers; + }); + } + }, + collect: (monitor) => ({ + isOver: !!monitor.isOver(), + canDrop: !!monitor.canDrop(), + }), + canDrop: () => { + return isEditing; + }, + }); - const closeEditModalDetails = () => { - setIsOpenDetails(false); - setEditingClass(null); - }; - - const handleModalSubmit = (updatedData) => { - if (editingClass) { - handleEdit(editingClass.id, updatedData); - } else { - handleCreate(updatedData); - } - closeEditModal(); + const handleRemoveTeacher = (id) => { + setLocalTeachers(prevTeachers => { + const updatedTeachers = prevTeachers.filter(teacher => teacher.id !== id); + return updatedTeachers; + }); }; return ( -
-
-

Gestion des classes

- -
-
- { - const ambiance = row.atmosphere_name ? row.atmosphere_name : ''; - const trancheAge = row.age_range ? `${row.age_range} ans` : ''; - - if (ambiance && trancheAge) { - return `${ambiance} (${trancheAge})`; - } else if (ambiance) { - return ambiance; - } else if (trancheAge) { - return trancheAge; - } else { - return 'Non spécifié'; - } - } - }, - { - name: 'NIVEAUX', - transform: (row) => { - const levelLabels = Array.isArray(row.levels) ? getNiveauxLabels(row.levels) : []; - return ( -
- {levelLabels.length > 0 - ? levelLabels.map((label, index) => ( - - )) - : 'Aucun niveau'} -
- ); - } - }, - { name: 'CAPACITÉ MAX', transform: (row) => row.number_of_students }, - { name: 'ANNÉE SCOLAIRE', transform: (row) => row.school_year }, - { - name: 'ENSEIGNANTS', - transform: (row) => ( -
- {row.teachers_details.map((teacher, index) => ( - - ))} -
- ) - }, - { name: 'DATE DE CREATION', transform: (row) => row.updated_date_formatted }, - { - name: 'ACTIONS', transform: (row) => ( - } - items={[ - { label: 'Inspecter', icon: ZoomIn, onClick: () => openEditModalDetails(row) }, - { label: 'Modifier', icon: Edit3, onClick: () => openEditModal(row) }, - { label: 'Supprimer', icon: Trash2, onClick: () => handleDelete(row.id) } - ] - } - buttonClassName="text-gray-400 hover:text-gray-600" - menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center" - /> - ) - } - ]} - data={classes} - /> - - {isOpen && ( - - ( - - )} - /> - - )} - - {isOpenDetails && ( - - - {editingClass ? ( - <> - {editingClass.atmosphere_name} - {editingClass.age_range[0]} à {editingClass.age_range[1]} ans - - ) : ''} - - )} - ContentComponent={() => ( - - )} - /> +
+ {isEditing && ( +
+ {/* Ajoutez l'icône Hand */} + Déposez un enseignant ici +
)} + {localTeachers.map((teacher, index) => ( +
+ + {isEditing && ( + + )} +
+ ))}
); }; -export default ClassesSection; +const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdit, handleDelete }) => { + const [formData, setFormData] = useState({}); + const [editingClass, setEditingClass] = useState(null); + const [newClass, setNewClass] = useState(null); + const [localErrors, setLocalErrors] = useState({}); + const [popupVisible, setPopupVisible] = useState(false); + const [popupMessage, setPopupMessage] = useState(""); + + const niveauxPremierCycle = [ + { id: 1, name: 'TPS', age: 2 }, + { id: 2, name: 'PS', age: 3 }, + { id: 3, name: 'MS', age: 4 }, + { id: 4, name: 'GS', age: 5 }, + ]; + + const niveauxSecondCycle = [ + { id: 5, name: 'CP', age: 6 }, + { id: 6, name: 'CE1', age: 7 }, + { id: 7, name: 'CE2', age: 8 }, + ]; + + const niveauxTroisiemeCycle = [ + { id: 8, name: 'CM1', age: 9 }, + { id: 9, name: 'CM2', age: 10 }, + ]; + + const allNiveaux = [...niveauxPremierCycle, ...niveauxSecondCycle, ...niveauxTroisiemeCycle]; + + const getNiveauxLabels = (levels) => { + return levels.map(niveauId => { + const niveau = allNiveaux.find(n => n.id === niveauId); + return niveau ? niveau.name : niveauId; + }); + }; + + // Fonction pour générer les années scolaires + const getSchoolYearChoices = () => { + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + const currentMonth = currentDate.getMonth() + 1; // Les mois sont indexés à partir de 0 + + // Si nous sommes avant septembre, l'année scolaire en cours a commencé l'année précédente + const startYear = currentMonth >= 9 ? currentYear : currentYear - 1; + + const choices = []; + for (let i = 0; i < 3; i++) { + const year = startYear + i; + choices.push({ value: `${year}-${year + 1}`, label: `${year}-${year + 1}` }); + } + return choices; + }; + + const handleAddClass = () => { + setNewClass({ id: Date.now(), atmosphere_name: '', age_range: '', levels: [], number_of_students: '', school_year: '', teachers: [] }); + setFormData({ atmosphere_name: '', age_range: '', levels: [], number_of_students: '', school_year: '', teachers: [] }); + }; + + const handleChange = (e) => { + const { name, value } = e.target; + + if (editingClass) { + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + } else if (newClass) { + setNewClass((prevData) => ({ + ...prevData, + [name]: value, + })); + } + }; + + const handleSaveNewClass = () => { + if (newClass.atmosphere_name) { + handleCreate(newClass) + .then((createdClass) => { + setClasses((prevClasses) => [createdClass, ...classes]); + setNewClass(null); + setLocalErrors({}); + }) + .catch((error) => { + console.error(error); + }); + } else { + setPopupMessage("Tous les champs doivent être remplis et valides"); + setPopupVisible(true); + } + }; + + const handleUpdateClass = (id, updatedData) => { + if (!updatedData.atmosphere_name) { + setLocalErrors({ atmosphere_name: 'Le nom d\'ambiance est requis.' }); + return; + } + + handleEdit(id, updatedData) + .then((updatedClass) => { + setClasses((prevClasses) => prevClasses.map((classe) => (classe.id === id ? updatedClass : classe))); + setEditingClass(null); + setFormData({}); + setLocalErrors({}); + }) + .catch((error) => { + console.error(error); + }); + }; + + const handleTeachersChange = (selectedTeachers) => { + if (editingClass) { + setFormData((prevData) => ({ + ...prevData, + teachers: selectedTeachers, + })); + } else if (newClass) { + setNewClass((prevData) => ({ + ...prevData, + teachers: selectedTeachers, + })); + setFormData((prevData) => ({ + ...prevData, + teachers: selectedTeachers, + })); + } + }; + + const handleMultiSelectChange = (selectedOptions) => { + const levels = selectedOptions.map(option => option.id); + + if (editingClass) { + setFormData((prevData) => ({ + ...prevData, + levels, + })); + } else if (newClass) { + setNewClass((prevData) => ({ + ...prevData, + levels, + })); + setFormData((prevData) => ({ + ...prevData, + levels, + })); + } + }; + + const renderClassCell = (classe, column) => { + const isEditing = editingClass === classe.id; + const isCreating = newClass && newClass.id === classe.id; + const currentData = isEditing ? formData : newClass || {}; + + if (isEditing || isCreating) { + switch (column) { + case 'AMBIANCE': + return ( + + ); + case 'TRANCHE D\'AGE': + return ( + + ) + case 'NIVEAUX': + return ( + allNiveaux.find(level => level.id === levelId)) : []} + onChange={handleMultiSelectChange} + errorMsg={localErrors && localErrors.levels && Array.isArray(localErrors.levels) ? localErrors.levels[0] : ''} + /> + ); + case 'CAPACITE': + return ( + + ) + case 'ANNÉE SCOLAIRE' : + return ( + + ) + case 'ENSEIGNANTS': + return ( + + ); + case 'ACTIONS': + return ( +
+ + +
+ ); + default: + return null; + } + } else { + switch (column) { + case 'AMBIANCE': + return classe.atmosphere_name; + case 'TRANCHE D\'AGE': + return classe.age_range; + case 'NIVEAUX': + const levelLabels = Array.isArray(classe.levels) ? getNiveauxLabels(classe.levels) : []; + return ( +
+ {levelLabels.length > 0 + ? levelLabels.map((label, index) => ( + + )) + : 'Aucun niveau'} +
+ ); + case 'CAPACITE': + return classe.number_of_students; + case 'ANNÉE SCOLAIRE' : + return classe.school_year; + case 'ENSEIGNANTS': + return ( +
+ {classe.teachers_details.map((teacher) => ( + + ))} +
+ ); + case 'MISE A JOUR': + return classe.updated_date_formatted; + case 'ACTIONS': + return ( +
+ + + +
+ ); + default: + return null; + } + } + }; + + const columns = [ + { name: 'AMBIANCE', label: 'Nom d\'ambiance' }, + { name: 'TRANCHE D\'AGE', label: 'Tranche d\'âge' }, + { name: 'NIVEAUX', label: 'Niveaux' }, + { name: 'CAPACITE', label: 'Capacité max' }, + { name: 'ANNÉE SCOLAIRE', label: 'Année scolaire' }, + { name: 'ENSEIGNANTS', label: 'Enseignants' }, + { name: 'MISE A JOUR', label: 'Date mise à jour' }, + { name: 'ACTIONS', label: 'Actions' } + ]; + + return ( + +
+
+
+ +

Classes

+
+ +
+
+ setPopupVisible(false)} + onCancel={() => setPopupVisible(false)} + uniqueConfirmButton={true} + /> + + + ); +}; + +export default ClassesSection; \ No newline at end of file diff --git a/Front-End/src/components/Structure/Configuration/SpecialityItem.js b/Front-End/src/components/Structure/Configuration/SpecialityItem.js index 8f0b801..6975b5d 100644 --- a/Front-End/src/components/Structure/Configuration/SpecialityItem.js +++ b/Front-End/src/components/Structure/Configuration/SpecialityItem.js @@ -5,6 +5,24 @@ const ItemTypes = { SPECIALITY: 'speciality', }; +const lightenColor = (color, percent) => { + const num = parseInt(color.slice(1), 16), + amt = Math.round(2.55 * percent), + R = (num >> 16) + amt, + G = (num >> 8 & 0x00FF) + amt, + B = (num & 0x0000FF) + amt; + return `#${(0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1).toUpperCase()}`; +}; + +const darkenColor = (color, percent) => { + const num = parseInt(color.slice(1), 16), + amt = Math.round(2.55 * percent), + R = (num >> 16) - amt, + G = (num >> 8 & 0x00FF) - amt, + B = (num & 0x0000FF) - amt; + return `#${(0x1000000 + (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 + (G < 255 ? G < 1 ? 0 : G : 255) * 0x100 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1).toUpperCase()}`; +}; + const SpecialityItem = ({ speciality, isDraggable = true }) => { const [{ isDragging }, drag] = useDrag(() => ({ type: ItemTypes.SPECIALITY, @@ -18,10 +36,15 @@ const SpecialityItem = ({ speciality, isDraggable = true }) => { return (
{speciality.name}
diff --git a/Front-End/src/components/Structure/Configuration/StructureManagement.js b/Front-End/src/components/Structure/Configuration/StructureManagement.js index f343ff7..bde54ca 100644 --- a/Front-End/src/components/Structure/Configuration/StructureManagement.js +++ b/Front-End/src/components/Structure/Configuration/StructureManagement.js @@ -7,7 +7,7 @@ import { BE_SCHOOL_SPECIALITY_URL, BE_SCHOOL_TEACHER_URL, BE_SCHOOL_SCHOOLCLASS_ const StructureManagement = ({ specialities, setSpecialities, teachers, setTeachers, classes, setClasses, handleCreate, handleEdit, handleDelete }) => { return ( -
+
handleDelete(`${BE_SCHOOL_TEACHER_URL}`, id, setTeachers)} />
-
+
handleCreate(`${BE_SCHOOL_SCHOOLCLASS_URL}`, newData, setClasses)} handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_SCHOOLCLASS_URL}`, id, updatedData, setClasses)} diff --git a/Front-End/src/components/Structure/Configuration/TeacherItem.js b/Front-End/src/components/Structure/Configuration/TeacherItem.js new file mode 100644 index 0000000..a0d6287 --- /dev/null +++ b/Front-End/src/components/Structure/Configuration/TeacherItem.js @@ -0,0 +1,35 @@ +import { useDrag } from 'react-dnd'; +import React from 'react'; + +const ItemTypes = { + TEACHER: 'teacher', +}; + +const TeacherItem = ({ teacher, isDraggable = true }) => { + const [{ isDragging }, drag] = useDrag(() => ({ + type: ItemTypes.TEACHER, + item: { id: teacher.id, name: `${teacher.last_name} ${teacher.first_name}` }, + collect: (monitor) => ({ + isDragging: !!monitor.isDragging(), + }), + canDrag: () => isDraggable, + }), [isDraggable]); + + return ( +
+ {teacher.last_name} {teacher.first_name} +
+ ); +}; + +export default TeacherItem; \ No newline at end of file diff --git a/Front-End/src/components/Structure/Configuration/TeachersSection.js b/Front-End/src/components/Structure/Configuration/TeachersSection.js index d6d7d0a..0799e50 100644 --- a/Front-End/src/components/Structure/Configuration/TeachersSection.js +++ b/Front-End/src/components/Structure/Configuration/TeachersSection.js @@ -9,6 +9,7 @@ import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import InputText from '@/components/InputText'; import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem'; +import TeacherItem from './TeacherItem'; const ItemTypes = { SPECIALITY: 'speciality', @@ -149,11 +150,11 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha }; const handleUpdateTeacher = (id, updatedData) => { - console.log('UpdatedData:', updatedData); + console.log(updatedData) const data = { email: updatedData.email, username: updatedData.email, - droit: updatedData.droit.id, + droit: updatedData.droit, }; updateProfile(updatedData.associated_profile, data, csrfToken) .then(response => { @@ -171,9 +172,15 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha }; const handleChange = (e) => { - const { name, value } = e.target; + const { name, value, type, checked } = e.target; let parsedValue = value; + if (type === 'checkbox') { + parsedValue = checked ? 1 : 0; + } + + console.log(`handleChange - name: ${name}, parsedValue: ${parsedValue}, type: ${type}, checked: ${checked}`); + if (editingTeacher) { setFormData((prevData) => ({ ...prevData, @@ -216,25 +223,26 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha if (isEditing || isCreating) { switch (column) { - case 'NOM': + case 'NOM - PRENOM': return ( - - ); - case 'PRENOM': - return ( - +
+ + +
); case 'EMAIL': return ( @@ -250,14 +258,16 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha return ( ); - // case 'PROFIL': - // return ( - // - // ); + case 'ADMINISTRATEUR': + return ( +
+ +
+ ); case 'ACTIONS': return (
@@ -282,10 +292,10 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha } } else { switch (column) { - case 'NOM': - return teacher.last_name; - case 'PRENOM': - return teacher.first_name; + case 'NOM - PRENOM': + return ( + + ); case 'EMAIL': return teacher.email; case 'SPECIALITES': @@ -296,13 +306,14 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha ))}
); - case 'PROFIL': + case 'ADMINISTRATEUR': if (teacher.associated_profile) { - const badgeClass = teacher.droit.label === 'ECOLE' ? 'bg-blue-100 text-blue-600' : 'bg-red-100 text-red-600'; + const badgeClass = teacher.droit === 1 ? 'bg-red-100 text-red-600' : 'bg-blue-100 text-blue-600'; + const label = teacher.droit === 1 ? 'OUI' : 'NON'; return (
- {teacher.droit.label} + {label}
); @@ -337,11 +348,10 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha }; const columns = [ - { name: 'NOM', label: 'Nom' }, - { name: 'PRENOM', label: 'Prénom' }, + { name: 'NOM - PRENOM', label: 'Nom et prénom' }, { name: 'EMAIL', label: 'Email' }, { name: 'SPECIALITES', label: 'Spécialités' }, - { name: 'PROFIL', label: 'Profil' }, + { name: 'ADMINISTRATEUR', label: 'Profil' }, { name: 'MISE A JOUR', label: 'Mise à jour' }, { name: 'ACTIONS', label: 'Actions' } ]; diff --git a/Front-End/src/components/ToggleSwitch.js b/Front-End/src/components/ToggleSwitch.js index 29fbfaa..262bd65 100644 --- a/Front-End/src/components/ToggleSwitch.js +++ b/Front-End/src/components/ToggleSwitch.js @@ -1,6 +1,6 @@ import { useRef } from 'react'; -const ToggleSwitch = ({ label, checked, onChange }) => { +const ToggleSwitch = ({ name, label, checked, onChange }) => { const inputRef = useRef(null); const handleChange = (e) => { @@ -16,15 +16,15 @@ const ToggleSwitch = ({ label, checked, onChange }) => {