feat: Création d'un annuaire / mise à jour du subscribe

This commit is contained in:
N3WT DE COMPET
2025-03-14 19:51:35 +01:00
parent cd9c10a88a
commit 6bd5704983
22 changed files with 585 additions and 185 deletions

View File

@ -1,6 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from Auth.models import Profile, ProfileRole from Auth.models import Profile, ProfileRole
from Establishment.models import Establishment from Establishment.models import Establishment
from Subscriptions.models import Guardian, RegistrationForm
from School.models import Teacher
class ProfileSerializer(serializers.ModelSerializer): class ProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)
@ -14,7 +16,7 @@ class ProfileSerializer(serializers.ModelSerializer):
def get_roles(self, obj): def get_roles(self, obj):
roles = ProfileRole.objects.filter(profile=obj) roles = ProfileRole.objects.filter(profile=obj)
return [{'role_type': role.role_type, 'establishment': role.establishment.id, 'is_active': role.is_active} for role in roles] return [{'role_type': role.role_type, 'establishment': role.establishment.id, 'establishment_name': role.establishment.name, 'is_active': role.is_active} for role in roles]
def create(self, validated_data): def create(self, validated_data):
user = Profile( user = Profile(
@ -48,10 +50,12 @@ class ProfileSerializer(serializers.ModelSerializer):
class ProfileRoleSerializer(serializers.ModelSerializer): class ProfileRoleSerializer(serializers.ModelSerializer):
profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=False) profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=False)
profile_data = ProfileSerializer(write_only=True, required=False) profile_data = ProfileSerializer(write_only=True, required=False)
associated_profile_email = serializers.SerializerMethodField()
associated_person = serializers.SerializerMethodField()
class Meta: class Meta:
model = ProfileRole model = ProfileRole
fields = ['role_type', 'establishment', 'is_active', 'profile', 'profile_data'] fields = ['id', 'role_type', 'establishment', 'is_active', 'profile', 'profile_data', 'associated_profile_email', 'associated_person']
def create(self, validated_data): def create(self, validated_data):
profile_data = validated_data.pop('profile_data', None) profile_data = validated_data.pop('profile_data', None)
@ -83,3 +87,39 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
instance.is_active = validated_data.get('is_active', instance.is_active) instance.is_active = validated_data.get('is_active', instance.is_active)
instance.save() instance.save()
return instance return instance
def get_associated_profile_email(self, obj):
if obj.profile:
return obj.profile.email
return None
def get_associated_person(self, obj):
if obj.role_type == ProfileRole.RoleType.PROFIL_PARENT:
guardian = Guardian.objects.filter(profile_role=obj).first()
if guardian:
students = guardian.student_set.all()
students_list = []
for student in students:
registration_form = RegistrationForm.objects.filter(student=student).first()
registration_status = registration_form.status if registration_form else None
students_list.append({
"student_name": f"{student.last_name} {student.first_name}",
"registration_status": registration_status
})
return {
"guardian_name": f"{guardian.last_name} {guardian.first_name}",
"students": students_list
}
else:
teacher = Teacher.objects.filter(profile_role=obj).first()
if teacher:
classes = teacher.schoolclass_set.all()
classes_list = [{"id": classe.id, "name": classe.atmosphere_name} for classe in classes]
specialities = teacher.specialities.all()
specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities]
return {
"teacher_name": f"{teacher.last_name} {teacher.first_name}",
"classes": classes_list,
"specialities": specialities_list
}
return None

View File

@ -200,7 +200,7 @@ class LoginView(APIView):
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first() primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
if not primary_role: if not primary_role:
return JsonResponse({"errorMessage": "Role not assigned to the user"}, status=status.HTTP_401_UNAUTHORIZED) return JsonResponse({"errorMessage": "Profil inactif"}, status=status.HTTP_401_UNAUTHORIZED)
login(request, user) login(request, user)
user.save() user.save()
@ -305,10 +305,10 @@ class RefreshJWTView(APIView):
role_type = payload.get('role_type') role_type = payload.get('role_type')
# Récupérer le rôle principal de l'utilisateur # Récupérer le rôle principal de l'utilisateur
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type).first() primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
if not primary_role: if not primary_role:
return JsonResponse({'errorMessage': 'No role assigned to the user'}, status=400) return JsonResponse({'errorMessage': 'Profil inactif'}, status=400)
# Générer un nouveau Access Token avec les informations complètes # Générer un nouveau Access Token avec les informations complètes
new_access_payload = { new_access_payload = {
@ -383,10 +383,7 @@ class SubscribeView(APIView):
retourErreur = error.returnMessage[error.BAD_URL] retourErreur = error.returnMessage[error.BAD_URL]
retour = '' retour = ''
newProfilConnection = JSONParser().parse(request) newProfilConnection = JSONParser().parse(request)
establishment_id = request.GET.get('establishment_id') establishment_id = newProfilConnection['establishment_id']
if not establishment_id:
return JsonResponse({'message': retour, 'errorMessage': 'establishment_id manquant', "errorFields": {}, "id": -1}, safe=False, status=status.HTTP_400_BAD_REQUEST)
validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection) validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection)
validationOk, errorFields = validatorSubscription.validate() validationOk, errorFields = validatorSubscription.validate()
@ -398,7 +395,7 @@ class SubscribeView(APIView):
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS] retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
else: else:
# Vérifier si le profil a déjà un rôle actif pour l'établissement donné # Vérifier si le profil a déjà un rôle actif pour l'établissement donné
active_roles = ProfileRole.objects.filter(profile=profil, establishment_id=establishment_id, is_active=True) active_roles = ProfileRole.objects.filter(profile=profil, establishment=establishment_id, is_active=True)
if active_roles.exists(): if active_roles.exists():
retourErreur = error.returnMessage[error.PROFIL_ACTIVE] retourErreur = error.returnMessage[error.PROFIL_ACTIVE]
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False) return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
@ -408,18 +405,23 @@ class SubscribeView(APIView):
profil.full_clean() profil.full_clean()
profil.save() profil.save()
# Utiliser le sérialiseur ProfileRoleSerializer pour créer ou mettre à jour le rôle # Récupérer le ProfileRole existant pour l'établissement et le profil
role_data = { profile_role = ProfileRole.objects.filter(profile=profil, establishment=establishment_id).first()
'profile': profil.id, if profile_role:
'establishment_id': establishment_id, profile_role.is_active = True
'role_type': ProfileRole.RoleType.PROFIL_PARENT, profile_role.save()
'is_active': True
}
role_serializer = ProfileRoleSerializer(data=role_data)
if role_serializer.is_valid():
role_serializer.save()
else: else:
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) # Si aucun ProfileRole n'existe, en créer un nouveau
role_data = {
'profile': profil.id,
'establishment': establishment_id,
'is_active': True
}
role_serializer = ProfileRoleSerializer(data=role_data)
if role_serializer.is_valid():
role_serializer.save()
else:
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
clear_cache() clear_cache()
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE] retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
@ -536,7 +538,13 @@ class ProfileRoleView(APIView):
responses={200: ProfileRoleSerializer(many=True)} responses={200: ProfileRoleSerializer(many=True)}
) )
def get(self, request): def get(self, request):
establishment_id = request.GET.get('establishment_id', None)
if establishment_id is None:
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
profiles_roles_List = bdd.getAllObjects(_objectName=ProfileRole) profiles_roles_List = bdd.getAllObjects(_objectName=ProfileRole)
if profiles_roles_List:
profiles_roles_List = profiles_roles_List.filter(establishment=establishment_id).distinct()
profile_roles_serializer = ProfileRoleSerializer(profiles_roles_List, many=True) profile_roles_serializer = ProfileRoleSerializer(profiles_roles_List, many=True)
return JsonResponse(profile_roles_serializer.data, safe=False) return JsonResponse(profile_roles_serializer.data, safe=False)

View File

@ -103,20 +103,26 @@ class Command(BaseCommand):
# Créer entre 1 et 3 ProfileRole pour chaque profil # Créer entre 1 et 3 ProfileRole pour chaque profil
num_roles = random.randint(1, 3) num_roles = random.randint(1, 3)
created_roles = set()
for _ in range(num_roles): for _ in range(num_roles):
establishment = random.choice(self.establishments) establishment = random.choice(self.establishments)
role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT]) role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT])
# Vérifier si le rôle existe déjà pour cet établissement
if (establishment.id, role_type) in created_roles:
continue
profile_role_data = { profile_role_data = {
"profile": profile.id, "profile": profile.id,
"establishment": establishment.id, "establishment": establishment.id,
"role_type": role_type, "role_type": role_type,
"is_active": True "is_active": random.choice([True, False])
} }
profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
if profile_role_serializer.is_valid(): if profile_role_serializer.is_valid():
profile_role_serializer.save() profile_role_serializer.save()
created_roles.add((establishment.id, role_type))
self.stdout.write(self.style.SUCCESS(f'ProfileRole for {profile.email} created successfully with role type {role_type}')) self.stdout.write(self.style.SUCCESS(f'ProfileRole for {profile.email} created successfully with role type {role_type}'))
else: else:
self.stdout.write(self.style.ERROR(f'Error in data for profile role: {profile_role_serializer.errors}')) self.stdout.write(self.style.ERROR(f'Error in data for profile role: {profile_role_serializer.errors}'))
@ -129,7 +135,7 @@ class Command(BaseCommand):
for fee_data in fees_data: for fee_data in fees_data:
establishment = random.choice(self.establishments) establishment = random.choice(self.establishments)
print(f'establishment : {establishment}') print(f'establishment : {establishment}')
fee_data["name"] = f"{fee_data['name']} - {establishment.name}" fee_data["name"] = fee_data['name']
fee_data["establishment"] = establishment.id fee_data["establishment"] = establishment.id
fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]) fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
@ -145,7 +151,7 @@ class Command(BaseCommand):
for discount_data in discounts_data: for discount_data in discounts_data:
establishment = random.choice(self.establishments) establishment = random.choice(self.establishments)
discount_data["name"] = f"{discount_data['name']} - {establishment.name}" discount_data["name"] = discount_data['name']
discount_data["establishment"] = establishment.id discount_data["establishment"] = establishment.id
discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]) discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT]) discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT])
@ -216,7 +222,7 @@ class Command(BaseCommand):
for speciality_data in specialities_data: for speciality_data in specialities_data:
establishment = random.choice(self.establishments) establishment = random.choice(self.establishments)
speciality_data["name"] = f"{speciality_data['name']} - {establishment.name}" speciality_data["name"] = speciality_data['name']
speciality_data["establishment"] = establishment.id speciality_data["establishment"] = establishment.id
serializer = SpecialitySerializer(data=speciality_data) serializer = SpecialitySerializer(data=speciality_data)
@ -260,7 +266,7 @@ class Command(BaseCommand):
# Générer des données fictives pour l'enseignant # Générer des données fictives pour l'enseignant
teacher_data = { teacher_data = {
"last_name": fake.last_name(), "last_name": fake.last_name(),
"first_name": f"{fake.first_name()} - {profile_role.establishment.name}", "first_name": fake.first_name(),
"profile_role": profile_role.id "profile_role": profile_role.id
} }
@ -287,7 +293,7 @@ class Command(BaseCommand):
for index, class_data in enumerate(school_classes_data, start=1): for index, class_data in enumerate(school_classes_data, start=1):
# Randomize establishment # Randomize establishment
establishment = random.choice(self.establishments) establishment = random.choice(self.establishments)
class_data["atmosphere_name"] = f"Classe {index} - {establishment.name}" class_data["atmosphere_name"] = f"Classe {index}"
class_data["establishment"] = establishment.id class_data["establishment"] = establishment.id
# Randomize levels # Randomize levels
@ -295,9 +301,12 @@ class Command(BaseCommand):
# Randomize teachers # Randomize teachers
establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment)) establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment))
num_teachers = min(random.randint(1, 10), len(establishment_teachers)) if len(establishment_teachers) > 0:
selected_teachers = random.sample(establishment_teachers, num_teachers) num_teachers = min(2, len(establishment_teachers))
teachers_ids = [teacher.id for teacher in selected_teachers] selected_teachers = random.sample(establishment_teachers, num_teachers)
teachers_ids = [teacher.id for teacher in selected_teachers]
else:
teachers_ids = []
# Use the serializer to create or update the school class # Use the serializer to create or update the school class
class_data["teachers"] = teachers_ids class_data["teachers"] = teachers_ids
@ -315,7 +324,7 @@ class Command(BaseCommand):
for establishment in self.establishments: for establishment in self.establishments:
for i in range(1, 4): # Créer 3 groupes de fichiers par établissement for i in range(1, 4): # Créer 3 groupes de fichiers par établissement
name = f"Fichiers d'inscription - {fake.word()} - {establishment.name}" name = f"Fichiers d'inscription - {fake.word()}"
description = fake.sentence() description = fake.sentence()
group_data = { group_data = {
"name": name, "name": name,
@ -342,8 +351,6 @@ class Command(BaseCommand):
used_profiles = set() used_profiles = set()
for _ in range(50): for _ in range(50):
establishment = random.choice(self.establishments)
# Récupérer un profil aléatoire qui n'a pas encore été utilisé # Récupérer un profil aléatoire qui n'a pas encore été utilisé
available_profiles = profiles_with_parent_role.exclude(id__in=used_profiles) available_profiles = profiles_with_parent_role.exclude(id__in=used_profiles)
if not available_profiles.exists(): if not available_profiles.exists():
@ -354,7 +361,8 @@ class Command(BaseCommand):
used_profiles.add(profile.id) used_profiles.add(profile.id)
# Récupérer le ProfileRole Parent associé au profil # Récupérer le ProfileRole Parent associé au profil
profile_role = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT).first() profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT)
profile_role = random.choice(profile_roles)
# Générer des données fictives pour le guardian # Générer des données fictives pour le guardian
guardian_data = { guardian_data = {
@ -370,7 +378,7 @@ class Command(BaseCommand):
# Générer des données fictives pour l'étudiant # Générer des données fictives pour l'étudiant
student_data = { student_data = {
"last_name": fake.last_name(), "last_name": fake.last_name(),
"first_name": f"{fake.first_name()} - {establishment.name}", "first_name": fake.first_name(),
"address": fake.address(), "address": fake.address(),
"birth_date": fake.date_of_birth(), "birth_date": fake.date_of_birth(),
"birth_place": fake.city(), "birth_place": fake.city(),
@ -398,14 +406,14 @@ class Command(BaseCommand):
# Créer les données du formulaire d'inscription # Créer les données du formulaire d'inscription
register_form_data = { register_form_data = {
"fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)), "fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
"establishment": establishment, "establishment": profile_role.establishment,
"status": fake.random_int(min=1, max=3) "status": fake.random_int(min=1, max=3)
} }
# Créer ou mettre à jour le formulaire d'inscription # Créer ou mettre à jour le formulaire d'inscription
register_form, created = RegistrationForm.objects.get_or_create( register_form, created = RegistrationForm.objects.get_or_create(
student=student, student=student,
establishment=establishment, establishment=profile_role.establishment,
defaults=register_form_data defaults=register_form_data
) )

View File

@ -49,7 +49,9 @@ class TeacherSerializer(serializers.ModelSerializer):
if profile_role_data: if profile_role_data:
establishment_id = profile_role_data.pop('establishment').id establishment_id = profile_role_data.pop('establishment').id
profile_id = profile_role_data.pop('profile').id
profile_role_data['establishment'] = establishment_id profile_role_data['establishment'] = establishment_id
profile_role_data['profile'] = profile_id
# Créer l'instance de ProfileRole # Créer l'instance de ProfileRole
profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
@ -95,7 +97,9 @@ class TeacherSerializer(serializers.ModelSerializer):
def get_role_type(self, obj): def get_role_type(self, obj):
profile_role = obj.profile_role profile_role = obj.profile_role
return {'role_type': profile_role.role_type, 'establishment': profile_role.establishment.name} if profile_role:
return {'role_type': profile_role.role_type, 'establishment': profile_role.establishment.name}
return None
def get_specialities_details(self, obj): def get_specialities_details(self, obj):
return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()] return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()]

View File

@ -23,7 +23,7 @@ def envoieReinitMotDePasse(recipients, code):
return errorMessage return errorMessage
def sendRegisterForm(recipients): def sendRegisterForm(recipients, establishment_id):
errorMessage = '' errorMessage = ''
try: try:
print(f'{settings.EMAIL_HOST_USER}') print(f'{settings.EMAIL_HOST_USER}')
@ -31,7 +31,8 @@ def sendRegisterForm(recipients):
EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription' EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription'
context = { context = {
'BASE_URL': settings.BASE_URL, 'BASE_URL': settings.BASE_URL,
'email': recipients 'email': recipients,
'establishment': establishment_id
} }
subject = EMAIL_INSCRIPTION_SUBJECT subject = EMAIL_INSCRIPTION_SUBJECT

View File

@ -38,7 +38,7 @@
<div class="content"> <div class="content">
<p>Bonjour,</p> <p>Bonjour,</p>
<p>Nous vous confirmons la réception de votre demande d'inscription, vous trouverez ci-joint le lien vers la page d'authentification : <a href="{{BASE_URL}}/users/login">{{BASE_URL}}/users/login</a></p> <p>Nous vous confirmons la réception de votre demande d'inscription, vous trouverez ci-joint le lien vers la page d'authentification : <a href="{{BASE_URL}}/users/login">{{BASE_URL}}/users/login</a></p>
<p>S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte à cette url : <a href="{{BASE_URL}}/users/subscribe">{{BASE_URL}}/users/subscribe</a></p> <p>S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte à cette url : <a href="{{BASE_URL}}/users/subscribe?establishment_id={{establishment}}">{{BASE_URL}}/users/subscribe</a></p>
<p>votre identifiant est : {{ email }}</p> <p>votre identifiant est : {{ email }}</p>
<p>Merci de compléter votre dossier d'inscription en suivant les instructions fournies.</p> <p>Merci de compléter votre dossier d'inscription en suivant les instructions fournies.</p>
<p>Cordialement,</p> <p>Cordialement,</p>

View File

@ -332,8 +332,8 @@ def send(request,id):
if register_form != None: if register_form != None:
student = register_form.student student = register_form.student
guardian = student.getMainGuardian() guardian = student.getMainGuardian()
email = guardian.email email = guardian.profile_role.profile.email
errorMessage = mailer.sendRegisterForm(email) errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk)
if errorMessage == '': if errorMessage == '':
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
updateStateMachine(register_form, 'envoiDI') updateStateMachine(register_form, 'envoiDI')

View File

@ -2,6 +2,7 @@
"dashboard": "Dashboard", "dashboard": "Dashboard",
"subscriptions": "Subscriptions", "subscriptions": "Subscriptions",
"structure": "Structure", "structure": "Structure",
"directory": "Directory",
"events": "Events", "events": "Events",
"grades": "Grades", "grades": "Grades",
"settings": "Settings", "settings": "Settings",

View File

@ -2,6 +2,7 @@
"dashboard": "Tableau de bord", "dashboard": "Tableau de bord",
"subscriptions": "Inscriptions", "subscriptions": "Inscriptions",
"structure": "Structure", "structure": "Structure",
"directory": "Annuaire",
"events": "Evenements", "events": "Evenements",
"grades": "Notes", "grades": "Notes",
"settings": "Paramètres", "settings": "Paramètres",

View File

@ -0,0 +1,66 @@
'use client'
import React, { useState, useEffect } from 'react';
import { fetchProfileRoles, updateProfileRoles, deleteProfileRoles } from '@/app/actions/authAction';
import logger from '@/utils/logger';
import { useEstablishment } from '@/context/EstablishmentContext';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import { useCsrfToken } from '@/context/CsrfContext';
import ProfileDirectory from '@/components/ProfileDirectory';
import { BE_AUTH_PROFILES_ROLES_URL } from '@/utils/Url';
export default function Page() {
const [profileRoles, setProfileRoles] = useState([]);
const csrfToken = useCsrfToken();
const { selectedEstablishmentId } = useEstablishment();
useEffect(() => {
if (selectedEstablishmentId) {
// Fetch data for profileRoles
handleProfiles();
}
}, [selectedEstablishmentId]);
const handleProfiles = () => {
fetchProfileRoles(selectedEstablishmentId)
.then(data => {
setProfileRoles(data);
})
.catch(error => logger.error('Error fetching profileRoles:', error));
};
const handleEdit = (profileRole) => {
const updatedData = { ...profileRole, is_active: !profileRole.is_active };
return updateProfileRoles(profileRole.id, updatedData, csrfToken)
.then(data => {
setProfileRoles(prevState => prevState.map(item => item.id === profileRole.id ? data : item));
return data;
})
.catch(error => {
logger.error('Error editing data:', error);
throw error;
});
};
const handleDelete = (id) => {
return deleteProfileRoles(id, csrfToken)
.then(() => {
setProfileRoles(prevState => prevState.filter(item => item.id !== id));
logger.debug("Profile deleted successfully:", id);
})
.catch(error => {
logger.error('Error deleting profile:', error);
throw error;
});
};
return (
<div className='p-8'>
<DjangoCSRFToken csrfToken={csrfToken} />
<div className="w-full p-4">
<ProfileDirectory profileRoles={profileRoles} handleActivateProfile={handleEdit} handleDeleteProfile={handleDelete} />
</div>
</div>
);
}

View File

@ -5,12 +5,13 @@ import { usePathname } from 'next/navigation';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import Image from 'next/image'; import Image from 'next/image';
import { import {
LayoutDashboard,
FileText,
School,
Users, Users,
Building, Award,
Home,
Calendar, Calendar,
Settings, Settings,
FileText,
LogOut, LogOut,
Menu, Menu,
X X
@ -22,6 +23,7 @@ import {
FE_ADMIN_HOME_URL, FE_ADMIN_HOME_URL,
FE_ADMIN_SUBSCRIPTIONS_URL, FE_ADMIN_SUBSCRIPTIONS_URL,
FE_ADMIN_STRUCTURE_URL, FE_ADMIN_STRUCTURE_URL,
FE_ADMIN_DIRECTORY_URL,
FE_ADMIN_GRADES_URL, FE_ADMIN_GRADES_URL,
FE_ADMIN_PLANNING_URL, FE_ADMIN_PLANNING_URL,
FE_ADMIN_SETTINGS_URL FE_ADMIN_SETTINGS_URL
@ -43,10 +45,11 @@ export default function Layout({
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const sidebarItems = { const sidebarItems = {
"admin": { "id": "admin", "name": t('dashboard'), "url": FE_ADMIN_HOME_URL, "icon": Home }, "admin": { "id": "admin", "name": t('dashboard'), "url": FE_ADMIN_HOME_URL, "icon": LayoutDashboard },
"subscriptions": { "id": "subscriptions", "name": t('subscriptions'), "url": FE_ADMIN_SUBSCRIPTIONS_URL, "icon": Users }, "subscriptions": { "id": "subscriptions", "name": t('subscriptions'), "url": FE_ADMIN_SUBSCRIPTIONS_URL, "icon": FileText },
"structure": { "id": "structure", "name": t('structure'), "url": FE_ADMIN_STRUCTURE_URL, "icon": Building }, "structure": { "id": "structure", "name": t('structure'), "url": FE_ADMIN_STRUCTURE_URL, "icon": School },
"grades": { "id": "grades", "name": t('grades'), "url": FE_ADMIN_GRADES_URL, "icon": FileText }, "directory": { "id": "directory", "name": t('directory'), "url": FE_ADMIN_DIRECTORY_URL, "icon": Users },
"grades": { "id": "grades", "name": t('grades'), "url": FE_ADMIN_GRADES_URL, "icon": Award },
"planning": { "id": "planning", "name": t('events'), "url": FE_ADMIN_PLANNING_URL, "icon": Calendar }, "planning": { "id": "planning", "name": t('events'), "url": FE_ADMIN_PLANNING_URL, "icon": Calendar },
"settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings } "settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings }
}; };

View File

@ -6,7 +6,8 @@ import FeesManagement from '@/components/Structure/Tarification/FeesManagement';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'; import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import { useCsrfToken } from '@/context/CsrfContext'; import { useCsrfToken } from '@/context/CsrfContext';
import { ClassesProvider } from '@/context/ClassesContext'; import { ClassesProvider } from '@/context/ClassesContext';
import { createDatas, import {
createDatas,
updateDatas, updateDatas,
removeDatas, removeDatas,
fetchSpecialities, fetchSpecialities,
@ -21,11 +22,10 @@ import { createDatas,
fetchTuitionPaymentPlans, fetchTuitionPaymentPlans,
fetchRegistrationPaymentModes, fetchRegistrationPaymentModes,
fetchTuitionPaymentModes } from '@/app/actions/schoolAction'; fetchTuitionPaymentModes } from '@/app/actions/schoolAction';
import { fetchProfileRoles } from '@/app/actions/authAction';
import SidebarTabs from '@/components/SidebarTabs'; import SidebarTabs from '@/components/SidebarTabs';
import FilesGroupsManagement from '@/components/Structure/Files/FilesGroupsManagement'; import FilesGroupsManagement from '@/components/Structure/Files/FilesGroupsManagement';
import { import { fetchRegistrationTemplateMaster } from "@/app/actions/registerFileGroupAction";
fetchRegistrationTemplateMaster
} from "@/app/actions/registerFileGroupAction";
import logger from '@/utils/logger'; import logger from '@/utils/logger';
import { useEstablishment } from '@/context/EstablishmentContext'; import { useEstablishment } from '@/context/EstablishmentContext';

View File

@ -453,8 +453,8 @@ useEffect(()=>{
const columns = [ const columns = [
{ name: t('studentName'), transform: (row) => row.student.last_name }, { name: t('studentName'), transform: (row) => row.student.last_name },
{ name: t('studentFistName'), transform: (row) => row.student.first_name }, { name: t('studentFistName'), transform: (row) => row.student.first_name },
{ name: t('mainContactMail'), transform: (row) => row.student.guardians[0].associated_profile_email }, { name: t('mainContactMail'), transform: (row) => (row.student.guardians && row.student.guardians.length > 0) ? row.student.guardians[0].associated_profile_email : '' },
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.student.guardians[0].phone) }, { name: t('phone'), transform: (row) => formatPhoneNumber(row.student.guardians[0]?.phone) },
{ name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update}, { name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update},
{ name: t('registrationFileStatus'), transform: (row) => ( { name: t('registrationFileStatus'), transform: (row) => (
<div className="flex justify-center items-center h-full"> <div className="flex justify-center items-center h-full">

View File

@ -13,8 +13,8 @@ import { User, KeySquare } from 'lucide-react'; // Importez directement les icô
import { FE_USERS_LOGIN_URL } from '@/utils/Url'; import { FE_USERS_LOGIN_URL } from '@/utils/Url';
import { useCsrfToken } from '@/context/CsrfContext'; import { useCsrfToken } from '@/context/CsrfContext';
import { subscribe } from '@/app/actions/authAction'; import { subscribe } from '@/app/actions/authAction';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
import logger from '@/utils/logger'; import logger from '@/utils/logger';
import { useEstablishment } from '@/context/EstablishmentContext';
export default function Page() { export default function Page() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
@ -30,39 +30,27 @@ export default function Page() {
const router = useRouter(); const router = useRouter();
const csrfToken = useCsrfToken(); const csrfToken = useCsrfToken();
useEffect(() => { const establishment_id = searchParams.get('establishment_id');
if (useFakeData) {
setIsLoading(true);
// Simuler une réponse réussie
const data = {
errorFields: {},
errorMessage: ""
};
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
setIsLoading(false);
}
}, []);
function isOK(data) { function isOK(data) {
return data.errorMessage === "" return data.errorMessage === ""
} }
function subscribeFormSubmit(formData) { function subscribeFormSubmit(formData) {
if (useFakeData) { const data ={
// Simuler une réponse réussie email: formData.get('login'),
const data = { password1: formData.get('password1'),
errorFields: {}, password2: formData.get('password2'),
errorMessage: "" establishment_id: establishment_id
}; }
subscribe(data,csrfToken).then(data => {
logger.debug('Success:', data);
setUserFieldError("") setUserFieldError("")
setPassword1FieldError("") setPassword1FieldError("")
setPassword2FieldError("") setPassword2FieldError("")
setErrorMessage("") setErrorMessage("")
if(isOK(data)){ if(isOK(data)){
setPopupMessage("Votre compte a été créé avec succès"); setPopupMessage(data.message);
setPopupVisible(true); setPopupVisible(true);
} else { } else {
if(data.errorMessage){ if(data.errorMessage){
@ -74,38 +62,12 @@ export default function Page() {
setPassword2FieldError(data.errorFields.password2) setPassword2FieldError(data.errorFields.password2)
} }
} }
} else { })
const data ={ .catch(error => {
email: formData.get('login'), logger.error('Error fetching data:', error);
password1: formData.get('password1'), error = error.errorMessage;
password2: formData.get('password2'), logger.debug(error);
} });
subscribe(data,csrfToken).then(data => {
logger.debug('Success:', data);
setUserFieldError("")
setPassword1FieldError("")
setPassword2FieldError("")
setErrorMessage("")
if(isOK(data)){
setPopupMessage(data.message);
setPopupVisible(true);
} else {
if(data.errorMessage){
setErrorMessage(data.errorMessage);
}
if(data.errorFields){
setUserFieldError(data.errorFields.email)
setPassword1FieldError(data.errorFields.password1)
setPassword2FieldError(data.errorFields.password2)
}
}
})
.catch(error => {
logger.error('Error fetching data:', error);
error = error.errorMessage;
logger.debug(error);
});
}
} }
if (isLoading === true) { if (isLoading === true) {

View File

@ -4,6 +4,7 @@ import {
BE_AUTH_REFRESH_JWT_URL, BE_AUTH_REFRESH_JWT_URL,
BE_AUTH_REGISTER_URL, BE_AUTH_REGISTER_URL,
BE_AUTH_PROFILES_URL, BE_AUTH_PROFILES_URL,
BE_AUTH_PROFILES_ROLES_URL,
BE_AUTH_RESET_PASSWORD_URL, BE_AUTH_RESET_PASSWORD_URL,
BE_AUTH_NEW_PASSWORD_URL, BE_AUTH_NEW_PASSWORD_URL,
FE_USERS_LOGIN_URL, FE_USERS_LOGIN_URL,
@ -77,6 +78,41 @@ export const disconnect = () => {
signOut({ callbackUrl: FE_USERS_LOGIN_URL }); signOut({ callbackUrl: FE_USERS_LOGIN_URL });
}; };
export const fetchProfileRoles = (establishment) => {
return fetch(`${BE_AUTH_PROFILES_ROLES_URL}?establishment_id=${establishment}`)
.then(requestResponseHandler)
};
export const updateProfileRoles = (id, data, csrfToken) => {
const request = new Request(
`${BE_AUTH_PROFILES_ROLES_URL}/${id}`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
credentials: 'include',
body: JSON.stringify(data),
}
);
return fetch(request).then(requestResponseHandler);
};
export const deleteProfileRoles = (id, csrfToken) => {
const request = new Request(
`${BE_AUTH_PROFILES_ROLES_URL}/${id}`,
{
method: 'DELETE',
headers: {
'X-CSRFToken': csrfToken
},
credentials: 'include'
}
);
return fetch(request).then(requestResponseHandler);
};
export const createProfile = (data, csrfToken) => { export const createProfile = (data, csrfToken) => {
const request = new Request( const request = new Request(
`${BE_AUTH_PROFILES_URL}`, `${BE_AUTH_PROFILES_URL}`,

View File

@ -22,7 +22,7 @@ const CheckBoxList = ({
<div className={`mt-2 grid ${horizontal ? 'grid-cols-6 gap-2' : 'grid-cols-1 gap-4'}`}> <div className={`mt-2 grid ${horizontal ? 'grid-cols-6 gap-2' : 'grid-cols-1 gap-4'}`}>
{items.map(item => ( {items.map(item => (
<CheckBox <CheckBox
key={item.id} key={`${fieldName}-${item.id}`}
item={item} item={item}
formData={formData} formData={formData}
handleChange={handleChange} handleChange={handleChange}

View File

@ -291,6 +291,24 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
</div> </div>
{formData.responsableType === 'new' && ( {formData.responsableType === 'new' && (
<> <>
<InputTextIcon
name="guardianLastName"
type="text"
IconItem={User}
placeholder="Nom du responsable (optionnel)"
value={formData.guardianLastName}
onChange={handleChange}
className="w-full mt-4"
/>
<InputTextIcon
name="guardianFirstName"
type="text"
IconItem={User}
placeholder="Prénom du responsable (optionnel)"
value={formData.guardianFirstName}
onChange={handleChange}
className="w-full mt-4"
/>
<InputTextIcon <InputTextIcon
name="guardianEmail" name="guardianEmail"
type="email" type="email"

View File

@ -0,0 +1,218 @@
import React, { useState } from 'react';
import { Trash2, Eye, EyeOff, ToggleLeft, ToggleRight } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import StatusLabel from '@/components/StatusLabel';
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
const roleTypeToLabel = (roleType) => {
switch (roleType) {
case 0:
return 'ECOLE';
case 1:
return 'ADMIN';
case 2:
return 'PARENT';
default:
return 'UNKNOWN';
}
};
const roleTypeToBadgeClass = (roleType) => {
switch (roleType) {
case 0:
return 'bg-blue-100 text-blue-600';
case 1:
return 'bg-red-100 text-red-600';
case 2:
return 'bg-green-100 text-green-600';
default:
return 'bg-gray-100 text-gray-600';
}
};
const ProfileDirectory = ({ profileRoles, handleActivateProfile, handleDeleteProfile }) => {
const parentProfiles = profileRoles.filter(profileRole => profileRole.role_type === 2);
const schoolAdminProfiles = profileRoles.filter(profileRole => profileRole.role_type !== 2);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const [confirmPopupVisible, setConfirmPopupVisible] = useState(false);
const [confirmPopupMessage, setConfirmPopupMessage] = useState("");
const [confirmPopupOnConfirm, setConfirmPopupOnConfirm] = useState(() => {});
const handleConfirmActivateProfile = (profileRole) => {
setConfirmPopupMessage(`Êtes-vous sûr de vouloir ${profileRole.is_active ? 'désactiver' : 'activer'} ce profil ?`);
setConfirmPopupOnConfirm(() => () => {
handleActivateProfile(profileRole)
.then(() => {
setPopupMessage(`Le profil a été ${profileRole.is_active ? 'désactivé' : 'activé'} avec succès.`);
setPopupVisible(true);
})
.catch(error => {
setPopupMessage(`Erreur lors de la ${profileRole.is_active ? 'désactivation' : 'activation'} du profil.`);
setPopupVisible(true);
});
setConfirmPopupVisible(false);
});
setConfirmPopupVisible(true);
};
const handleConfirmDeleteProfile = (id) => {
setConfirmPopupMessage("Êtes-vous sûr de vouloir supprimer ce profil ?");
setConfirmPopupOnConfirm(() => () => {
handleDeleteProfile(id)
.then(() => {
setPopupMessage("Le profil a été supprimé avec succès.");
setPopupVisible(true);
})
.catch(error => {
setPopupMessage("Erreur lors de la suppression du profil.");
setPopupVisible(true);
});
setConfirmPopupVisible(false);
});
setConfirmPopupVisible(true);
};
const parentColumns = [
{ name: 'Identifiant', transform: (row) => row.associated_profile_email },
{ name: 'Rôle', transform: (row) => (
<span className={`px-2 py-1 rounded-full font-bold ${roleTypeToBadgeClass(row.role_type)}`}>
{roleTypeToLabel(row.role_type)}
</span>
)
},
{ name: 'Utilisateur', transform: (row) => row.associated_person?.guardian_name },
{ name: 'Elève(s) associé(s)', transform: (row) => (
<div className="flex flex-col justify-center space-y-2">
{row.associated_person?.students?.map(student => (
<span key={student.student_name} className="px-2 py-1 rounded-full text-gray-800">
{student.student_name}
</span>
))}
</div>
)
},
{ name: 'Etat du dossier d\'inscription', transform: (row) => (
<div className="flex flex-col justify-center items-center space-y-2">
{row.associated_person?.students?.map(student => (
<StatusLabel key={student.student_name} status={student.registration_status} showDropdown={false} />
))}
</div>
)
},
{
name: 'Actions',
transform: (row) => (
<div className="flex justify-center space-x-2">
<button
type="button"
className={row.is_active ? 'text-emerald-500 hover:text-emerald-700' : 'text-orange-500 hover:text-orange-700'}
onClick={() => handleConfirmActivateProfile(row)}
>
{row.is_active ? <ToggleRight className="w-5 h-5 " /> : <ToggleLeft className="w-5 h-5" />}
</button>
<button
type="button"
className="text-red-500 hover:text-red-700"
onClick={() => handleConfirmDeleteProfile(row.id)}
>
<Trash2 className="w-5 h-5" />
</button>
</div>
)
}
];
const schoolAdminColumns = [
{ name: 'Identifiant', transform: (row) => row.associated_profile_email },
{ name: 'Rôle', transform: (row) => (
<span className={`px-2 py-1 rounded-full font-bold ${roleTypeToBadgeClass(row.role_type)}`}>
{roleTypeToLabel(row.role_type)}
</span>
)
},
{ name: 'Utilisateur', transform: (row) => row.associated_person?.teacher_name },
{ name: 'Classe(s) associée(s)', transform: (row) => (
<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>
)
},
{ name: 'Spécialités', transform: (row) => (
<div className="flex flex-wrap justify-center space-x-2">
{row.associated_person?.specialities?.map(speciality => (
<SpecialityItem speciality={speciality} isDraggable={false}/>
))}
</div>
)
},
{
name: 'Actions',
transform: (row) => (
<div className="flex justify-center space-x-2">
<button
type="button"
className={row.is_active ? 'text-emerald-500 hover:text-emerald-700' : 'text-orange-500 hover:text-orange-700'}
onClick={() => handleConfirmActivateProfile(row)}
>
{row.is_active ? <ToggleRight className="w-5 h-5 " /> : <ToggleLeft className="w-5 h-5" />}
</button>
<button
type="button"
className="text-red-500 hover:text-red-700"
onClick={() => handleConfirmDeleteProfile(row.id)}
>
<Trash2 className="w-5 h-5" />
</button>
</div>
)
}
];
return (
<div className="bg-white rounded-lg shadow-lg w-full p-6">
<div className="space-y-8">
<div className="max-h-128 overflow-y-auto border rounded p-4">
{parentProfiles.length === 0 ? (
<div>Aucun profil trouvé</div>
) : (
<Table
data={parentProfiles}
columns={parentColumns}
/>
)}
</div>
<div className="max-h-128 overflow-y-auto border rounded p-4">
{schoolAdminProfiles.length === 0 ? (
<div>Aucun profil trouvé</div>
) : (
<Table
data={schoolAdminProfiles}
columns={schoolAdminColumns}
/>
)}
</div>
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
uniqueConfirmButton={true}
/>
<Popup
visible={confirmPopupVisible}
message={confirmPopupMessage}
onConfirm={confirmPopupOnConfirm}
onCancel={() => setConfirmPopupVisible(false)}
/>
</div>
);
};
export default ProfileDirectory;

View File

@ -4,6 +4,8 @@ import { SessionProvider } from "next-auth/react"
import { CsrfProvider } from '@/context/CsrfContext' import { CsrfProvider } from '@/context/CsrfContext'
import { NextIntlClientProvider } from 'next-intl' import { NextIntlClientProvider } from 'next-intl'
import { EstablishmentProvider } from '@/context/EstablishmentContext'; import { EstablishmentProvider } from '@/context/EstablishmentContext';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
export default function Providers({ children, messages, locale, session }) { export default function Providers({ children, messages, locale, session }) {
@ -13,13 +15,15 @@ export default function Providers({ children, messages, locale, session }) {
} }
return ( return (
<SessionProvider session={session}> <SessionProvider session={session}>
<CsrfProvider> <DndProvider backend={HTML5Backend}>
<EstablishmentProvider> <CsrfProvider>
<NextIntlClientProvider messages={messages} locale={locale}> <EstablishmentProvider>
{children} <NextIntlClientProvider messages={messages} locale={locale}>
</NextIntlClientProvider> {children}
</EstablishmentProvider> </NextIntlClientProvider>
</CsrfProvider> </EstablishmentProvider>
</CsrfProvider>
</DndProvider>
</SessionProvider> </SessionProvider>
) )
} }

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Plus, Edit3, Trash2, GraduationCap, Check, X, Hand } from 'lucide-react'; import { Plus, Edit3, Trash2, GraduationCap, Check, X, Hand, Search } from 'lucide-react';
import Table from '@/components/Table'; import Table from '@/components/Table';
import Popup from '@/components/Popup'; import Popup from '@/components/Popup';
import ToggleSwitch from '@/components/ToggleSwitch'; import ToggleSwitch from '@/components/ToggleSwitch';
@ -11,6 +11,8 @@ import InputText from '@/components/InputText';
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem'; import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
import TeacherItem from './TeacherItem'; import TeacherItem from './TeacherItem';
import logger from '@/utils/logger'; import logger from '@/utils/logger';
import { fetchProfiles } from '@/app/actions/authAction';
import { useEstablishment } from '@/context/EstablishmentContext';
const ItemTypes = { const ItemTypes = {
SPECIALITY: 'speciality', SPECIALITY: 'speciality',
@ -103,14 +105,40 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
const [removePopupMessage, setRemovePopupMessage] = useState(""); const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const [confirmPopupVisible, setConfirmPopupVisible] = useState(false);
const [confirmPopupMessage, setConfirmPopupMessage] = useState("");
const [confirmPopupOnConfirm, setConfirmPopupOnConfirm] = useState(() => {});
const { selectedEstablishmentId } = useEstablishment();
const handleSelectProfile = (profile) => {
setNewTeacher((prevData) => ({
...prevData,
selectedProfile: profile,
}));
setFormData((prevData) => ({
...prevData,
selectedProfile: profile
}));
setConfirmPopupMessage(`Vous êtes sur le point de rattacher l'enseignant ${newTeacher?.first_name} ${newTeacher?.last_name} au profil ${profile.email} ID = ${profile.id}.`);
setConfirmPopupOnConfirm(() => () => {
setConfirmPopupVisible(false);
});
setConfirmPopupVisible(true);
};
const handleCancelConfirmation = () => {
setConfirmPopupVisible(false);
};
// Récupération des messages d'erreur // Récupération des messages d'erreur
const getError = (field) => { const getError = (field) => {
return localErrors?.[field]?.[0]; return localErrors?.[field]?.[0];
}; };
const handleAddTeacher = () => { const handleAddTeacher = () => {
setNewTeacher({ id: Date.now(), last_name: '', first_name: '', email: '', specialities: [], droit: 0 }); setNewTeacher({ id: Date.now(), last_name: '', first_name: '', selectedProfile: null, specialities: [], droit: 0 });
setFormData({ last_name: '', first_name: '', email: '', specialities: [], droit: 0 }); setFormData({ last_name: '', first_name: '', selectedProfile: null, specialities: [], droit: 0});
}; };
const handleRemoveTeacher = (id) => { const handleRemoveTeacher = (id) => {
@ -124,43 +152,32 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
}; };
const handleSaveNewTeacher = () => { const handleSaveNewTeacher = () => {
if (formData.last_name && formData.first_name && formData.email) { if (formData.last_name && formData.first_name && formData.selectedProfile) {
const data = { const data = {
email: formData.email, last_name: formData.last_name,
password: 'Provisoire01!', first_name: formData.first_name,
username: formData.email, profile_role_data: {
is_active: 1, role_type: formData.droit,
droit: formData.droit, establishment: selectedEstablishmentId,
is_active: true,
profile: formData.selectedProfile.id
},
specialities: formData.specialities
}; };
createProfile(data, csrfToken)
.then(response => { handleCreate(data)
logger.debug('Success:', response); .then((createdTeacher) => {
if (response.id) { setTeachers([createdTeacher, ...teachers]);
let idProfil = response.id; setNewTeacher(null);
newTeacher.associated_profile = idProfil; setLocalErrors({});
handleCreate(newTeacher) })
.then((createdTeacher) => { .catch((error) => {
setTeachers([createdTeacher, ...teachers]); logger.error('Error:', error.message);
setNewTeacher(null); if (error.details) {
setLocalErrors({});
})
.catch((error) => {
logger.error('Error:', error.message);
if (error.details) {
logger.error('Form errors:', error.details);
setLocalErrors(error.details);
}
});
}
setLocalErrors({});
})
.catch((error) => {
logger.error('Error:', error.message);
if (error.details) {
logger.error('Form errors:', error.details); logger.error('Form errors:', error.details);
setLocalErrors(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");
setPopupVisible(true); setPopupVisible(true);
@ -260,7 +277,6 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
<div className="flex justify-center space-x-2"> <div className="flex justify-center space-x-2">
<InputText <InputText
name="last_name" name="last_name"
label="nom"
value={currentData.last_name} value={currentData.last_name}
onChange={handleChange} onChange={handleChange}
placeholder="Nom de l'enseignant" placeholder="Nom de l'enseignant"
@ -268,7 +284,6 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
/> />
<InputText <InputText
name="first_name" name="first_name"
label="prénom"
value={currentData.first_name} value={currentData.first_name}
onChange={handleChange} onChange={handleChange}
placeholder="Prénom de l'enseignant" placeholder="Prénom de l'enseignant"
@ -276,16 +291,26 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
/> />
</div> </div>
); );
case 'EMAIL': case 'EMAIL':
return ( return (
<InputText <div className="flex items-center space-x-2">
name="email" {currentData.selectedProfile ? (
value={currentData.email} <span className="text-gray-900">
onChange={handleChange} {currentData.selectedProfile.email}
placeholder="Email de l'enseignant" </span>
errorMsg={getError('email')} ) : (
/> <span className="text-gray-500 italic">
); Rechercher un profil existant
</span>
)}
<button
type="button"
onClick={() => setDirectoryPopupVisible(true)}
>
<Search className="w-5 h-5 text-emerald-500 hover:text-emerald-700"/>
</button>
</div>
);
case 'SPECIALITES': case 'SPECIALITES':
return ( return (
<SpecialitiesDropZone teacher={currentData} handleSpecialitiesChange={handleSpecialitiesChange} specialities={specialities} isEditing={isEditing || isCreating} /> <SpecialitiesDropZone teacher={currentData} handleSpecialitiesChange={handleSpecialitiesChange} specialities={specialities} isEditing={isEditing || isCreating} />
@ -339,9 +364,9 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
</div> </div>
); );
case 'ADMINISTRATEUR': case 'ADMINISTRATEUR':
if (teacher.associated_profile) { if (teacher.associated_profile_email) {
const badgeClass = teacher.droit === 1 ? 'bg-red-100 text-red-600' : 'bg-blue-100 text-blue-600'; const badgeClass = teacher.role_type.role_type === 1 ? 'bg-red-100 text-red-600' : 'bg-blue-100 text-blue-600';
const label = teacher.droit === 1 ? 'OUI' : 'NON'; const label = teacher.role_type.role_type === 1 ? 'OUI' : 'NON';
return ( return (
<div key={teacher.id} className="flex justify-center items-center space-x-2"> <div key={teacher.id} className="flex justify-center items-center space-x-2">
<span className={`px-3 py-1 rounded-full font-bold ${badgeClass}`}> <span className={`px-3 py-1 rounded-full font-bold ${badgeClass}`}>

View File

@ -6,6 +6,7 @@ const localePrefixes = {
export function formatPhoneNumber(phoneString, fromFormat = 'XX-XX-XX-XX-XX', toFormat = 'LX-XX-XX-XX-XX', locale = "fr-FR") { export function formatPhoneNumber(phoneString, fromFormat = 'XX-XX-XX-XX-XX', toFormat = 'LX-XX-XX-XX-XX', locale = "fr-FR") {
if (!phoneString) return;
// Extraire les chiffres du numéro de téléphone // Extraire les chiffres du numéro de téléphone
const digits = phoneString.replace(/\D/g, ''); const digits = phoneString.replace(/\D/g, '');

View File

@ -18,6 +18,7 @@ export const BE_AUTH_LOGIN_URL = `${BASE_URL}/Auth/login`
export const BE_AUTH_REFRESH_JWT_URL = `${BASE_URL}/Auth/refreshJWT` export const BE_AUTH_REFRESH_JWT_URL = `${BASE_URL}/Auth/refreshJWT`
export const BE_AUTH_LOGOUT_URL = `${BASE_URL}/Auth/logout` export const BE_AUTH_LOGOUT_URL = `${BASE_URL}/Auth/logout`
export const BE_AUTH_PROFILES_URL = `${BASE_URL}/Auth/profiles` export const BE_AUTH_PROFILES_URL = `${BASE_URL}/Auth/profiles`
export const BE_AUTH_PROFILES_ROLES_URL = `${BASE_URL}/Auth/profileRoles`
export const BE_AUTH_CSRF_URL = `${BASE_URL}/Auth/csrf` export const BE_AUTH_CSRF_URL = `${BASE_URL}/Auth/csrf`
export const BE_AUTH_INFO_SESSION = `${BASE_URL}/Auth/infoSession` export const BE_AUTH_INFO_SESSION = `${BASE_URL}/Auth/infoSession`
@ -79,6 +80,9 @@ export const FE_ADMIN_CLASSES_URL = `/admin/classes`
//ADMIN/STRUCTURE URL //ADMIN/STRUCTURE URL
export const FE_ADMIN_STRUCTURE_URL = `/admin/structure` export const FE_ADMIN_STRUCTURE_URL = `/admin/structure`
//ADMIN/DIRECTORY URL
export const FE_ADMIN_DIRECTORY_URL = `/admin/directory`
//ADMIN/GRADES URL //ADMIN/GRADES URL
export const FE_ADMIN_GRADES_URL = `/admin/grades` export const FE_ADMIN_GRADES_URL = `/admin/grades`