mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout de la fratrie [#27]
This commit is contained in:
@ -27,7 +27,7 @@ class Guardian(models.Model):
|
|||||||
"""
|
"""
|
||||||
last_name = models.CharField(max_length=200, default="")
|
last_name = models.CharField(max_length=200, default="")
|
||||||
first_name = models.CharField(max_length=200, default="")
|
first_name = models.CharField(max_length=200, default="")
|
||||||
birth_date = models.CharField(max_length=200, default="", blank=True)
|
birth_date = models.DateField(null=True, blank=True)
|
||||||
address = models.CharField(max_length=200, default="", blank=True)
|
address = models.CharField(max_length=200, default="", blank=True)
|
||||||
phone = models.CharField(max_length=200, default="", blank=True)
|
phone = models.CharField(max_length=200, default="", blank=True)
|
||||||
profession = models.CharField(max_length=200, default="", blank=True)
|
profession = models.CharField(max_length=200, default="", blank=True)
|
||||||
@ -43,7 +43,7 @@ class Sibling(models.Model):
|
|||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
last_name = models.CharField(max_length=200, default="")
|
last_name = models.CharField(max_length=200, default="")
|
||||||
first_name = models.CharField(max_length=200, default="")
|
first_name = models.CharField(max_length=200, default="")
|
||||||
birth_date = models.CharField(max_length=200, default="", blank=True)
|
birth_date = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SIBLING"
|
return "SIBLING"
|
||||||
|
|||||||
@ -158,15 +158,33 @@ class StudentSerializer(serializers.ModelSerializer):
|
|||||||
guardians_ids.append(guardian_instance.id)
|
guardians_ids.append(guardian_instance.id)
|
||||||
return guardians_ids
|
return guardians_ids
|
||||||
|
|
||||||
def create_or_update_siblings(self, siblings_data):
|
def create_or_update_siblings(self, siblings_data, student_instance):
|
||||||
siblings_ids = []
|
"""
|
||||||
|
Crée ou met à jour les frères et sœurs associés à un étudiant.
|
||||||
|
Supprime les frères et sœurs qui ne sont plus présents dans siblings_data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Si siblings_data est vide, supprimer tous les frères et sœurs associés
|
||||||
|
if not siblings_data:
|
||||||
|
student_instance.siblings.clear() # Supprime toutes les relations
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Récupérer les IDs des frères et sœurs existants
|
||||||
|
existing_sibling_ids = set(student_instance.siblings.values_list('id', flat=True))
|
||||||
|
|
||||||
|
# Créer ou mettre à jour les frères et sœurs
|
||||||
|
updated_sibling_ids = []
|
||||||
for sibling_data in siblings_data:
|
for sibling_data in siblings_data:
|
||||||
sibling_instance, created = Sibling.objects.update_or_create(
|
sibling_instance, created = Sibling.objects.update_or_create(
|
||||||
id=sibling_data.get('id'),
|
id=sibling_data.get('id'),
|
||||||
defaults=sibling_data
|
defaults=sibling_data
|
||||||
)
|
)
|
||||||
siblings_ids.append(sibling_instance.id)
|
updated_sibling_ids.append(sibling_instance.id)
|
||||||
return siblings_ids
|
|
||||||
|
# Supprimer les frères et sœurs qui ne sont plus dans siblings_data
|
||||||
|
siblings_to_delete = existing_sibling_ids - set(updated_sibling_ids)
|
||||||
|
Sibling.objects.filter(id__in=siblings_to_delete).delete()
|
||||||
|
return updated_sibling_ids
|
||||||
|
|
||||||
def create_or_update_languages(self, languages_data):
|
def create_or_update_languages(self, languages_data):
|
||||||
languages_ids = []
|
languages_ids = []
|
||||||
@ -195,8 +213,10 @@ class StudentSerializer(serializers.ModelSerializer):
|
|||||||
languages_data = validated_data.pop('spoken_languages', [])
|
languages_data = validated_data.pop('spoken_languages', [])
|
||||||
if guardians_data:
|
if guardians_data:
|
||||||
instance.guardians.set(self.create_or_update_guardians(guardians_data))
|
instance.guardians.set(self.create_or_update_guardians(guardians_data))
|
||||||
if siblings_data:
|
|
||||||
instance.siblings.set(self.create_or_update_siblings(siblings_data))
|
sibling_ids = self.create_or_update_siblings(siblings_data, instance)
|
||||||
|
instance.siblings.set(sibling_ids)
|
||||||
|
|
||||||
if languages_data:
|
if languages_data:
|
||||||
instance.spoken_languages.set(self.create_or_update_languages(languages_data))
|
instance.spoken_languages.set(self.create_or_update_languages(languages_data))
|
||||||
|
|
||||||
|
|||||||
@ -146,7 +146,6 @@ def rfToPDF(registerForm, filename):
|
|||||||
|
|
||||||
# Vérifier si le fichier existe et le supprimer
|
# Vérifier si le fichier existe et le supprimer
|
||||||
if os.path.exists(existing_file_path):
|
if os.path.exists(existing_file_path):
|
||||||
print(f'exist ! REMOVE')
|
|
||||||
os.remove(existing_file_path)
|
os.remove(existing_file_path)
|
||||||
registerForm.registration_file.delete(save=False)
|
registerForm.registration_file.delete(save=False)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -266,6 +266,23 @@ class RegisterFormWithIdView(APIView):
|
|||||||
# Sauvegarder la photo si elle est présente dans la requête
|
# Sauvegarder la photo si elle est présente dans la requête
|
||||||
if photo_file:
|
if photo_file:
|
||||||
student = registerForm.student
|
student = registerForm.student
|
||||||
|
|
||||||
|
# Vérifier si une photo existante est déjà associée à l'étudiant
|
||||||
|
if student.photo and student.photo.name:
|
||||||
|
# Construire le chemin complet du fichier existant
|
||||||
|
if os.path.isabs(student.photo.name):
|
||||||
|
existing_file_path = student.photo.name
|
||||||
|
else:
|
||||||
|
existing_file_path = os.path.join(settings.MEDIA_ROOT, student.photo.name.lstrip('/'))
|
||||||
|
|
||||||
|
# Vérifier si le fichier existe et le supprimer
|
||||||
|
if os.path.exists(existing_file_path):
|
||||||
|
os.remove(existing_file_path)
|
||||||
|
student.photo.delete(save=False)
|
||||||
|
else:
|
||||||
|
print(f'File does not exist: {existing_file_path}')
|
||||||
|
|
||||||
|
# Sauvegarder la nouvelle photo
|
||||||
student.photo.save(photo_file.name, photo_file, save=True)
|
student.photo.save(photo_file.name, photo_file, save=True)
|
||||||
else:
|
else:
|
||||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { CloudUpload } from 'lucide-react';
|
import { CloudUpload } from 'lucide-react';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
import { BASE_URL } from '@/utils/Url';
|
||||||
|
|
||||||
export default function FileUpload({
|
export default function FileUpload({
|
||||||
selectionMessage,
|
selectionMessage,
|
||||||
@ -69,9 +70,18 @@ export default function FileUpload({
|
|||||||
<CloudUpload className="w-6 h-6 text-emerald-500" />
|
<CloudUpload className="w-6 h-6 text-emerald-500" />
|
||||||
<p className="text-sm font-medium text-gray-800">
|
<p className="text-sm font-medium text-gray-800">
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{typeof existingFile === 'string'
|
{typeof existingFile === 'string' ? (
|
||||||
? existingFile.split('/').pop()
|
<a
|
||||||
: existingFile?.name || 'Fichier inconnu'}
|
href={`${BASE_URL}${existingFile}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-500 hover:text-blue-700 underline"
|
||||||
|
>
|
||||||
|
{existingFile.split('/').pop()}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
existingFile?.name || 'Fichier inconnu'
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
|||||||
import { DocusealForm } from '@docuseal/react';
|
import { DocusealForm } from '@docuseal/react';
|
||||||
import StudentInfoForm from '@/components/Inscription/StudentInfoForm';
|
import StudentInfoForm from '@/components/Inscription/StudentInfoForm';
|
||||||
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
||||||
|
import SiblingInputFields from '@/components/Inscription/SiblingInputFields';
|
||||||
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
||||||
import ProgressStep from '@/components/ProgressStep';
|
import ProgressStep from '@/components/ProgressStep';
|
||||||
import { CheckCircle, Hourglass } from 'lucide-react';
|
import { CheckCircle, Hourglass } from 'lucide-react';
|
||||||
@ -58,6 +59,7 @@ export default function InscriptionFormShared({
|
|||||||
photo: '',
|
photo: '',
|
||||||
});
|
});
|
||||||
const [guardians, setGuardians] = useState([]);
|
const [guardians, setGuardians] = useState([]);
|
||||||
|
const [siblings, setSiblings] = useState([]);
|
||||||
|
|
||||||
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
||||||
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
||||||
@ -75,6 +77,7 @@ export default function InscriptionFormShared({
|
|||||||
const [isPage3Valid, setIsPage3Valid] = useState(false);
|
const [isPage3Valid, setIsPage3Valid] = useState(false);
|
||||||
const [isPage4Valid, setIsPage4Valid] = useState(false);
|
const [isPage4Valid, setIsPage4Valid] = useState(false);
|
||||||
const [isPage5Valid, setIsPage5Valid] = useState(false);
|
const [isPage5Valid, setIsPage5Valid] = useState(false);
|
||||||
|
const [isPage6Valid, setIsPage6Valid] = useState(false);
|
||||||
|
|
||||||
const [hasInteracted, setHasInteracted] = useState(false);
|
const [hasInteracted, setHasInteracted] = useState(false);
|
||||||
|
|
||||||
@ -103,7 +106,7 @@ export default function InscriptionFormShared({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Mettre à jour isPage4Valid en fonction de cette condition
|
// Mettre à jour isPage4Valid en fonction de cette condition
|
||||||
setIsPage4Valid(allSigned);
|
setIsPage5Valid(allSigned);
|
||||||
|
|
||||||
if (allSigned) {
|
if (allSigned) {
|
||||||
setCurrentTemplateIndex(0);
|
setCurrentTemplateIndex(0);
|
||||||
@ -116,8 +119,8 @@ export default function InscriptionFormShared({
|
|||||||
(template) => template.file !== null
|
(template) => template.file !== null
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mettre à jour isPage5Valid en fonction de cette condition
|
// Mettre à jour isPage6Valid en fonction de cette condition
|
||||||
setIsPage5Valid(allUploaded);
|
setIsPage6Valid(allUploaded);
|
||||||
}, [parentFileTemplates]);
|
}, [parentFileTemplates]);
|
||||||
|
|
||||||
const handleTemplateSigned = (index) => {
|
const handleTemplateSigned = (index) => {
|
||||||
@ -333,7 +336,7 @@ export default function InscriptionFormShared({
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
logger.debug('Fichier supprimé avec succès dans la base :', response);
|
logger.debug('Fichier supprimé avec succès dans la base :', response);
|
||||||
|
|
||||||
setIsPage5Valid(false);
|
setIsPage6Valid(false);
|
||||||
|
|
||||||
// Mettre à jour l'état local pour refléter la suppression
|
// Mettre à jour l'état local pour refléter la suppression
|
||||||
setUploadedFiles((prev) =>
|
setUploadedFiles((prev) =>
|
||||||
@ -374,6 +377,7 @@ export default function InscriptionFormShared({
|
|||||||
student: {
|
student: {
|
||||||
...formData,
|
...formData,
|
||||||
guardians: guardians,
|
guardians: guardians,
|
||||||
|
siblings: siblings,
|
||||||
},
|
},
|
||||||
establishment: selectedEstablishmentId,
|
establishment: selectedEstablishmentId,
|
||||||
status: isSepaPayment ? 8 : 3,
|
status: isSepaPayment ? 8 : 3,
|
||||||
@ -383,8 +387,6 @@ export default function InscriptionFormShared({
|
|||||||
registration_payment_plan: formData.registration_payment_plan,
|
registration_payment_plan: formData.registration_payment_plan,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('jsonData : ', jsonData);
|
|
||||||
|
|
||||||
// Créer un objet FormData
|
// Créer un objet FormData
|
||||||
const formDataToSend = new FormData();
|
const formDataToSend = new FormData();
|
||||||
|
|
||||||
@ -411,14 +413,16 @@ export default function InscriptionFormShared({
|
|||||||
const stepTitles = {
|
const stepTitles = {
|
||||||
1: 'Elève',
|
1: 'Elève',
|
||||||
2: 'Responsables légaux',
|
2: 'Responsables légaux',
|
||||||
3: 'Modalités de paiement',
|
3: 'Frères et soeurs',
|
||||||
4: 'Formulaires à signer',
|
4: 'Modalités de paiement',
|
||||||
5: 'Pièces à fournir',
|
5: 'Formulaires à signer',
|
||||||
|
6: 'Pièces à fournir',
|
||||||
};
|
};
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
'Élève',
|
'Élève',
|
||||||
'Responsable',
|
'Responsable',
|
||||||
|
'Fratrie',
|
||||||
'Paiement',
|
'Paiement',
|
||||||
'Formulaires',
|
'Formulaires',
|
||||||
'Documents parent',
|
'Documents parent',
|
||||||
@ -436,6 +440,8 @@ export default function InscriptionFormShared({
|
|||||||
return isPage4Valid;
|
return isPage4Valid;
|
||||||
case 5:
|
case 5:
|
||||||
return isPage5Valid;
|
return isPage5Valid;
|
||||||
|
case 6:
|
||||||
|
return isPage6Valid;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -464,6 +470,7 @@ export default function InscriptionFormShared({
|
|||||||
setFormData={setFormData}
|
setFormData={setFormData}
|
||||||
guardians={guardians}
|
guardians={guardians}
|
||||||
setGuardians={setGuardians}
|
setGuardians={setGuardians}
|
||||||
|
setSiblings={setSiblings}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
setIsPageValid={setIsPage1Valid}
|
setIsPageValid={setIsPage1Valid}
|
||||||
hasInteracted={hasInteracted}
|
hasInteracted={hasInteracted}
|
||||||
@ -481,8 +488,19 @@ export default function InscriptionFormShared({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Page 3 : Informations sur les modalités de paiement */}
|
{/* Étape 3 : Frères et Sœurs */}
|
||||||
{currentPage === 3 && (
|
{currentPage === 3 && (
|
||||||
|
<SiblingInputFields
|
||||||
|
siblings={siblings}
|
||||||
|
setSiblings={setSiblings}
|
||||||
|
setFormData={setFormData}
|
||||||
|
errors={errors.siblings || []}
|
||||||
|
setIsPageValid={setIsPage3Valid}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Page 4 : Informations sur les modalités de paiement */}
|
||||||
|
{currentPage === 4 && (
|
||||||
<>
|
<>
|
||||||
<PaymentMethodSelector
|
<PaymentMethodSelector
|
||||||
formData={formData}
|
formData={formData}
|
||||||
@ -492,13 +510,13 @@ export default function InscriptionFormShared({
|
|||||||
registrationPaymentPlans={registrationPaymentPlans}
|
registrationPaymentPlans={registrationPaymentPlans}
|
||||||
tuitionPaymentPlans={tuitionPaymentPlans}
|
tuitionPaymentPlans={tuitionPaymentPlans}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
setIsPageValid={setIsPage3Valid}
|
setIsPageValid={setIsPage4Valid}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Pages suivantes : Section Fichiers d'inscription */}
|
{/* Page 5 : Section Fichiers d'inscription */}
|
||||||
{currentPage === 4 && (
|
{currentPage === 5 && (
|
||||||
<div className="mt-8 mb-4 w-full mx-auto flex gap-8">
|
<div className="mt-8 mb-4 w-full mx-auto flex gap-8">
|
||||||
{/* Liste des états de signature */}
|
{/* Liste des états de signature */}
|
||||||
<div className="w-1/4 bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200">
|
<div className="w-1/4 bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200">
|
||||||
@ -613,7 +631,8 @@ export default function InscriptionFormShared({
|
|||||||
(currentPage === 1 && !isPage1Valid) ||
|
(currentPage === 1 && !isPage1Valid) ||
|
||||||
(currentPage === 2 && !isPage2Valid) ||
|
(currentPage === 2 && !isPage2Valid) ||
|
||||||
(currentPage === 3 && !isPage3Valid) ||
|
(currentPage === 3 && !isPage3Valid) ||
|
||||||
(currentPage === 4 && !isPage4Valid)
|
(currentPage === 4 && !isPage4Valid) ||
|
||||||
|
(currentPage === 5 && !isPage5Valid)
|
||||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||||
}`}
|
}`}
|
||||||
@ -621,7 +640,8 @@ export default function InscriptionFormShared({
|
|||||||
(currentPage === 1 && !isPage1Valid) ||
|
(currentPage === 1 && !isPage1Valid) ||
|
||||||
(currentPage === 2 && !isPage2Valid) ||
|
(currentPage === 2 && !isPage2Valid) ||
|
||||||
(currentPage === 3 && !isPage3Valid) ||
|
(currentPage === 3 && !isPage3Valid) ||
|
||||||
(currentPage === 4 && !isPage4Valid)
|
(currentPage === 4 && !isPage4Valid) ||
|
||||||
|
(currentPage === 5 && !isPage5Valid)
|
||||||
}
|
}
|
||||||
primary
|
primary
|
||||||
name="Next"
|
name="Next"
|
||||||
@ -633,11 +653,11 @@ export default function InscriptionFormShared({
|
|||||||
text="Valider"
|
text="Valider"
|
||||||
primary
|
primary
|
||||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||||
currentPage === 5 && !isPage5Valid
|
currentPage === 6 && !isPage6Valid
|
||||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||||
}`}
|
}`}
|
||||||
disabled={currentPage === 5 && !isPage5Valid}
|
disabled={currentPage === 6 && !isPage6Valid}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
157
Front-End/src/components/Inscription/SiblingInputFields.js
Normal file
157
Front-End/src/components/Inscription/SiblingInputFields.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import InputText from '@/components/InputText';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { Trash2, Plus, Users } from 'lucide-react';
|
||||||
|
import SectionHeader from '@/components/SectionHeader';
|
||||||
|
|
||||||
|
export default function SiblingInputFields({
|
||||||
|
siblings,
|
||||||
|
setSiblings,
|
||||||
|
setFormData,
|
||||||
|
errors,
|
||||||
|
setIsPageValid,
|
||||||
|
}) {
|
||||||
|
useEffect(() => {
|
||||||
|
// Si siblings est null ou undefined, le valoriser à un tableau vide
|
||||||
|
if (!siblings || siblings.length === 0) {
|
||||||
|
setSiblings([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchroniser siblings avec formData
|
||||||
|
setFormData((prevFormData) => ({
|
||||||
|
...prevFormData,
|
||||||
|
siblings: siblings, // Mettre à jour siblings dans formData
|
||||||
|
}));
|
||||||
|
|
||||||
|
const isValid = siblings.every((sibling, index) => {
|
||||||
|
return !Object.keys(sibling).some(
|
||||||
|
(field) => getLocalError(index, field) !== ''
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsPageValid(isValid);
|
||||||
|
}, [siblings, setSiblings, setFormData, setIsPageValid]);
|
||||||
|
|
||||||
|
const getError = (index, field) => {
|
||||||
|
return errors[index]?.[field]?.[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLocalError = (index, field) => {
|
||||||
|
if (
|
||||||
|
(field === 'last_name' &&
|
||||||
|
(!siblings[index].last_name ||
|
||||||
|
siblings[index].last_name.trim() === '')) ||
|
||||||
|
(field === 'first_name' &&
|
||||||
|
(!siblings[index].first_name ||
|
||||||
|
siblings[index].first_name.trim() === '')) ||
|
||||||
|
(field === 'birth_date' &&
|
||||||
|
(!siblings[index].birth_date ||
|
||||||
|
siblings[index].birth_date.trim() === ''))
|
||||||
|
) {
|
||||||
|
return 'Champs requis';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSiblingsChange = (id, field, value) => {
|
||||||
|
const updatedSiblings = siblings.map((sibling) => {
|
||||||
|
if (sibling.id === id) {
|
||||||
|
return { ...sibling, [field]: value };
|
||||||
|
}
|
||||||
|
return sibling;
|
||||||
|
});
|
||||||
|
|
||||||
|
setSiblings(updatedSiblings);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSibling = () => {
|
||||||
|
setSiblings([
|
||||||
|
...siblings,
|
||||||
|
{
|
||||||
|
last_name: '',
|
||||||
|
first_name: '',
|
||||||
|
birth_date: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteSibling = (index) => {
|
||||||
|
const updatedSiblings = siblings.filter((_, i) => i !== index);
|
||||||
|
setSiblings(updatedSiblings);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||||
|
<SectionHeader
|
||||||
|
icon={Users}
|
||||||
|
title={`Frères et Sœurs`}
|
||||||
|
description={`Ajoutez les informations des frères et sœurs`}
|
||||||
|
/>
|
||||||
|
{siblings.map((item, index) => (
|
||||||
|
<div className="p-6" key={index}>
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h3 className="text-xl font-bold">Frère/Sœur {index + 1}</h3>
|
||||||
|
<Trash2
|
||||||
|
className="w-5 h-5 text-red-500 cursor-pointer hover:text-red-700 transition-colors"
|
||||||
|
onClick={() => deleteSibling(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||||
|
<InputText
|
||||||
|
name="last_name"
|
||||||
|
type="text"
|
||||||
|
label="Nom"
|
||||||
|
value={item.last_name}
|
||||||
|
onChange={(event) => {
|
||||||
|
onSiblingsChange(item.id, 'last_name', event.target.value);
|
||||||
|
}}
|
||||||
|
errorMsg={
|
||||||
|
getError(index, 'last_name') ||
|
||||||
|
getLocalError(index, 'last_name')
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<InputText
|
||||||
|
name="first_name"
|
||||||
|
type="text"
|
||||||
|
label="Prénom"
|
||||||
|
value={item.first_name}
|
||||||
|
onChange={(event) => {
|
||||||
|
onSiblingsChange(item.id, 'first_name', event.target.value);
|
||||||
|
}}
|
||||||
|
errorMsg={
|
||||||
|
getError(index, 'first_name') ||
|
||||||
|
getLocalError(index, 'first_name')
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<InputText
|
||||||
|
name="birth_date"
|
||||||
|
type="date"
|
||||||
|
label="Date de Naissance"
|
||||||
|
value={item.birth_date}
|
||||||
|
onChange={(event) => {
|
||||||
|
onSiblingsChange(item.id, 'birth_date', event.target.value);
|
||||||
|
}}
|
||||||
|
errorMsg={
|
||||||
|
getError(index, 'birth_date') ||
|
||||||
|
getLocalError(index, 'birth_date')
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<Plus
|
||||||
|
className="w-8 h-8 text-green-500 cursor-pointer hover:text-green-700 transition-colors border-2 border-green-500 hover:border-green-700 rounded-full p-1"
|
||||||
|
onClick={addSibling}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import logger from '@/utils/logger';
|
|||||||
import SectionHeader from '@/components/SectionHeader';
|
import SectionHeader from '@/components/SectionHeader';
|
||||||
import { User } from 'lucide-react';
|
import { User } from 'lucide-react';
|
||||||
import FileUpload from '@/components/FileUpload';
|
import FileUpload from '@/components/FileUpload';
|
||||||
|
import { BASE_URL } from '@/utils/Url';
|
||||||
|
|
||||||
const levels = [
|
const levels = [
|
||||||
{ value: '1', label: 'TPS - Très Petite Section' },
|
{ value: '1', label: 'TPS - Très Petite Section' },
|
||||||
@ -25,6 +26,7 @@ export default function StudentInfoForm({
|
|||||||
formData,
|
formData,
|
||||||
setFormData,
|
setFormData,
|
||||||
setGuardians,
|
setGuardians,
|
||||||
|
setSiblings,
|
||||||
errors,
|
errors,
|
||||||
setIsPageValid,
|
setIsPageValid,
|
||||||
hasInteracted,
|
hasInteracted,
|
||||||
@ -37,9 +39,11 @@ export default function StudentInfoForm({
|
|||||||
fetchRegisterForm(studentId).then((data) => {
|
fetchRegisterForm(studentId).then((data) => {
|
||||||
logger.debug(data);
|
logger.debug(data);
|
||||||
|
|
||||||
|
const photoPath = data?.student?.photo || null;
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
id: data?.student?.id || '',
|
id: data?.student?.id || '',
|
||||||
photo: data?.student?.photo || null,
|
photo: photoPath,
|
||||||
last_name: data?.student?.last_name || '',
|
last_name: data?.student?.last_name || '',
|
||||||
first_name: data?.student?.first_name || '',
|
first_name: data?.student?.first_name || '',
|
||||||
address: data?.student?.address || '',
|
address: data?.student?.address || '',
|
||||||
@ -56,7 +60,29 @@ export default function StudentInfoForm({
|
|||||||
totalRegistrationFees: data?.totalRegistrationFees,
|
totalRegistrationFees: data?.totalRegistrationFees,
|
||||||
totalTuitionFees: data?.totalTuitionFees,
|
totalTuitionFees: data?.totalTuitionFees,
|
||||||
});
|
});
|
||||||
|
|
||||||
setGuardians(data?.student?.guardians || []);
|
setGuardians(data?.student?.guardians || []);
|
||||||
|
setSiblings(data?.student?.siblings || []);
|
||||||
|
|
||||||
|
// Convertir la photo en fichier binaire si elle est un chemin ou une URL
|
||||||
|
if (photoPath && typeof photoPath === 'string') {
|
||||||
|
fetch(`${BASE_URL}${photoPath}`)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Erreur lors de la récupération de la photo.');
|
||||||
|
}
|
||||||
|
return response.blob();
|
||||||
|
})
|
||||||
|
.then((blob) => {
|
||||||
|
const file = new File([blob], photoPath.split('/').pop(), {
|
||||||
|
type: blob.type,
|
||||||
|
});
|
||||||
|
handlePhotoUpload(file); // Utiliser handlePhotoUpload pour valoriser la photo
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error('Erreur lors de la conversion de la photo :', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user