Files
n3wt-school/Front-End/src/components/Structure/Configuration/TeachersSection.js
2025-02-17 16:11:15 +01:00

444 lines
15 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { Plus, Edit3, Trash2, GraduationCap, Check, X, Hand } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import ToggleSwitch from '@/components/ToggleSwitch';
import { createProfile, updateProfile } from '@/app/lib/authAction';
import { useCsrfToken } from '@/context/CsrfContext';
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',
};
const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities, isEditing }) => {
const [localSpecialities, setLocalSpecialities] = useState(teacher.specialities_details || []);
useEffect(() => {
}, [specialities]);
useEffect(() => {
setLocalSpecialities(teacher.specialities_details || []);
}, [teacher.specialities_details]);
useEffect(() => {
handleSpecialitiesChange(localSpecialities.map(speciality => speciality.id));
}, [localSpecialities]);
const [{ isOver, canDrop }, drop] = useDrop({
accept: ItemTypes.SPECIALITY,
drop: (item) => {
const specialityDetails = specialities.find(speciality => speciality.id === item.id);
const exists = localSpecialities.some(speciality => speciality.id === item.id);
if (!exists) {
setLocalSpecialities(prevSpecialities => {
const updatedSpecialities = [
...prevSpecialities,
{ id: item.id, name: specialityDetails.name, color_code: specialityDetails.color_code }
];
return updatedSpecialities;
});
}
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
canDrop: () => {
return isEditing;
},
});
const handleRemoveSpeciality = (id) => {
setLocalSpecialities(prevSpecialities => {
const updatedSpecialities = prevSpecialities.filter(speciality => speciality.id !== id);
return updatedSpecialities;
});
};
return (
<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) => (
<div key={`${speciality.id}-${index}`} className="flex items-center space-x-2 mb-2">
<SpecialityItem key={speciality.id} speciality={speciality} isDraggable={false}/>
{isEditing && (
<button
type="button"
onClick={() => handleRemoveSpeciality(speciality.id)}
className="text-red-500 hover:text-red-700"
>
<X className="w-4 h-4" />
</button>
)}
</div>
))}
</div>
);
};
const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, handleEdit, handleDelete }) => {
const csrfToken = useCsrfToken();
const [editingTeacher, setEditingTeacher] = useState(null);
const [newTeacher, setNewTeacher] = useState(null);
const [formData, setFormData] = useState({});
const [localErrors, setLocalErrors] = useState({});
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
// Récupération des messages d'erreur
const getError = (field) => {
return localErrors?.[field]?.[0];
};
const handleAddTeacher = () => {
setNewTeacher({ id: Date.now(), last_name: '', first_name: '', email: '', specialities: [], droit: 0 });
setFormData({ last_name: '', first_name: '', email: '', specialities: [], droit: 0 });
};
const handleRemoveTeacher = (id) => {
return handleDelete(id)
.then(() => {
setTeachers(prevTeachers => prevTeachers.filter(teacher => teacher.id !== id));
})
.catch(error => {
console.error(error);
});
};
const handleSaveNewTeacher = () => {
if (formData.last_name && formData.first_name && formData.email) {
const data = {
email: formData.email,
password: 'Provisoire01!',
username: formData.email,
is_active: 1,
droit: formData.droit,
};
createProfile(data, csrfToken)
.then(response => {
console.log('Success:', response);
if (response.id) {
let idProfil = response.id;
newTeacher.associated_profile = idProfil;
handleCreate(newTeacher)
.then((createdTeacher) => {
setTeachers([createdTeacher, ...teachers]);
setNewTeacher(null);
setLocalErrors({});
})
.catch((error) => {
console.error('Error:', error.message);
if (error.details) {
console.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
}
setLocalErrors({});
})
.catch((error) => {
console.error('Error:', error.message);
if (error.details) {
console.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
} else {
setPopupMessage("Tous les champs doivent être remplis et valides");
setPopupVisible(true);
}
};
const handleUpdateTeacher = (id, updatedData) => {
if (updatedData.last_name && updatedData.first_name && updatedData.email) {
const data = {
email: updatedData.email,
username: updatedData.email,
droit: updatedData.droit,
};
updateProfile(updatedData.associated_profile, data, csrfToken)
.then(response => {
console.log('Success:', response);
handleEdit(id, updatedData)
.then((updatedTeacher) => {
setTeachers(prevTeachers => prevTeachers.map(teacher => teacher.id === id ? { ...teacher, ...updatedTeacher } : teacher));
setEditingTeacher(null);
setFormData({});
})
.catch((error) => {
console.error('Error:', error.message);
if (error.details) {
console.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
})
.catch((error) => {
console.error('Error:', error.message);
if (error.details) {
console.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
}
else {
setPopupMessage("Tous les champs doivent être remplis et valides");
setPopupVisible(true);
}
};
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
let parsedValue = value;
if (type === 'checkbox') {
parsedValue = checked ? 1 : 0;
}
if (editingTeacher) {
setFormData((prevData) => ({
...prevData,
[name]: parsedValue,
}));
} else if (newTeacher) {
setNewTeacher((prevData) => ({
...prevData,
[name]: parsedValue,
}));
setFormData((prevData) => ({
...prevData,
[name]: parsedValue,
}));
}
};
const handleSpecialitiesChange = (selectedSpecialities) => {
if (editingTeacher) {
setFormData((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
} else if (newTeacher) {
setNewTeacher((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
setFormData((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
}
};
const renderTeacherCell = (teacher, column) => {
const isEditing = editingTeacher === teacher.id;
const isCreating = newTeacher && newTeacher.id === teacher.id;
const currentData = isEditing ? formData : newTeacher;
if (isEditing || isCreating) {
switch (column) {
case 'NOM - PRENOM':
return (
<div className="flex justify-center space-x-2">
<InputText
name="last_name"
label="nom"
value={currentData.last_name}
onChange={handleChange}
placeholder="Nom de l'enseignant"
errorMsg={getError('last_name')}
/>
<InputText
name="first_name"
label="prénom"
value={currentData.first_name}
onChange={handleChange}
placeholder="Prénom de l'enseignant"
errorMsg={getError('first_name')}
/>
</div>
);
case 'EMAIL':
return (
<InputText
name="email"
value={currentData.email}
onChange={handleChange}
placeholder="Email de l'enseignant"
errorMsg={getError('email')}
/>
);
case 'SPECIALITES':
return (
<SpecialitiesDropZone teacher={currentData} handleSpecialitiesChange={handleSpecialitiesChange} specialities={specialities} isEditing={isEditing || isCreating} />
);
case 'ADMINISTRATEUR':
return (
<div className="flex justify-center">
<ToggleSwitch
name="droit"
checked={currentData.droit === 1}
onChange={handleChange}
/>
</div>
);
case 'ACTIONS':
return (
<div className="flex justify-center space-x-2">
<button
type="button"
onClick={() => (isEditing ? handleUpdateTeacher(editingTeacher, formData) : handleSaveNewTeacher())}
className="text-green-500 hover:text-green-700"
>
<Check className="w-5 h-5" />
</button>
<button
type="button"
onClick={() => (isEditing ? setEditingTeacher(null) : setNewTeacher(null))}
className="text-red-500 hover:text-red-700"
>
<X className="w-5 h-5" />
</button>
</div>
);
default:
return null;
}
} else {
switch (column) {
case 'NOM - PRENOM':
return (
<TeacherItem key={teacher.id} teacher={teacher} />
);
case 'EMAIL':
return teacher.email;
case 'SPECIALITES':
return (
<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 'ADMINISTRATEUR':
if (teacher.associated_profile) {
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 (
<div key={teacher.id} className="flex justify-center items-center space-x-2">
<span className={`px-3 py-1 rounded-full font-bold ${badgeClass}`}>
{label}
</span>
</div>
);
} else {
return <i>Non définie</i>;
};
case 'MISE A JOUR':
return teacher.updated_date_formatted;
case 'ACTIONS':
return (
<div className="flex justify-center space-x-2">
<button
type="button"
onClick={() => setEditingTeacher(teacher.id) || setFormData(teacher)}
className="text-blue-500 hover:text-blue-700"
>
<Edit3 className="w-5 h-5" />
</button>
<button
type="button"
onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer l'enseignant " + teacher.last_name + " " + teacher.first_name + ".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleRemoveTeacher(teacher.id)
.then(data => {
console.log('Success:', data);
setPopupMessage("L'enseignant " + teacher.last_name + " " + teacher.first_name + " a été correctement supprimé");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression de l'enseignant " + teacher.last_name + " " + teacher.first_name);
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700"
>
<Trash2 className="w-5 h-5" />
</button>
</div>
);
default:
return null;
}
}
};
const columns = [
{ name: 'NOM - PRENOM', label: 'Nom et prénom' },
{ name: 'EMAIL', label: 'Email' },
{ name: 'SPECIALITES', label: 'Spécialités' },
{ name: 'ADMINISTRATEUR', label: 'Profil' },
{ name: 'MISE A JOUR', label: '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">
<GraduationCap className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">Enseignants</h2>
</div>
<button type="button" onClick={handleAddTeacher} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" />
</button>
</div>
<Table
data={newTeacher ? [newTeacher, ...teachers] : teachers}
columns={columns}
renderCell={renderTeacherCell}
/>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true}
/>
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div>
</DndProvider>
);
};
export default TeachersSection;