feat: Gestion des profils ADMIN/ECOLE (création des enseignants)

This commit is contained in:
N3WT DE COMPET
2025-03-22 17:20:49 +01:00
parent c9350a796b
commit e0bfd3e115
9 changed files with 249 additions and 134 deletions

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { User, Mail, Phone, UserCheck, DollarSign, Percent } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon';
import ToggleSwitch from '@/components/ToggleSwitch';
@ -51,6 +51,8 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const formDataRef = useRef(formData);
const stepTitles = {
1: 'Nouvel élève',
2: 'Nouveau Responsable',
@ -103,6 +105,10 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}, [registrationDiscounts, registrationFees]);
useEffect(() => {
formDataRef.current = formData; // Mettre à jour la référence à chaque changement de formData
}, [formData]);
useEffect(() => {
setStep(currentStep || 1);
}, [currentStep]);
@ -119,24 +125,39 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}));
};
const nextStep = () => {
if (step === 2) {
// Vérifier si l'adresse email saisie est rattachée à un profil existant
const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail);
if (existingProfile) {
// Vérifier si le profil a un rôle de type PARENT
const parentRole = existingProfile.roles.find(role => role.role_type === 2);
console.log('Profil associé trouvé !', existingProfile);
setFormData((prevData) => ({
...prevData,
guardianEmail: existingProfile.email, // Mettre à jour le champ guardianEmail avec l'email du profil
isExistingParentProfile: true, // Indiquer que le profil est un parent existant
existingProfileId: existingProfile.id
}));
}
const validateAndSubmit = async () => {
const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail);
if (existingProfile) {
console.log('existingProfile : ', existingProfile);
await setFormData((prevData) => ({
...prevData,
guardianEmail: existingProfile.email, // Mettre à jour le champ guardianEmail avec l'email du profil
isExistingParentProfile: true, // Indiquer que le profil est un parent existant
existingProfileId: existingProfile.id,
}));
}
// Utiliser la dernière version de formData via formDataRef
logger.debug('Submitting form data:', formDataRef.current);
onSubmit(formDataRef.current);
};
const nextStep = async () => {
if (step === 2) {
const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail);
if (existingProfile) {
console.log('existingProfile : ', existingProfile);
await setFormData((prevData) => ({
...prevData,
guardianEmail: existingProfile.email, // Mettre à jour le champ guardianEmail avec l'email du profil
isExistingParentProfile: true, // Indiquer que le profil est un parent existant
existingProfileId: existingProfile.id,
}));
}
}
if (!showOnlyStep2 && step < steps.length) {
setStep(step + 1);
}
@ -182,9 +203,11 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
};
const submit = () => {
setTimeout(() => {
logger.debug('Submitting form data:', formData);
onSubmit(formData);
}
}, 0);
};
const handleRegistrationFeeSelection = (feeId) => {
setFormData((prevData) => {
@ -683,7 +706,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
<>
<Button
text="Valider"
onClick={submit}
onClick={validateAndSubmit}
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
(
(step === 1 && !isStep1Valid) ||

View File

@ -196,35 +196,44 @@ const ProfileDirectory = ({ profileRoles, handleActivateProfile, handleDeletePro
)
},
{ name: 'Utilisateur', transform: (row) => (
<div className="flex items-center justify-center space-x-2">
<div className="flex items-center justify-center space-x-2 relative">
<span>{row.associated_person?.teacher_name}</span>
{row.associated_person && (
<Tooltip content={
<div>
<div className="mb-2">
<strong>Classes associées:</strong>
<div className="flex flex-wrap justify-center space-x-2">
{row.associated_person?.classes?.map(classe => (
<span key={classe.id} className="px-2 py-1 rounded-full bg-gray-200 text-gray-800">
{classe.name}
</span>
))}
</div>
<div
className="relative group"
onMouseEnter={() => handleTooltipVisibility(row.id)} // Afficher la tooltip pour cette ligne
onMouseLeave={handleTooltipHide} // Cacher la tooltip
>
<button className="relative text-blue-500 hover:text-blue-700 flex items-center justify-center">
<div className="w-6 h-6 bg-blue-100 text-blue-700 rounded-full flex items-center justify-center font-bold">
<Info className="w-4 h-4" /> {/* Icône Info */}
</div>
<div>
<strong>Spécialités:</strong>
<div className="flex flex-wrap justify-center space-x-2">
{row.associated_person?.specialities?.map(speciality => (
<SpecialityItem key={speciality.name} speciality={speciality} isDraggable={false} />
))}
</div>
</div>
</div>
}>
<button className="text-blue-500 hover:text-blue-700">
<Info className="w-5 h-5" />
</button>
</Tooltip>
{visibleTooltipId === row.id && ( // Afficher uniquement si l'ID correspond
<div
className="fixed z-50 w-96 p-4 bg-white border border-gray-200 rounded shadow-lg -translate-x-1/2"
>
<div className="mb-2">
<strong>Classes associées:</strong>
<div className="flex flex-wrap justify-center space-x-2 mt-4">
{row.associated_person?.classes?.map(classe => (
<span key={classe.id} className="px-2 py-1 rounded-full bg-gray-200 text-gray-800">
{classe.name}
</span>
))}
</div>
</div>
<div>
<strong>Spécialités:</strong>
<div className="flex flex-wrap justify-center space-x-2 mt-4">
{row.associated_person?.specialities?.map(speciality => (
<SpecialityItem key={speciality.name} speciality={speciality} isDraggable={false} />
))}
</div>
</div>
</div>
)}
</div>
)}
</div>
)

View File

@ -5,7 +5,7 @@ import ClassesSection from '@/components/Structure/Configuration/ClassesSection'
import { ClassesProvider } from '@/context/ClassesContext';
import { BE_SCHOOL_SPECIALITIES_URL, BE_SCHOOL_TEACHERS_URL, BE_SCHOOL_SCHOOLCLASSES_URL } from '@/utils/Url';
const StructureManagement = ({ specialities, setSpecialities, teachers, setTeachers, classes, setClasses, handleCreate, handleEdit, handleDelete }) => {
const StructureManagement = ({ specialities, setSpecialities, teachers, setTeachers, classes, setClasses, profiles, handleCreate, handleEdit, handleDelete }) => {
return (
<div className="max-w-8xl mx-auto p-4 mt-6 space-y-8">
<ClassesProvider>
@ -23,6 +23,7 @@ const StructureManagement = ({ specialities, setSpecialities, teachers, setTeach
teachers={teachers}
setTeachers={setTeachers}
specialities={specialities}
profiles={profiles}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_TEACHERS_URL}`, newData, setTeachers)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_TEACHERS_URL}`, id, updatedData, setTeachers)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_TEACHERS_URL}`, id, setTeachers)}

View File

@ -92,7 +92,7 @@ const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities,
);
};
const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, handleEdit, handleDelete }) => {
const TeachersSection = ({ teachers, setTeachers, specialities, profiles, handleCreate, handleEdit, handleDelete }) => {
const csrfToken = useCsrfToken();
const [editingTeacher, setEditingTeacher] = useState(null);
const [newTeacher, setNewTeacher] = useState(null);
@ -111,6 +111,27 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
const { selectedEstablishmentId } = useEstablishment();
const handleEmailChange = (e) => {
const email = e.target.value;
// Vérifier si l'email correspond à un profil existant
const existingProfile = profiles.find((profile) => profile.email === email);
setFormData((prevData) => ({
...prevData,
associated_profile_email: email,
existingProfileId: existingProfile ? existingProfile.id : null,
}));
if (newTeacher) {
setNewTeacher((prevData) => ({
...prevData,
associated_profile_email: email,
existingProfileId: existingProfile ? existingProfile.id : null,
}));
}
};
const handleCancelConfirmation = () => {
setConfirmPopupVisible(false);
};
@ -140,12 +161,23 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
const data = {
last_name: formData.last_name,
first_name: formData.first_name,
associated_profile_email: formData.associated_profile_email,
establishment: selectedEstablishmentId,
role_type: formData.role_type,
specialities: formData.specialities
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: formData.role_type || 0,
is_active: true,
...(formData.existingProfileId
? { profile: formData.existingProfileId }
: {
profile_data: {
email: formData.associated_profile_email,
username: formData.associated_profile_email,
password: 'Provisoire01!',
},
}),
},
specialities: formData.specialities || [],
};
handleCreate(data)
.then((createdTeacher) => {
setTeachers([createdTeacher, ...teachers]);
@ -166,30 +198,59 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
};
const handleUpdateTeacher = (id, updatedData) => {
if (updatedData.last_name && updatedData.first_name && updatedData.associated_profile_email_display) {
// Récupérer l'enseignant actuel à partir de la liste des enseignants
const currentTeacher = teachers.find((teacher) => teacher.id === id);
// Vérifier si l'email correspond à un profil existant
const existingProfile = profiles.find((profile) => profile.email === currentTeacher.associated_profile_email);
// Vérifier si l'email a été modifié
const isEmailModified = currentTeacher
? currentTeacher.associated_profile_email !== updatedData.associated_profile_email
: true;
// Mettre à jour existingProfileId en fonction de l'email
updatedData.existingProfileId = existingProfile ? existingProfile.id : null;
if (updatedData.last_name && updatedData.first_name && updatedData.associated_profile_email) {
const data = {
last_name: formData.last_name,
first_name: formData.first_name,
associated_profile_email: formData.associated_profile_email || formData.associated_profile_email_display,
establishment: selectedEstablishmentId,
role_type: formData.role_type,
specialities: formData.specialities
last_name: updatedData.last_name,
first_name: updatedData.first_name,
profile_role_data: {
id: updatedData.profile_role,
establishment: selectedEstablishmentId,
role_type: updatedData.role_type || 0,
is_active: true,
...(isEmailModified
? {
profile_data: {
id: updatedData.existingProfileId,
email: updatedData.associated_profile_email,
username: updatedData.associated_profile_email,
password: 'Provisoire01!',
},
}
: { profile: updatedData.existingProfileId }),
},
specialities: updatedData.specialities || [],
};
handleEdit(id, data)
.then((updatedTeacher) => {
setTeachers(prevTeachers => prevTeachers.map(teacher => teacher.id === id ? { ...teacher, ...updatedTeacher } : teacher));
setEditingTeacher(null);
setFormData({});
})
.catch((error) => {
logger.error('Error:', error.message);
if (error.details) {
.then((updatedTeacher) => {
setTeachers((prevTeachers) =>
prevTeachers.map((teacher) => (teacher.id === id ? { ...teacher, ...updatedTeacher } : teacher))
);
setEditingTeacher(null);
setFormData({});
})
.catch((error) => {
logger.error('Error:', error.message);
if (error.details) {
logger.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
}
else {
}
});
} else {
setPopupMessage("Tous les champs doivent être remplis et valides");
setPopupVisible(true);
}
@ -242,8 +303,8 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
setEditingTeacher(teacher.id);
setFormData({
...teacher,
associated_profile_email: teacher.associated_profile_email_display,
role_type: teacher.role_type_display,
associated_profile_email: teacher.associated_profile_email,
role_type: teacher.role_type,
});
};
@ -278,8 +339,8 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
<InputText
name="associated_profile_email"
type="email"
value={currentData.associated_profile_email || teacher.associated_profile_email_display || ''}
onChange={handleChange}
value={currentData.associated_profile_email || ''}
onChange={handleEmailChange}
placeholder="Adresse email de l'enseignant"
errorMsg={getError('email')}
/>
@ -327,7 +388,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
<TeacherItem key={teacher.id} teacher={teacher} />
);
case 'EMAIL':
return teacher.associated_profile_email_display;
return teacher.associated_profile_email;
case 'SPECIALITES':
return (
<div className="flex justify-center space-x-2 flex-wrap">
@ -337,9 +398,9 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
</div>
);
case 'ADMINISTRATEUR':
if (teacher.associated_profile_email_display) {
const badgeClass = teacher.role_type_display === 1 ? 'bg-red-100 text-red-600' : 'bg-blue-100 text-blue-600';
const label = teacher.role_type_display === 1 ? 'OUI' : 'NON';
if (teacher.associated_profile_email) {
const badgeClass = teacher.role_type === 1 ? 'bg-red-100 text-red-600' : 'bg-blue-100 text-blue-600';
const label = teacher.role_type === 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}`}>