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:
N3WT DE COMPET
2025-03-18 21:06:44 +01:00
parent 173ac47fb2
commit fb73f9e9a8
11 changed files with 277 additions and 181 deletions

View File

@ -9,11 +9,14 @@ import DiscountsSection from '@/components/Structure/Tarification/DiscountsSecti
import SectionTitle from '@/components/SectionTitle';
import ProgressStep from '@/components/ProgressStep';
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({
studentLastName: '',
studentFirstName: '',
guardianLastName: '',
guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
selectedGuardians: [],
@ -31,6 +34,10 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
const [existingGuardians, setExistingGuardians] = useState([]);
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
const [filteredStudents, setFilteredStudents] = useState(students);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const stepTitles = {
1: 'Nouvel élève',
@ -45,8 +52,8 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
const isStep2Valid = (
(formData.responsableType === "new" && formData.guardianEmail.length > 0) ||
(formData.responsableType === "existing" && formData.selectedGuardians.length > 0)
formData.selectedGuardians.length > 0 ||
(!formData.emailError && formData.guardianEmail.length > 0 && filteredStudents.length === 0)
);
const isStep3Valid = formData.selectedRegistrationFees.length > 0;
const isStep4Valid = formData.selectedTuitionFees.length > 0;
@ -99,9 +106,35 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
};
const nextStep = () => {
if (step < steps.length) {
setStep(step + 1);
}
if (step === 2) {
// 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 = () => {
@ -120,13 +153,27 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
};
const handleResponsableSelection = (guardianId, guardianEmail) => {
setFormData((prevData) => {
const isSelected = prevData.selectedGuardians.includes(guardianId);
const selectedGuardians = isSelected
? prevData.selectedGuardians.filter(id => id !== guardianId)
: [...prevData.selectedGuardians, guardianId];
return { ...prevData, selectedGuardians, guardianEmail };
});
setFormData((prevData) => {
const isSelected = prevData.selectedGuardians.includes(guardianId);
const selectedGuardians = isSelected
? prevData.selectedGuardians.filter(id => id !== guardianId) // Retirer le guardian si déjà sélectionné
: [...prevData.selectedGuardians, guardianId]; // Ajouter le guardian s'il n'est pas sélectionné
// 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 = () => {
@ -265,117 +312,141 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
{step === 2 && (
<div className="mt-6">
<div className="flex flex-col space-y-4 mt-6">
<label className="flex items-center space-x-3">
<input
type="radio"
name="responsableType"
value="new"
checked={formData.responsableType === 'new'}
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"
/>
<span className="text-gray-900">Nouveau Responsable</span>
</label>
<label className="flex items-center space-x-3">
<input
type="radio"
name="responsableType"
value="existing"
checked={formData.responsableType === 'existing'}
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"
/>
<span className="text-gray-900">Responsable Existant</span>
</label>
</div>
{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
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"
{/* Nom, Prénom et Téléphone */}
<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
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' && (
<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>
{/* Email du responsable */}
<InputTextIcon
name="guardianEmail"
type="email"
IconItem={Mail}
placeholder="Email du responsable"
value={formData.guardianEmail}
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>
</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>
)}
{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>
)}
@ -641,6 +712,13 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
name="Create" />
)}
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
onCancel={() => setPopupVisible(false)}
/>
</div>
);
}

View File

@ -28,14 +28,14 @@ const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = fa
<div className="flex justify-end space-x-2">
{!uniqueConfirmButton && (
<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}
>
Annuler
</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}
>
{uniqueConfirmButton ? 'Fermer' : 'Confirmer'}