mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout de la photo pour le dossier de l'élève + correction
sauvegarde des datas des responsables
This commit is contained in:
@ -48,6 +48,9 @@ class Sibling(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SIBLING"
|
return "SIBLING"
|
||||||
|
|
||||||
|
def registration_photo_upload_to(instance, filename):
|
||||||
|
return f"registration_files/dossier_rf_{instance.pk}/parent/{filename}"
|
||||||
|
|
||||||
class Student(models.Model):
|
class Student(models.Model):
|
||||||
"""
|
"""
|
||||||
Représente l’élève inscrit ou en cours d’inscription.
|
Représente l’élève inscrit ou en cours d’inscription.
|
||||||
@ -64,6 +67,11 @@ class Student(models.Model):
|
|||||||
MS = 3, _('MS - Moyenne Section')
|
MS = 3, _('MS - Moyenne Section')
|
||||||
GS = 4, _('GS - Grande Section')
|
GS = 4, _('GS - Grande Section')
|
||||||
|
|
||||||
|
photo = models.FileField(
|
||||||
|
upload_to=registration_photo_upload_to,
|
||||||
|
null=True,
|
||||||
|
blank=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="")
|
||||||
gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True)
|
gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True)
|
||||||
@ -239,12 +247,6 @@ class RegistrationForm(models.Model):
|
|||||||
# Appeler la méthode save originale
|
# Appeler la méthode save originale
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def registration_school_file_upload_to(instance, filename):
|
|
||||||
return f"registration_files/dossier_rf_{instance.registration_form.pk}/school/{filename}"
|
|
||||||
|
|
||||||
def registration_parent_file_upload_to(instance, filename):
|
|
||||||
return f"registration_files/dossier_rf_{instance.registration_form.pk}/parent/{filename}"
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
####################### MASTER FILES ########################
|
####################### MASTER FILES ########################
|
||||||
#############################################################
|
#############################################################
|
||||||
@ -269,6 +271,12 @@ class RegistrationParentFileMaster(models.Model):
|
|||||||
####################### CLONE FILES ########################
|
####################### CLONE FILES ########################
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
def registration_school_file_upload_to(instance, filename):
|
||||||
|
return f"registration_files/dossier_rf_{instance.registration_form.pk}/school/{filename}"
|
||||||
|
|
||||||
|
def registration_parent_file_upload_to(instance, filename):
|
||||||
|
return f"registration_files/dossier_rf_{instance.registration_form.pk}/parent/{filename}"
|
||||||
|
|
||||||
####### DocuSeal templates (par dossier d'inscription) #######
|
####### DocuSeal templates (par dossier d'inscription) #######
|
||||||
class RegistrationSchoolFileTemplate(models.Model):
|
class RegistrationSchoolFileTemplate(models.Model):
|
||||||
master = models.ForeignKey(RegistrationSchoolFileMaster, on_delete=models.CASCADE, related_name='school_file_templates', blank=True)
|
master = models.ForeignKey(RegistrationSchoolFileMaster, on_delete=models.CASCADE, related_name='school_file_templates', blank=True)
|
||||||
|
|||||||
@ -91,7 +91,25 @@
|
|||||||
{% load myTemplateTag %}
|
{% load myTemplateTag %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 class="title">{{ pdf_title }}</h1>
|
<div style="position: relative;">
|
||||||
|
<h1 class="title">{{ pdf_title }}</h1>
|
||||||
|
{% if student.photo %}
|
||||||
|
<img
|
||||||
|
src="{{ student.photo }}"
|
||||||
|
alt="Photo de l'élève"
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid #333;
|
||||||
|
border-radius: 10px;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
|
|||||||
@ -229,25 +229,37 @@ class RegisterFormWithIdView(APIView):
|
|||||||
"""
|
"""
|
||||||
Modifie un dossier d'inscription donné.
|
Modifie un dossier d'inscription donné.
|
||||||
"""
|
"""
|
||||||
# Récupérer les données de la requête
|
|
||||||
studentForm_data = request.data.copy()
|
|
||||||
|
|
||||||
logger.info(f"Mise à jour du dossier d'inscription {studentForm_data}")
|
studentForm_data = request.data.get('data', '{}')
|
||||||
|
try:
|
||||||
|
data = json.loads(studentForm_data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
_status = studentForm_data.pop('status', 0)
|
# Extraire le fichier photo
|
||||||
if isinstance(_status, list): # Cas Multipart/data, les données sont envoyées sous forme de liste, c'est nul
|
photo_file = request.FILES.get('photo')
|
||||||
_status = int(_status[0])
|
|
||||||
else:
|
# Ajouter la photo aux données de l'étudiant
|
||||||
_status = int(_status)
|
if photo_file:
|
||||||
|
data['student']['photo'] = photo_file
|
||||||
|
|
||||||
|
# Gérer le champ `_status`
|
||||||
|
_status = data.pop('status', 0)
|
||||||
|
_status = int(_status)
|
||||||
|
|
||||||
# Récupérer le dossier d'inscription
|
# Récupérer le dossier d'inscription
|
||||||
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||||
if not registerForm:
|
if not registerForm:
|
||||||
return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)
|
return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data, partial=True)
|
studentForm_serializer = RegistrationFormSerializer(registerForm, data=data, partial=True)
|
||||||
if studentForm_serializer.is_valid():
|
if studentForm_serializer.is_valid():
|
||||||
studentForm_serializer.save()
|
studentForm_serializer.save()
|
||||||
|
|
||||||
|
# Sauvegarder la photo si elle est présente dans la requête
|
||||||
|
if photo_file:
|
||||||
|
student = registerForm.student
|
||||||
|
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)
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
"pending": "Pending Registrations",
|
"pending": "Pending Registrations",
|
||||||
"subscribed": "Subscribed",
|
"subscribed": "Subscribed",
|
||||||
"archived": "Archived",
|
"archived": "Archived",
|
||||||
|
"photo": "Photo",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"class": "Class",
|
"class": "Class",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
"pending": "Inscriptions en attente",
|
"pending": "Inscriptions en attente",
|
||||||
"subscribed": "Inscrits",
|
"subscribed": "Inscrits",
|
||||||
"archived": "Archivés",
|
"archived": "Archivés",
|
||||||
|
"photo": "Photo",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"class": "Classe",
|
"class": "Classe",
|
||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import InscriptionFormShared from '@/components/Inscription/InscriptionFormShare
|
|||||||
import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url';
|
import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url';
|
||||||
import { useCsrfToken } from '@/context/CsrfContext';
|
import { useCsrfToken } from '@/context/CsrfContext';
|
||||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
import { editRegisterForm } from '@/app/actions/subscriptionAction';
|
import { editRegisterFormWithBinaryFile } from '@/app/actions/subscriptionAction';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import Loader from '@/components/Loader';
|
import Loader from '@/components/Loader';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export default function Page() {
|
|||||||
|
|
||||||
const handleSubmit = (data) => {
|
const handleSubmit = (data) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
editRegisterForm(studentId, data, csrfToken)
|
editRegisterFormWithBinaryFile(studentId, data, csrfToken)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
logger.debug('Success:', result);
|
logger.debug('Success:', result);
|
||||||
|
|||||||
@ -60,6 +60,7 @@ import { fetchProfiles } from '@/app/actions/authAction';
|
|||||||
import {
|
import {
|
||||||
FE_ADMIN_SUBSCRIPTIONS_EDIT_URL,
|
FE_ADMIN_SUBSCRIPTIONS_EDIT_URL,
|
||||||
FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL,
|
FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL,
|
||||||
|
BASE_URL,
|
||||||
} from '@/utils/Url';
|
} from '@/utils/Url';
|
||||||
|
|
||||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||||
@ -743,17 +744,6 @@ export default function Page({ params: { locale } }) {
|
|||||||
const getActionsByStatus = (row) => {
|
const getActionsByStatus = (row) => {
|
||||||
const actions = {
|
const actions = {
|
||||||
1: [
|
1: [
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<span title="Editer le dossier">
|
|
||||||
<Edit className="w-5 h-5 text-blue-500 hover:text-blue-700" />
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
onClick: () =>
|
|
||||||
router.push(
|
|
||||||
`${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<span title="Envoyer le dossier">
|
<span title="Envoyer le dossier">
|
||||||
@ -865,6 +855,33 @@ export default function Page({ params: { locale } }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
{
|
||||||
|
name: t('photo'),
|
||||||
|
transform: (row) => (
|
||||||
|
<div className="flex justify-center items-center">
|
||||||
|
{row.student.photo ? (
|
||||||
|
<a
|
||||||
|
href={`${BASE_URL}${row.student.photo}`} // Lien vers la photo
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`${BASE_URL}${row.student.photo}`}
|
||||||
|
alt={`${row.student.first_name} ${row.student.last_name}`}
|
||||||
|
className="w-10 h-10 object-cover transition-transform duration-200 hover:scale-125 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</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.student.first_name[0]}
|
||||||
|
{row.student.last_name[0]}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
||||||
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
||||||
{
|
{
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export default function FileUpload({
|
|||||||
{/* Icône de cloud */}
|
{/* Icône de cloud */}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept=".pdf"
|
accept=".pdf, .png, .jpg, .jpeg, .gif, .bmp"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
ref={fileInputRef} // Attachement de la référence
|
ref={fileInputRef} // Attachement de la référence
|
||||||
|
|||||||
@ -43,6 +43,7 @@ export default function InscriptionFormShared({
|
|||||||
// États pour gérer les données du formulaire
|
// États pour gérer les données du formulaire
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
id: '',
|
id: '',
|
||||||
|
photo: null,
|
||||||
last_name: '',
|
last_name: '',
|
||||||
first_name: '',
|
first_name: '',
|
||||||
address: '',
|
address: '',
|
||||||
@ -55,7 +56,6 @@ export default function InscriptionFormShared({
|
|||||||
registration_payment: '',
|
registration_payment: '',
|
||||||
tuition_payment: '',
|
tuition_payment: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [guardians, setGuardians] = useState([]);
|
const [guardians, setGuardians] = useState([]);
|
||||||
|
|
||||||
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
||||||
@ -333,10 +333,12 @@ export default function InscriptionFormShared({
|
|||||||
// Vérifier si le mode de paiement sélectionné est un prélèvement SEPA
|
// Vérifier si le mode de paiement sélectionné est un prélèvement SEPA
|
||||||
const isSepaPayment =
|
const isSepaPayment =
|
||||||
formData.registration_payment === '1' || formData.tuition_payment === '1';
|
formData.registration_payment === '1' || formData.tuition_payment === '1';
|
||||||
const data = {
|
|
||||||
|
// Préparer les données JSON
|
||||||
|
const jsonData = {
|
||||||
student: {
|
student: {
|
||||||
...formData,
|
...formData,
|
||||||
guardians,
|
guardians: guardians,
|
||||||
},
|
},
|
||||||
establishment: selectedEstablishmentId,
|
establishment: selectedEstablishmentId,
|
||||||
status: isSepaPayment ? 8 : 3,
|
status: isSepaPayment ? 8 : 3,
|
||||||
@ -344,7 +346,19 @@ export default function InscriptionFormShared({
|
|||||||
registration_payment: formData.registration_payment,
|
registration_payment: formData.registration_payment,
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit(data);
|
// Créer un objet FormData
|
||||||
|
const formDataToSend = new FormData();
|
||||||
|
|
||||||
|
// Ajouter les données JSON sous forme de chaîne
|
||||||
|
formDataToSend.append('data', JSON.stringify(jsonData));
|
||||||
|
|
||||||
|
// Ajouter la photo si elle est présente
|
||||||
|
if (formData.photo) {
|
||||||
|
formDataToSend.append('photo', formData.photo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appeler la fonction onSubmit avec les données FormData
|
||||||
|
onSubmit(formDataToSend);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNextPage = () => {
|
const handleNextPage = () => {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import React, { useEffect } from 'react';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { Trash2, Plus, Users } from 'lucide-react';
|
import { Trash2, Plus, Users } from 'lucide-react';
|
||||||
import SectionHeader from '@/components/SectionHeader';
|
import SectionHeader from '@/components/SectionHeader';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
export default function ResponsableInputFields({
|
export default function ResponsableInputFields({
|
||||||
guardians,
|
guardians,
|
||||||
@ -12,6 +13,7 @@ export default function ResponsableInputFields({
|
|||||||
setIsPageValid,
|
setIsPageValid,
|
||||||
}) {
|
}) {
|
||||||
const t = useTranslations('ResponsableInputFields');
|
const t = useTranslations('ResponsableInputFields');
|
||||||
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isValid =
|
const isValid =
|
||||||
@ -38,7 +40,7 @@ export default function ResponsableInputFields({
|
|||||||
(field === 'first_name' &&
|
(field === 'first_name' &&
|
||||||
(!guardians[index].first_name ||
|
(!guardians[index].first_name ||
|
||||||
guardians[index].first_name.trim() === '')) ||
|
guardians[index].first_name.trim() === '')) ||
|
||||||
(field === 'email' &&
|
(field === 'associated_profile_email' &&
|
||||||
(!guardians[index].associated_profile_email ||
|
(!guardians[index].associated_profile_email ||
|
||||||
guardians[index].associated_profile_email.trim() === '')) ||
|
guardians[index].associated_profile_email.trim() === '')) ||
|
||||||
(field === 'birth_date' &&
|
(field === 'birth_date' &&
|
||||||
@ -56,16 +58,45 @@ export default function ResponsableInputFields({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onGuardiansChange = (id, field, value) => {
|
const onGuardiansChange = (id, field, value) => {
|
||||||
const updatedGuardians = guardians.map((guardian) =>
|
const updatedGuardians = guardians.map((guardian) => {
|
||||||
guardian.id === id ? { ...guardian, [field]: value } : guardian
|
if (guardian.id === id) {
|
||||||
);
|
const updatedGuardian = { ...guardian, [field]: value };
|
||||||
|
|
||||||
|
// Synchroniser profile_data.email et profile_data.username avec associated_profile_email
|
||||||
|
if (field === 'associated_profile_email') {
|
||||||
|
updatedGuardian.profile_role_data.profile_data.email = value;
|
||||||
|
updatedGuardian.profile_role_data.profile_data.username = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedGuardian;
|
||||||
|
}
|
||||||
|
return guardian;
|
||||||
|
});
|
||||||
|
|
||||||
setGuardians(updatedGuardians);
|
setGuardians(updatedGuardians);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addGuardian = () => {
|
const addGuardian = () => {
|
||||||
setGuardians([
|
setGuardians([
|
||||||
...guardians,
|
...guardians,
|
||||||
{ id: Date.now(), name: '', email: '', phone: '' },
|
{
|
||||||
|
profile_role_data: {
|
||||||
|
establishment: selectedEstablishmentId,
|
||||||
|
role_type: 2,
|
||||||
|
is_active: false,
|
||||||
|
profile_data: {
|
||||||
|
email: '',
|
||||||
|
password: 'Provisoire01!',
|
||||||
|
username: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
last_name: '',
|
||||||
|
first_name: '',
|
||||||
|
birth_date: '',
|
||||||
|
address: '',
|
||||||
|
phone: '',
|
||||||
|
profession: '',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,7 +174,8 @@ export default function ResponsableInputFields({
|
|||||||
}}
|
}}
|
||||||
required
|
required
|
||||||
errorMsg={
|
errorMsg={
|
||||||
getError(index, 'email') || getLocalError(index, 'email')
|
getError(index, 'associated_profile_email') ||
|
||||||
|
getLocalError(index, 'associated_profile_email')
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<InputPhone
|
<InputPhone
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { fetchRegisterForm } from '@/app/actions/subscriptionAction';
|
|||||||
import logger from '@/utils/logger';
|
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';
|
||||||
|
|
||||||
const levels = [
|
const levels = [
|
||||||
{ value: '1', label: 'TPS - Très Petite Section' },
|
{ value: '1', label: 'TPS - Très Petite Section' },
|
||||||
@ -38,6 +39,7 @@ export default function StudentInfoForm({
|
|||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
id: data?.student?.id || '',
|
id: data?.student?.id || '',
|
||||||
|
photo: data?.student?.photo || null,
|
||||||
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 || '',
|
||||||
@ -110,6 +112,16 @@ export default function StudentInfoForm({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePhotoUpload = (file) => {
|
||||||
|
if (file) {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
photo: file,
|
||||||
|
}));
|
||||||
|
logger.debug('Photo sélectionnée :', file.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Affichage du loader pendant le chargement
|
// Affichage du loader pendant le chargement
|
||||||
if (isLoading) return <Loader />;
|
if (isLoading) return <Loader />;
|
||||||
|
|
||||||
@ -208,54 +220,18 @@ export default function StudentInfoForm({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
{/* Section pour l'upload des fichiers */}
|
||||||
<h2 className="text-xl font-bold mb-4 text-gray-800">Responsables</h2>
|
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 mt-6">
|
||||||
<ResponsableInputFields
|
<SectionHeader
|
||||||
guardians={guardians}
|
icon={User}
|
||||||
onGuardiansChange={(id, field, value) => {
|
title={`Photo de l'élève`}
|
||||||
const updatedGuardians = guardians.map((resp) =>
|
description={`Ajoutez une photo de votre enfant`}
|
||||||
resp.id === id ? { ...resp, [field]: value } : resp
|
/>
|
||||||
);
|
<FileUpload
|
||||||
setGuardians(updatedGuardians);
|
selectionMessage="Sélectionnez une photo à uploader"
|
||||||
}}
|
onFileSelect={(file) => handlePhotoUpload(file)}
|
||||||
addGuardian={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setGuardians([...guardians, { id: Date.now() }]);
|
|
||||||
}}
|
|
||||||
deleteGuardian={(index) => {
|
|
||||||
const newArray = [...guardians];
|
|
||||||
newArray.splice(index, 1);
|
|
||||||
setGuardians(newArray);
|
|
||||||
}}
|
|
||||||
errors={errors?.student?.guardians || []}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PaymentMethodSelector
|
|
||||||
formData={formData}
|
|
||||||
title="Frais d'inscription"
|
|
||||||
name="registration_payment"
|
|
||||||
onChange={onChange}
|
|
||||||
selected={formData.registration_payment}
|
|
||||||
paymentModes={registrationPaymentModes}
|
|
||||||
paymentModesOptions={paymentModesOptions}
|
|
||||||
amount={formData.totalRegistrationFees}
|
|
||||||
getError={getError}
|
|
||||||
getLocalError={getLocalError}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PaymentMethodSelector
|
|
||||||
formData={formData}
|
|
||||||
title="Frais de scolarité"
|
|
||||||
name="tuition_payment"
|
|
||||||
onChange={onChange}
|
|
||||||
selected={formData.tuition_payment}
|
|
||||||
paymentModes={tuitionPaymentModes}
|
|
||||||
paymentModesOptions={paymentModesOptions}
|
|
||||||
amount={formData.totalTuitionFees}
|
|
||||||
getError={getError}
|
|
||||||
getLocalError={getLocalError}
|
|
||||||
/> */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import logger from '@/utils/logger';
|
|||||||
import MultiSelect from '@/components/MultiSelect'; // Import du composant MultiSelect
|
import MultiSelect from '@/components/MultiSelect'; // Import du composant MultiSelect
|
||||||
import { useCsrfToken } from '@/context/CsrfContext';
|
import { useCsrfToken } from '@/context/CsrfContext';
|
||||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
import Popup from '@/components/Popup';
|
||||||
|
|
||||||
export default function FileUploadDocuSeal({
|
export default function FileUploadDocuSeal({
|
||||||
handleCreateTemplateMaster,
|
handleCreateTemplateMaster,
|
||||||
@ -24,6 +25,9 @@ export default function FileUploadDocuSeal({
|
|||||||
const [selectedGroups, setSelectedGroups] = useState([]);
|
const [selectedGroups, setSelectedGroups] = useState([]);
|
||||||
const [guardianDetails, setGuardianDetails] = useState([]);
|
const [guardianDetails, setGuardianDetails] = useState([]);
|
||||||
|
|
||||||
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
|
const [popupMessage, setPopupMessage] = useState('');
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
|
||||||
const { selectedEstablishmentId } = useEstablishment();
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
@ -84,6 +88,17 @@ export default function FileUploadDocuSeal({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (data) => {
|
const handleSubmit = (data) => {
|
||||||
|
// Vérifier si au moins un champ a la propriété "required" à true
|
||||||
|
const hasRequiredField = data.fields.some(
|
||||||
|
(field) => field.required === true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasRequiredField) {
|
||||||
|
setPopupMessage('Veuillez définir au moins un champ comme requis.');
|
||||||
|
setPopupVisible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const is_required = data.fields.length > 0;
|
const is_required = data.fields.length > 0;
|
||||||
if (fileToEdit) {
|
if (fileToEdit) {
|
||||||
logger.debug('Modification du template master:', templateMaster?.id);
|
logger.debug('Modification du template master:', templateMaster?.id);
|
||||||
@ -139,6 +154,13 @@ export default function FileUploadDocuSeal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col mt-4 space-y-6">
|
<div className="h-full flex flex-col mt-4 space-y-6">
|
||||||
|
<Popup
|
||||||
|
visible={popupVisible}
|
||||||
|
message={popupMessage}
|
||||||
|
onConfirm={() => setPopupVisible(false)}
|
||||||
|
onCancel={() => setPopupVisible(false)}
|
||||||
|
uniqueConfirmButton={true}
|
||||||
|
/>
|
||||||
{/* Contenu principal */}
|
{/* Contenu principal */}
|
||||||
<div className="grid grid-cols-10 gap-6 items-start">
|
<div className="grid grid-cols-10 gap-6 items-start">
|
||||||
{/* Sélection des groupes */}
|
{/* Sélection des groupes */}
|
||||||
|
|||||||
Reference in New Issue
Block a user