Files
n3wt-school/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js

1247 lines
42 KiB
JavaScript

'use client';
import React, { useState, useRef, useEffect } from 'react';
import { User, Mail } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon';
import ToggleSwitch from '@/components/ToggleSwitch';
import Button from '@/components/Button';
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 InputPhone from '@/components/InputPhone';
import CheckBox from '@/components/CheckBox';
import RadioList from '@/components/RadioList';
import SelectChoice from '@/components/SelectChoice';
import Loader from '@/components/Loader';
import { getCurrentSchoolYear, getNextSchoolYear } from '@/utils/Date';
import logger from '@/utils/logger';
import { levels, genders } from '@/utils/constants';
import { useEstablishment } from '@/context/EstablishmentContext';
import { useSearchParams, useRouter } from 'next/navigation';
import {
fetchRegisterForm,
fetchStudents,
createRegisterForm,
editRegisterForm,
} from '@/app/actions/subscriptionAction';
import {
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchTuitionFees,
} from '@/app/actions/schoolAction';
import {
fetchRegistrationFileGroups,
fetchRegistrationSchoolFileMasters,
fetchRegistrationParentFileMasters,
cloneTemplate,
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, BASE_URL } from '@/utils/Url';
import { useNotification } from '@/context/NotificationContext';
export default function CreateSubscriptionPage() {
const [formData, setFormData] = useState({
studentLastName: '',
studentFirstName: '',
studentLevel: '',
studentGender: '',
guardianLastName: '',
guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
guardianProfileRole: '',
selectedGuardians: [],
associatedGuardians: [],
autoMail: false,
selectedRegistrationDiscounts: [],
selectedRegistrationFees: [],
selectedTuitionDiscounts: [],
selectedTuitionFees: [],
selectedFileGroup: null,
schoolYear: getCurrentSchoolYear(),
});
const searchParams = useSearchParams();
const { showNotification } = useNotification();
// 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([]);
const [registrationFees, setRegistrationFees] = useState([]);
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 [selectedStudent, setSelectedEleve] = useState(null);
const [isNewResponsable, setIsNewResponsable] = useState(true);
const [initialGuardianEmail, setInitialGuardianEmail] = useState('');
const { getNiveauLabel } = useClasses();
const formDataRef = useRef(formData);
const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
const csrfToken = useCsrfToken();
const router = useRouter();
const isSubmitDisabled = () => {
// Vérifie si les champs requis sont remplis
const requiredFields = [
'schoolYear',
'studentLastName',
'studentFirstName',
];
const hasErrors = requiredFields.some(
(field) => getLocalError(field) !== ''
);
// Vérifie si un groupe de fichiers est sélectionné
const isFileGroupSelected = formData.selectedFileGroup !== null;
// Vérifie si au moins un frais de scolarité est sélectionné
const hasSelectedTuitionFees = formData.selectedTuitionFees.length > 0;
// Vérifie les conditions spécifiques pour le responsable
const isGuardianValid = isNewResponsable
? getLocalError('guardianEmail') === ''
: formData.selectedGuardians.length > 0;
// Retourne true si une des conditions n'est pas remplie
return (
hasErrors ||
!isFileGroupSelected ||
!hasSelectedTuitionFees ||
!isGuardianValid
);
};
const getLocalError = (field) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (field === 'guardianEmail') {
if (!formData.guardianEmail || formData.guardianEmail.trim() === '') {
return 'Champs requis';
}
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
);
if (existingGuardian) {
return ''; // Pas d'erreur, mais l'email existe déjà
}
}
if (
// Student Form
(field === 'studentLastName' &&
(!formData.studentLastName ||
formData.studentLastName.trim() === '')) ||
(field === 'studentFirstName' &&
(!formData.studentFirstName ||
formData.studentFirstName.trim() === '')) ||
(field === 'schoolYear' &&
(!formData.schoolYear || formData.schoolYear.trim() === ''))
) {
return 'Champs requis';
}
return '';
};
const requestErrorHandler = (err) => {
logger.error('Error fetching data:', err);
//setErrors(err);
};
useEffect(() => {
formDataRef.current = formData;
}, [formData]);
useEffect(() => {
if (!formData.guardianEmail) {
// Si l'email est vide, réinitialiser existingProfileId et existingProfileInSchool
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: false,
existingProfileId: null,
existingProfileInSchool: false,
associatedGuardians: [],
}));
return;
}
// Vérifiez si le profil existe dans la liste des profils
const existingProfile = profiles.find(
(profile) => profile.email === formData.guardianEmail
);
if (existingProfile) {
// Vérifiez si le profil parent est associé à l'établissement sélectionné
const isInSchool = existingProfile.roles.some(
(role) =>
role.role_type === 2 && role.establishment === selectedEstablishmentId
);
// Récupérer l'ID de l'id_associated_person si applicable
const associatedPersonId = existingProfile.roles.find(
(role) =>
role.role_type === 2 && role.establishment === selectedEstablishmentId
)?.id_associated_person;
// Mettre à jour les variables en fonction des résultats
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: true,
existingProfileId: existingProfile.id, // Récupérer l'ID du profil associé
existingProfileInSchool: isInSchool, // Vérifie si le profil est dans l'établissement
guardianEmail: existingProfile.email || '',
associatedGuardians: associatedPersonId
? [associatedPersonId] // Ajouter l'ID de l'id_associated_person si trouvé
: [],
}));
} else {
// Si aucun profil avec cet email n'existe, réinitialiser les champs
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: false,
existingProfileId: null,
existingProfileInSchool: false,
associatedGuardians: [],
}));
}
}, [formData.guardianEmail, profiles, selectedEstablishmentId]);
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 || '',
guardianProfileRole:
data?.student?.guardians[0]?.profile_role || '',
}));
setIsNewResponsable(
!Array.isArray(data?.student?.guardians) ||
data.student.guardians.length <= 1
);
// Définir l'email initial
setInitialGuardianEmail(
data?.student?.guardians[0]?.associated_profile_email || ''
);
})
.catch(requestErrorHandler);
}
fetchStudents(selectedEstablishmentId)
.then((studentsData) => {
setStudents(studentsData);
})
.catch(requestErrorHandler);
fetchRegistrationDiscounts(selectedEstablishmentId)
.then((data) => {
setRegistrationDiscounts(data);
})
.catch(requestErrorHandler);
fetchTuitionDiscounts(selectedEstablishmentId)
.then((data) => {
setTuitionDiscounts(data);
})
.catch(requestErrorHandler);
fetchRegistrationFees(selectedEstablishmentId)
.then((data) => {
setRegistrationFees(data);
// Sélectionner par défaut les frais d'inscription
setFormData((prevData) => ({
...prevData,
selectedRegistrationFees: data.map((fee) => fee.id),
}));
})
.catch(requestErrorHandler);
fetchTuitionFees(selectedEstablishmentId)
.then((data) => {
setTuitionFees(data);
})
.catch(requestErrorHandler);
fetchRegistrationFileGroups(selectedEstablishmentId)
.then((data) => {
setGroups(data);
})
.catch(requestErrorHandler);
fetchRegistrationSchoolFileMasters()
.then((data) => {
setSchoolFileMasters(data);
})
.catch(requestErrorHandler);
fetchRegistrationParentFileMasters()
.then((data) => {
setParentFileMasters(data);
})
.catch(requestErrorHandler);
}
}, [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) => ({
...prevState,
[name]: value,
}));
};
const resetGuardianFields = () => {
setFormData((prevData) => ({
...prevData,
guardianLastName: '',
guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
selectedGuardians: [],
}));
};
// Gestion du changement du toggle
const handleToggleChange = (e) => {
const isChecked = e.target.checked;
setIsNewResponsable(isChecked);
if (isChecked) {
// Réinitialiser les champs si on passe à "Nouveau responsable"
resetGuardianFields();
}
};
const submit = () => {
logger.debug('formData:', formDataRef.current);
const selectedRegistrationFeesIds =
formDataRef.current.selectedRegistrationFees.map((feeId) => feeId);
const selectedRegistrationDiscountsIds =
formDataRef.current.selectedRegistrationDiscounts.map(
(discountId) => discountId
);
const selectedTuitionFeesIds = formDataRef.current.selectedTuitionFees.map(
(feeId) => feeId
);
const selectedTuitionDiscountsIds =
formDataRef.current.selectedTuitionDiscounts.map(
(discountId) => discountId
);
const selectedFileGroup = formDataRef.current.selectedFileGroup;
const allFeesIds = [
...selectedRegistrationFeesIds,
...selectedTuitionFeesIds,
];
const allDiscountsIds = [
...selectedRegistrationDiscountsIds,
...selectedTuitionDiscountsIds,
];
// Vérifiez si le profil existe dans la liste des profils
const existingProfile = profiles.find(
(profile) => profile.id === formDataRef.current.existingProfileId
);
const guardians = (() => {
if (formDataRef.current.selectedGuardians.length > 0) {
// Cas 3 : Des guardians sont sélectionnés
logger.debug('Cas 3 : Des guardians sont sélectionnés');
return formDataRef.current.selectedGuardians.map((guardianId) => ({
id: guardianId,
}));
} else if (formDataRef.current.isExistingParentProfile) {
if (initialGuardianEmail !== existingProfile?.email) {
// Cas 2 : Profil existant différent de l'ancien
logger.debug(
"Cas 2 : Profil existant différent de l'ancien, mise à jour du profil",
{
existingProfile,
guardianEmail: formDataRef.current.guardianEmail,
}
);
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: true,
profile: formDataRef.current.existingProfileId,
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
phone: formDataRef.current.guardianPhone,
},
];
} else {
// Cas 4 : Profil existant avec le même email
logger.debug('Cas 4 : Profil existant avec le même email', {
existingProfile,
});
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: true,
profile: formDataRef.current.existingProfileId,
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
phone: formDataRef.current.guardianPhone,
},
];
}
} else {
// Cas 1 : Profil inexistant
logger.debug("Cas 1 : Profil inexistant, création d'un nouveau profil");
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: false,
profile_data: {
email: formDataRef.current.guardianEmail,
password: 'Provisoire01!',
username: formDataRef.current.guardianEmail,
},
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
birth_date: formDataRef.current.guardianBirthDate,
phone: formDataRef.current.guardianPhone,
},
];
}
})();
const data = {
student: {
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,
sibling: [],
},
fees: allFeesIds,
discounts: allDiscountsIds,
fileGroup: selectedFileGroup,
establishment: selectedEstablishmentId,
school_year: formDataRef.current.schoolYear,
};
setIsLoading(true);
if (registerFormID) {
const formData = new FormData();
// Ajouter les données JSON sous forme de chaîne
formData.append('data', JSON.stringify(data));
// Mode édition
editRegisterForm(registerFormID, formData, csrfToken)
.then((response) => {
logger.debug('Dossier mis à jour avec succès:', response);
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
logger.error('Erreur lors de la mise à jour du dossier:', error);
showNotification(
"Erreur lors de la mise à jour du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_06'
);
});
} else {
// Création du dossier d'inscription
createRegisterForm(data, csrfToken)
.then((data) => {
// Clonage des schoolFileTemplates
const masters = schoolFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const parentMasters = parentFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const clonePromises = masters.map((templateMaster) =>
cloneTemplate(
templateMaster.id,
formDataRef.current.guardianEmail,
templateMaster.is_required,
selectedEstablishmentId,
apiDocuseal
)
.then((clonedDocument) => {
const cloneData = {
name: `${templateMaster.name}_${formDataRef.current.studentFirstName}_${formDataRef.current.studentLastName}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster.id,
registration_form: data.student.id,
};
return createRegistrationSchoolFileTemplate(
cloneData,
csrfToken
)
.then((response) =>
logger.debug('Template enregistré avec succès:', response)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_03'
);
});
})
.catch((error) => {
setIsLoading(false);
logger.error('Error during cloning or sending:', error);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_05'
);
})
);
// Clonage des parentFileTemplates
const parentClonePromises = parentMasters.map((parentMaster) => {
const parentTemplateData = {
master: parentMaster.id,
registration_form: data.student.id,
};
return createRegistrationParentFileTemplate(
parentTemplateData,
csrfToken
)
.then((response) =>
logger.debug(
'Parent template enregistré avec succès:',
response
)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du parent template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_02'
);
});
});
// Attendre que tous les clones soient créés
Promise.all([...clonePromises, ...parentClonePromises])
.then(() => {
// Redirection après succès
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_04'
);
logger.error('Error during cloning or sending:', error);
});
})
.catch((error) => {
setIsLoading(false);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_01'
);
logger.error('Error during register form creation:', error);
});
}
};
const handleEleveSelection = (student) => {
setSelectedEleve(student);
setExistingGuardians(student.guardians);
};
// Gestion des changements dans la checkbox
const handleGuardianCheckboxChange = (guardian) => {
setFormData((prevData) => {
const isSelected = prevData.selectedGuardians.includes(guardian.id);
const updatedSelectedGuardians = isSelected
? prevData.selectedGuardians.filter((id) => id !== guardian.id)
: [...prevData.selectedGuardians, guardian.id];
return {
...prevData,
selectedGuardians: updatedSelectedGuardians,
guardianEmail: guardian.associated_profile_email,
};
});
};
// Gestion de la désélection d'un élève
const handleEleveDeselection = () => {
setSelectedEleve(null);
setExistingGuardians([]);
resetGuardianFields();
};
const handleRegistrationFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedRegistrationFees =
prevData.selectedRegistrationFees.includes(feeId)
? prevData.selectedRegistrationFees.filter((id) => id !== feeId)
: [...prevData.selectedRegistrationFees, feeId];
const finalAmount = calculateFinalRegistrationAmount(
selectedRegistrationFees,
prevData.selectedRegistrationDiscounts
);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationFees };
});
};
const handleTuitionFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedTuitionFees = prevData.selectedTuitionFees.includes(feeId)
? prevData.selectedTuitionFees.filter((id) => id !== feeId)
: [...prevData.selectedTuitionFees, feeId];
const finalAmount = calculateFinalTuitionAmount(
selectedTuitionFees,
prevData.selectedTuitionDiscounts
);
setTotalTuitionAmount(finalAmount);
return { ...prevData, selectedTuitionFees };
});
};
const handleRegistrationDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedRegistrationDiscounts =
prevData.selectedRegistrationDiscounts.includes(discountId)
? prevData.selectedRegistrationDiscounts.filter(
(id) => id !== discountId
)
: [...prevData.selectedRegistrationDiscounts, discountId];
const finalAmount = calculateFinalRegistrationAmount(
prevData.selectedRegistrationFees,
selectedRegistrationDiscounts
);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationDiscounts };
});
};
const handleTuitionDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedTuitionDiscounts =
prevData.selectedTuitionDiscounts.includes(discountId)
? prevData.selectedTuitionDiscounts.filter((id) => id !== discountId)
: [...prevData.selectedTuitionDiscounts, discountId];
const finalAmount = calculateFinalTuitionAmount(
prevData.selectedTuitionFees,
selectedTuitionDiscounts
);
setTotalTuitionAmount(finalAmount);
return { ...prevData, selectedTuitionDiscounts };
});
};
const calculateFinalRegistrationAmount = (
selectedRegistrationFees,
selectedRegistrationDiscounts
) => {
const totalFees = selectedRegistrationFees.reduce((sum, feeId) => {
const fee = registrationFees.find((f) => f.id === feeId);
if (fee && !isNaN(parseFloat(fee.base_amount))) {
return sum + parseFloat(fee.base_amount);
}
return sum;
}, 0);
const totalDiscounts = selectedRegistrationDiscounts.reduce(
(sum, discountId) => {
const discount = registrationDiscounts.find((d) => d.id === discountId);
if (discount) {
if (
discount.discount_type === 0 &&
!isNaN(parseFloat(discount.amount))
) {
// Currency
return sum + parseFloat(discount.amount);
} else if (
discount.discount_type === 1 &&
!isNaN(parseFloat(discount.amount))
) {
// Percent
return sum + (totalFees * parseFloat(discount.amount)) / 100;
}
}
return sum;
},
0
);
const finalAmount = totalFees - totalDiscounts;
return finalAmount.toFixed(2);
};
const calculateFinalTuitionAmount = (
selectedTuitionFees,
selectedTuitionDiscounts
) => {
const totalFees = selectedTuitionFees.reduce((sum, feeId) => {
const fee = tuitionFees.find((f) => f.id === feeId);
if (fee && !isNaN(parseFloat(fee.base_amount))) {
return sum + parseFloat(fee.base_amount);
}
return sum;
}, 0);
const totalDiscounts = selectedTuitionDiscounts.reduce(
(sum, discountId) => {
const discount = tuitionDiscounts.find((d) => d.id === discountId);
if (discount) {
if (
discount.discount_type === 0 &&
!isNaN(parseFloat(discount.amount))
) {
// Currency
return sum + parseFloat(discount.amount);
} else if (
discount.discount_type === 1 &&
!isNaN(parseFloat(discount.amount))
) {
// Percent
return sum + (totalFees * parseFloat(discount.amount)) / 100;
}
}
return sum;
},
0
);
const finalAmount = totalFees - totalDiscounts;
return finalAmount.toFixed(2);
};
if (isLoading === true) {
return <Loader />; // Affichez le composant Loader
}
return (
<div className="mx-auto p-12 space-y-12">
{registerFormID ? (
<h1 className="text-2xl font-bold">
Modifier un dossier d&apos;inscription
</h1>
) : (
<h1 className="text-2xl font-bold">
Créer un dossier d&apos;inscription
</h1>
)}
{/* Sélection de l'année scolaire */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-8">
<div>
<SelectChoice
name="schoolYear"
label="Année scolaire"
placeHolder="Sélectionnez une année scolaire"
choices={[
{ value: getCurrentSchoolYear(), label: getCurrentSchoolYear() },
{ value: getNextSchoolYear(), label: getNextSchoolYear() },
]}
selected={formData.schoolYear}
callback={(e) =>
setFormData((prevData) => ({
...prevData,
schoolYear: e.target.value,
}))
}
errorLocalMsg={getLocalError('schoolYear')}
required
/>
</div>
</div>
{/* Informations sur l'élève */}
<SectionTitle title="Elève">
{/* Nom et Prénom côte à côte */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<InputTextIcon
name="studentLastName"
type="text"
IconItem={User}
label="Nom de l'élève"
placeholder="Nom de l'élève"
value={formData.studentLastName}
onChange={handleChange}
errorLocalMsg={getLocalError('studentLastName')}
required
/>
<InputTextIcon
name="studentFirstName"
type="text"
IconItem={User}
label="Prénom de l'élève"
placeholder="Prénom de l'élève"
value={formData.studentFirstName}
onChange={handleChange}
errorLocalMsg={getLocalError('studentFirstName')}
required
/>
</div>
{/* Genre et Niveau côte à côte */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-4">
<SelectChoice
name="studentGender"
label="Genre"
placeHolder="Sélectionnez un genre"
choices={genders}
selected={formData.studentGender}
callback={(e) =>
setFormData((prevData) => ({
...prevData,
studentGender: e.target.value,
}))
}
/>
<SelectChoice
name="studentLevel"
label="Niveau"
placeHolder="Sélectionnez un niveau"
choices={levels}
selected={formData.studentLevel}
callback={(e) =>
setFormData((prevData) => ({
...prevData,
studentLevel: e.target.value,
}))
}
/>
</div>
</SectionTitle>
{/* Informations sur le responsable */}
<SectionTitle title="Responsable principal">
<ToggleSwitch
name="isNewResponsable"
label="Nouveau responsable ?"
checked={isNewResponsable}
onChange={handleToggleChange}
/>
{/* Nom et Prénom côte à côte */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-8">
<InputTextIcon
name="guardianLastName"
type="text"
IconItem={User}
label="Nom du responsable"
placeholder="Nom du responsable"
value={formData.guardianLastName}
onChange={handleChange}
enable={isNewResponsable}
/>
<InputTextIcon
name="guardianFirstName"
type="text"
IconItem={User}
label="Prénom du responsable"
placeholder="Prénom du responsable"
value={formData.guardianFirstName}
onChange={handleChange}
enable={isNewResponsable}
/>
</div>
{/* Email et Numéro de téléphone côte à côte */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-4">
<InputTextIcon
name="guardianEmail"
type="email"
IconItem={Mail}
label="Email du responsable"
placeholder="Email du responsable"
value={formData.guardianEmail}
onChange={handleChange}
errorMsg={getLocalError('guardianEmail') && isNewResponsable}
required
enable={isNewResponsable}
/>
<InputPhone
name="guardianPhone"
label="Numéro de téléphone"
value={formData.guardianPhone}
onChange={handleChange}
enable={isNewResponsable}
/>
</div>
</SectionTitle>
{/* Tableau des élèves pour responsable existant */}
{!isNewResponsable && (
<div className="mt-4">
<Table
data={students}
columns={[
{
name: 'photo',
transform: (row) => (
<div className="flex justify-center items-center">
{row.photo ? (
<a
href={`${BASE_URL}${row.photo}`} // Lien vers la photo
target="_blank"
rel="noopener noreferrer"
>
<img
src={`${BASE_URL}${row.photo}`}
alt={`${row.first_name} ${row.last_name}`}
className="w-10 h-10 object-cover transition-transform duration-200 hover:scale-125 cursor-pointer rounded-full"
/>
</a>
) : (
<div className="w-10 h-10 flex items-center justify-center bg-gray-200 rounded-full">
<span className="text-gray-500 text-sm font-semibold">
{row.first_name[0]}
{row.last_name[0]}
</span>
</div>
)}
</div>
),
},
{
name: 'Nom',
transform: (row) => row.last_name,
},
{
name: 'Prénom',
transform: (row) => row.first_name,
},
{
name: 'Niveau',
transform: (row) => (
<div className="text-center">{getNiveauLabel(row.level)}</div>
),
},
]}
isSelectable={true}
onRowClick={(event) => {
if (event.deselected) {
handleEleveDeselection();
} else {
handleEleveSelection(event);
}
}}
rowClassName={(row) =>
selectedStudent && selectedStudent.id === row.id
? 'bg-emerald-600 text-white'
: ''
}
selectedRows={selectedStudent ? [selectedStudent.id] : []} // Assurez-vous que selectedRows est un tableau
/>
{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} className="mt-2">
<CheckBox
item={{ id: guardian.id }}
formData={{
selectedGuardians: formData.selectedGuardians,
}}
handleChange={() => handleGuardianCheckboxChange(guardian)}
fieldName="selectedGuardians"
itemLabelFunc={() =>
guardian.last_name && guardian.first_name
? `${guardian.last_name} ${guardian.first_name} - ${guardian.associated_profile_email}`
: `${guardian.associated_profile_email}`
}
/>
</div>
))}
</div>
)}
</div>
)}
{/* Section des frais d'inscription */}
<SectionTitle title="Frais d'inscription">
{registrationFees.length > 0 || registrationDiscounts.length > 0 ? (
<>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Tableau des frais */}
<div>
<FeesSection
fees={registrationFees}
type={0}
subscriptionMode={true}
selectedFees={formData.selectedRegistrationFees}
handleFeeSelection={handleRegistrationFeeSelection}
/>
</div>
{/* Tableau des réductions */}
<div>
{registrationDiscounts.length > 0 ? (
<DiscountsSection
discounts={registrationDiscounts}
type={0}
subscriptionMode={true}
selectedDiscounts={formData.selectedRegistrationDiscounts}
handleDiscountSelection={
handleRegistrationDiscountSelection
}
/>
) : (
<p
className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Information</strong>
<span className="block sm:inline">
Aucune réduction n&apos;a été créée sur les frais
d&apos;inscription.
</span>
</p>
)}
</div>
</div>
{/* Montant total */}
<div className="flex items-center justify-between bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-300 mt-4">
<span className="text-sm font-medium text-gray-600">
Montant total des frais d&apos;inscription :
</span>
<span className="text-lg font-semibold text-gray-800">
{totalRegistrationAmount}
</span>
</div>
</>
) : (
<p
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Attention!</strong>
<span className="block sm:inline">
Aucun frais d&apos;inscription n&apos;a été créé.
</span>
</p>
)}
</SectionTitle>
{/* Section des frais de scolarité */}
<SectionTitle title="Frais de scolarité">
{tuitionFees.length > 0 || tuitionDiscounts.length > 0 ? (
<>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Tableau des frais */}
<div>
<FeesSection
fees={tuitionFees}
type={1}
subscriptionMode={true}
selectedFees={formData.selectedTuitionFees}
handleFeeSelection={handleTuitionFeeSelection}
/>
</div>
{/* Tableau des réductions */}
<div>
{tuitionDiscounts.length > 0 ? (
<DiscountsSection
discounts={tuitionDiscounts}
type={1}
subscriptionMode={true}
selectedDiscounts={formData.selectedTuitionDiscounts}
handleDiscountSelection={handleTuitionDiscountSelection}
/>
) : (
<p
className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Information</strong>
<span className="block sm:inline">
Aucune réduction n&apos;a été créée sur les frais de
scolarité.
</span>
</p>
)}
</div>
</div>
{/* Montant total */}
<div className="flex items-center justify-between bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-300 mt-4">
<span className="text-sm font-medium text-gray-600">
Montant total des frais de scolarité :
</span>
<span className="text-lg font-semibold text-gray-800">
{totalTuitionAmount}
</span>
</div>
</>
) : (
<p
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Attention!</strong>
<span className="block sm:inline">
Aucun frais de scolarité n&apos;a été créé.
</span>
</p>
)}
</SectionTitle>
{/* Section des groupes de documents */}
<SectionTitle title="Formulaires d'inscription">
{groups.length > 0 ? (
<div className="space-y-4">
<RadioList
sectionLabel="Sélectionner un formulaire"
items={groups.map((group) => ({
id: group.id,
label: `${group.name}${
group.description ? ` (${group.description})` : ''
}`,
}))}
formData={{
...formData,
selectedFileGroup: parseInt(formData.selectedFileGroup, 10),
}}
handleChange={(e) => {
const value = parseInt(e.target.value, 10);
setFormData((prevData) => ({
...prevData,
selectedFileGroup: value,
}));
}}
fieldName="selectedFileGroup"
required
className="mt-4"
/>
</div>
) : (
<p
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<strong className="font-bold">Attention!</strong>
<span className="block sm:inline">
Aucun groupe de documents n&apos;a été créé.
</span>
</p>
)}
</SectionTitle>
{/* Bouton de soumission */}
<div className="flex justify-end">
<Button
text={`${registerFormID ? 'Modifier' : 'Créer'} le dossier`}
onClick={submit}
className={`px-6 py-2 rounded-md shadow ${
isSubmitDisabled()
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
: 'bg-emerald-500 text-white hover:bg-emerald-600'
}`}
primary
disabled={isSubmitDisabled()}
/>
</div>
</div>
);
}