mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
refactor: SpecialitySection + TeacherSection (en cours)
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
export default function InputText({name, type, label, value, onChange, errorMsg, placeholder, className, required}) {
|
export default function InputText({name, type, label, value, onChange, errorMsg, placeholder, className, required}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`mb-4 ${className}`}>
|
<div className={`${className}`}>
|
||||||
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
|
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
|
||||||
{label}
|
{label}
|
||||||
{required && <span className="text-red-500 ml-1">*</span>}
|
{required && <span className="text-red-500 ml-1">*</span>}
|
||||||
|
|||||||
@ -100,7 +100,7 @@ const ClassesSection = ({ classes, teachers, handleCreate, handleEdit, handleDel
|
|||||||
transform: (row) => (
|
transform: (row) => (
|
||||||
<div key={row.id} className="flex flex-wrap justify-center items-center space-x-2">
|
<div key={row.id} className="flex flex-wrap justify-center items-center space-x-2">
|
||||||
{row.teachers_details.map((teacher, index) => (
|
{row.teachers_details.map((teacher, index) => (
|
||||||
<TeacherLabel key={teacher.id} nom={teacher.last_name} prenom={teacher.first_name} index={index} />
|
<TeacherLabel key={`${teacher.id}-${index}`} nom={teacher.last_name} prenom={teacher.first_name} index={index} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,33 +3,9 @@ import { useState } from 'react';
|
|||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Popup from '@/components/Popup';
|
import Popup from '@/components/Popup';
|
||||||
import InputTextWithColorIcon from '@/components/InputTextWithColorIcon';
|
import InputTextWithColorIcon from '@/components/InputTextWithColorIcon';
|
||||||
import { DndProvider, useDrag } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
|
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
|
||||||
const ItemTypes = {
|
|
||||||
SPECIALITY: 'speciality',
|
|
||||||
};
|
|
||||||
|
|
||||||
const SpecialityItem = ({ speciality }) => {
|
|
||||||
const [{ isDragging }, drag] = useDrag(() => ({
|
|
||||||
type: ItemTypes.SPECIALITY,
|
|
||||||
item: { id: speciality.id, name: speciality.name },
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isDragging: !!monitor.isDragging(),
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={drag}
|
|
||||||
className="inline-block px-3 py-1 rounded-full font-bold text-white text-center"
|
|
||||||
style={{ backgroundColor: speciality.color_code, opacity: isDragging ? 0.5 : 1 }}
|
|
||||||
title={speciality.name}
|
|
||||||
>
|
|
||||||
{speciality.name}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, handleEdit, handleDelete }) => {
|
const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, handleEdit, handleDelete }) => {
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { useDrag } from 'react-dnd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ItemTypes = {
|
||||||
|
SPECIALITY: 'speciality',
|
||||||
|
};
|
||||||
|
|
||||||
|
const SpecialityItem = ({ speciality, isDraggable = true }) => {
|
||||||
|
const [{ isDragging }, drag] = useDrag(() => ({
|
||||||
|
type: ItemTypes.SPECIALITY,
|
||||||
|
item: { id: speciality.id, name: speciality.name },
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: !!monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
canDrag: () => isDraggable,
|
||||||
|
}), [isDraggable]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={isDraggable ? drag : null}
|
||||||
|
className={`inline-block px-3 py-1 rounded-lg font-bold text-white text-center ${isDraggable ? 'cursor-pointer' : ''} transition-transform duration-200 ease-in-out ${
|
||||||
|
isDragging ? 'opacity-30' : 'opacity-100'
|
||||||
|
} ${isDraggable ? 'hover:shadow-lg hover:scale-125' : ''}`}
|
||||||
|
style={{ backgroundColor: speciality.color_code }}
|
||||||
|
>
|
||||||
|
{speciality.name}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpecialityItem;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import SpecialitiesSection from '@/components/Structure/Configuration/SpecialitiesSection';
|
import SpecialitiesSection from '@/components/Structure/Configuration/SpecialitiesSection';
|
||||||
import TeachersSection from '@/components/Structure/Configuration/TeachersSection';
|
import TeachersSection from '@/components/Structure/Configuration/TeachersSection';
|
||||||
import ClassesSection from '@/components/Structure/Configuration/ClassesSection';
|
import ClassesSection from '@/components/Structure/Configuration/ClassesSection';
|
||||||
@ -9,7 +9,7 @@ const StructureManagement = ({ specialities, setSpecialities, teachers, setTeach
|
|||||||
return (
|
return (
|
||||||
<div className="max-w-8xl mx-auto p-4 mt-6 space-y-6">
|
<div className="max-w-8xl mx-auto p-4 mt-6 space-y-6">
|
||||||
<ClassesProvider>
|
<ClassesProvider>
|
||||||
<div className="max-w-4xl p-4 bg-white rounded-lg shadow-md">
|
<div className="w-2/5 p-4 bg-white rounded-lg shadow-md">
|
||||||
<SpecialitiesSection
|
<SpecialitiesSection
|
||||||
specialities={specialities}
|
specialities={specialities}
|
||||||
setSpecialities={setSpecialities}
|
setSpecialities={setSpecialities}
|
||||||
@ -18,7 +18,7 @@ const StructureManagement = ({ specialities, setSpecialities, teachers, setTeach
|
|||||||
handleDelete={(id) => handleDelete(`${BE_SCHOOL_SPECIALITY_URL}`, id, setSpecialities)}
|
handleDelete={(id) => handleDelete(`${BE_SCHOOL_SPECIALITY_URL}`, id, setSpecialities)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-8xl p-4 bg-white rounded-lg shadow-md">
|
<div className="w-4/5 p-4 bg-white rounded-lg shadow-md">
|
||||||
<TeachersSection
|
<TeachersSection
|
||||||
teachers={teachers}
|
teachers={teachers}
|
||||||
setTeachers={setTeachers}
|
setTeachers={setTeachers}
|
||||||
@ -28,7 +28,7 @@ const StructureManagement = ({ specialities, setSpecialities, teachers, setTeach
|
|||||||
handleDelete={(id) => handleDelete(`${BE_SCHOOL_TEACHER_URL}`, id, setTeachers)}
|
handleDelete={(id) => handleDelete(`${BE_SCHOOL_TEACHER_URL}`, id, setTeachers)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 bg-white rounded-lg shadow-md">
|
<div className="w-4/5 p-4 bg-white rounded-lg shadow-md">
|
||||||
<ClassesSection
|
<ClassesSection
|
||||||
classes={classes}
|
classes={classes}
|
||||||
teachers={teachers}
|
teachers={teachers}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Plus, Edit3, Trash2, GraduationCap, Check, X } from 'lucide-react';
|
import { Plus, Edit3, Trash2, GraduationCap, Check, X, Hand } from 'lucide-react';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Popup from '@/components/Popup';
|
import Popup from '@/components/Popup';
|
||||||
import InputTextIcon from '@/components/InputTextIcon';
|
|
||||||
import ToggleSwitch from '@/components/ToggleSwitch';
|
import ToggleSwitch from '@/components/ToggleSwitch';
|
||||||
import { createProfile, updateProfile } from '@/app/lib/authAction';
|
import { createProfile, updateProfile } from '@/app/lib/authAction';
|
||||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import InputText from '@/components/InputText';
|
import InputText from '@/components/InputText';
|
||||||
|
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
|
||||||
|
|
||||||
const ItemTypes = {
|
const ItemTypes = {
|
||||||
SPECIALITY: 'speciality',
|
SPECIALITY: 'speciality',
|
||||||
@ -17,6 +17,9 @@ const ItemTypes = {
|
|||||||
const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities, isEditing }) => {
|
const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities, isEditing }) => {
|
||||||
const [localSpecialities, setLocalSpecialities] = useState(teacher.specialities_details || []);
|
const [localSpecialities, setLocalSpecialities] = useState(teacher.specialities_details || []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, [specialities]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalSpecialities(teacher.specialities_details || []);
|
setLocalSpecialities(teacher.specialities_details || []);
|
||||||
}, [teacher.specialities_details]);
|
}, [teacher.specialities_details]);
|
||||||
@ -25,21 +28,29 @@ const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities,
|
|||||||
handleSpecialitiesChange(localSpecialities.map(speciality => speciality.id));
|
handleSpecialitiesChange(localSpecialities.map(speciality => speciality.id));
|
||||||
}, [localSpecialities]);
|
}, [localSpecialities]);
|
||||||
|
|
||||||
const [{ isOver }, drop] = useDrop(() => ({
|
const [{ isOver, canDrop }, drop] = useDrop({
|
||||||
accept: ItemTypes.SPECIALITY,
|
accept: ItemTypes.SPECIALITY,
|
||||||
drop: (item) => {
|
drop: (item) => {
|
||||||
const specialityDetails = specialities.find(speciality => speciality.id === item.id);
|
const specialityDetails = specialities.find(speciality => speciality.id === item.id);
|
||||||
if (!localSpecialities.some(speciality => speciality.id === item.id)) {
|
const exists = localSpecialities.some(speciality => speciality.id === item.id);
|
||||||
setLocalSpecialities(prevSpecialities => [
|
if (!exists) {
|
||||||
...prevSpecialities,
|
setLocalSpecialities(prevSpecialities => {
|
||||||
{ id: item.id, name: specialityDetails.name, color_code: specialityDetails.color_code }
|
const updatedSpecialities = [
|
||||||
]);
|
...prevSpecialities,
|
||||||
|
{ id: item.id, name: specialityDetails.name, color_code: specialityDetails.color_code }
|
||||||
|
];
|
||||||
|
return updatedSpecialities;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
collect: (monitor) => ({
|
collect: (monitor) => ({
|
||||||
isOver: !!monitor.isOver(),
|
isOver: !!monitor.isOver(),
|
||||||
|
canDrop: !!monitor.canDrop(),
|
||||||
}),
|
}),
|
||||||
}));
|
canDrop: () => {
|
||||||
|
return isEditing;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleRemoveSpeciality = (id) => {
|
const handleRemoveSpeciality = (id) => {
|
||||||
setLocalSpecialities(prevSpecialities => {
|
setLocalSpecialities(prevSpecialities => {
|
||||||
@ -49,16 +60,19 @@ const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities,
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={drop} className="p-2 rounded-md flex flex-col items-center">
|
<div ref={drop} className={`p-2 rounded-md flex flex-col items-center ${isEditing ? 'border-2 border-dashed border-blue-500 bg-blue-50' : ''} ${
|
||||||
|
isOver && canDrop ? 'border-2 border-solid border-blue-300' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isEditing && (
|
||||||
|
<div className="mb-2 text-blue-500 font-semibold flex items-center space-x-2">
|
||||||
|
<Hand className="w-5 h-5" /> {/* Ajoutez l'icône Hand */}
|
||||||
|
<span>Déposez une spécialité ici</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{localSpecialities.map((speciality, index) => (
|
{localSpecialities.map((speciality, index) => (
|
||||||
<div key={`${speciality.id}-${index}`} className="flex items-center space-x-2 mb-2">
|
<div key={`${speciality.id}-${index}`} className="flex items-center space-x-2 mb-2">
|
||||||
<span
|
<SpecialityItem key={speciality.id} speciality={speciality} isDraggable={false}/>
|
||||||
className="px-3 py-1 rounded-full font-bold text-white"
|
|
||||||
style={{ backgroundColor: speciality.color_code }}
|
|
||||||
title={speciality.name}
|
|
||||||
>
|
|
||||||
{speciality.name}
|
|
||||||
</span>
|
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -276,7 +290,11 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
|
|||||||
return teacher.email;
|
return teacher.email;
|
||||||
case 'SPECIALITES':
|
case 'SPECIALITES':
|
||||||
return (
|
return (
|
||||||
<SpecialitiesDropZone teacher={teacher} handleSpecialitiesChange={handleSpecialitiesChange} specialities={specialities} isEditing={false} />
|
<div className="flex justify-center space-x-2 flex-wrap">
|
||||||
|
{teacher.specialities_details.map((speciality) => (
|
||||||
|
<SpecialityItem key={speciality.id} speciality={speciality} isDraggable={false} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
case 'PROFIL':
|
case 'PROFIL':
|
||||||
if (teacher.associated_profile) {
|
if (teacher.associated_profile) {
|
||||||
|
|||||||
Reference in New Issue
Block a user