mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: Gestion de la création d'un nouveau guardian, de l'association
avec un guardian dumême établissement, et de l'association avec un guardian d'un autre établissement
This commit is contained in:
@ -16,7 +16,24 @@ 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, 'establishment_name': role.establishment.name, 'is_active': role.is_active} for role in roles]
|
roles_data = []
|
||||||
|
for role in roles:
|
||||||
|
# Récupérer l'ID de l'associated_person en fonction du type de rôle
|
||||||
|
if role.role_type == ProfileRole.RoleType.PROFIL_PARENT:
|
||||||
|
guardian = Guardian.objects.filter(profile_role=role).first()
|
||||||
|
id_associated_person = guardian.id if guardian else None
|
||||||
|
else:
|
||||||
|
teacher = Teacher.objects.filter(profile_role=role).first()
|
||||||
|
id_associated_person = teacher.id if teacher else None
|
||||||
|
|
||||||
|
roles_data.append({
|
||||||
|
'id_associated_person': id_associated_person,
|
||||||
|
'role_type': role.role_type,
|
||||||
|
'establishment': role.establishment.id,
|
||||||
|
'establishment_name': role.establishment.name,
|
||||||
|
'is_active': role.is_active,
|
||||||
|
})
|
||||||
|
return roles_data
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
user = Profile(
|
user = Profile(
|
||||||
@ -107,6 +124,7 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
|
|||||||
"registration_status": registration_status
|
"registration_status": registration_status
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
"id": guardian.id,
|
||||||
"guardian_name": f"{guardian.last_name} {guardian.first_name}",
|
"guardian_name": f"{guardian.last_name} {guardian.first_name}",
|
||||||
"students": students_list
|
"students": students_list
|
||||||
}
|
}
|
||||||
@ -118,6 +136,7 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
|
|||||||
specialities = teacher.specialities.all()
|
specialities = teacher.specialities.all()
|
||||||
specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities]
|
specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities]
|
||||||
return {
|
return {
|
||||||
|
"id": teacher.id,
|
||||||
"teacher_name": f"{teacher.last_name} {teacher.first_name}",
|
"teacher_name": f"{teacher.last_name} {teacher.first_name}",
|
||||||
"classes": classes_list,
|
"classes": classes_list,
|
||||||
"specialities": specialities_list
|
"specialities": specialities_list
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from django.http.response import JsonResponse
|
|||||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect
|
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.cache import cache
|
|
||||||
from django.middleware.csrf import get_token
|
from django.middleware.csrf import get_token
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.parsers import JSONParser
|
from rest_framework.parsers import JSONParser
|
||||||
@ -24,7 +23,6 @@ from rest_framework.decorators import action, api_view
|
|||||||
|
|
||||||
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
||||||
from Subscriptions.models import RegistrationForm
|
from Subscriptions.models import RegistrationForm
|
||||||
from Subscriptions.signals import clear_cache
|
|
||||||
import Subscriptions.mailManager as mailer
|
import Subscriptions.mailManager as mailer
|
||||||
import Subscriptions.util as util
|
import Subscriptions.util as util
|
||||||
import logging
|
import logging
|
||||||
@ -204,7 +202,6 @@ class LoginView(APIView):
|
|||||||
|
|
||||||
login(request, user)
|
login(request, user)
|
||||||
user.save()
|
user.save()
|
||||||
clear_cache()
|
|
||||||
retour = ''
|
retour = ''
|
||||||
|
|
||||||
# Récupérer tous les rôles de l'utilisateur avec le type spécifié
|
# Récupérer tous les rôles de l'utilisateur avec le type spécifié
|
||||||
@ -423,7 +420,6 @@ class SubscribeView(APIView):
|
|||||||
else:
|
else:
|
||||||
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
clear_cache()
|
|
||||||
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
||||||
retourErreur = ''
|
retourErreur = ''
|
||||||
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)
|
||||||
@ -473,7 +469,6 @@ class NewPasswordView(APIView):
|
|||||||
profil.code = util.genereRandomCode(12)
|
profil.code = util.genereRandomCode(12)
|
||||||
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
|
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
|
||||||
profil.save()
|
profil.save()
|
||||||
clear_cache()
|
|
||||||
retourErreur = ''
|
retourErreur = ''
|
||||||
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email'))
|
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email'))
|
||||||
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
|
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
|
||||||
@ -527,7 +522,6 @@ class ResetPasswordView(APIView):
|
|||||||
profil.code = ''
|
profil.code = ''
|
||||||
profil.datePeremption = ''
|
profil.datePeremption = ''
|
||||||
profil.save()
|
profil.save()
|
||||||
clear_cache()
|
|
||||||
retourErreur = ''
|
retourErreur = ''
|
||||||
|
|
||||||
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)
|
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)
|
||||||
|
|||||||
@ -5,6 +5,3 @@ class GestioninscriptionsConfig(AppConfig):
|
|||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'Subscriptions'
|
name = 'Subscriptions'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
from Subscriptions.signals import clear_cache
|
|
||||||
clear_cache()
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
# state_machine.py
|
# state_machine.py
|
||||||
import json
|
import json
|
||||||
from Subscriptions.models import RegistrationForm
|
from Subscriptions.models import RegistrationForm
|
||||||
from Subscriptions.signals import clear_cache
|
|
||||||
|
|
||||||
state_mapping = {
|
state_mapping = {
|
||||||
"ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
|
"ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
|
||||||
@ -31,7 +30,6 @@ def updateStateMachine(rf, transition) :
|
|||||||
if state_machine.trigger(transition, automateModel):
|
if state_machine.trigger(transition, automateModel):
|
||||||
rf.status = state_machine.state
|
rf.status = state_machine.state
|
||||||
rf.save()
|
rf.save()
|
||||||
clear_cache()
|
|
||||||
|
|
||||||
class Automate_RF_Register:
|
class Automate_RF_Register:
|
||||||
def __init__(self, initial_state):
|
def __init__(self, initial_state):
|
||||||
|
|||||||
@ -110,6 +110,9 @@ class StudentSerializer(serializers.ModelSerializer):
|
|||||||
profile_role = guardian_data.pop('profile_role', None)
|
profile_role = guardian_data.pop('profile_role', None)
|
||||||
|
|
||||||
if profile_role_data:
|
if profile_role_data:
|
||||||
|
# Vérifiez si 'profile' est un objet ou une clé primaire
|
||||||
|
if isinstance(profile_role_data.get('profile'), Profile):
|
||||||
|
profile_role_data['profile'] = profile_role_data['profile'].id
|
||||||
establishment_id = profile_role_data.pop('establishment').id
|
establishment_id = profile_role_data.pop('establishment').id
|
||||||
profile_role_data['establishment'] = establishment_id
|
profile_role_data['establishment'] = establishment_id
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
from django.db.models.signals import post_save, post_delete, m2m_changed
|
from django.db.models.signals import post_save, post_delete, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.core.cache import cache
|
|
||||||
from .models import RegistrationForm, Student, Guardian
|
from .models import RegistrationForm, Student, Guardian
|
||||||
from Auth.models import Profile
|
from Auth.models import Profile
|
||||||
from N3wtSchool import settings
|
from N3wtSchool import settings
|
||||||
@ -8,23 +7,6 @@ from N3wtSchool.redis_client import redis_client
|
|||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def clear_cache():
|
|
||||||
# Préfixes des clés à supprimer
|
|
||||||
prefixes = ['N3WT_']
|
|
||||||
|
|
||||||
for prefix in prefixes:
|
|
||||||
# Utiliser le motif pour obtenir les clés correspondant au préfixe
|
|
||||||
pattern = f'*{prefix}*'
|
|
||||||
logger.debug(f'pattern : {pattern}')
|
|
||||||
for key in redis_client.scan_iter(pattern):
|
|
||||||
redis_client.delete(key)
|
|
||||||
logger.debug(f'deleting : {key}')
|
|
||||||
|
|
||||||
@receiver(post_save, sender=RegistrationForm)
|
|
||||||
@receiver(post_delete, sender=RegistrationForm)
|
|
||||||
def clear_cache_after_change(sender, instance, **kwargs):
|
|
||||||
clear_cache()
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=Student.guardians.through)
|
@receiver(m2m_changed, sender=Student.guardians.through)
|
||||||
def check_orphan_reponsables(sender, **kwargs):
|
def check_orphan_reponsables(sender, **kwargs):
|
||||||
action = kwargs.pop('action', None)
|
action = kwargs.pop('action', None)
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from django.http.response import JsonResponse
|
from django.http.response import JsonResponse
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.core.cache import cache
|
|
||||||
from rest_framework.parsers import JSONParser
|
from rest_framework.parsers import JSONParser
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.decorators import action, api_view
|
from rest_framework.decorators import action, api_view
|
||||||
@ -18,7 +17,6 @@ import Subscriptions.util as util
|
|||||||
|
|
||||||
from Subscriptions.serializers import RegistrationFormSerializer
|
from Subscriptions.serializers import RegistrationFormSerializer
|
||||||
from Subscriptions.pagination import CustomPagination
|
from Subscriptions.pagination import CustomPagination
|
||||||
from Subscriptions.signals import clear_cache
|
|
||||||
from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationTemplate, RegistrationFileGroup
|
from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationTemplate, RegistrationFileGroup
|
||||||
from Subscriptions.automate import updateStateMachine
|
from Subscriptions.automate import updateStateMachine
|
||||||
|
|
||||||
@ -88,13 +86,6 @@ class RegisterFormView(APIView):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
page_size = settings.NB_RESULT_PER_PAGE
|
page_size = settings.NB_RESULT_PER_PAGE
|
||||||
|
|
||||||
# Définir le cache_key en fonction du filtre
|
|
||||||
page_number = request.GET.get('page', 1)
|
|
||||||
cache_key = f'N3WT_ficheInscriptions_{establishment_id}_{filter}_page_{page_number}_search_{search if filter == "pending" else ""}'
|
|
||||||
cached_page = cache.get(cache_key)
|
|
||||||
if cached_page:
|
|
||||||
return JsonResponse(cached_page, safe=False)
|
|
||||||
|
|
||||||
# Récupérer les dossier d'inscriptions en fonction du filtre
|
# Récupérer les dossier d'inscriptions en fonction du filtre
|
||||||
registerForms_List = None
|
registerForms_List = None
|
||||||
if filter == 'pending':
|
if filter == 'pending':
|
||||||
@ -120,7 +111,6 @@ class RegisterFormView(APIView):
|
|||||||
if page is not None:
|
if page is not None:
|
||||||
registerForms_serializer = RegistrationFormSerializer(page, many=True)
|
registerForms_serializer = RegistrationFormSerializer(page, many=True)
|
||||||
response_data = paginator.get_paginated_response(registerForms_serializer.data)
|
response_data = paginator.get_paginated_response(registerForms_serializer.data)
|
||||||
cache.set(cache_key, response_data, timeout=60 * 15)
|
|
||||||
return JsonResponse(response_data, safe=False)
|
return JsonResponse(response_data, safe=False)
|
||||||
|
|
||||||
return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False)
|
return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False)
|
||||||
@ -308,7 +298,6 @@ class RegisterFormWithIdView(APIView):
|
|||||||
student.profiles.clear()
|
student.profiles.clear()
|
||||||
student.registration_files.clear()
|
student.registration_files.clear()
|
||||||
student.delete()
|
student.delete()
|
||||||
clear_cache()
|
|
||||||
|
|
||||||
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
|
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import {
|
|||||||
fetchRegistrationFees,
|
fetchRegistrationFees,
|
||||||
fetchTuitionFees } from '@/app/actions/schoolAction';
|
fetchTuitionFees } from '@/app/actions/schoolAction';
|
||||||
|
|
||||||
import { createProfile, deleteProfile } from '@/app/actions/authAction';
|
import { createProfile, deleteProfile, fetchProfiles } from '@/app/actions/authAction';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BASE_URL,
|
BASE_URL,
|
||||||
@ -86,6 +86,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [registrationFees, setRegistrationFees] = useState([]);
|
const [registrationFees, setRegistrationFees] = useState([]);
|
||||||
const [tuitionFees, setTuitionFees] = useState([]);
|
const [tuitionFees, setTuitionFees] = useState([]);
|
||||||
const [groups, setGroups] = useState([]);
|
const [groups, setGroups] = useState([]);
|
||||||
|
const [profiles, setProfiles] = useState([]);
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
const { selectedEstablishmentId } = useEstablishment();
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
@ -235,7 +236,14 @@ useEffect(() => {
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error('Error fetching file groups:', error);
|
logger.error('Error fetching file groups:', error);
|
||||||
|
}),
|
||||||
|
fetchProfiles()
|
||||||
|
.then(data => {
|
||||||
|
setProfiles(data);
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
logger.error('Error fetching profileRoles:', error);
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -373,24 +381,46 @@ useEffect(()=>{
|
|||||||
student: {
|
student: {
|
||||||
last_name: updatedData.studentLastName,
|
last_name: updatedData.studentLastName,
|
||||||
first_name: updatedData.studentFirstName,
|
first_name: updatedData.studentFirstName,
|
||||||
guardians: updatedData.selectedGuardians.length !== 0 ? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId })) : [{
|
guardians: updatedData.selectedGuardians.length !== 0
|
||||||
profile_role_data: {
|
? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId }))
|
||||||
establishment: selectedEstablishmentId,
|
: (() => {
|
||||||
role_type: 2,
|
if (updatedData.isExistingParentProfile) {
|
||||||
is_active: false,
|
return [{
|
||||||
profile_data: {
|
profile_role_data: {
|
||||||
email: updatedData.guardianEmail,
|
establishment: selectedEstablishmentId,
|
||||||
password: 'Provisoire01!',
|
role_type: 2,
|
||||||
username: updatedData.guardianEmail,
|
is_active: false,
|
||||||
}
|
profile: updatedData.existingProfileId, // Associer au profil existant
|
||||||
},
|
},
|
||||||
last_name: updatedData.guardianLastName,
|
last_name: updatedData.guardianLastName,
|
||||||
first_name: updatedData.guardianFirstName,
|
first_name: updatedData.guardianFirstName,
|
||||||
birth_date: updatedData.guardianBirthDate,
|
birth_date: updatedData.guardianBirthDate,
|
||||||
address: updatedData.guardianAddress,
|
address: updatedData.guardianAddress,
|
||||||
phone: updatedData.guardianPhone,
|
phone: updatedData.guardianPhone,
|
||||||
profession: updatedData.guardianProfession
|
profession: updatedData.guardianProfession
|
||||||
}],
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si aucun profil existant n'est trouvé, créer un nouveau profil
|
||||||
|
return [{
|
||||||
|
profile_role_data: {
|
||||||
|
establishment: selectedEstablishmentId,
|
||||||
|
role_type: 2,
|
||||||
|
is_active: false,
|
||||||
|
profile_data: {
|
||||||
|
email: updatedData.guardianEmail,
|
||||||
|
password: 'Provisoire01!',
|
||||||
|
username: updatedData.guardianEmail,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
last_name: updatedData.guardianLastName,
|
||||||
|
first_name: updatedData.guardianFirstName,
|
||||||
|
birth_date: updatedData.guardianBirthDate,
|
||||||
|
address: updatedData.guardianAddress,
|
||||||
|
phone: updatedData.guardianPhone,
|
||||||
|
profession: updatedData.guardianProfession
|
||||||
|
}];
|
||||||
|
})(),
|
||||||
sibling: []
|
sibling: []
|
||||||
},
|
},
|
||||||
fees: allFeesIds,
|
fees: allFeesIds,
|
||||||
@ -699,6 +729,7 @@ const columnsSubscribed = [
|
|||||||
registrationFees={registrationFees.filter(fee => fee.is_active)}
|
registrationFees={registrationFees.filter(fee => fee.is_active)}
|
||||||
tuitionFees={tuitionFees.filter(fee => fee.is_active)}
|
tuitionFees={tuitionFees.filter(fee => fee.is_active)}
|
||||||
groups={groups}
|
groups={groups}
|
||||||
|
profiles={profiles}
|
||||||
onSubmit={createRF}
|
onSubmit={createRF}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -113,6 +113,11 @@ export const deleteProfileRoles = (id, csrfToken) => {
|
|||||||
return fetch(request).then(requestResponseHandler);
|
return fetch(request).then(requestResponseHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchProfiles = () => {
|
||||||
|
return fetch(`${BE_AUTH_PROFILES_URL}`)
|
||||||
|
.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}`,
|
||||||
|
|||||||
@ -9,11 +9,14 @@ import DiscountsSection from '@/components/Structure/Tarification/DiscountsSecti
|
|||||||
import SectionTitle from '@/components/SectionTitle';
|
import SectionTitle from '@/components/SectionTitle';
|
||||||
import ProgressStep from '@/components/ProgressStep';
|
import ProgressStep from '@/components/ProgressStep';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
import Popup from '@/components/Popup';
|
||||||
|
|
||||||
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit, currentStep, groups }) => {
|
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, profiles, onSubmit, currentStep, groups }) => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
studentLastName: '',
|
studentLastName: '',
|
||||||
studentFirstName: '',
|
studentFirstName: '',
|
||||||
|
guardianLastName: '',
|
||||||
|
guardianFirstName: '',
|
||||||
guardianEmail: '',
|
guardianEmail: '',
|
||||||
guardianPhone: '',
|
guardianPhone: '',
|
||||||
selectedGuardians: [],
|
selectedGuardians: [],
|
||||||
@ -31,6 +34,10 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
const [existingGuardians, setExistingGuardians] = useState([]);
|
const [existingGuardians, setExistingGuardians] = useState([]);
|
||||||
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
|
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
|
||||||
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
|
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
|
||||||
|
const [filteredStudents, setFilteredStudents] = useState(students);
|
||||||
|
|
||||||
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
|
const [popupMessage, setPopupMessage] = useState("");
|
||||||
|
|
||||||
const stepTitles = {
|
const stepTitles = {
|
||||||
1: 'Nouvel élève',
|
1: 'Nouvel élève',
|
||||||
@ -45,8 +52,8 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
|
|
||||||
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
|
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
|
||||||
const isStep2Valid = (
|
const isStep2Valid = (
|
||||||
(formData.responsableType === "new" && formData.guardianEmail.length > 0) ||
|
formData.selectedGuardians.length > 0 ||
|
||||||
(formData.responsableType === "existing" && formData.selectedGuardians.length > 0)
|
(!formData.emailError && formData.guardianEmail.length > 0 && filteredStudents.length === 0)
|
||||||
);
|
);
|
||||||
const isStep3Valid = formData.selectedRegistrationFees.length > 0;
|
const isStep3Valid = formData.selectedRegistrationFees.length > 0;
|
||||||
const isStep4Valid = formData.selectedTuitionFees.length > 0;
|
const isStep4Valid = formData.selectedTuitionFees.length > 0;
|
||||||
@ -99,9 +106,35 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nextStep = () => {
|
const nextStep = () => {
|
||||||
if (step < steps.length) {
|
if (step === 2) {
|
||||||
setStep(step + 1);
|
// 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);
|
||||||
|
|
||||||
|
if (parentRole) {
|
||||||
|
console.log('Profil parent 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
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
console.log('Le profil existe mais n\'est pas de type PARENT.');
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
isExistingParentProfile: false, // Réinitialiser si le profil n'est pas de type PARENT
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step < steps.length) {
|
||||||
|
setStep(step + 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prevStep = () => {
|
const prevStep = () => {
|
||||||
@ -120,13 +153,27 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleResponsableSelection = (guardianId, guardianEmail) => {
|
const handleResponsableSelection = (guardianId, guardianEmail) => {
|
||||||
setFormData((prevData) => {
|
setFormData((prevData) => {
|
||||||
const isSelected = prevData.selectedGuardians.includes(guardianId);
|
const isSelected = prevData.selectedGuardians.includes(guardianId);
|
||||||
const selectedGuardians = isSelected
|
const selectedGuardians = isSelected
|
||||||
? prevData.selectedGuardians.filter(id => id !== guardianId)
|
? prevData.selectedGuardians.filter(id => id !== guardianId) // Retirer le guardian si déjà sélectionné
|
||||||
: [...prevData.selectedGuardians, guardianId];
|
: [...prevData.selectedGuardians, guardianId]; // Ajouter le guardian s'il n'est pas sélectionné
|
||||||
return { ...prevData, selectedGuardians, guardianEmail };
|
|
||||||
});
|
// Mettre à jour l'email uniquement si un guardian est sélectionné
|
||||||
|
const updatedGuardianEmail = isSelected ? '' : guardianEmail;
|
||||||
|
|
||||||
|
// Si aucun guardian n'est sélectionné, réinitialiser le tableau des élèves
|
||||||
|
if (selectedGuardians.length === 0) {
|
||||||
|
setFilteredStudents(students); // Réafficher tous les élèves
|
||||||
|
setSelectedEleve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevData,
|
||||||
|
selectedGuardians,
|
||||||
|
guardianEmail: updatedGuardianEmail,
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
@ -265,117 +312,141 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
|
|
||||||
{step === 2 && (
|
{step === 2 && (
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<div className="flex flex-col space-y-4 mt-6">
|
{/* Nom, Prénom et Téléphone */}
|
||||||
<label className="flex items-center space-x-3">
|
<InputTextIcon
|
||||||
<input
|
name="guardianLastName"
|
||||||
type="radio"
|
type="text"
|
||||||
name="responsableType"
|
IconItem={User}
|
||||||
value="new"
|
placeholder="Nom du responsable (optionnel)"
|
||||||
checked={formData.responsableType === 'new'}
|
value={formData.guardianLastName}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
className="w-full mt-4"
|
||||||
/>
|
/>
|
||||||
<span className="text-gray-900">Nouveau Responsable</span>
|
<InputTextIcon
|
||||||
</label>
|
name="guardianFirstName"
|
||||||
<label className="flex items-center space-x-3">
|
type="text"
|
||||||
<input
|
IconItem={User}
|
||||||
type="radio"
|
placeholder="Prénom du responsable (optionnel)"
|
||||||
name="responsableType"
|
value={formData.guardianFirstName}
|
||||||
value="existing"
|
onChange={handleChange}
|
||||||
checked={formData.responsableType === 'existing'}
|
className="w-full mt-4"
|
||||||
onChange={handleChange}
|
/>
|
||||||
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
<InputTextIcon
|
||||||
/>
|
name="guardianPhone"
|
||||||
<span className="text-gray-900">Responsable Existant</span>
|
type="tel"
|
||||||
</label>
|
IconItem={Phone}
|
||||||
</div>
|
placeholder="Numéro de téléphone (optionnel)"
|
||||||
{formData.responsableType === 'new' && (
|
value={formData.guardianPhone}
|
||||||
<>
|
onChange={handleChange}
|
||||||
<InputTextIcon
|
className="w-full mt-4"
|
||||||
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
|
|
||||||
name="guardianEmail"
|
|
||||||
type="email"
|
|
||||||
IconItem={Mail}
|
|
||||||
placeholder="Email du responsable"
|
|
||||||
value={formData.guardianEmail}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full mt-4"
|
|
||||||
/>
|
|
||||||
<InputTextIcon
|
|
||||||
name="guardianPhone"
|
|
||||||
type="tel"
|
|
||||||
IconItem={Phone}
|
|
||||||
placeholder="Numéro de téléphone (optionnel)"
|
|
||||||
value={formData.guardianPhone}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full mt-4"
|
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{formData.responsableType === 'existing' && (
|
{/* Email du responsable */}
|
||||||
<div className="mt-4">
|
<InputTextIcon
|
||||||
<div className="mt-4" style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
name="guardianEmail"
|
||||||
<table className="min-w-full bg-white border">
|
type="email"
|
||||||
<thead>
|
IconItem={Mail}
|
||||||
<tr>
|
placeholder="Email du responsable"
|
||||||
<th className="px-4 py-2 border">Nom</th>
|
value={formData.guardianEmail}
|
||||||
<th className="px-4 py-2 border">Prénom</th>
|
onChange={(e) => {
|
||||||
|
const email = e.target.value;
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
guardianEmail: email,
|
||||||
|
emailError:
|
||||||
|
email.length > 0 && // Le champ de mail est non null
|
||||||
|
(!emailRegex.test(email) && filteredStudents.length === 0) // Format invalide ou aucun résultat
|
||||||
|
? "Format d'email invalide ou aucun élève trouvé"
|
||||||
|
: "",
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Filtrer les responsables affichés dans le tableau
|
||||||
|
const filteredGuardians = students
|
||||||
|
.flatMap((student) => student.guardians) // Récupérer tous les guardians
|
||||||
|
.filter((guardian) =>
|
||||||
|
guardian.associated_profile_email.toLowerCase().includes(email.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filtrer les élèves en fonction des responsables
|
||||||
|
const filteredStudents = students.filter((student) =>
|
||||||
|
student.guardians.some((guardian) =>
|
||||||
|
filteredGuardians.some((filteredGuardian) => filteredGuardian.id === guardian.id)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
setFilteredStudents(filteredStudents); // Mettre à jour l'état des élèves filtrés
|
||||||
|
|
||||||
|
// Réinitialiser existingGuardians et selectedStudent si l'élève sélectionné n'est plus dans le tableau
|
||||||
|
if (!filteredStudents.some((student) => student.id === selectedStudent?.id)) {
|
||||||
|
setSelectedEleve(null); // Réinitialiser l'élève sélectionné
|
||||||
|
setExistingGuardians([]); // Réinitialiser les responsables associés
|
||||||
|
setFormData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
selectedGuardians: [] // Réinitialiser les responsables sélectionnés
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
errorMsg={formData.emailError}
|
||||||
|
className="w-full mt-4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Tableau des élèves */}
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="mt-4" style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
||||||
|
<table className="min-w-full bg-white border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border">Nom</th>
|
||||||
|
<th className="px-4 py-2 border">Prénom</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{filteredStudents.map((student, index) => ( // Utiliser les élèves filtrés
|
||||||
|
<tr
|
||||||
|
key={student.id}
|
||||||
|
className={`cursor-pointer ${
|
||||||
|
selectedStudent && selectedStudent.id === student.id
|
||||||
|
? 'bg-emerald-600 text-white'
|
||||||
|
: index % 2 === 0
|
||||||
|
? 'bg-emerald-100'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
onClick={() => handleEleveSelection(student)}
|
||||||
|
>
|
||||||
|
<td className="px-4 py-2 border">{student.last_name}</td>
|
||||||
|
<td className="px-4 py-2 border">{student.first_name}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{students.map((student, index) => (
|
|
||||||
<tr
|
|
||||||
key={student.id}
|
|
||||||
className={`cursor-pointer ${selectedStudent && selectedStudent.id === student.id ? 'bg-emerald-600 text-white' : index % 2 === 0 ? 'bg-emerald-100' : ''}`}
|
|
||||||
onClick={() => handleEleveSelection(student)}
|
|
||||||
>
|
|
||||||
<td className="px-4 py-2 border">{student.last_name}</td>
|
|
||||||
<td className="px-4 py-2 border">{student.first_name}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{selectedStudent && (
|
|
||||||
<div className="mt-4">
|
|
||||||
<h3 className="font-bold">Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :</h3>
|
|
||||||
{existingGuardians.map((guardian) => (
|
|
||||||
<div key={guardian.id}>
|
|
||||||
<label className="flex items-center space-x-3 mt-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={formData.selectedGuardians.includes(guardian.id)}
|
|
||||||
className="form-checkbox h-5 w-5 text-emerald-600"
|
|
||||||
onChange={() => handleResponsableSelection(guardian.id, guardian.associated_profile_email)}
|
|
||||||
/>
|
|
||||||
<span className="text-gray-900">
|
|
||||||
{guardian.last_name && guardian.first_name ? `${guardian.last_name} ${guardian.first_name}` : `${guardian.associated_profile_email}`}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</tbody>
|
||||||
)}
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{selectedStudent && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<h3 className="font-bold">
|
||||||
|
Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :
|
||||||
|
</h3>
|
||||||
|
{existingGuardians.map((guardian) => (
|
||||||
|
<div key={guardian.id}>
|
||||||
|
<label className="flex items-center space-x-3 mt-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.selectedGuardians.includes(guardian.id)}
|
||||||
|
className="form-checkbox h-5 w-5 text-emerald-600"
|
||||||
|
onChange={() => handleResponsableSelection(guardian.id, guardian.associated_profile_email)}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-900">
|
||||||
|
{guardian.last_name && guardian.first_name
|
||||||
|
? `${guardian.last_name} ${guardian.first_name} - ${guardian.associated_profile_email}`
|
||||||
|
: `${guardian.associated_profile_email}`}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -641,6 +712,13 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
name="Create" />
|
name="Create" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
visible={popupVisible}
|
||||||
|
message={popupMessage}
|
||||||
|
onConfirm={() => setPopupVisible(false)}
|
||||||
|
onCancel={() => setPopupVisible(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,14 +28,14 @@ const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = fa
|
|||||||
<div className="flex justify-end space-x-2">
|
<div className="flex justify-end space-x-2">
|
||||||
{!uniqueConfirmButton && (
|
{!uniqueConfirmButton && (
|
||||||
<button
|
<button
|
||||||
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
className="px-4 py-2 bg-gray-300 text-white rounded hover:bg-gray-700 hover:bg-gray-400"
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
className="px-4 py-2 bg-emerald-500 text-white rounded hover:bg-emerald-600"
|
className="px-4 py-2 bg-emerald-500 text-white hover:bg-emerald-600 rounded hover:bg-emerald-600"
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
>
|
>
|
||||||
{uniqueConfirmButton ? 'Fermer' : 'Confirmer'}
|
{uniqueConfirmButton ? 'Fermer' : 'Confirmer'}
|
||||||
|
|||||||
Reference in New Issue
Block a user