fix: Edition d'un teacher, champ email désactivé [N3WTS-1]

This commit is contained in:
N3WT DE COMPET
2026-02-15 15:47:51 +01:00
parent 92c6a31740
commit 176edc5c45
3 changed files with 105 additions and 113 deletions

View File

@ -60,6 +60,7 @@ class TeacherSerializer(serializers.ModelSerializer):
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False) profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False)
profile_role_data = ProfileRoleSerializer(write_only=True, required=False) profile_role_data = ProfileRoleSerializer(write_only=True, required=False)
associated_profile_email = serializers.SerializerMethodField() associated_profile_email = serializers.SerializerMethodField()
profile = serializers.SerializerMethodField()
class Meta: class Meta:
model = Teacher model = Teacher
@ -155,6 +156,12 @@ class TeacherSerializer(serializers.ModelSerializer):
return obj.profile_role.role_type return obj.profile_role.role_type
return None return None
def get_profile(self, obj):
# Retourne l'id du profile associé via profile_role
if obj.profile_role and obj.profile_role.profile:
return obj.profile_role.profile.id
return None
class PlanningSerializer(serializers.ModelSerializer): class PlanningSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Planning model = Planning

View File

@ -118,11 +118,23 @@ class TeacherDetailView(APIView):
return JsonResponse(teacher_serializer.data, safe=False) return JsonResponse(teacher_serializer.data, safe=False)
def put(self, request, id): def put(self, request, id):
teacher_data=JSONParser().parse(request) teacher_data = JSONParser().parse(request)
teacher = getObject(_objectName=Teacher, _columnName='id', _value=id) teacher = getObject(_objectName=Teacher, _columnName='id', _value=id)
# Récupérer l'ancien profile avant modification
old_profile_role = getattr(teacher, 'profile_role', None)
old_profile = getattr(old_profile_role, 'profile', None) if old_profile_role else None
teacher_serializer = TeacherSerializer(teacher, data=teacher_data) teacher_serializer = TeacherSerializer(teacher, data=teacher_data)
if teacher_serializer.is_valid(): if teacher_serializer.is_valid():
teacher_serializer.save() teacher_serializer.save()
# Après modification, vérifier si l'ancien profile n'a plus de ProfileRole
if old_profile:
from Auth.models import ProfileRole # import local pour éviter les imports circulaires
if not ProfileRole.objects.filter(profile=old_profile).exists():
old_profile.delete()
return JsonResponse(teacher_serializer.data, safe=False) return JsonResponse(teacher_serializer.data, safe=False)
return JsonResponse(teacher_serializer.errors, safe=False) return JsonResponse(teacher_serializer.errors, safe=False)

View File

@ -146,43 +146,61 @@ const TeachersSection = ({
const { selectedEstablishmentId } = useEstablishment(); const { selectedEstablishmentId } = useEstablishment();
const handleEmailChange = (e) => { // --- UTILS ---
const email = e.target.value;
// Vérifier si l'email correspond à un profil existant // Retourne le profil existant pour un email, utilisé par un teacher actif ou le teacher en cours d'édition
const existingProfile = profiles.find((profile) => profile.email === email); const getUsedProfileForEmail = (email, teacherId = null) => {
const usedProfileIds = new Set(
// Ajout du log si l'adresse email est déjà utilisée pour un profil existant teachers.map(t => t.profile_role && t.profile).filter(Boolean)
if (existingProfile) { );
logger.info( // Ajoute le profil du teacher en cours d'édition si besoin
`Adresse email déjà utilisée pour le profil ${existingProfile.id}` if (teacherId) {
); const currentTeacher = teachers.find(t => t.id === teacherId);
} if (currentTeacher && currentTeacher.profile_role && currentTeacher.profile) {
const profileObj = profiles.find(p => p.id === currentTeacher.profile);
setFormData((prevData) => ({ if (profileObj && profileObj.email === email) {
...prevData, usedProfileIds.add(profileObj.id);
associated_profile_email: email, }
existingProfileId: existingProfile ? existingProfile.id : null, } else {
})); // Cas création immédiate : on cherche le profil par email dans profiles
const profileObj = profiles.find(p => p.email === email);
if (newTeacher) { if (profileObj) {
setNewTeacher((prevData) => ({ usedProfileIds.add(profileObj.id);
...prevData, }
associated_profile_email: email, }
existingProfileId: existingProfile ? existingProfile.id : null,
}));
} }
return profiles.find(
(profile) => profile.email === email && usedProfileIds.has(profile.id)
);
}; };
const handleCancelConfirmation = () => { // Met à jour le formData et newTeacher si besoin
setConfirmPopupVisible(false); const updateFormData = (data) => {
setFormData(prev => ({ ...prev, ...data }));
if (newTeacher) setNewTeacher(prev => ({ ...prev, ...data }));
}; };
// Récupération des messages d'erreur // Récupération des messages d'erreur pour un champ donné
const getError = (field) => { const getError = (field) => {
return localErrors?.[field]?.[0]; return localErrors?.[field]?.[0];
}; };
// --- HANDLERS ---
const handleEmailChange = (e) => {
const email = e.target.value;
const existingProfile = getUsedProfileForEmail(email, editingTeacher);
if (existingProfile) {
logger.info(`Adresse email déjà utilisée pour le profil ${existingProfile.id}`);
}
updateFormData({
associated_profile_email: email,
existingProfileId: existingProfile ? existingProfile.id : null,
});
};
const handleAddTeacher = () => { const handleAddTeacher = () => {
setNewTeacher({ setNewTeacher({
id: Date.now(), id: Date.now(),
@ -202,15 +220,15 @@ const TeachersSection = ({
}; };
const handleRemoveTeacher = (id) => { const handleRemoveTeacher = (id) => {
logger.debug('[DELETE] Suppression teacher id:', id);
return handleDelete(id) return handleDelete(id)
.then(() => { .then(() => {
setTeachers((prevTeachers) => setTeachers(prevTeachers =>
prevTeachers.filter((teacher) => teacher.id !== id) prevTeachers.filter(teacher => teacher.id !== id)
); );
logger.debug('[DELETE] Teacher supprimé:', id);
}) })
.catch((error) => { .catch(logger.error);
logger.error(error);
});
}; };
const handleSaveNewTeacher = () => { const handleSaveNewTeacher = () => {
@ -241,16 +259,29 @@ const TeachersSection = ({
handleCreate(data) handleCreate(data)
.then((createdTeacher) => { .then((createdTeacher) => {
// Recherche du profile associé dans profiles
let newProfileId = undefined;
let foundProfile = undefined;
if (
createdTeacher &&
createdTeacher.profile_role &&
createdTeacher.profile
) {
newProfileId = createdTeacher.profile;
foundProfile = profiles.find(p => p.id === newProfileId);
}
setTeachers([createdTeacher, ...teachers]); setTeachers([createdTeacher, ...teachers]);
setNewTeacher(null); setNewTeacher(null);
setLocalErrors({}); setLocalErrors({});
setFormData(prev => ({
...prev,
existingProfileId: newProfileId,
}));
}) })
.catch((error) => { .catch((error) => {
logger.error('Error:', error.message); logger.error('Error:', error.message);
if (error.details) { if (error.details) setLocalErrors(error.details);
logger.error('Form errors:', error.details);
setLocalErrors(error.details);
}
}); });
} else { } else {
setPopupMessage('Tous les champs doivent être remplis et valides'); setPopupMessage('Tous les champs doivent être remplis et valides');
@ -259,51 +290,28 @@ const TeachersSection = ({
}; };
const handleUpdateTeacher = (id, updatedData) => { const handleUpdateTeacher = (id, updatedData) => {
// Récupérer l'enseignant actuel à partir de la liste des enseignants // Simplification : le profil est forcément existant, on utilise directement existingProfileId du formData
const currentTeacher = teachers.find((teacher) => teacher.id === id); 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 ( if (
updatedData.last_name && updatedData.last_name &&
updatedData.first_name && updatedData.first_name &&
updatedData.associated_profile_email updatedData.associated_profile_email
) { ) {
const data = { const profileRoleData = {
last_name: updatedData.last_name, id: updatedData.profile_role,
first_name: updatedData.first_name, establishment: selectedEstablishmentId,
profile_role_data: { role_type: updatedData.role_type || 0,
id: updatedData.profile_role, is_active: true,
establishment: selectedEstablishmentId, profile: updatedData.existingProfileId,
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) handleEdit(id, {
last_name: updatedData.last_name,
first_name: updatedData.first_name,
profile_role_data: profileRoleData,
specialities: updatedData.specialities || [],
})
.then((updatedTeacher) => { .then((updatedTeacher) => {
setTeachers((prevTeachers) => setTeachers((prevTeachers) =>
prevTeachers.map((teacher) => prevTeachers.map((teacher) =>
@ -315,10 +323,7 @@ const TeachersSection = ({
}) })
.catch((error) => { .catch((error) => {
logger.error('Error:', error.message); logger.error('Error:', error.message);
if (error.details) { if (error.details) setLocalErrors(error.details);
logger.error('Form errors:', error.details);
setLocalErrors(error.details);
}
}); });
} else { } else {
setPopupMessage('Tous les champs doivent être remplis et valides'); setPopupMessage('Tous les champs doivent être remplis et valides');
@ -328,45 +333,12 @@ const TeachersSection = ({
const handleChange = (e) => { const handleChange = (e) => {
const { name, value, type, checked } = e.target; const { name, value, type, checked } = e.target;
let parsedValue = value; let parsedValue = type === 'checkbox' ? (checked ? 1 : 0) : value;
updateFormData({ [name]: parsedValue });
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) => { const handleSpecialitiesChange = (selectedSpecialities) => {
if (editingTeacher) { updateFormData({ specialities: selectedSpecialities });
setFormData((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
} else if (newTeacher) {
setNewTeacher((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
setFormData((prevData) => ({
...prevData,
specialities: selectedSpecialities,
}));
}
}; };
const handleEditTeacher = (teacher) => { const handleEditTeacher = (teacher) => {
@ -413,6 +385,7 @@ const TeachersSection = ({
onChange={handleEmailChange} onChange={handleEmailChange}
placeholder="Adresse email de l'enseignant" placeholder="Adresse email de l'enseignant"
errorMsg={getError('email')} errorMsg={getError('email')}
enable={!isEditing}
/> />
); );
case 'SPECIALITES': case 'SPECIALITES':