mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-04 04:01:27 +00:00
refactor: Refactoring de la section ClassSection
This commit is contained in:
@ -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 (
|
||||
<div className="mb-8">
|
||||
<div className="flex justify-between items-center mb-4 max-w-8xl ml-0">
|
||||
<h2 className="text-xl font-bold mb-4">Gestion des classes</h2>
|
||||
<button
|
||||
onClick={() => openEditModal(null)} // ouvrir le modal pour créer une nouvelle spécialité
|
||||
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg border border-gray-200 max-w-8xl ml-0">
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
name: 'AMBIANCE',
|
||||
transform: (row) => {
|
||||
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 (
|
||||
<div className="flex flex-wrap justify-center items-center space-x-2">
|
||||
{levelLabels.length > 0
|
||||
? levelLabels.map((label, index) => (
|
||||
<LevelLabel key={index} label={label} index={index} />
|
||||
))
|
||||
: 'Aucun niveau'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{ name: 'CAPACITÉ MAX', transform: (row) => row.number_of_students },
|
||||
{ name: 'ANNÉE SCOLAIRE', transform: (row) => row.school_year },
|
||||
{
|
||||
name: 'ENSEIGNANTS',
|
||||
transform: (row) => (
|
||||
<div key={row.id} className="flex flex-wrap justify-center items-center space-x-2">
|
||||
{row.teachers_details.map((teacher, index) => (
|
||||
<TeacherLabel key={`${teacher.id}-${index}`} nom={teacher.last_name} prenom={teacher.first_name} index={index} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{ name: 'DATE DE CREATION', transform: (row) => row.updated_date_formatted },
|
||||
{
|
||||
name: 'ACTIONS', transform: (row) => (
|
||||
<DropdownMenu
|
||||
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<ClasseFormProvider initialClasse={editingClass || {}}>
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
setIsOpen={setIsOpen}
|
||||
title={editingClass ? "Modification de la classe" : "Création d'une nouvelle classe"}
|
||||
size='sm:w-1/2'
|
||||
ContentComponent={() => (
|
||||
<ClassForm classe={editingClass || {}} onSubmit={handleModalSubmit} isNew={!editingClass} teachers={teachers} />
|
||||
)}
|
||||
/>
|
||||
</ClasseFormProvider>
|
||||
)}
|
||||
|
||||
{isOpenDetails && (
|
||||
<Modal
|
||||
isOpen={isOpenDetails}
|
||||
setIsOpen={setIsOpenDetails}
|
||||
title={(
|
||||
<div className="flex items-center">
|
||||
<Users className="w-8 h-8 mr-2" />
|
||||
{editingClass ? (
|
||||
<>
|
||||
{editingClass.atmosphere_name} - {editingClass.age_range[0]} à {editingClass.age_range[1]} ans
|
||||
</>
|
||||
) : ''}
|
||||
</div>
|
||||
)}
|
||||
ContentComponent={() => (
|
||||
<ClasseDetails classe={editingClass} />
|
||||
)}
|
||||
/>
|
||||
<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 un enseignant ici</span>
|
||||
</div>
|
||||
)}
|
||||
{localTeachers.map((teacher, index) => (
|
||||
<div key={`${teacher.id}-${index}`} className="flex items-center space-x-2 mb-2">
|
||||
<TeacherItem key={teacher.id} teacher={teacher} isDraggable={false}/>
|
||||
{isEditing && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveTeacher(teacher.id)}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<InputText
|
||||
name="atmosphere_name"
|
||||
value={currentData.atmosphere_name}
|
||||
onChange={handleChange}
|
||||
placeholder="Nom d'ambiance"
|
||||
errorMsg={localErrors && localErrors.atmosphere_name && Array.isArray(localErrors.atmosphere_name) ? localErrors.atmosphere_name[0] : ''}
|
||||
/>
|
||||
);
|
||||
case 'TRANCHE D\'AGE':
|
||||
return (
|
||||
<InputText
|
||||
name="age_range"
|
||||
value={currentData.age_range}
|
||||
onChange={handleChange}
|
||||
placeholder="Tranche d'âge (ex: 3-6)"
|
||||
errorMsg={localErrors && localErrors.age_range && Array.isArray(localErrors.age_range) ? localErrors.age_range[0] : ''}
|
||||
/>
|
||||
)
|
||||
case 'NIVEAUX':
|
||||
return (
|
||||
<MultiSelect
|
||||
name="levels"
|
||||
options={allNiveaux}
|
||||
selectedOptions={currentData.levels ? currentData.levels.map(levelId => allNiveaux.find(level => level.id === levelId)) : []}
|
||||
onChange={handleMultiSelectChange}
|
||||
errorMsg={localErrors && localErrors.levels && Array.isArray(localErrors.levels) ? localErrors.levels[0] : ''}
|
||||
/>
|
||||
);
|
||||
case 'CAPACITE':
|
||||
return (
|
||||
<InputText
|
||||
name="number_of_students"
|
||||
type="number"
|
||||
value={currentData.number_of_students}
|
||||
onChange={handleChange}
|
||||
placeholder="Capacité"
|
||||
errorMsg={localErrors && localErrors.number_of_students && Array.isArray(localErrors.number_of_students) ? localErrors.number_of_students[0] : ''}
|
||||
/>
|
||||
)
|
||||
case 'ANNÉE SCOLAIRE' :
|
||||
return (
|
||||
<SelectChoice
|
||||
type="select"
|
||||
name="school_year"
|
||||
placeHolder="Sélectionnez une année scolaire"
|
||||
choices={getSchoolYearChoices()}
|
||||
callback={handleChange}
|
||||
selected={currentData.school_year || ''}
|
||||
errorMsg={localErrors && localErrors.school_year && Array.isArray(localErrors.school_year) ? localErrors.school_year[0] : ''}
|
||||
IconItem={null}
|
||||
disabled={false}
|
||||
/>
|
||||
)
|
||||
case 'ENSEIGNANTS':
|
||||
return (
|
||||
<TeachersDropZone classe={currentData} handleTeachersChange={handleTeachersChange} teachers={teachers} isEditing={isEditing || isCreating} />
|
||||
);
|
||||
case 'ACTIONS':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (isEditing ? handleUpdateClass(editingClass, formData) : handleSaveNewClass())}
|
||||
className="text-green-500 hover:text-green-700"
|
||||
>
|
||||
<Check className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (isEditing ? setEditingClass(null) : setNewClass(null))}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<div className="flex flex-wrap justify-center items-center space-x-2">
|
||||
{levelLabels.length > 0
|
||||
? levelLabels.map((label, index) => (
|
||||
<LevelLabel key={index} label={label} index={index} />
|
||||
))
|
||||
: 'Aucun niveau'}
|
||||
</div>
|
||||
);
|
||||
case 'CAPACITE':
|
||||
return classe.number_of_students;
|
||||
case 'ANNÉE SCOLAIRE' :
|
||||
return classe.school_year;
|
||||
case 'ENSEIGNANTS':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2 flex-wrap">
|
||||
{classe.teachers_details.map((teacher) => (
|
||||
<TeacherItem key={teacher.id} teacher={teacher} isDraggable={false} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
case 'MISE A JOUR':
|
||||
return classe.updated_date_formatted;
|
||||
case 'ACTIONS':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditingClass(classe.id) || setFormData(classe)}
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
>
|
||||
<Edit3 className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(classe.id)}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openEditModalDetails(classe)}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<ZoomIn className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center mb-4">
|
||||
<Users className="w-6 h-6 text-emerald-500 mr-2" />
|
||||
<h2 className="text-xl font-semibold">Classes</h2>
|
||||
</div>
|
||||
<button type="button" onClick={handleAddClass} className="text-emerald-500 hover:text-emerald-700">
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<Table
|
||||
data={newClass ? [newClass, ...classes] : classes}
|
||||
columns={columns}
|
||||
renderCell={renderClassCell}
|
||||
/>
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
message={popupMessage}
|
||||
onConfirm={() => setPopupVisible(false)}
|
||||
onCancel={() => setPopupVisible(false)}
|
||||
uniqueConfirmButton={true}
|
||||
/>
|
||||
</div>
|
||||
</DndProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClassesSection;
|
||||
Reference in New Issue
Block a user