diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index d67a246..2ecdcb6 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -340,16 +340,22 @@ class RegistrationFormByParentSerializer(serializers.ModelSerializer): class GuardianByDICreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) associated_profile_email = serializers.SerializerMethodField() + profile = serializers.SerializerMethodField() class Meta: model = Guardian - fields = ['id', 'last_name', 'first_name', 'associated_profile_email', 'phone'] + fields = ['id', 'last_name', 'first_name', 'associated_profile_email', 'phone', 'profile_role', 'profile'] def get_associated_profile_email(self, obj): if obj.profile_role and obj.profile_role.profile: return obj.profile_role.profile.email return None + def get_profile(self, obj): + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.id # Retourne l'ID du profil associé + return None + class StudentByRFCreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianByDICreationSerializer(many=True, required=False) diff --git a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js index 835d7b5..15325f5 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js @@ -9,7 +9,6 @@ import Table from '@/components/Table'; import FeesSection from '@/components/Structure/Tarification/FeesSection'; import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection'; import SectionTitle from '@/components/SectionTitle'; -import Popup from '@/components/Popup'; import InputPhone from '@/components/InputPhone'; import CheckBox from '@/components/CheckBox'; import RadioList from '@/components/RadioList'; @@ -18,8 +17,9 @@ import { getCurrentSchoolYear, getNextSchoolYear } from '@/utils/Date'; import logger from '@/utils/logger'; import { levels, genders } from '@/utils/constants'; import { useEstablishment } from '@/context/EstablishmentContext'; -import { useRouter } from 'next/navigation'; +import { useSearchParams, useRouter } from 'next/navigation'; import { + fetchRegisterForm, fetchStudents, createRegisterForm, } from '@/app/actions/subscriptionAction'; @@ -37,6 +37,7 @@ import { createRegistrationSchoolFileTemplate, createRegistrationParentFileTemplate, } from '@/app/actions/registerFileGroupAction'; +import { fetchProfiles } from '@/app/actions/authAction'; import { useClasses } from '@/context/ClassesContext'; import { useCsrfToken } from '@/context/CsrfContext'; import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url'; @@ -46,6 +47,7 @@ export default function CreateSubscriptionPage() { studentLastName: '', studentFirstName: '', studentLevel: '', + studentGender: '', guardianLastName: '', guardianFirstName: '', guardianEmail: '', @@ -60,6 +62,11 @@ export default function CreateSubscriptionPage() { schoolYear: getCurrentSchoolYear(), }); + const searchParams = useSearchParams(); + // Si l'ID est valorisé, alors on est en mode édition + const registerFormID = searchParams.get('id'); + const registerFormMoment = searchParams.get('school_year'); + const [students, setStudents] = useState([]); const [registrationDiscounts, setRegistrationDiscounts] = useState([]); const [tuitionDiscounts, setTuitionDiscounts] = useState([]); @@ -67,16 +74,14 @@ export default function CreateSubscriptionPage() { const [tuitionFees, setTuitionFees] = useState([]); const [groups, setGroups] = useState([]); const [schoolFileMasters, setSchoolFileMasters] = useState([]); - const [parentFileMasters, setParentFileMasters] = useState([]); + const [profiles, setProfiles] = useState([]); const [isLoading, setIsLoading] = useState(false); const [existingGuardians, setExistingGuardians] = useState([]); const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0); const [totalTuitionAmount, setTotalTuitionAmount] = useState(0); - const [popupVisible, setPopupVisible] = useState(false); - const [popupMessage, setPopupMessage] = useState(''); const [selectedStudent, setSelectedEleve] = useState(null); const [isNewResponsable, setIsNewResponsable] = useState(true); @@ -122,39 +127,31 @@ export default function CreateSubscriptionPage() { const getLocalError = (field) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - // Extraire tous les guardians des students - const allGuardians = students.flatMap((student) => student.guardians); + if (field === 'guardianEmail') { + if (!formData.guardianEmail || formData.guardianEmail.trim() === '') { + return 'Champs requis'; + } - if ( - // Student Form - (field === 'schoolYear' && - (!formData.schoolYear || String(formData.schoolYear).trim() === '')) || - (field === 'studentLastName' && - (!formData.studentLastName || - formData.studentLastName.trim() === '')) || - (field === 'studentFirstName' && - (!formData.studentFirstName || - formData.studentFirstName.trim() === '')) || - (field === 'guardianEmail' && - (!formData.guardianEmail || - formData.guardianEmail.trim() === '' || - !emailRegex.test(formData.guardianEmail) || - allGuardians.some( - (guardian) => - guardian.associated_profile_email === formData.guardianEmail - ))) // Vérifie si l'email existe déjà dans la liste des guardians - ) { - return field === 'guardianEmail' && - allGuardians.some( + if (!emailRegex.test(formData.guardianEmail)) { + return 'Email invalide'; + } + + // Vérifiez si l'email existe déjà, mais ne mettez pas à jour `formData` ici + const existingGuardian = students + .flatMap((student) => student.guardians) + .find( (guardian) => guardian.associated_profile_email === formData.guardianEmail - ) - ? 'Cet email est déjà associé à un responsable existant' - : 'Champs requis'; + ); + + if (existingGuardian) { + return ''; // Pas d'erreur, mais l'email existe déjà + } } return ''; }; + const requestErrorHandler = (err) => { logger.error('Error fetching data:', err); setErrors(err); @@ -165,7 +162,69 @@ export default function CreateSubscriptionPage() { }, [formData]); useEffect(() => { + if (!formData.guardianEmail) { + // Si l'email est vide, réinitialiser existingProfileId + setFormData((prevData) => ({ + ...prevData, + isExistingParentProfile: false, + existingProfileId: null, + })); + return; + } + + // Vérifiez si le profil existe dans la liste des profils + const existingProfile = profiles.find( + (profile) => profile.email === formData.guardianEmail + ); + + if (existingProfile) { + // Si un profil avec cet email existe, valoriser isExistingParentProfile et existingProfileId + setFormData((prevData) => ({ + ...prevData, + isExistingParentProfile: true, + existingProfileId: existingProfile.id, // Récupérer l'ID du profil associé + guardianLastName: existingProfile.last_name || '', + guardianFirstName: existingProfile.first_name || '', + guardianPhone: existingProfile.phone || '', + })); + } else { + // Si aucun profil avec cet email n'existe, réinitialiser les champs + setFormData((prevData) => ({ + ...prevData, + isExistingParentProfile: false, + existingProfileId: null, + })); + } + }, [formData.guardianEmail, profiles]); + + useEffect(() => { + fetchProfiles() + .then((data) => { + setProfiles(data); + }) + .catch(requestErrorHandler); if (selectedEstablishmentId) { + if (registerFormID) { + fetchRegisterForm(registerFormID) + .then((data) => { + setFormData((prevData) => ({ + ...prevData, + studentLastName: data?.student?.last_name || '', + studentFirstName: data?.student?.first_name || '', + studentLevel: data?.student?.level || '', + studentGender: data?.student?.gender || '', + guardianLastName: data?.student?.guardians[0]?.last_name || '', + guardianFirstName: data?.student?.guardians[0]?.first_name || '', + guardianEmail: + data?.student?.guardians[0]?.associated_profile_email || '', + guardianPhone: data?.student?.guardians[0]?.phone || '', + selectedFileGroup: data?.fileGroup || '', + schoolYear: data?.school_year || '', + })); + }) + .catch(requestErrorHandler); + } + fetchStudents(selectedEstablishmentId) .then((studentsData) => { setStudents(studentsData); @@ -219,6 +278,17 @@ export default function CreateSubscriptionPage() { } }, [selectedEstablishmentId]); + useEffect(() => { + if (registrationFees.length > 0) { + const defaultSelectedFees = registrationFees.map((fee) => fee.id); + const totalAmount = calculateFinalRegistrationAmount( + defaultSelectedFees, + formData.selectedRegistrationDiscounts + ); + setTotalRegistrationAmount(totalAmount); + } + }, [registrationFees]); + const handleChange = (e) => { const { name, value } = e.target; setFormData((prevState) => ({ @@ -250,21 +320,22 @@ export default function CreateSubscriptionPage() { }; const createRF = () => { - logger.debug('createRF formData:', formData); + logger.debug('createRF formData:', formDataRef.current); - // Préparation des données - const selectedRegistrationFeesIds = formData.selectedRegistrationFees.map( - (feeId) => feeId - ); + const selectedRegistrationFeesIds = + formDataRef.current.selectedRegistrationFees.map((feeId) => feeId); const selectedRegistrationDiscountsIds = - formData.selectedRegistrationDiscounts.map((discountId) => discountId); - const selectedTuitionFeesIds = formData.selectedTuitionFees.map( + formDataRef.current.selectedRegistrationDiscounts.map( + (discountId) => discountId + ); + const selectedTuitionFeesIds = formDataRef.current.selectedTuitionFees.map( (feeId) => feeId ); - const selectedTuitionDiscountsIds = formData.selectedTuitionDiscounts.map( - (discountId) => discountId - ); - const selectedFileGroup = formData.selectedFileGroup; + const selectedTuitionDiscountsIds = + formDataRef.current.selectedTuitionDiscounts.map( + (discountId) => discountId + ); + const selectedFileGroup = formDataRef.current.selectedFileGroup; const allFeesIds = [ ...selectedRegistrationFeesIds, @@ -277,25 +348,31 @@ export default function CreateSubscriptionPage() { const data = { student: { - last_name: formData.studentLastName, - first_name: formData.studentFirstName, - level: formData.studentLevel, - gender: formData.studentGender, - guardians: formData.selectedGuardians.length - ? formData.selectedGuardians.map((guardianId) => ({ id: guardianId })) - : formData.isExistingParentProfile + last_name: formDataRef.current.studentLastName, + first_name: formDataRef.current.studentFirstName, + ...(formDataRef.current.studentLevel && { + level: formDataRef.current.studentLevel, + }), + ...(formDataRef.current.studentGender && { + gender: formDataRef.current.studentGender, + }), + guardians: formDataRef.current.selectedGuardians.length + ? formDataRef.current.selectedGuardians.map((guardianId) => ({ + id: guardianId, + })) + : formDataRef.current.isExistingParentProfile ? [ { profile_role_data: { establishment: selectedEstablishmentId, role_type: 2, - is_active: false, - profile: formData.existingProfileId, + is_active: true, + profile: formDataRef.current.existingProfileId, }, - last_name: formData.guardianLastName, - first_name: formData.guardianFirstName, - birth_date: formData.guardianBirthDate, - phone: formData.guardianPhone, + last_name: formDataRef.current.guardianLastName, + first_name: formDataRef.current.guardianFirstName, + birth_date: formDataRef.current.guardianBirthDate, + phone: formDataRef.current.guardianPhone, }, ] : [ @@ -305,14 +382,14 @@ export default function CreateSubscriptionPage() { role_type: 2, is_active: false, profile_data: { - email: formData.guardianEmail, + email: formDataRef.current.guardianEmail, password: 'Provisoire01!', - username: formData.guardianEmail, + username: formDataRef.current.guardianEmail, }, }, - last_name: formData.guardianLastName, - first_name: formData.guardianFirstName, - phone: formData.guardianPhone, + last_name: formDataRef.current.guardianLastName, + first_name: formDataRef.current.guardianFirstName, + phone: formDataRef.current.guardianPhone, }, ], sibling: [], @@ -321,7 +398,7 @@ export default function CreateSubscriptionPage() { discounts: allDiscountsIds, fileGroup: selectedFileGroup, establishment: selectedEstablishmentId, - school_year: formData.schoolYear, + school_year: formDataRef.current.schoolYear, }; setIsLoading(true); @@ -590,9 +667,19 @@ export default function CreateSubscriptionPage() { return finalAmount.toFixed(2); }; + if (isLoading === true) { + return ; // Affichez le composant Loader + } + return (
-

Créer un dossier d'inscription

+ {registerFormID ? ( +

+ Modifier un dossier d'inscription +

+ ) : ( +

Créer un dossier d'inscription

+ )} {/* Sélection de l'année scolaire */}
@@ -1000,7 +1087,7 @@ export default function CreateSubscriptionPage() { {/* Bouton de soumission */}
- {activeTab === 'currentYear' || - activeTab === 'nextYear' || - activeTab === 'historical' ? ( + {activeTab === CURRENT_YEAR || + activeTab === NEXT_YEAR || + activeTab === HISTORICAL ? (
@@ -752,25 +860,25 @@ export default function Page({ params: { locale } }) { setPopupVisible(false)} + uniqueConfirmButton={true} />
diff --git a/Front-End/src/app/[locale]/users/subscribe/page.js b/Front-End/src/app/[locale]/users/subscribe/page.js index b1f3c38..3c07064 100644 --- a/Front-End/src/app/[locale]/users/subscribe/page.js +++ b/Front-End/src/app/[locale]/users/subscribe/page.js @@ -14,7 +14,6 @@ import { FE_USERS_LOGIN_URL } from '@/utils/Url'; import { useCsrfToken } from '@/context/CsrfContext'; import { subscribe } from '@/app/actions/authAction'; import logger from '@/utils/logger'; -import { useEstablishment } from '@/context/EstablishmentContext'; export default function Page() { const searchParams = useSearchParams(); @@ -43,6 +42,7 @@ export default function Page() { password2: formData.get('password2'), establishment_id: establishment_id, }; + setIsLoading(true); subscribe(data, csrfToken) .then((data) => { logger.debug('Success:', data); @@ -51,9 +51,11 @@ export default function Page() { setPassword2FieldError(''); setErrorMessage(''); if (isOK(data)) { + setIsLoading(false); setPopupMessage(data.message); setPopupVisible(true); } else { + setIsLoading(false); if (data.errorMessage) { setErrorMessage(data.errorMessage); } @@ -65,6 +67,7 @@ export default function Page() { } }) .catch((error) => { + setIsLoading(false); logger.error('Error fetching data:', error); error = error.errorMessage; logger.debug(error); @@ -148,6 +151,7 @@ export default function Page() { router.push(`${FE_USERS_LOGIN_URL}`); }} onCancel={() => setPopupVisible(false)} + uniqueConfirmButton={true} /> ); diff --git a/Front-End/src/app/actions/subscriptionAction.js b/Front-End/src/app/actions/subscriptionAction.js index 0e3ea36..c3a28cd 100644 --- a/Front-End/src/app/actions/subscriptionAction.js +++ b/Front-End/src/app/actions/subscriptionAction.js @@ -6,9 +6,7 @@ import { BE_SUBSCRIPTION_ABSENCES_URL, } from '@/utils/Url'; -export const CURRENT_YEAR = 'current_year'; -export const NEXT_YEAR = 'next_year'; -export const HISTORICAL = 'historical'; +import { CURRENT_YEAR, NEXT_YEAR, HISTORICAL } from '@/utils/constants'; const requestResponseHandler = async (response) => { const body = await response.json(); diff --git a/Front-End/src/utils/Url.js b/Front-End/src/utils/Url.js index 7b2e7ff..72cc8a4 100644 --- a/Front-End/src/utils/Url.js +++ b/Front-End/src/utils/Url.js @@ -69,7 +69,7 @@ export const FE_ADMIN_HOME_URL = `/admin`; // ADMIN/SUBSCRIPTIONS URL export const FE_ADMIN_SUBSCRIPTIONS_URL = `/admin/subscriptions`; export const FE_ADMIN_SUBSCRIPTIONS_CREATE_URL = `/admin/subscriptions/createSubscription`; -export const FE_ADMIN_SUBSCRIPTIONS_EDIT_URL = `/admin/subscriptions/editInscription`; +export const FE_ADMIN_SUBSCRIPTIONS_EDIT_URL = `/admin/subscriptions/editSubscription`; export const FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL = `/admin/subscriptions/validateSubscription`; //ADMIN/CLASSES URL @@ -98,7 +98,7 @@ export const FE_ADMIN_SETTINGS_URL = `/admin/settings`; export const FE_PARENTS_HOME_URL = `/parents`; export const FE_PARENTS_MESSAGERIE_URL = `/parents/messagerie`; export const FE_PARENTS_SETTINGS_URL = `/parents/settings`; -export const FE_PARENTS_EDIT_INSCRIPTION_URL = `/parents/editInscription`; +export const FE_PARENTS_EDIT_SUBSCRIPTION_URL = `/parents/editSubscription`; // API DOCUSEAL export const FE_API_DOCUSEAL_GENERATE_TOKEN = `/api/docuseal/generateToken`; diff --git a/Front-End/src/utils/constants.js b/Front-End/src/utils/constants.js index 7af7231..f0ff33d 100644 --- a/Front-End/src/utils/constants.js +++ b/Front-End/src/utils/constants.js @@ -14,3 +14,18 @@ export const genders = [ { value: '1', label: 'Garçon' }, { value: '2', label: 'Fille' }, ]; + +export const RegistrationFormStatus = { + STATUS_TO_SEND: 1, + STATUS_PENDING: 2, + STATUS_TO_VALIDATE: 3, + STATUS_TO_FOLLOW_UP: 4, // Non testé + STATUS_VALIDATED: 5, + STATUS_ARCHIVED: 6, + STATUS_SEPA_PENDING: 7, + STATUS_SEPA_TO_SEND: 8, +}; + +export const CURRENT_YEAR = 'current_year'; +export const NEXT_YEAR = 'next_year'; +export const HISTORICAL = 'historical';