diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index b183107..582cc67 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -182,6 +182,10 @@ class Student(models.Model): return self.birth_date.strftime('%d-%m-%Y') return None +def registration_file_path(instance, filename): + # Génère le chemin : registration_files/dossier_rf_{student_id}/filename + return f'registration_files/dossier_rf_{instance.student_id}/{filename}' + class RegistrationForm(models.Model): """ Gère le dossier d’inscription lié à un élève donné. @@ -201,7 +205,11 @@ class RegistrationForm(models.Model): last_update = models.DateTimeField(auto_now=True) notes = models.CharField(max_length=200, blank=True) registration_link_code = models.CharField(max_length=200, default="", blank=True) - registration_file = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True) + registration_file = models.FileField( + upload_to=registration_file_path, + null=True, + blank=True + ) associated_rf = models.CharField(max_length=200, default="", blank=True) def __str__(self): diff --git a/Back-End/Subscriptions/util.py b/Back-End/Subscriptions/util.py index e76d06b..622bac5 100644 --- a/Back-End/Subscriptions/util.py +++ b/Back-End/Subscriptions/util.py @@ -93,31 +93,57 @@ def getArgFromRequest(_argument, _request): def merge_files_pdf(filenames, output_filename): """ - Insère plusieurs fichiers PDF dans un seul document de sortie. + Fusionne plusieurs fichiers PDF en un seul document. + Vérifie l'existence des fichiers sources avant la fusion. """ merger = pymupdf.open() + valid_files = [] + + # Vérifier l'existence des fichiers et ne garder que ceux qui existent for filename in filenames: + if os.path.exists(filename): + valid_files.append(filename) + + # Fusionner les fichiers valides + for filename in valid_files: merger.insert_file(filename) + + # S'assurer que le dossier de destination existe + os.makedirs(os.path.dirname(output_filename), exist_ok=True) + + # Sauvegarder le fichier fusionné merger.save(output_filename) merger.close() -def rfToPDF(registerForm,filename): + return output_filename + +def rfToPDF(registerForm, filename): """ - Génère le PDF d’un dossier d’inscription et l’associe au RegistrationForm. + Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm. """ - # Ajout du fichier d'inscriptions data = { - 'pdf_title': "Dossier d'inscription de %s"%registerForm.student.first_name, + 'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}", 'signatureDate': convertToStr(_now(), '%d-%m-%Y'), 'signatureTime': convertToStr(_now(), '%H:%M'), - 'student':registerForm.student, + 'student': registerForm.student, } - PDFFileName = filename + + # S'assurer que le dossier parent existe + os.makedirs(os.path.dirname(filename), exist_ok=True) + + # Générer le PDF pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data) - pathFichier = Path(filename) - if os.path.exists(str(pathFichier)): - print(f'File exists : {str(pathFichier)}') - os.remove(str(pathFichier)) - receipt_file = BytesIO(pdf.content) - registerForm.fichierInscription = File(receipt_file, PDFFileName) - registerForm.fichierInscription.save() \ No newline at end of file + + # Écrire le fichier directement + with open(filename, 'wb') as f: + f.write(pdf.content) + + # Mettre à jour le champ registration_file du registerForm + with open(filename, 'rb') as f: + registerForm.registration_file.save( + os.path.basename(filename), + File(f), + save=True + ) + + return registerForm.registration_file \ No newline at end of file diff --git a/Back-End/Subscriptions/views.py b/Back-End/Subscriptions/views.py index 5a29bd8..d635bf7 100644 --- a/Back-End/Subscriptions/views.py +++ b/Back-End/Subscriptions/views.py @@ -179,20 +179,37 @@ class RegisterFormView(APIView): registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id) if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: - # Le parent a complété le dossier d'inscription, il est soumis à validation par l'école - json.dumps(studentForm_data) - #Génération de la fiche d'inscription au format PDF - PDFFileName = "rf_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name) - path = Path(f"registration_files/dossier_rf_{registerForm.pk}/{PDFFileName}") - registerForm.fichierInscription = util.rfToPDF(registerForm, path) - # Récupération des fichiers d'inscription - fileNames = RegistrationFile.get_files_from_rf(registerForm.pk) - fileNames.insert(0,path) - # Création du fichier PDF Fusionné avec le dossier complet - output_path = f"registration_files/dossier_rf_{registerForm.pk}/dossier_{registerForm.pk}.pdf" - util.merge_files_pdf(fileNames, output_path) - # Mise à jour de l'automate - updateStateMachine(registerForm, 'saisiDI') + try: + # Génération de la fiche d'inscription au format PDF + base_dir = f"registration_files/dossier_rf_{registerForm.pk}" + os.makedirs(base_dir, exist_ok=True) + + # Fichier PDF initial + initial_pdf = f"{base_dir}/rf_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf" + registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf) + registerForm.save() + + # Récupération des fichiers d'inscription + fileNames = RegistrationFile.get_files_from_rf(registerForm.pk) + if registerForm.registration_file: + fileNames.insert(0, registerForm.registration_file.path) + + # Création du fichier PDF Fusionné + merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf" + util.merge_files_pdf(fileNames, merged_pdf) + + # Mise à jour du champ registration_file avec le fichier fusionné + with open(merged_pdf, 'rb') as f: + registerForm.registration_file.save( + os.path.basename(merged_pdf), + File(f), + save=True + ) + + # Mise à jour de l'automate + updateStateMachine(registerForm, 'saisiDI') + except Exception as e: + return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED: # L'école a validé le dossier d'inscription # Mise à jour de l'automate diff --git a/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js index 35d0520..510e121 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js @@ -16,29 +16,10 @@ export default function Page() { const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId const [initialData, setInitialData] = useState(null); - const [isLoading, setIsLoading] = useState(true); + const [formErrors, setFormErrors] = useState({}); const csrfToken = useCsrfToken(); - useEffect(() => { - if (useFakeData) { - setInitialData(mockStudent); - } else { - fetchRegisterForm(studentId) - .then(data => { - console.log('Fetched data:', data); // Pour le débogage - const formattedData = { - ...data, - guardians: data.guardians || [] - }; - setInitialData(formattedData); - }) - .catch(error => { - console.error('Error fetching student data:', error); - }); - } - setIsLoading(false); - }, [studentId]); // Dépendance changée à studentId const handleSubmit = (data) => { if (useFakeData) { @@ -64,11 +45,10 @@ export default function Page() { return ( ); diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 5ddb2da..516916d 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -320,99 +320,79 @@ useEffect(()=>{ }; const createRF = (updatedData) => { - console.log('createRF updatedData:', updatedData); - if (updatedData.selectedGuardians.length !== 0) { const selectedGuardiansIds = updatedData.selectedGuardians.map(guardianId => guardianId) - const data = { - student: { - last_name: updatedData.studentLastName, - first_name: updatedData.studentFirstName, - }, - idGuardians: selectedGuardiansIds + student: { + last_name: updatedData.studentLastName, + first_name: updatedData.studentFirstName, + }, + idGuardians: selectedGuardiansIds }; - createRegisterForm(data,csrfToken) - .then(data => { - console.log('Success:', data); - setRegistrationFormsDataPending(prevState => { - if (prevState) { - return [...prevState, data]; + createRegisterForm(data, csrfToken) + .then(data => { + // Mise à jour immédiate des données + setRegistrationFormsDataPending(prevState => [...(prevState || []), data]); + setTotalPending(prev => prev + 1); + if (updatedData.autoMail) { + sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); } - return [data]; - }); - setTotalPending(totalPending+1); - if (updatedData.autoMail) { - sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); - } - }) - .catch((error) => { + closeModal(); + // Forcer le rechargement complet des données + setReloadFetch(true); + }) + .catch((error) => { console.error('Error:', error); - }); - } - else { - // Création d'un profil associé à l'adresse mail du responsable saisie - // Le profil est inactif - const data = { - email: updatedData.guardianEmail, - password: 'Provisoire01!', - username: updatedData.guardianEmail, - is_active: 0, // On rend le profil inactif : impossible de s'y connecter dans la fenêtre du login tant qu'il ne s'est pas inscrit - droit:2 // Profil PARENT - } - createProfile(data,csrfToken) + }); + } else { + const data = { + email: updatedData.guardianEmail, + password: 'Provisoire01!', + username: updatedData.guardianEmail, + is_active: 0, + droit: 2 + } + + createProfile(data, csrfToken) .then(response => { - console.log('Success:', response); - if (response.id) { - let idProfile = response.id; + if (response.id) { + const data = { + student: { + last_name: updatedData.studentLastName, + first_name: updatedData.studentFirstName, + guardians: [{ + email: updatedData.guardianEmail, + phone: updatedData.guardianPhone, + associated_profile: response.id + }], + sibling: [] + } + }; - const data = { - student: { - last_name: updatedData.studentLastName, - first_name: updatedData.studentFirstName, - guardians: [ - { - email: updatedData.guardianEmail, - phone: updatedData.guardianPhone, - associated_profile: idProfile // Association entre le responsable de l'élève et le profil créé par défaut précédemment - } - ], - sibling: [] - } - }; - - createRegisterForm(data,csrfToken) - .then(data => { - console.log('Success:', data); - setRegistrationFormsDataPending(prevState => { - if (prevState && prevState.length > 0) { - return [...prevState, data]; - } - return prevState; - }); - setTotalPending(totalPending+1); - if (updatedData.autoMail) { - sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); - } - }) - .catch((error) => { - console.error('Error:', error); - }); - } + createRegisterForm(data, csrfToken) + .then(data => { + // Mise à jour immédiate des données + setRegistrationFormsDataPending(prevState => [...(prevState || []), data]); + setTotalPending(prev => prev + 1); + if (updatedData.autoMail) { + sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); + } + closeModal(); + // Forcer le rechargement complet des données + setReloadFetch(true); + }) + .catch((error) => { + console.error('Error:', error); + }); + } }) .catch(error => { - console.error('Error fetching data:', error); - error = error.errorMessage; - console.log(error); + console.error('Error:', error); }); } - closeModal(); - setReloadFetch(true); } - - const columns = [ { name: t('studentName'), transform: (row) => row.student.last_name }, { name: t('studentFistName'), transform: (row) => row.student.first_name }, @@ -426,11 +406,11 @@ const columns = [ ) }, { name: t('files'), transform: (row) => - (row.registerForms != null) &&( + (row.registration_file != null) &&( ) }, @@ -507,11 +487,11 @@ const columnsSubscribed = [ ) }, { name: t('files'), transform: (row) => - (row.registerForm != null) &&( + (row.registration_file != null) &&( ) }, @@ -677,7 +657,7 @@ const handleFileUpload = ({file, name, is_required, order}) => { text={( <> {t('subscribeFiles')} - ({totalSubscribed}) + ({fichiers.length}) )} active={activeTab === 'subscribeFiles'} @@ -735,12 +715,14 @@ const handleFileUpload = ({file, name, is_required, order}) => { {/*SI STATE == subscribeFiles */} {activeTab === 'subscribeFiles' && (
- +
+ +
{ - if (!studentId || !idProfil) { - console.error('Missing studentId or idProfil'); - return; - } - if (useFakeData) { - setInitialData(mockStudent); - setLastGuardianId(999); - setIsLoading(false); - } else { - Promise.all([ - // Fetch eleve data - fetchRegisterForm(studentId), - // Fetch last guardian ID - fetchLastGuardian() - ]) - .then(async ([studentData, guardianData]) => { - const formattedData = { - ...studentData, - guardians: studentData.guardians || [] - }; - - setInitialData(formattedData); - setLastGuardianId(guardianData.lastid); - - let profils = studentData.profils; - const currentProf = profils.find(profil => profil.id === idProfil); - if (currentProf) { - setCurrentProfil(currentProf); - } - }) - .catch(error => { - console.error('Error fetching data:', error); - }) - .finally(() => { - setIsLoading(false); - }); - } - }, [studentId, idProfil]); const handleSubmit = async (data) => { if (useFakeData) { console.log('Fake submit:', data); return; } - try { - const result = await editRegisterForm(studentId, data, csrfToken); console.log('Success:', result); router.push(FE_PARENTS_HOME_URL); @@ -80,7 +37,7 @@ export default function Page() { return ( { - return fetch(`${BE_SUBSCRIPTION_STUDENT_URL}/${id}`) // Utilisation de studentId au lieu de codeDI + return fetch(`${BE_SUBSCRIPTION_REGISTERFORM_URL}/${id}`) // Utilisation de studentId au lieu de codeDI .then(requestResponseHandler) } export const fetchLastGuardian = () =>{ @@ -98,31 +98,36 @@ export const sendRegisterForm = (id) => { } -export const fetchRegisterFormFileTemplate = () => { - const request = new Request( - `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`, - { - method:'GET', - headers: { - 'Content-Type':'application/json' - }, - } - ); - return fetch(request).then(requestResponseHandler) + + +export const fetchRegisterFormFile = (id = null) => { + let url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}` + if (id) { + url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${id}`; + } + const request = new Request( + `${url}`, + { + method:'GET', + headers: { + 'Content-Type':'application/json' + }, + } + ); + return fetch(request).then(requestResponseHandler) }; -export const fetchRegisterFormFile = (id) => { - const request = new Request( - `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${id}`, - { - method:'GET', - headers: { - 'Content-Type':'application/json' - }, - } - ); - return fetch(request).then(requestResponseHandler) -}; +export const editRegistrationFormFile= (fileId, data, csrfToken) => { + return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${fileId}`, { + method: 'PUT', + body: data, + headers: { + 'X-CSRFToken': csrfToken, + }, + credentials: 'include', + }) + .then(requestResponseHandler) +} export const createRegistrationFormFile = (data,csrfToken) => { @@ -137,6 +142,33 @@ export const createRegistrationFormFile = (data,csrfToken) => { .then(requestResponseHandler) } +export const deleteRegisterFormFile= (fileId,csrfToken) => { + return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${fileId}`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': csrfToken, + }, + credentials: 'include', + }) +} + +export const fetchRegisterFormFileTemplate = (id = null) => { + let url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`; + if(id){ + url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}/${id}`; + } + const request = new Request( + `${url}`, + { + method:'GET', + headers: { + 'Content-Type':'application/json' + }, + } + ); + return fetch(request).then(requestResponseHandler) +}; + export const createRegistrationFormFileTemplate = (data,csrfToken) => { return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`, { diff --git a/Front-End/src/components/AffectationClasseForm.js b/Front-End/src/components/AffectationClasseForm.js index d118c56..b96db19 100644 --- a/Front-End/src/components/AffectationClasseForm.js +++ b/Front-End/src/components/AffectationClasseForm.js @@ -1,9 +1,9 @@ import React, { useState } from 'react'; -const AffectationClasseForm = ({ eleve, onSubmit, classes }) => { +const AffectationClasseForm = ({ eleve = {}, onSubmit, classes }) => { const [formData, setFormData] = useState({ - classeAssocie_id: eleve.classeAssocie_id || null, + classeAssocie_id: eleve?.classeAssocie_id || null, }); const handleChange = (e) => { diff --git a/Front-End/src/components/FileStatusLabel.js b/Front-End/src/components/FileStatusLabel.js new file mode 100644 index 0000000..29330c5 --- /dev/null +++ b/Front-End/src/components/FileStatusLabel.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { Check, Clock } from 'lucide-react'; + +const FileStatusLabel = ({ status }) => { + const getStatusConfig = () => { + switch (status) { + case 'sent': + return { + label: 'Envoyé', + className: 'bg-green-50 text-green-600', + icon: + }; + case 'pending': + default: + return { + label: 'En attente', + className: 'bg-orange-50 text-orange-600', + icon: + }; + } + }; + + const { label, className, icon } = getStatusConfig(); + + return ( +
+ {icon} + {label} +
+ ); +}; + +export default FileStatusLabel; diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js index 051f749..34aa4c3 100644 --- a/Front-End/src/components/Inscription/InscriptionFormShared.js +++ b/Front-End/src/components/Inscription/InscriptionFormShared.js @@ -1,3 +1,4 @@ +// Import des dépendances nécessaires import React, { useState, useEffect } from 'react'; import InputText from '@/components/InputText'; import SelectChoice from '@/components/SelectChoice'; @@ -6,12 +7,14 @@ import Loader from '@/components/Loader'; import Button from '@/components/Button'; import DjangoCSRFToken from '@/components/DjangoCSRFToken'; import Table from '@/components/Table'; -import { fetchRegisterFormFileTemplate, createRegistrationFormFile } from '@/app/lib/subscriptionAction'; -import { Download, Upload } from 'lucide-react'; +import { fetchRegisterFormFileTemplate, createRegistrationFormFile, fetchRegisterForm, deleteRegisterFormFile } from '@/app/lib/subscriptionAction'; +import { Download, Upload, Trash2, Eye } from 'lucide-react'; import { BASE_URL } from '@/utils/Url'; import DraggableFileUpload from '@/app/[locale]/admin/subscriptions/components/DraggableFileUpload'; import Modal from '@/components/Modal'; +import FileStatusLabel from '@/components/FileStatusLabel'; +// Définition des niveaux scolaires disponibles const levels = [ { value:'1', label: 'TPS - Très Petite Section'}, { value:'2', label: 'PS - Petite Section'}, @@ -19,32 +22,28 @@ const levels = [ { value:'4', label: 'GS - Grande Section'}, ]; +/** + * Composant de formulaire d'inscription partagé + * @param {string} studentId - ID de l'étudiant + * @param {string} csrfToken - Token CSRF pour la sécurité + * @param {function} onSubmit - Fonction de soumission du formulaire + * @param {string} cancelUrl - URL de redirection en cas d'annulation + * @param {object} errors - Erreurs de validation du formulaire + */ export default function InscriptionFormShared({ - initialData, + studentId, csrfToken, onSubmit, cancelUrl, - isLoading = false, errors = {} // Nouvelle prop pour les erreurs }) { + // États pour gérer les données du formulaire + const [isLoading, setIsLoading] = useState(true); + const [formData, setFormData] = useState({}); - const [formData, setFormData] = useState(() => ({ - id: initialData?.id || '', - last_name: initialData?.last_name || '', - first_name: initialData?.first_name || '', - address: initialData?.address || '', - birth_date: initialData?.birth_date || '', - birth_place: initialData?.birth_place || '', - birth_postal_code: initialData?.birth_postal_code || '', - nationality: initialData?.nationality || '', - attending_physician: initialData?.attending_physician || '', - level: initialData?.level || '' - })); - - const [guardians, setGuardians] = useState(() => - initialData?.guardians || [] - ); + const [guardians, setGuardians] = useState([]); + // États pour la gestion des fichiers const [uploadedFiles, setUploadedFiles] = useState([]); const [fileTemplates, setFileTemplates] = useState([]); const [fileName, setFileName] = useState(""); @@ -52,50 +51,104 @@ export default function InscriptionFormShared({ const [showUploadModal, setShowUploadModal] = useState(false); const [currentTemplateId, setCurrentTemplateId] = useState(null); + // Chargement initial des données // Mettre à jour les données quand initialData change useEffect(() => { - if (initialData) { - setFormData({ - id: initialData.id || '', - last_name: initialData.last_name || '', - first_name: initialData.first_name || '', - address: initialData.address || '', - birth_date: initialData.birth_date || '', - birth_place: initialData.birth_place || '', - birth_postal_code: initialData.birth_postal_code || '', - nationality: initialData.nationality || '', - attending_physician: initialData.attending_physician || '', - level: initialData.level || '' + if (studentId) { + fetchRegisterForm(studentId).then((data) => { + console.log(data); + + setFormData({ + id: data?.student?.id || '', + last_name: data?.student?.last_name || '', + first_name: data?.student?.first_name || '', + address: data?.student?.address || '', + birth_date: data?.student?.birth_date || '', + birth_place: data?.student?.birth_place || '', + birth_postal_code: data?.student?.birth_postal_code || '', + nationality: data?.student?.nationality || '', + attending_physician: data?.student?.attending_physician || '', + level: data?.student?.level || '' + }); + setGuardians(data?.student?.guardians || []); + setUploadedFiles(data.registration_files || []); }); - setGuardians(initialData.guardians || []); + fetchRegisterFormFileTemplate().then((data) => { setFileTemplates(data); }); + setIsLoading(false); } - }, [initialData]); + }, [studentId]); + // Fonctions de gestion du formulaire et des fichiers const updateFormField = (field, value) => { setFormData(prev => ({...prev, [field]: value})); }; + // Gestion du téléversement de fichiers const handleFileUpload = async (file, fileName) => { + if (!file || !currentTemplateId || !formData.id) { + console.error('Missing required data for upload'); + return; + } + const data = new FormData(); data.append('file', file); - data.append('name',fileName); + data.append('name', fileName); data.append('template', currentTemplateId); data.append('register_form', formData.id); try { - await createRegistrationFormFile(data, csrfToken); - // Optionnellement, rafraîchir la liste des fichiers - fetchRegisterFormFileTemplate().then((data) => { - setFileTemplates(data); - }); + const response = await createRegistrationFormFile(data, csrfToken); + if (response) { + setUploadedFiles(prev => { + const newFiles = prev.filter(f => parseInt(f.template) !== currentTemplateId); + return [...newFiles, { + name: fileName, + template: currentTemplateId, + file: response.file + }]; + }); + + // Rafraîchir les données du formulaire pour avoir les fichiers à jour + if (studentId) { + fetchRegisterForm(studentId).then((data) => { + setUploadedFiles(data.registration_files || []); + }); + } + } } catch (error) { console.error('Error uploading file:', error); } }; + // Vérification si un fichier est déjà uploadé + const isFileUploaded = (templateId) => { + return uploadedFiles.find(template => + template.template === templateId + ); + }; + + // Récupération d'un fichier uploadé + const getUploadedFile = (templateId) => { + return uploadedFiles.find(file => parseInt(file.template) === templateId); + }; + + // Suppression d'un fichier + const handleDeleteFile = async (templateId) => { + const fileToDelete = getUploadedFile(templateId); + if (!fileToDelete) return; + + try { + await deleteRegisterFormFile(fileToDelete.id, csrfToken); + setUploadedFiles(prev => prev.filter(f => parseInt(f.template) !== templateId)); + } catch (error) { + console.error('Error deleting file:', error); + } + }; + + // Soumission du formulaire const handleSubmit = (e) => { e.preventDefault(); const data ={ @@ -107,36 +160,70 @@ export default function InscriptionFormShared({ onSubmit(data); }; + // Récupération des messages d'erreur const getError = (field) => { return errors?.student?.[field]?.[0]; }; - const getGuardianError = (index, field) => { - return errors?.student?.guardians?.[index]?.[field]?.[0]; - }; - + // Configuration des colonnes pour le tableau des fichiers const columns = [ { name: 'Nom du fichier', transform: (row) => row.name }, { name: 'Fichier à Remplir', transform: (row) => row.is_required ? 'Oui' : 'Non' }, { name: 'Fichier de référence', transform: (row) => row.file && }, - { name: 'Actions', transform: (row) => ( -
- {row.is_required && - +
+ ); + } + + return ( + - } -
- ) }, + ); + }}, ]; + // Affichage du loader pendant le chargement if (isLoading) return ; + // Rendu du composant return (
@@ -245,17 +332,19 @@ export default function InscriptionFormShared({
{/* Section Fichiers d'inscription */} -
-

Fichiers à remplir

- {}} - /> - + {fileTemplates.length > 0 && ( +
+

Fichiers à remplir

+
{}} + /> + + )} {/* Boutons de contrôle */}
@@ -263,44 +352,52 @@ export default function InscriptionFormShared({
- ( - <> - { - setFile(selectedFile); - setFileName(selectedFile.name); - }} - > - - - -
-
- - )} - /> +
+
+ + )} + /> + )} ); } \ No newline at end of file diff --git a/Front-End/src/components/Inscription/ResponsableInputFields.js b/Front-End/src/components/Inscription/ResponsableInputFields.js index edaf92d..4cdb49b 100644 --- a/Front-End/src/components/Inscription/ResponsableInputFields.js +++ b/Front-End/src/components/Inscription/ResponsableInputFields.js @@ -4,6 +4,7 @@ import Button from '@/components/Button'; import React from 'react'; import { useTranslations } from 'next-intl'; import 'react-phone-number-input/style.css' +import { Trash2, Plus } from 'lucide-react'; export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) { const t = useTranslations('ResponsableInputFields'); @@ -19,10 +20,9 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad

{t('responsable')} {index+1}

{guardians.length > 1 && ( -
@@ -102,13 +102,9 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad ))}
-
diff --git a/Front-End/src/hooks/useCsrfToken.js b/Front-End/src/hooks/useCsrfToken.js index 284dc8b..1489d70 100644 --- a/Front-End/src/hooks/useCsrfToken.js +++ b/Front-End/src/hooks/useCsrfToken.js @@ -14,7 +14,7 @@ const useCsrfToken = () => { if (data) { if(data.csrfToken != token) { setToken(data.csrfToken); - console.log('------------> CSRF Token reçu:', data.csrfToken); + //console.log('------------> CSRF Token reçu:', data.csrfToken); } } })