From 8417d3eb141b116e2e9f8c6038831ce1bbe30e2a Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 20 Apr 2025 19:19:27 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Upload=20du=20SEPA=20par=20les=20parent?= =?UTF-8?q?s=20/=20Cr=C3=A9ation=20d'un=20composant=20header=20pour=20les?= =?UTF-8?q?=20titres=20de=20tableau?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Back-End/N3wtSchool/renderers.py | 17 +- Back-End/Subscriptions/models.py | 29 +- Back-End/Subscriptions/serializers.py | 4 +- Back-End/Subscriptions/util.py | 39 +- .../views/register_form_views.py | 35 +- .../views/registration_file_views.py | 3 +- Back-End/Subscriptions/views/student_views.py | 9 +- .../src/app/[locale]/admin/structure/page.js | 12 +- .../app/[locale]/admin/subscriptions/page.js | 77 +++- .../validateSubscription/page.js | 4 +- Front-End/src/app/[locale]/parents/page.js | 118 ++++-- .../app/actions/registerFileGroupAction.js | 26 +- .../{Inscription => }/FileUpload.js | 7 +- .../components/Inscription/FilesToUpload.js | 213 +++++++++- .../Inscription/InscriptionFormShared.js | 370 +++++++----------- .../Inscription/ValidateSubscription.js | 61 +-- Front-End/src/components/SectionHeader.js | 44 +++ Front-End/src/components/SidebarTabs.js | 10 +- .../Structure/Configuration/ClassesSection.js | 17 +- .../Configuration/SpecialitiesSection.js | 17 +- .../Configuration/StructureManagement.js | 8 +- .../Configuration/TeachersSection.js | 19 +- .../Structure/Files/FilesGroupsManagement.js | 154 ++------ .../Structure/Files/ParentFilesSection.js | 30 +- .../Tarification/DiscountsSection.js | 33 +- .../Structure/Tarification/FeesManagement.js | 175 +++++---- .../Structure/Tarification/FeesSection.js | 32 +- Front-End/src/components/Table.js | 25 +- 28 files changed, 893 insertions(+), 695 deletions(-) rename Front-End/src/components/{Inscription => }/FileUpload.js (87%) create mode 100644 Front-End/src/components/SectionHeader.js diff --git a/Back-End/N3wtSchool/renderers.py b/Back-End/N3wtSchool/renderers.py index 96b2b67..9411cf3 100644 --- a/Back-End/N3wtSchool/renderers.py +++ b/Back-End/N3wtSchool/renderers.py @@ -4,11 +4,22 @@ from django.template.loader import get_template from xhtml2pdf import pisa +class PDFResult: + def __init__(self, content): + self.content = content + def render_to_pdf(template_src, context_dict={}): + """ + Génère un PDF à partir d'un template HTML et retourne le contenu en mémoire. + """ template = get_template(template_src) - html = template.render(context_dict) + html = template.render(context_dict) result = BytesIO() pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result) + if pdf.err: - return HttpResponse("Invalid PDF", status_code=400, content_type='text/plain') - return HttpResponse(result.getvalue(), content_type='application/pdf') \ No newline at end of file + # Lever une exception ou retourner None en cas d'erreur + raise ValueError("Erreur lors de la génération du PDF.") + + # Retourner le contenu du PDF en mémoire + return PDFResult(result.getvalue()) \ No newline at end of file diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 26283b6..a0dd4af 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -9,6 +9,8 @@ from Establishment.models import Establishment from datetime import datetime +import os + class Language(models.Model): """ Représente une langue parlée par l’élève. @@ -231,8 +233,11 @@ class RegistrationForm(models.Model): # Appeler la méthode save originale super().save(*args, **kwargs) -def registration_file_upload_to(instance, filename): - return f"registration_files/dossier_rf_{instance.registration_form.pk}/{filename}" +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 ######################## @@ -265,7 +270,7 @@ class RegistrationSchoolFileTemplate(models.Model): slug = models.CharField(max_length=255, default="") name = models.CharField(max_length=255, default="") registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='school_file_templates', blank=True) - file = models.FileField(null=True,blank=True, upload_to=registration_file_upload_to) + file = models.FileField(null=True,blank=True, upload_to=registration_school_file_upload_to) def __str__(self): return self.name @@ -285,17 +290,31 @@ class RegistrationSchoolFileTemplate(models.Model): class RegistrationParentFileTemplate(models.Model): master = models.ForeignKey(RegistrationParentFileMaster, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) - file = models.FileField(null=True,blank=True, upload_to=registration_file_upload_to) + file = models.FileField(null=True,blank=True, upload_to=registration_parent_file_upload_to) def __str__(self): return self.name + def save(self, *args, **kwargs): + if self.pk: # Si l'objet existe déjà dans la base de données + try: + old_instance = RegistrationParentFileTemplate.objects.get(pk=self.pk) + if old_instance.file and (not self.file or self.file.name == ''): + if os.path.exists(old_instance.file.path): + old_instance.file.delete(save=False) + self.file = None + else: + print(f"Le fichier {old_instance.file.path} n'existe pas.") + except RegistrationParentFileTemplate.DoesNotExist: + print("Ancienne instance introuvable.") + super().save(*args, **kwargs) + @staticmethod def get_files_from_rf(register_form_id): """ Récupère tous les fichiers liés à un dossier d’inscription donné. """ - registration_files = RegistrationSchoolFileTemplate.objects.filter(registration_form=register_form_id) + registration_files = RegistrationParentFileTemplate.objects.filter(registration_form=register_form_id) filenames = [] for reg_file in registration_files: filenames.append(reg_file.file.path) diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index aad1cfa..4348a49 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -38,14 +38,14 @@ class RegistrationSchoolFileTemplateSerializer(serializers.ModelSerializer): class RegistrationParentFileTemplateSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) - file = serializers.SerializerMethodField() + file_url = serializers.SerializerMethodField() master_name = serializers.CharField(source='master.name', read_only=True) master_description = serializers.CharField(source='master.description', read_only=True) class Meta: model = RegistrationParentFileTemplate fields = '__all__' - def get_file(self, obj): + def get_file_url(self, obj): # Retourne l'URL complète du fichier si disponible return obj.file.url if obj.file else None diff --git a/Back-End/Subscriptions/util.py b/Back-End/Subscriptions/util.py index b440d5d..3f2406b 100644 --- a/Back-End/Subscriptions/util.py +++ b/Back-End/Subscriptions/util.py @@ -19,6 +19,9 @@ from rest_framework.parsers import JSONParser from PyPDF2 import PdfMerger import shutil +import logging + +logger = logging.getLogger(__name__) def recupereListeFichesInscription(): """ @@ -121,7 +124,6 @@ def rfToPDF(registerForm, filename): Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm. """ filename = filename.replace(" ", "_") - data = { 'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}", 'signatureDate': convertToStr(_now(), '%d-%m-%Y'), @@ -131,20 +133,37 @@ def rfToPDF(registerForm, filename): # Générer le PDF pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data) + if not pdf: + raise ValueError("Erreur lors de la génération du PDF.") # Vérifier si un fichier avec le même nom existe déjà et le supprimer - if registerForm.registration_file and os.path.exists(registerForm.registration_file.path): - os.remove(registerForm.registration_file.path) - registerForm.registration_file.delete(save=False) + if registerForm.registration_file and registerForm.registration_file.name: + # Vérifiez si le chemin est déjà absolu ou relatif + if os.path.isabs(registerForm.registration_file.name): + existing_file_path = registerForm.registration_file.name + else: + existing_file_path = os.path.join(settings.MEDIA_ROOT, registerForm.registration_file.name.lstrip('/')) + + # Vérifier si le fichier existe et le supprimer + if os.path.exists(existing_file_path): + print(f'exist ! REMOVE') + os.remove(existing_file_path) + registerForm.registration_file.delete(save=False) + else: + print(f'File does not exist: {existing_file_path}') # Enregistrer directement le fichier dans le champ registration_file - registerForm.registration_file.save( - os.path.basename(filename), - File(BytesIO(pdf.content)), # Utilisation de BytesIO pour éviter l'écriture sur le disque - save=True - ) + try: + registerForm.registration_file.save( + os.path.basename(filename), # Utiliser uniquement le nom de fichier + File(BytesIO(pdf.content)), + save=True + ) + except Exception as e: + logger.error(f"Erreur lors de la sauvegarde du fichier PDF : {e}") + raise - return registerForm.registration_file.path + return registerForm.registration_file def delete_registration_files(registerForm): """ diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py index 3d57610..2646af3 100644 --- a/Back-End/Subscriptions/views/register_form_views.py +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -254,30 +254,37 @@ class RegisterFormWithIdView(APIView): if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: try: # Génération de la fiche d'inscription au format PDF - base_dir = f"data/registration_files/dossier_rf_{registerForm.pk}" + base_dir = os.path.join(settings.MEDIA_ROOT, 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" + initial_pdf = f"{base_dir}/Inscription_{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 = RegistrationSchoolFileTemplate.get_files_from_rf(registerForm.pk) - if registerForm.registration_file: - fileNames.insert(0, registerForm.registration_file.path) + # fileNames = RegistrationSchoolFileTemplate.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_content = util.merge_files_pdf(fileNames) + # # Création du fichier PDF Fusionné + # merged_pdf_content = util.merge_files_pdf(fileNames) - # Mise à jour du champ registration_file avec le fichier fusionné - registerForm.registration_file.save( - f"dossier_complet_{registerForm.pk}.pdf", - File(merged_pdf_content), - save=True - ) + # # Mise à jour du champ registration_file avec le fichier fusionné + # registerForm.registration_file.save( + # f"dossier_complet.pdf", + # File(merged_pdf_content), + # save=True + # ) + # Mise à jour de l'automate - updateStateMachine(registerForm, 'EVENT_SIGNATURE') + # Vérification de la présence du fichier SEPA + if registerForm.sepa_file: + # Mise à jour de l'automate pour SEPA + updateStateMachine(registerForm, 'EVENT_SIGNATURE_SEPA') + else: + # Mise à jour de l'automate pour une signature classique + updateStateMachine(registerForm, 'EVENT_SIGNATURE') except Exception as e: return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/Back-End/Subscriptions/views/registration_file_views.py b/Back-End/Subscriptions/views/registration_file_views.py index 2a02ced..84fc514 100644 --- a/Back-End/Subscriptions/views/registration_file_views.py +++ b/Back-End/Subscriptions/views/registration_file_views.py @@ -285,7 +285,8 @@ class RegistrationParentFileTemplateSimpleView(APIView): template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id) if template is None: return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) - serializer = RegistrationParentFileTemplateSerializer(template, data=request.data) + + serializer = RegistrationParentFileTemplateSerializer(template, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK) diff --git a/Back-End/Subscriptions/views/student_views.py b/Back-End/Subscriptions/views/student_views.py index 02cfed4..8abcd99 100644 --- a/Back-End/Subscriptions/views/student_views.py +++ b/Back-End/Subscriptions/views/student_views.py @@ -96,6 +96,13 @@ class ChildrenListView(APIView): students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profile_role__profile__id', _value=id) if students: - students = students.filter(establishment=establishment_id).distinct() + students = students.filter( + establishment=establishment_id, + status__in=[ + RegistrationForm.RegistrationFormStatus.RF_SENT, + RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW, + RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT + ] + ).distinct() students_serializer = RegistrationFormByParentSerializer(students, many=True) return JsonResponse(students_serializer.data, safe=False) diff --git a/Front-End/src/app/[locale]/admin/structure/page.js b/Front-End/src/app/[locale]/admin/structure/page.js index aa72c8e..5ffb434 100644 --- a/Front-End/src/app/[locale]/admin/structure/page.js +++ b/Front-End/src/app/[locale]/admin/structure/page.js @@ -22,7 +22,7 @@ import { fetchTuitionPaymentPlans, fetchRegistrationPaymentModes, fetchTuitionPaymentModes } from '@/app/actions/schoolAction'; -import { fetchProfileRoles, fetchProfiles } from '@/app/actions/authAction'; +import { fetchProfiles } from '@/app/actions/authAction'; import SidebarTabs from '@/components/SidebarTabs'; import FilesGroupsManagement from '@/components/Structure/Files/FilesGroupsManagement'; import { fetchRegistrationSchoolFileMasters } from "@/app/actions/registerFileGroupAction"; @@ -258,7 +258,7 @@ export default function Page() { const tabs = [ { id: 'Configuration', - label: "Configuration de l'école", + label: "Classes", content: ( } ]; return ( -
+
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 65659dc..91768e1 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -9,7 +9,7 @@ import Popup from '@/components/Popup'; import Loader from '@/components/Loader'; import AlertWithModal from '@/components/AlertWithModal'; import DropdownMenu from "@/components/DropdownMenu"; -import { MoreVertical, Send, Edit, Archive, FileText, CircleCheck, Plus, XCircle } from 'lucide-react'; +import { MoreVertical, Send, Edit, Archive, FileText, CheckCircle, Plus, XCircle } from 'lucide-react'; import Modal from '@/components/Modal'; import InscriptionForm from '@/components/Inscription/InscriptionForm' import AffectationClasseForm from '@/components/AffectationClasseForm' @@ -606,35 +606,62 @@ useEffect(()=>{ const actions = { 1: [ { - icon: , + icon: ( + + + + ), onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`, }, { - icon: , + icon: ( + + + + ), onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name), }, ], 2: [ { - icon: , + icon: ( + + + + ), onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`, }, ], 3: [ { - icon: , - onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL}?studentId=${row.student.id}&firstName=${row.student.first_name}&lastName=${row.student.last_name}&paymentMode=${row.registration_payment}&file=${row.registration_file}`, + icon: ( + + + + ), + onClick: () => { + const paymentSepa = row.registration_payment === 1 || row.tuition_payment === 1 ? 1 : 0; + window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL}?studentId=${row.student.id}&firstName=${row.student.first_name}&lastName=${row.student.last_name}&paymentSepa=${paymentSepa}&file=${row.registration_file}` + } }, ], 5: [ { - icon: , + icon: ( + + + + ), onClick: () => openModalAssociationEleve(row.student), }, ], default: [ { - icon: , + icon: ( + + + + ), onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name), }, ], @@ -674,15 +701,35 @@ const columns = [
) }, - { name: t('files'), transform: (row) => - (row.registration_file != null) &&( -
    -
  • + { name: t('files'), transform: (row) => ( + - ) }, + ) +}, { name: 'Actions', transform: (row) => (
    @@ -728,7 +775,7 @@ const columnsSubscribed = [ items={[ { label: ( <> - Rattacher + Rattacher ), onClick: () => openModalAssociationEleve(row.student) diff --git a/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js index fe5c8e1..d9bd7e4 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js @@ -15,7 +15,7 @@ export default function Page() { const studentId = searchParams.get('studentId'); const firstName = searchParams.get('firstName'); const lastName = searchParams.get('lastName'); - const paymentMode = searchParams.get('paymentMode'); + const paymentSepa = searchParams.get('paymentSepa') === '1'; const file = searchParams.get('file'); const csrfToken = useCsrfToken(); @@ -45,7 +45,7 @@ export default function Page() { studentId={studentId} firstName={firstName} lastName={lastName} - paymentMode={paymentMode} + paymentSepa={paymentSepa} file={file} onAccept={handleAcceptRF} /> diff --git a/Front-End/src/app/[locale]/parents/page.js b/Front-End/src/app/[locale]/parents/page.js index 1058271..50ae150 100644 --- a/Front-End/src/app/[locale]/parents/page.js +++ b/Front-End/src/app/[locale]/parents/page.js @@ -2,14 +2,16 @@ import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import Table from '@/components/Table'; -import { Edit3, Users, Download, Eye } from 'lucide-react'; +import { Edit3, Users, Download, Eye, Upload, CheckCircle } from 'lucide-react'; import StatusLabel from '@/components/StatusLabel'; +import FileUpload from '@/components/FileUpload'; import { FE_PARENTS_EDIT_INSCRIPTION_URL } from '@/utils/Url'; -import { fetchChildren } from '@/app/actions/subscriptionAction'; +import { fetchChildren, sendSEPARegisterForm } from '@/app/actions/subscriptionAction'; import logger from '@/utils/logger'; import { useSession } from 'next-auth/react'; import { FE_USERS_LOGIN_URL, BASE_URL } from '@/utils/Url'; import { useEstablishment } from '@/context/EstablishmentContext'; +import { useCsrfToken } from '@/context/CsrfContext'; export default function ParentHomePage() { const [children, setChildren] = useState([]); @@ -18,8 +20,11 @@ export default function ParentHomePage() { const [currentPage, setCurrentPage] = useState(1); const [establishments, setEstablishments] = useState([]); const { selectedEstablishmentId, setSelectedEstablishmentId } = useEstablishment(); - + const [uploadingStudentId, setUploadingStudentId] = useState(null); // ID de l'étudiant pour l'upload + const [uploadedFile, setUploadedFile] = useState(null); // Fichier uploadé + const [uploadState, setUploadState] = useState("off"); // État "on" ou "off" pour l'affichage du composant const router = useRouter(); + const csrfToken = useCsrfToken(); useEffect(() => { if (status === 'loading') return; @@ -55,22 +60,57 @@ export default function ParentHomePage() { }; function handleView(eleveId) { - // Logique pour éditer le dossier de l'élève logger.debug(`View dossier for student id: ${eleveId}`); router.push(`${FE_PARENTS_EDIT_INSCRIPTION_URL}?id=${userId}&studentId=${eleveId}&view=true`); } function handleEdit(eleveId) { - // Logique pour éditer le dossier de l'élève logger.debug(`Edit dossier for student id: ${eleveId}`); router.push(`${FE_PARENTS_EDIT_INSCRIPTION_URL}?id=${userId}&studentId=${eleveId}`); } - const actionColumns = [ - { name: 'Action', transform: (row) => row.action }, - ]; + const handleFileUpload = (file) => { + if (!file) { + logger.error('Aucun fichier sélectionné pour l\'upload.'); + return; + } + setUploadedFile(file); // Conserve le fichier en mémoire + logger.debug('Fichier sélectionné :', file.name); + }; + + const handleSubmit = () => { + if (!uploadedFile || !uploadingStudentId) { + logger.error('Aucun fichier ou étudiant sélectionné.'); + return; + } + + const formData = new FormData(); + formData.append('sepa_file', uploadedFile); // Ajoute le fichier SEPA + formData.append('status', 3); // Statut à envoyer + + sendSEPARegisterForm(uploadingStudentId, formData, csrfToken) + .then((response) => { + logger.debug('RF mis à jour avec succès:', response); + // Logique supplémentaire après la mise à jour (par exemple, redirection ou notification) + }) + .catch((error) => { + logger.error('Erreur lors de la mise à jour du RF:', error); + }); + }; + + const toggleUpload = (studentId) => { + if (uploadingStudentId === studentId && uploadState === "on") { + // Si le composant est déjà affiché pour cet étudiant, on le masque + setUploadState("off"); + setUploadingStudentId(null); + setUploadedFile(null); // Réinitialise le fichier + } else { + // Sinon, on l'affiche pour cet étudiant + setUploadState("on"); + setUploadingStudentId(studentId); + } + }; - // Définir les colonnes du tableau const childrenColumns = [ { name: 'Nom', transform: (row) => `${row.student.last_name}` }, { name: 'Prénom', transform: (row) => `${row.student.first_name}` }, @@ -86,54 +126,67 @@ export default function ParentHomePage() { name: 'Actions', transform: (row) => (
    - {/* Actions en fonction du statut */} {row.status === 2 && ( )} - + {row.status === 3 && ( )} - + {row.status === 7 && ( <> + {/* Nouvelle action Upload */} + )}
    @@ -141,13 +194,6 @@ export default function ParentHomePage() { } ]; - const itemsPerPage = 5; - const totalPages = Math.ceil(children.length / itemsPerPage) || 1; - - const handlePageChange = (newPage) => { - setCurrentPage(newPage); - }; - return (
    @@ -175,13 +221,27 @@ export default function ParentHomePage() { + {/* Composant FileUpload et bouton Valider en dessous du tableau */} + {uploadState === "on" && uploadingStudentId && ( +
    + + +
    + )} ); diff --git a/Front-End/src/app/actions/registerFileGroupAction.js b/Front-End/src/app/actions/registerFileGroupAction.js index 56c39d7..f0895c5 100644 --- a/Front-End/src/app/actions/registerFileGroupAction.js +++ b/Front-End/src/app/actions/registerFileGroupAction.js @@ -229,6 +229,18 @@ export const editRegistrationSchoolFileTemplates = (fileId, data, csrfToken) => .then(requestResponseHandler) }; +export const editRegistrationParentFileTemplates = (fileId, data, csrfToken) => { + return fetch(`${BE_SUBSCRIPTION_REGISTRATION_PARENT_FILE_TEMPLATES_URL}/${fileId}`, { + method: 'PUT', + body: data, + headers: { + 'X-CSRFToken': csrfToken, + }, + credentials: 'include', + }) + .then(requestResponseHandler) +}; + // DELETE requests export async function deleteRegistrationFileGroup(groupId, csrfToken) { @@ -243,7 +255,7 @@ export async function deleteRegistrationFileGroup(groupId, csrfToken) { return response; }; -export const deleteRegistrationSchoolFileMaster = (fileId,csrfToken) => { +export const deleteRegistrationSchoolFileMaster = (fileId, csrfToken) => { return fetch(`${BE_SUBSCRIPTION_REGISTRATION_SCHOOL_FILE_MASTERS_URL}/${fileId}`, { method: 'DELETE', headers: { @@ -263,7 +275,7 @@ export const deleteRegistrationParentFileMaster = (id, csrfToken) => { }) }; -export const deleteRegistrationSchoolFileTemplates = (fileId,csrfToken) => { +export const deleteRegistrationSchoolFileTemplates = (fileId, csrfToken) => { return fetch(`${BE_SUBSCRIPTION_REGISTRATION_SCHOOL_FILE_TEMPLATES_URL}/${fileId}`, { method: 'DELETE', headers: { @@ -273,6 +285,16 @@ export const deleteRegistrationSchoolFileTemplates = (fileId,csrfToken) => { }) }; +export const deleteRegistrationParentFileTemplate = (id, csrfToken) => { + return fetch(`${BE_SUBSCRIPTION_REGISTRATION_PARENT_FILE_TEMPLATES_URL}/${id}`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': csrfToken, + }, + credentials: 'include', + }) +}; + // API requests export const cloneTemplate = (templateId, email, is_required) => { diff --git a/Front-End/src/components/Inscription/FileUpload.js b/Front-End/src/components/FileUpload.js similarity index 87% rename from Front-End/src/components/Inscription/FileUpload.js rename to Front-End/src/components/FileUpload.js index 66c4c75..d5020f2 100644 --- a/Front-End/src/components/Inscription/FileUpload.js +++ b/Front-End/src/components/FileUpload.js @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; import { CloudUpload } from 'lucide-react'; import logger from '@/utils/logger'; export default function FileUpload({ selectionMessage, onFileSelect, uploadedFileName }) { const [localFileName, setLocalFileName] = useState(uploadedFileName || ''); + const fileInputRef = useRef(null); // Utilisation de useRef pour cibler l'input const handleFileChange = (e) => { const file = e.target.files[0]; @@ -29,7 +30,7 @@ export default function FileUpload({ selectionMessage, onFileSelect, uploadedFil

    {`${selectionMessage}`}

    document.getElementById('fileInput').click()} // Ouvre l'explorateur de fichiers au clic + onClick={() => fileInputRef.current.click()} // Utilisation de la référence pour ouvrir l'explorateur onDragOver={(e) => e.preventDefault()} onDrop={handleFileDrop} > @@ -39,7 +40,7 @@ export default function FileUpload({ selectionMessage, onFileSelect, uploadedFil accept=".pdf" onChange={handleFileChange} className="hidden" - id="fileInput" + ref={fileInputRef} // Attachement de la référence />
    {}} + /> + {selectedFile && ( +
    + {actionType === 'view' && selectedFile.fileName ? ( +