mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-03 16:51:26 +00:00
feat: WIP finalisation partie signature des parents [N3WTS-17]
This commit is contained in:
@ -487,7 +487,12 @@ def generate_form_json_pdf(register_form, form_json):
|
|||||||
for field in fields:
|
for field in fields:
|
||||||
label = field.get("label", field.get("id", ""))
|
label = field.get("label", field.get("id", ""))
|
||||||
ftype = field.get("type", "")
|
ftype = field.get("type", "")
|
||||||
c.drawString(100, y, f"{label} [{ftype}]")
|
value = field.get("value", "")
|
||||||
|
# Afficher la valeur si elle existe
|
||||||
|
if value not in (None, ""):
|
||||||
|
c.drawString(100, y, f"{label} [{ftype}] : {value}")
|
||||||
|
else:
|
||||||
|
c.drawString(100, y, f"{label} [{ftype}]")
|
||||||
y -= 25
|
y -= 25
|
||||||
if y < 100:
|
if y < 100:
|
||||||
c.showPage()
|
c.showPage()
|
||||||
|
|||||||
@ -6,11 +6,10 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from Subscriptions.serializers import RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
|
from Subscriptions.serializers import RegistrationParentFileMasterSerializer
|
||||||
from Subscriptions.models import (
|
from Subscriptions.models import (
|
||||||
RegistrationForm,
|
RegistrationForm,
|
||||||
RegistrationParentFileMaster,
|
RegistrationParentFileMaster
|
||||||
RegistrationParentFileTemplate
|
|
||||||
)
|
)
|
||||||
from N3wtSchool import bdd
|
from N3wtSchool import bdd
|
||||||
import logging
|
import logging
|
||||||
@ -176,97 +175,3 @@ class RegistrationParentFileMasterSimpleView(APIView):
|
|||||||
return JsonResponse({'message': 'La suppression du fichier parent a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
return JsonResponse({'message': 'La suppression du fichier parent a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
class RegistrationParentFileTemplateView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère tous les templates parents pour un établissement donné",
|
|
||||||
manual_parameters=[
|
|
||||||
openapi.Parameter(
|
|
||||||
'establishment_id',
|
|
||||||
openapi.IN_QUERY,
|
|
||||||
description="ID de l'établissement",
|
|
||||||
type=openapi.TYPE_INTEGER,
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
],
|
|
||||||
responses={200: RegistrationParentFileTemplateSerializer(many=True)}
|
|
||||||
)
|
|
||||||
def get(self, request):
|
|
||||||
establishment_id = request.GET.get('establishment_id')
|
|
||||||
if not establishment_id:
|
|
||||||
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Filtrer les templates parents liés à l'établissement via master.groups.establishment
|
|
||||||
templates = RegistrationParentFileTemplate.objects.filter(
|
|
||||||
master__groups__establishment__id=establishment_id
|
|
||||||
).distinct()
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(templates, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Crée un nouveau template d'inscription",
|
|
||||||
request_body=RegistrationParentFileTemplateSerializer,
|
|
||||||
responses={
|
|
||||||
201: RegistrationParentFileTemplateSerializer,
|
|
||||||
400: "Données invalides"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(data=request.data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
serializer.save()
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
class RegistrationParentFileTemplateSimpleView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère un template d'inscription spécifique",
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileTemplateSerializer,
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def get(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
|
|
||||||
if template is None:
|
|
||||||
return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(template)
|
|
||||||
return JsonResponse(serializer.data, safe=False)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Met à jour un template d'inscription existant",
|
|
||||||
request_body=RegistrationParentFileTemplateSerializer,
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileTemplateSerializer,
|
|
||||||
400: "Données invalides",
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def put(self, request, id):
|
|
||||||
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, 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)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Supprime un template d'inscription",
|
|
||||||
responses={
|
|
||||||
204: "Suppression réussie",
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def delete(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
|
|
||||||
if template is not None:
|
|
||||||
# Suppression du fichier PDF associé avant suppression de l'objet
|
|
||||||
if template.file and template.file.name:
|
|
||||||
template.file.delete(save=False)
|
|
||||||
template.delete()
|
|
||||||
return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
|
||||||
else:
|
|
||||||
return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|||||||
@ -1,128 +1,21 @@
|
|||||||
from django.http.response import JsonResponse
|
from django.http.response import JsonResponse
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from rest_framework.parsers import MultiPartParser, FormParser
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
|
from Subscriptions.serializers import RegistrationSchoolFileTemplateSerializer
|
||||||
from Subscriptions.models import RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate
|
from Subscriptions.models import RegistrationSchoolFileTemplate
|
||||||
from N3wtSchool import bdd
|
from N3wtSchool import bdd
|
||||||
import logging
|
import logging
|
||||||
|
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||||
import Subscriptions.util as util
|
import Subscriptions.util as util
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class RegistrationSchoolFileMasterView(APIView):
|
|
||||||
parser_classes = [MultiPartParser, FormParser]
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère tous les masters de templates d'inscription pour un établissement donné",
|
|
||||||
manual_parameters=[
|
|
||||||
openapi.Parameter(
|
|
||||||
'establishment_id',
|
|
||||||
openapi.IN_QUERY,
|
|
||||||
description="ID de l'établissement",
|
|
||||||
type=openapi.TYPE_INTEGER,
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
],
|
|
||||||
responses={200: RegistrationSchoolFileMasterSerializer(many=True)}
|
|
||||||
)
|
|
||||||
def get(self, request):
|
|
||||||
establishment_id = request.GET.get('establishment_id')
|
|
||||||
if not establishment_id:
|
|
||||||
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Filtrer les masters liés à l'établissement via groups.establishment
|
|
||||||
masters = RegistrationSchoolFileMaster.objects.filter(
|
|
||||||
groups__establishment__id=establishment_id
|
|
||||||
).distinct()
|
|
||||||
serializer = RegistrationSchoolFileMasterSerializer(masters, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Crée un nouveau master de template d'inscription",
|
|
||||||
request_body=RegistrationSchoolFileMasterSerializer,
|
|
||||||
responses={
|
|
||||||
201: RegistrationSchoolFileMasterSerializer,
|
|
||||||
400: "Données invalides"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
logger.info(f"raw request.data: {request.data}")
|
|
||||||
|
|
||||||
payload, resp = util.build_payload_from_request(request)
|
|
||||||
if resp:
|
|
||||||
return resp
|
|
||||||
|
|
||||||
logger.info(f"payload for serializer: {payload}")
|
|
||||||
serializer = RegistrationSchoolFileMasterSerializer(data=payload, partial=True)
|
|
||||||
if serializer.is_valid():
|
|
||||||
obj = serializer.save()
|
|
||||||
return Response(RegistrationSchoolFileMasterSerializer(obj).data, status=status.HTTP_201_CREATED)
|
|
||||||
|
|
||||||
logger.error(f"serializer errors: {serializer.errors}")
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
class RegistrationSchoolFileMasterSimpleView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère un master de template d'inscription spécifique",
|
|
||||||
responses={
|
|
||||||
200: RegistrationSchoolFileMasterSerializer,
|
|
||||||
404: "Master non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def get(self, request, id):
|
|
||||||
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
|
|
||||||
if master is None:
|
|
||||||
return JsonResponse({"errorMessage":'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
serializer = RegistrationSchoolFileMasterSerializer(master)
|
|
||||||
return JsonResponse(serializer.data, safe=False)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Met à jour un master de template d'inscription existant",
|
|
||||||
request_body=RegistrationSchoolFileMasterSerializer,
|
|
||||||
responses={
|
|
||||||
200: RegistrationSchoolFileMasterSerializer,
|
|
||||||
400: "Données invalides",
|
|
||||||
404: "Master non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def put(self, request, id):
|
|
||||||
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
|
|
||||||
if master is None:
|
|
||||||
return JsonResponse({'erreur': "Le master de template n'a pas été trouvé"}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
# Normaliser payload (supporte form-data avec champ 'data' JSON ou fichier JSON)
|
|
||||||
payload, resp = util.build_payload_from_request(request)
|
|
||||||
if resp:
|
|
||||||
return resp
|
|
||||||
|
|
||||||
logger.info(f"payload for update serializer: {payload}")
|
|
||||||
serializer = RegistrationSchoolFileMasterSerializer(master, data=payload, partial=True)
|
|
||||||
if serializer.is_valid():
|
|
||||||
serializer.save()
|
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
||||||
logger.error(f"serializer errors on put: {serializer.errors}")
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Supprime un master de template d'inscription",
|
|
||||||
responses={
|
|
||||||
204: "Suppression réussie",
|
|
||||||
404: "Master non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def delete(self, request, id):
|
|
||||||
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
|
|
||||||
if master is not None:
|
|
||||||
master.delete()
|
|
||||||
return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_200_OK)
|
|
||||||
else:
|
|
||||||
return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
class RegistrationSchoolFileTemplateView(APIView):
|
class RegistrationSchoolFileTemplateView(APIView):
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
operation_description="Récupère tous les templates d'inscription pour un établissement donné",
|
operation_description="Récupère tous les templates d'inscription pour un établissement donné",
|
||||||
@ -165,6 +58,8 @@ class RegistrationSchoolFileTemplateView(APIView):
|
|||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class RegistrationSchoolFileTemplateSimpleView(APIView):
|
class RegistrationSchoolFileTemplateSimpleView(APIView):
|
||||||
|
parser_classes = [MultiPartParser, FormParser, JSONParser]
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
operation_description="Récupère un template d'inscription spécifique",
|
operation_description="Récupère un template d'inscription spécifique",
|
||||||
responses={
|
responses={
|
||||||
@ -189,12 +84,83 @@ class RegistrationSchoolFileTemplateSimpleView(APIView):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
def put(self, request, id):
|
def put(self, request, id):
|
||||||
|
# Normaliser la payload (support form-data avec champ 'data' JSON ou fichier JSON)
|
||||||
|
payload, resp = util.build_payload_from_request(request)
|
||||||
|
if resp is not None:
|
||||||
|
return resp
|
||||||
|
|
||||||
|
# Synchroniser fields[].value dans le payload AVANT le serializer (pour les formulaires dynamiques)
|
||||||
|
formTemplateData = payload.get('formTemplateData')
|
||||||
|
if formTemplateData and isinstance(formTemplateData, dict):
|
||||||
|
responses = None
|
||||||
|
if "responses" in formTemplateData:
|
||||||
|
resp = formTemplateData["responses"]
|
||||||
|
if isinstance(resp, dict) and "responses" in resp:
|
||||||
|
responses = resp["responses"]
|
||||||
|
elif isinstance(resp, dict):
|
||||||
|
responses = resp
|
||||||
|
if responses and "fields" in formTemplateData:
|
||||||
|
for field in formTemplateData["fields"]:
|
||||||
|
field_id = field.get("id")
|
||||||
|
if field_id and field_id in responses:
|
||||||
|
field["value"] = responses[field_id]
|
||||||
|
payload['formTemplateData'] = formTemplateData
|
||||||
|
|
||||||
template = bdd.getObject(_objectName=RegistrationSchoolFileTemplate, _columnName='id', _value=id)
|
template = bdd.getObject(_objectName=RegistrationSchoolFileTemplate, _columnName='id', _value=id)
|
||||||
if template is None:
|
if template is None:
|
||||||
return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||||
serializer = RegistrationSchoolFileTemplateSerializer(template, data=request.data)
|
|
||||||
|
# Cas 1 : Upload d'un fichier existant (PDF/image)
|
||||||
|
if 'file' in request.FILES:
|
||||||
|
upload = request.FILES['file']
|
||||||
|
file_field = template.file
|
||||||
|
upload_name = upload.name
|
||||||
|
upload_dir = os.path.dirname(file_field.path) if file_field and file_field.name else None
|
||||||
|
if upload_dir:
|
||||||
|
base_name, _ = os.path.splitext(upload_name)
|
||||||
|
pattern = os.path.join(upload_dir, f"{base_name}.*")
|
||||||
|
for f in glob.glob(pattern):
|
||||||
|
try:
|
||||||
|
if os.path.exists(f):
|
||||||
|
os.remove(f)
|
||||||
|
logger.info(f"Suppression du fichier existant (pattern): {f}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur suppression fichier existant (pattern): {e}")
|
||||||
|
target_path = os.path.join(upload_dir, upload_name)
|
||||||
|
if os.path.exists(target_path):
|
||||||
|
try:
|
||||||
|
os.remove(target_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur suppression fichier cible: {e}")
|
||||||
|
# On écrase le fichier existant sans passer par le serializer
|
||||||
|
template.file.save(upload_name, upload, save=True)
|
||||||
|
return Response({'message': 'Template mis à jour avec succès', 'data': RegistrationSchoolFileTemplateSerializer(template).data}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
# Cas 2 : Formulaire dynamique (JSON)
|
||||||
|
serializer = RegistrationSchoolFileTemplateSerializer(template, data=payload)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
# Régénérer le PDF si besoin
|
||||||
|
formTemplateData = serializer.validated_data.get('formTemplateData')
|
||||||
|
if (
|
||||||
|
formTemplateData
|
||||||
|
and isinstance(formTemplateData, dict)
|
||||||
|
and formTemplateData.get("fields")
|
||||||
|
and hasattr(template, "file")
|
||||||
|
):
|
||||||
|
old_pdf_name = None
|
||||||
|
if template.file and template.file.name:
|
||||||
|
old_pdf_name = os.path.basename(template.file.name)
|
||||||
|
try:
|
||||||
|
template.file.delete(save=False)
|
||||||
|
if os.path.exists(template.file.path):
|
||||||
|
os.remove(template.file.path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erreur lors de la suppression du fichier existant: {e}")
|
||||||
|
from Subscriptions.util import generate_form_json_pdf
|
||||||
|
pdf_file = generate_form_json_pdf(template.registration_form, formTemplateData)
|
||||||
|
pdf_filename = old_pdf_name or f"{template.name}_{template.id}.pdf"
|
||||||
|
template.file.save(pdf_filename, pdf_file, save=True)
|
||||||
return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
|
return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@ -223,195 +189,3 @@ class RegistrationSchoolFileTemplateSimpleView(APIView):
|
|||||||
return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
class RegistrationParentFileMasterView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère tous les fichiers parents pour un établissement donné",
|
|
||||||
manual_parameters=[
|
|
||||||
openapi.Parameter(
|
|
||||||
'establishment_id',
|
|
||||||
openapi.IN_QUERY,
|
|
||||||
description="ID de l'établissement",
|
|
||||||
type=openapi.TYPE_INTEGER,
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
],
|
|
||||||
responses={200: RegistrationParentFileMasterSerializer(many=True)}
|
|
||||||
)
|
|
||||||
def get(self, request):
|
|
||||||
establishment_id = request.GET.get('establishment_id')
|
|
||||||
if not establishment_id:
|
|
||||||
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Filtrer les fichiers parents liés à l'établissement
|
|
||||||
templates = RegistrationParentFileMaster.objects.filter(
|
|
||||||
groups__establishment__id=establishment_id
|
|
||||||
).distinct()
|
|
||||||
serializer = RegistrationParentFileMasterSerializer(templates, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Crée un nouveau fichier parent",
|
|
||||||
request_body=RegistrationParentFileMasterSerializer,
|
|
||||||
responses={
|
|
||||||
201: RegistrationParentFileMasterSerializer,
|
|
||||||
400: "Données invalides"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
serializer = RegistrationParentFileMasterSerializer(data=request.data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
serializer.save()
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
class RegistrationParentFileMasterSimpleView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère un fichier parent spécifique",
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileMasterSerializer,
|
|
||||||
404: "Fichier parent non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def get(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
|
|
||||||
if template is None:
|
|
||||||
return JsonResponse({"errorMessage":'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
serializer = RegistrationParentFileMasterSerializer(template)
|
|
||||||
return JsonResponse(serializer.data, safe=False)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Met à jour un fichier parent existant",
|
|
||||||
request_body=RegistrationParentFileMasterSerializer,
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileMasterSerializer,
|
|
||||||
400: "Données invalides",
|
|
||||||
404: "Fichier parent non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def put(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
|
|
||||||
if template is None:
|
|
||||||
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
serializer = RegistrationParentFileMasterSerializer(template, data=request.data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
serializer.save()
|
|
||||||
return Response({'message': 'Fichier parent mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Supprime un fichier parent",
|
|
||||||
responses={
|
|
||||||
204: "Suppression réussie",
|
|
||||||
404: "Fichier parent non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def delete(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
|
|
||||||
if template is not None:
|
|
||||||
template.delete()
|
|
||||||
return JsonResponse({'message': 'La suppression du fichier parent a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
|
||||||
else:
|
|
||||||
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
class RegistrationParentFileTemplateView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère tous les templates parents pour un établissement donné",
|
|
||||||
manual_parameters=[
|
|
||||||
openapi.Parameter(
|
|
||||||
'establishment_id',
|
|
||||||
openapi.IN_QUERY,
|
|
||||||
description="ID de l'établissement",
|
|
||||||
type=openapi.TYPE_INTEGER,
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
],
|
|
||||||
responses={200: RegistrationParentFileTemplateSerializer(many=True)}
|
|
||||||
)
|
|
||||||
def get(self, request):
|
|
||||||
establishment_id = request.GET.get('establishment_id')
|
|
||||||
if not establishment_id:
|
|
||||||
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Filtrer les templates parents liés à l'établissement via master.groups.establishment
|
|
||||||
templates = RegistrationParentFileTemplate.objects.filter(
|
|
||||||
master__groups__establishment__id=establishment_id
|
|
||||||
).distinct()
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(templates, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Crée un nouveau template d'inscription",
|
|
||||||
request_body=RegistrationParentFileTemplateSerializer,
|
|
||||||
responses={
|
|
||||||
201: RegistrationParentFileTemplateSerializer,
|
|
||||||
400: "Données invalides"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(data=request.data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
serializer.save()
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
class RegistrationParentFileTemplateSimpleView(APIView):
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Récupère un template d'inscription spécifique",
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileTemplateSerializer,
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def get(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
|
|
||||||
if template is None:
|
|
||||||
return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
serializer = RegistrationParentFileTemplateSerializer(template)
|
|
||||||
return JsonResponse(serializer.data, safe=False)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Met à jour un template d'inscription existant",
|
|
||||||
request_body=RegistrationParentFileTemplateSerializer,
|
|
||||||
responses={
|
|
||||||
200: RegistrationParentFileTemplateSerializer,
|
|
||||||
400: "Données invalides",
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def put(self, request, id):
|
|
||||||
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, 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)
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
|
||||||
operation_description="Supprime un template d'inscription",
|
|
||||||
responses={
|
|
||||||
204: "Suppression réussie",
|
|
||||||
404: "Template non trouvé"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def delete(self, request, id):
|
|
||||||
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
|
|
||||||
if template is not None:
|
|
||||||
# Suppression du fichier PDF associé
|
|
||||||
if template.file and template.file.name:
|
|
||||||
file_path = template.file.path
|
|
||||||
template.file.delete(save=False)
|
|
||||||
# Vérification post-suppression
|
|
||||||
if os.path.exists(file_path):
|
|
||||||
try:
|
|
||||||
os.remove(file_path)
|
|
||||||
logger.info(f"Fichier supprimé manuellement: {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur lors de la suppression manuelle du fichier: {e}")
|
|
||||||
template.delete()
|
|
||||||
return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
|
||||||
else:
|
|
||||||
return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import FormRenderer from '@/components/Form/FormRenderer';
|
import FormRenderer from '@/components/Form/FormRenderer';
|
||||||
import { CheckCircle, Hourglass, FileText } from 'lucide-react';
|
import FileUpload from '@/components/Form/FileUpload';
|
||||||
|
import { CheckCircle, Hourglass, FileText, Download, Upload } from 'lucide-react';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
import { BASE_URL } from '@/utils/Url';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composant pour afficher et gérer les formulaires dynamiques d'inscription
|
* Composant pour afficher et gérer les formulaires dynamiques d'inscription
|
||||||
@ -10,6 +12,7 @@ import logger from '@/utils/logger';
|
|||||||
* @param {Object} existingResponses - Réponses déjà sauvegardées
|
* @param {Object} existingResponses - Réponses déjà sauvegardées
|
||||||
* @param {Function} onFormSubmit - Callback appelé quand un formulaire est soumis
|
* @param {Function} onFormSubmit - Callback appelé quand un formulaire est soumis
|
||||||
* @param {Boolean} enable - Si les formulaires sont modifiables
|
* @param {Boolean} enable - Si les formulaires sont modifiables
|
||||||
|
* @param {Function} onFileUpload - Callback appelé quand un fichier est sélectionné
|
||||||
*/
|
*/
|
||||||
export default function DynamicFormsList({
|
export default function DynamicFormsList({
|
||||||
schoolFileMasters,
|
schoolFileMasters,
|
||||||
@ -17,10 +20,12 @@ export default function DynamicFormsList({
|
|||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
enable = true,
|
enable = true,
|
||||||
onValidationChange,
|
onValidationChange,
|
||||||
|
onFileUpload, // nouvelle prop pour gérer l'upload (à passer depuis le parent)
|
||||||
}) {
|
}) {
|
||||||
const [currentTemplateIndex, setCurrentTemplateIndex] = useState(0);
|
const [currentTemplateIndex, setCurrentTemplateIndex] = useState(0);
|
||||||
const [formsData, setFormsData] = useState({});
|
const [formsData, setFormsData] = useState({});
|
||||||
const [formsValidation, setFormsValidation] = useState({});
|
const [formsValidation, setFormsValidation] = useState({});
|
||||||
|
const fileInputRefs = React.useRef({});
|
||||||
|
|
||||||
// Initialiser les données avec les réponses existantes
|
// Initialiser les données avec les réponses existantes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -138,6 +143,27 @@ export default function DynamicFormsList({
|
|||||||
return schoolFileMasters[currentTemplateIndex];
|
return schoolFileMasters[currentTemplateIndex];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handler d'upload pour formulaire existant
|
||||||
|
const handleUpload = async (file, selectedFile) => {
|
||||||
|
if (!file || !selectedFile) return;
|
||||||
|
try {
|
||||||
|
if (onFileUpload) {
|
||||||
|
await onFileUpload(file, selectedFile);
|
||||||
|
setFormsValidation((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[selectedFile.id]: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Erreur lors de l\'upload du fichier :', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDynamicForm = (template) =>
|
||||||
|
template.formTemplateData &&
|
||||||
|
Array.isArray(template.formTemplateData.fields) &&
|
||||||
|
template.formTemplateData.fields.length > 0;
|
||||||
|
|
||||||
if (!schoolFileMasters || schoolFileMasters.length === 0) {
|
if (!schoolFileMasters || schoolFileMasters.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
@ -223,13 +249,13 @@ export default function DynamicFormsList({
|
|||||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h3 className="text-xl font-semibold text-gray-800 mb-2">
|
<h3 className="text-xl font-semibold text-gray-800 mb-2">
|
||||||
{currentTemplate.formMasterData?.title ||
|
{currentTemplate.formTemplateData?.title ||
|
||||||
currentTemplate.title ||
|
currentTemplate.title ||
|
||||||
currentTemplate.name ||
|
currentTemplate.name ||
|
||||||
'Formulaire sans nom'}
|
'Formulaire sans nom'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{currentTemplate.formMasterData?.description ||
|
{currentTemplate.formTemplateData?.description ||
|
||||||
currentTemplate.description ||
|
currentTemplate.description ||
|
||||||
'Veuillez compléter ce formulaire pour continuer votre inscription.'}
|
'Veuillez compléter ce formulaire pour continuer votre inscription.'}
|
||||||
</p>
|
</p>
|
||||||
@ -239,39 +265,57 @@ export default function DynamicFormsList({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Vérifier si le formulaire maître a des données de configuration */}
|
{/* Affichage dynamique ou existant */}
|
||||||
{(currentTemplate.formMasterData?.fields &&
|
{isDynamicForm(currentTemplate) ? (
|
||||||
currentTemplate.formMasterData.fields.length > 0) ||
|
|
||||||
(currentTemplate.fields && currentTemplate.fields.length > 0) ? (
|
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
key={currentTemplate.id}
|
key={currentTemplate.id}
|
||||||
formConfig={{
|
formConfig={{
|
||||||
id: currentTemplate.id,
|
id: currentTemplate.id,
|
||||||
title:
|
title:
|
||||||
currentTemplate.formMasterData?.title ||
|
currentTemplate.formTemplateData?.title ||
|
||||||
currentTemplate.title ||
|
currentTemplate.title ||
|
||||||
currentTemplate.name ||
|
currentTemplate.name ||
|
||||||
'Formulaire',
|
'Formulaire',
|
||||||
fields:
|
fields:
|
||||||
currentTemplate.formMasterData?.fields ||
|
currentTemplate.formTemplateData?.fields ||
|
||||||
currentTemplate.fields ||
|
currentTemplate.fields ||
|
||||||
[],
|
[],
|
||||||
submitLabel:
|
submitLabel:
|
||||||
currentTemplate.formMasterData?.submitLabel || 'Valider',
|
currentTemplate.formTemplateData?.submitLabel || 'Valider',
|
||||||
}}
|
}}
|
||||||
onFormSubmit={(formData) =>
|
onFormSubmit={(formData) =>
|
||||||
handleFormSubmit(formData, currentTemplate.id)
|
handleFormSubmit(formData, currentTemplate.id)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center py-8">
|
// Formulaire existant (PDF, image, etc.)
|
||||||
<FileText className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
<div className="flex flex-col items-center gap-6">
|
||||||
<p className="text-gray-600">
|
<div className="flex flex-col items-center gap-2">
|
||||||
Ce formulaire n'est pas encore configuré.
|
<FileText className="w-16 h-16 text-gray-400" />
|
||||||
</p>
|
<div className="text-lg font-semibold text-gray-700">
|
||||||
<p className="text-sm text-gray-500 mt-2">
|
{currentTemplate.name}
|
||||||
Contactez l'administration pour plus d'informations.
|
</div>
|
||||||
</p>
|
{currentTemplate.file && (
|
||||||
|
<a
|
||||||
|
href={`${BASE_URL}${currentTemplate.file}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition"
|
||||||
|
download
|
||||||
|
>
|
||||||
|
<Download className="w-5 h-5" />
|
||||||
|
Télécharger le document
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{enable && (
|
||||||
|
<FileUpload
|
||||||
|
selectionMessage="Sélectionnez le fichier du document"
|
||||||
|
onFileSelect={(file) => handleUpload(file, currentTemplate)}
|
||||||
|
required
|
||||||
|
enable
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,15 +5,12 @@ import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
|||||||
import {
|
import {
|
||||||
fetchSchoolFileTemplatesFromRegistrationFiles,
|
fetchSchoolFileTemplatesFromRegistrationFiles,
|
||||||
fetchParentFileTemplatesFromRegistrationFiles,
|
fetchParentFileTemplatesFromRegistrationFiles,
|
||||||
fetchRegistrationSchoolFileMasters,
|
|
||||||
saveFormResponses,
|
saveFormResponses,
|
||||||
fetchFormResponses,
|
fetchFormResponses,
|
||||||
autoSaveRegisterForm,
|
autoSaveRegisterForm,
|
||||||
} from '@/app/actions/subscriptionAction';
|
} from '@/app/actions/subscriptionAction';
|
||||||
import {
|
import {
|
||||||
downloadTemplate,
|
|
||||||
editRegistrationSchoolFileTemplates,
|
editRegistrationSchoolFileTemplates,
|
||||||
editRegistrationParentFileTemplates,
|
|
||||||
} from '@/app/actions/registerFileGroupAction';
|
} from '@/app/actions/registerFileGroupAction';
|
||||||
import {
|
import {
|
||||||
fetchRegistrationPaymentModes,
|
fetchRegistrationPaymentModes,
|
||||||
@ -22,7 +19,7 @@ import {
|
|||||||
fetchTuitionPaymentPlans,
|
fetchTuitionPaymentPlans,
|
||||||
} from '@/app/actions/schoolAction';
|
} from '@/app/actions/schoolAction';
|
||||||
import { fetchProfiles } from '@/app/actions/authAction';
|
import { fetchProfiles } from '@/app/actions/authAction';
|
||||||
import { BASE_URL, FE_PARENTS_HOME_URL } from '@/utils/Url';
|
import { FE_PARENTS_HOME_URL } from '@/utils/Url';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||||
import DynamicFormsList from '@/components/Inscription/DynamicFormsList';
|
import DynamicFormsList from '@/components/Inscription/DynamicFormsList';
|
||||||
@ -32,7 +29,6 @@ import ResponsableInputFields from '@/components/Inscription/ResponsableInputFie
|
|||||||
import SiblingInputFields from '@/components/Inscription/SiblingInputFields';
|
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 { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +43,6 @@ export default function InscriptionFormShared({
|
|||||||
studentId,
|
studentId,
|
||||||
csrfToken,
|
csrfToken,
|
||||||
selectedEstablishmentId,
|
selectedEstablishmentId,
|
||||||
apiDocuseal,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
errors = {}, // Nouvelle prop pour les erreurs
|
errors = {}, // Nouvelle prop pour les erreurs
|
||||||
enable = true,
|
enable = true,
|
||||||
@ -82,7 +77,7 @@ export default function InscriptionFormShared({
|
|||||||
const [parentFileTemplates, setParentFileTemplates] = useState([]);
|
const [parentFileTemplates, setParentFileTemplates] = useState([]);
|
||||||
const [schoolFileMasters, setSchoolFileMasters] = useState([]);
|
const [schoolFileMasters, setSchoolFileMasters] = useState([]);
|
||||||
const [formResponses, setFormResponses] = useState({});
|
const [formResponses, setFormResponses] = useState({});
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(5);
|
||||||
|
|
||||||
const [isPage1Valid, setIsPage1Valid] = useState(false);
|
const [isPage1Valid, setIsPage1Valid] = useState(false);
|
||||||
const [isPage2Valid, setIsPage2Valid] = useState(false);
|
const [isPage2Valid, setIsPage2Valid] = useState(false);
|
||||||
@ -283,8 +278,8 @@ export default function InscriptionFormShared({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Trouver le template correspondant pour récupérer sa configuration
|
// Trouver le template correspondant pour récupérer sa configuration
|
||||||
const currentTemplate = schoolFileMasters.find(
|
const currentTemplate = schoolFileTemplates.find(
|
||||||
(master) => master.id === templateId
|
(template) => template.id === templateId
|
||||||
);
|
);
|
||||||
if (!currentTemplate) {
|
if (!currentTemplate) {
|
||||||
throw new Error(`Template avec l'ID ${templateId} non trouvé`);
|
throw new Error(`Template avec l'ID ${templateId} non trouvé`);
|
||||||
@ -294,17 +289,16 @@ export default function InscriptionFormShared({
|
|||||||
const formTemplateData = {
|
const formTemplateData = {
|
||||||
id: currentTemplate.id,
|
id: currentTemplate.id,
|
||||||
title:
|
title:
|
||||||
currentTemplate.formMasterData?.title ||
|
currentTemplate.formTemplateData?.title ||
|
||||||
currentTemplate.title ||
|
currentTemplate.title ||
|
||||||
currentTemplate.name ||
|
currentTemplate.name ||
|
||||||
'Formulaire',
|
'Formulaire',
|
||||||
fields: (
|
fields: (
|
||||||
currentTemplate.formMasterData?.fields ||
|
currentTemplate.formTemplateData?.fields ||
|
||||||
currentTemplate.fields ||
|
currentTemplate.fields ||
|
||||||
[]
|
[]
|
||||||
).map((field) => ({
|
).map((field) => ({
|
||||||
...field,
|
...field,
|
||||||
// Ajouter la réponse de l'utilisateur selon le type de champ
|
|
||||||
...(field.type === 'checkbox'
|
...(field.type === 'checkbox'
|
||||||
? { checked: formData[field.id] || false }
|
? { checked: formData[field.id] || false }
|
||||||
: {}),
|
: {}),
|
||||||
@ -315,8 +309,8 @@ export default function InscriptionFormShared({
|
|||||||
? { value: formData[field.id] || '' }
|
? { value: formData[field.id] || '' }
|
||||||
: {}),
|
: {}),
|
||||||
})),
|
})),
|
||||||
submitLabel: currentTemplate.formMasterData?.submitLabel || 'Valider',
|
submitLabel: currentTemplate.formTemplateData?.submitLabel || 'Valider',
|
||||||
responses: formData, // Garder aussi les réponses brutes pour facilité d'accès
|
responses: formData,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sauvegarder les réponses du formulaire via l'API RegistrationSchoolFileTemplate
|
// Sauvegarder les réponses du formulaire via l'API RegistrationSchoolFileTemplate
|
||||||
@ -331,18 +325,37 @@ export default function InscriptionFormShared({
|
|||||||
);
|
);
|
||||||
logger.debug("Réponse de l'API:", result);
|
logger.debug("Réponse de l'API:", result);
|
||||||
|
|
||||||
// Mettre à jour l'état local des réponses
|
// Prendre en compte la réponse du back pour mettre à jour les réponses locales
|
||||||
|
let newResponses = formData;
|
||||||
|
if (
|
||||||
|
result &&
|
||||||
|
result.data &&
|
||||||
|
result.data.formTemplateData &&
|
||||||
|
result.data.formTemplateData.responses &&
|
||||||
|
result.data.formTemplateData.responses.responses
|
||||||
|
) {
|
||||||
|
// Si la structure responses.responses existe, on la prend
|
||||||
|
newResponses = result.data.formTemplateData.responses.responses;
|
||||||
|
} else if (
|
||||||
|
result &&
|
||||||
|
result.data &&
|
||||||
|
result.data.formTemplateData &&
|
||||||
|
result.data.formTemplateData.responses
|
||||||
|
) {
|
||||||
|
// Sinon, on prend responses directement
|
||||||
|
newResponses = result.data.formTemplateData.responses;
|
||||||
|
}
|
||||||
|
|
||||||
setFormResponses((prev) => ({
|
setFormResponses((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[templateId]: formData,
|
[templateId]: newResponses,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mettre à jour l'état local pour indiquer que le formulaire est complété
|
setSchoolFileTemplates((prevTemplates) => {
|
||||||
setSchoolFileMasters((prevMasters) => {
|
return prevTemplates.map((template) =>
|
||||||
return prevMasters.map((master) =>
|
template.id === templateId
|
||||||
master.id === templateId
|
? { ...template, completed: true, responses: newResponses }
|
||||||
? { ...master, completed: true, responses: formData }
|
: template
|
||||||
: master
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -354,7 +367,6 @@ export default function InscriptionFormShared({
|
|||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
});
|
});
|
||||||
// Afficher l'erreur à l'utilisateur
|
|
||||||
alert(`Erreur lors de la sauvegarde du formulaire: ${error.message}`);
|
alert(`Erreur lors de la sauvegarde du formulaire: ${error.message}`);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
@ -370,6 +382,56 @@ export default function InscriptionFormShared({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSchoolFileTemplatesFromRegistrationFiles(studentId).then((data) => {
|
fetchSchoolFileTemplatesFromRegistrationFiles(studentId).then((data) => {
|
||||||
setSchoolFileTemplates(data);
|
setSchoolFileTemplates(data);
|
||||||
|
|
||||||
|
// Récupérer les réponses existantes pour chaque template
|
||||||
|
const fetchAllResponses = async () => {
|
||||||
|
const responsesMap = {};
|
||||||
|
for (const template of data) {
|
||||||
|
if (template.id) {
|
||||||
|
try {
|
||||||
|
const templateData = await fetchFormResponses(template.id);
|
||||||
|
if (templateData && templateData.formTemplateData) {
|
||||||
|
if (templateData.formTemplateData.responses) {
|
||||||
|
responsesMap[template.id] = templateData.formTemplateData.responses;
|
||||||
|
} else {
|
||||||
|
// Extraire les réponses depuis les champs
|
||||||
|
const responses = {};
|
||||||
|
if (templateData.formTemplateData.fields) {
|
||||||
|
templateData.formTemplateData.fields.forEach((field) => {
|
||||||
|
if (
|
||||||
|
field.type === 'checkbox' &&
|
||||||
|
field.checked !== undefined
|
||||||
|
) {
|
||||||
|
responses[field.id] = field.checked;
|
||||||
|
} else if (
|
||||||
|
field.type === 'radio' &&
|
||||||
|
field.selected !== undefined
|
||||||
|
) {
|
||||||
|
responses[field.id] = field.selected;
|
||||||
|
} else if (
|
||||||
|
(field.type === 'text' ||
|
||||||
|
field.type === 'textarea' ||
|
||||||
|
field.type === 'email') &&
|
||||||
|
field.value !== undefined
|
||||||
|
) {
|
||||||
|
responses[field.id] = field.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
responsesMap[template.id] = responses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug(
|
||||||
|
`Pas de données existantes pour le template ${template.id}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFormResponses(responsesMap);
|
||||||
|
};
|
||||||
|
fetchAllResponses();
|
||||||
});
|
});
|
||||||
|
|
||||||
fetchParentFileTemplatesFromRegistrationFiles(studentId).then((data) => {
|
fetchParentFileTemplatesFromRegistrationFiles(studentId).then((data) => {
|
||||||
@ -392,66 +454,6 @@ export default function InscriptionFormShared({
|
|||||||
.catch((error) => logger.error('Error fetching profiles : ', error));
|
.catch((error) => logger.error('Error fetching profiles : ', error));
|
||||||
|
|
||||||
if (selectedEstablishmentId) {
|
if (selectedEstablishmentId) {
|
||||||
// Fetch data for school file masters
|
|
||||||
fetchRegistrationSchoolFileMasters(selectedEstablishmentId)
|
|
||||||
.then(async (data) => {
|
|
||||||
logger.debug('School file masters fetched:', data);
|
|
||||||
setSchoolFileMasters(data);
|
|
||||||
|
|
||||||
// Récupérer les données existantes de chaque template
|
|
||||||
const responsesMap = {};
|
|
||||||
for (const master of data) {
|
|
||||||
if (master.id) {
|
|
||||||
try {
|
|
||||||
const templateData = await fetchFormResponses(master.id);
|
|
||||||
if (templateData && templateData.formTemplateData) {
|
|
||||||
// Si on a les réponses brutes sauvegardées, les utiliser
|
|
||||||
if (templateData.formTemplateData.responses) {
|
|
||||||
responsesMap[master.id] =
|
|
||||||
templateData.formTemplateData.responses;
|
|
||||||
} else {
|
|
||||||
// Sinon, extraire les réponses depuis les champs
|
|
||||||
const responses = {};
|
|
||||||
if (templateData.formTemplateData.fields) {
|
|
||||||
templateData.formTemplateData.fields.forEach((field) => {
|
|
||||||
if (
|
|
||||||
field.type === 'checkbox' &&
|
|
||||||
field.checked !== undefined
|
|
||||||
) {
|
|
||||||
responses[field.id] = field.checked;
|
|
||||||
} else if (
|
|
||||||
field.type === 'radio' &&
|
|
||||||
field.selected !== undefined
|
|
||||||
) {
|
|
||||||
responses[field.id] = field.selected;
|
|
||||||
} else if (
|
|
||||||
(field.type === 'text' ||
|
|
||||||
field.type === 'textarea' ||
|
|
||||||
field.type === 'email') &&
|
|
||||||
field.value !== undefined
|
|
||||||
) {
|
|
||||||
responses[field.id] = field.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
responsesMap[master.id] = responses;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.debug(
|
|
||||||
`Pas de données existantes pour le template ${master.id}:`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
// Ce n'est pas critique si un template n'a pas de données
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setFormResponses(responsesMap);
|
|
||||||
})
|
|
||||||
.catch((error) =>
|
|
||||||
logger.error('Error fetching school file masters:', error)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fetch data for registration payment modes
|
// Fetch data for registration payment modes
|
||||||
handleRegistrationPaymentModes();
|
handleRegistrationPaymentModes();
|
||||||
|
|
||||||
@ -464,7 +466,7 @@ export default function InscriptionFormShared({
|
|||||||
// Fetch data for tuition payment plans
|
// Fetch data for tuition payment plans
|
||||||
handleTuitionnPaymentPlans();
|
handleTuitionnPaymentPlans();
|
||||||
}
|
}
|
||||||
}, [selectedEstablishmentId]);
|
}, [studentId, selectedEstablishmentId]);
|
||||||
|
|
||||||
const handleRegistrationPaymentModes = () => {
|
const handleRegistrationPaymentModes = () => {
|
||||||
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
||||||
@ -514,10 +516,22 @@ export default function InscriptionFormShared({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateData = new FormData();
|
// Générer le nom du fichier : <nom_template>.<extension d'origine>
|
||||||
updateData.append('file', file);
|
let extension = '';
|
||||||
|
if (file.name && file.name.lastIndexOf('.') !== -1) {
|
||||||
|
extension = file.name.substring(file.name.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
// Nettoyer le nom du template pour éviter les caractères spéciaux
|
||||||
|
const cleanName = (selectedFile.name || 'document')
|
||||||
|
.replace(/[^a-zA-Z0-9_\-]/g, '_')
|
||||||
|
.replace(/_+/g, '_')
|
||||||
|
.replace(/^_+|_+$/g, '');
|
||||||
|
const finalFileName = `${cleanName}${extension}`;
|
||||||
|
|
||||||
return editRegistrationParentFileTemplates(
|
const updateData = new FormData();
|
||||||
|
updateData.append('file', file, finalFileName);
|
||||||
|
|
||||||
|
return editRegistrationSchoolFileTemplates(
|
||||||
selectedFile.id,
|
selectedFile.id,
|
||||||
updateData,
|
updateData,
|
||||||
csrfToken
|
csrfToken
|
||||||
@ -528,11 +542,10 @@ export default function InscriptionFormShared({
|
|||||||
setUploadedFiles((prev) => {
|
setUploadedFiles((prev) => {
|
||||||
const updatedFiles = prev.map((uploadedFile) =>
|
const updatedFiles = prev.map((uploadedFile) =>
|
||||||
uploadedFile.id === selectedFile.id
|
uploadedFile.id === selectedFile.id
|
||||||
? { ...uploadedFile, fileName: response.data.file } // Met à jour le fichier téléversé
|
? { ...uploadedFile, fileName: response.data.file }
|
||||||
: uploadedFile
|
: uploadedFile
|
||||||
);
|
);
|
||||||
|
|
||||||
// Si le fichier n'existe pas encore, l'ajouter
|
|
||||||
if (!updatedFiles.find((file) => file.id === selectedFile.id)) {
|
if (!updatedFiles.find((file) => file.id === selectedFile.id)) {
|
||||||
updatedFiles.push({
|
updatedFiles.push({
|
||||||
id: selectedFile.id,
|
id: selectedFile.id,
|
||||||
@ -552,11 +565,11 @@ export default function InscriptionFormShared({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return response; // Retourner la réponse pour signaler le succès
|
return response;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.error('Erreur lors de la mise à jour du fichier :', error);
|
logger.error('Erreur lors de la mise à jour du fichier :', error);
|
||||||
throw error; // Relancer l'erreur pour que l'appelant puisse la capturer
|
throw error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -587,7 +600,7 @@ export default function InscriptionFormShared({
|
|||||||
setUploadedFiles((prev) =>
|
setUploadedFiles((prev) =>
|
||||||
prev.map((uploadedFile) =>
|
prev.map((uploadedFile) =>
|
||||||
uploadedFile.id === templateId
|
uploadedFile.id === templateId
|
||||||
? { ...uploadedFile, fileName: null, fileUrl: null } // Réinitialiser les champs liés au fichier
|
? { ...uploadedFile, fileName: null, fileUrl: null }
|
||||||
: uploadedFile
|
: uploadedFile
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -786,11 +799,12 @@ export default function InscriptionFormShared({
|
|||||||
{/* Page 5 : Formulaires dynamiques d'inscription */}
|
{/* Page 5 : Formulaires dynamiques d'inscription */}
|
||||||
{currentPage === 5 && (
|
{currentPage === 5 && (
|
||||||
<DynamicFormsList
|
<DynamicFormsList
|
||||||
schoolFileMasters={schoolFileMasters}
|
schoolFileMasters={schoolFileTemplates}
|
||||||
existingResponses={formResponses}
|
existingResponses={formResponses}
|
||||||
onFormSubmit={handleDynamicFormSubmit}
|
onFormSubmit={handleDynamicFormSubmit}
|
||||||
onValidationChange={handleDynamicFormsValidationChange}
|
onValidationChange={handleDynamicFormsValidationChange}
|
||||||
enable={enable}
|
enable={enable}
|
||||||
|
onFileUpload={handleFileUpload}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -1,209 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import Modal from '@/components/Modal';
|
|
||||||
import { FolderPlus, FileText, FilePlus2, ArrowLeft, Settings2, Upload as UploadIcon } from 'lucide-react';
|
|
||||||
import FormTemplateBuilder from '@/components/Form/FormTemplateBuilder';
|
|
||||||
import FileUpload from '@/components/Form/FileUpload';
|
|
||||||
|
|
||||||
export default function CreateDocumentModal({
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onCreateGroup,
|
|
||||||
onCreateParentFile,
|
|
||||||
onCreateSchoolFileMaster,
|
|
||||||
groups = [],
|
|
||||||
}) {
|
|
||||||
const [step, setStep] = useState('main'); // main | choose_form | form_builder | file_upload
|
|
||||||
const [fileName, setFileName] = useState('');
|
|
||||||
const [selectedGroupsFileUpload, setSelectedGroupsFileUpload] = useState([]);
|
|
||||||
const [uploadedFile, setUploadedFile] = useState(null);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!isOpen) {
|
|
||||||
setStep('main');
|
|
||||||
setFileName('');
|
|
||||||
setSelectedGroupsFileUpload([]);
|
|
||||||
setUploadedFile(null);
|
|
||||||
}
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
// Handler pour chaque type
|
|
||||||
const handleSelect = (type) => {
|
|
||||||
if (type === 'groupe') {
|
|
||||||
setStep('main');
|
|
||||||
onCreateGroup();
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
if (type === 'formulaire') {
|
|
||||||
setStep('choose_form');
|
|
||||||
}
|
|
||||||
if (type === 'parent') {
|
|
||||||
setStep('main');
|
|
||||||
onCreateParentFile();
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Retour au menu principal
|
|
||||||
const handleBack = () => setStep('main');
|
|
||||||
|
|
||||||
// Submit pour formulaire existant
|
|
||||||
const handleFileUploadSubmit = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!fileName || selectedGroupsFileUpload.length === 0 || !uploadedFile) return;
|
|
||||||
onCreateSchoolFileMaster({
|
|
||||||
name: fileName,
|
|
||||||
group_ids: selectedGroupsFileUpload,
|
|
||||||
file: uploadedFile,
|
|
||||||
});
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
setIsOpen={onClose}
|
|
||||||
title="Créer un document"
|
|
||||||
modalClassName="w-full max-w-md"
|
|
||||||
>
|
|
||||||
{step === 'main' && (
|
|
||||||
<div className="flex flex-col gap-6 py-4">
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg bg-blue-50 hover:bg-blue-100 border border-blue-200 transition"
|
|
||||||
onClick={() => handleSelect('groupe')}
|
|
||||||
>
|
|
||||||
<FolderPlus className="w-6 h-6 text-blue-600" />
|
|
||||||
<span className="font-semibold text-blue-800">Dossier d&aposinscription</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg bg-emerald-50 hover:bg-emerald-100 border border-emerald-200 transition"
|
|
||||||
onClick={() => handleSelect('formulaire')}
|
|
||||||
>
|
|
||||||
<FileText className="w-6 h-6 text-emerald-600" />
|
|
||||||
<span className="font-semibold text-emerald-800">Formulaire scolaire</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg bg-orange-50 hover:bg-orange-100 border border-orange-200 transition"
|
|
||||||
onClick={() => handleSelect('parent')}
|
|
||||||
>
|
|
||||||
<FilePlus2 className="w-6 h-6 text-orange-500" />
|
|
||||||
<span className="font-semibold text-orange-700">Pièce à fournir</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{step === 'choose_form' && (
|
|
||||||
<div className="flex flex-col gap-4 py-4">
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg bg-emerald-100 hover:bg-emerald-200 border border-emerald-300 transition"
|
|
||||||
onClick={() => setStep('form_builder')}
|
|
||||||
>
|
|
||||||
<Settings2 className="w-6 h-6 text-emerald-700" />
|
|
||||||
<span className="font-semibold text-emerald-900">Formulaire personnalisé</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-3 px-4 py-3 rounded-lg bg-gray-100 hover:bg-gray-200 border border-gray-300 transition"
|
|
||||||
onClick={() => setStep('file_upload')}
|
|
||||||
>
|
|
||||||
<UploadIcon className="w-6 h-6 text-gray-700" />
|
|
||||||
<span className="font-semibold text-gray-900">Importer un formulaire existant</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mt-2"
|
|
||||||
onClick={handleBack}
|
|
||||||
>
|
|
||||||
<ArrowLeft className="w-5 h-5" />
|
|
||||||
<span>Retour</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{step === 'form_builder' && (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-2"
|
|
||||||
onClick={handleBack}
|
|
||||||
>
|
|
||||||
<ArrowLeft className="w-5 h-5" />
|
|
||||||
<span>Retour</span>
|
|
||||||
</button>
|
|
||||||
<FormTemplateBuilder
|
|
||||||
onSave={(data) => {
|
|
||||||
onCreateSchoolFileMaster(data);
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
groups={groups}
|
|
||||||
isEditing={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{step === 'file_upload' && (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-2"
|
|
||||||
onClick={handleBack}
|
|
||||||
>
|
|
||||||
<ArrowLeft className="w-5 h-5" />
|
|
||||||
<span>Retour</span>
|
|
||||||
</button>
|
|
||||||
<form className="flex flex-col gap-4" onSubmit={handleFileUploadSubmit}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="border rounded px-3 py-2"
|
|
||||||
placeholder="Nom du formulaire"
|
|
||||||
value={fileName}
|
|
||||||
onChange={e => setFileName(e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
{/* Sélecteur de groupes à cocher */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
|
||||||
Groupes d'inscription <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<div className="space-y-2 max-h-32 overflow-y-auto border rounded-md p-2">
|
|
||||||
{groups && groups.length > 0 ? (
|
|
||||||
groups.map((group) => (
|
|
||||||
<label key={group.id} className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={selectedGroupsFileUpload.includes(group.id)}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.checked) {
|
|
||||||
setSelectedGroupsFileUpload([
|
|
||||||
...selectedGroupsFileUpload,
|
|
||||||
group.id,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setSelectedGroupsFileUpload(
|
|
||||||
selectedGroupsFileUpload.filter((id) => id !== group.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="mr-2 text-blue-600"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">{group.name}</span>
|
|
||||||
</label>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<p className="text-gray-500 text-sm">
|
|
||||||
Aucun groupe disponible
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FileUpload
|
|
||||||
selectionMessage="Sélectionnez le fichier du formulaire"
|
|
||||||
onFileSelect={setUploadedFile}
|
|
||||||
required
|
|
||||||
enable
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="bg-emerald-600 text-white px-4 py-2 rounded font-bold mt-2"
|
|
||||||
disabled={!fileName || selectedGroupsFileUpload.length === 0 || !uploadedFile}
|
|
||||||
>
|
|
||||||
Créer le formulaire
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -6,8 +6,6 @@ import {
|
|||||||
Star,
|
Star,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Plus,
|
Plus,
|
||||||
Archive,
|
|
||||||
Eye
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import FormTemplateBuilder from '@/components/Form/FormTemplateBuilder';
|
import FormTemplateBuilder from '@/components/Form/FormTemplateBuilder';
|
||||||
@ -31,11 +29,9 @@ import {
|
|||||||
} from '@/app/actions/registerFileGroupAction';
|
} from '@/app/actions/registerFileGroupAction';
|
||||||
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
|
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import ParentFiles from './ParentFiles';
|
|
||||||
import Popup from '@/components/Popup';
|
import Popup from '@/components/Popup';
|
||||||
import Loader from '@/components/Loader';
|
import Loader from '@/components/Loader';
|
||||||
import { useNotification } from '@/context/NotificationContext';
|
import { useNotification } from '@/context/NotificationContext';
|
||||||
import CreateDocumentModal from '@/components/Structure/Files/CreateDocumentModal';
|
|
||||||
import FileUpload from '@/components/Form/FileUpload';
|
import FileUpload from '@/components/Form/FileUpload';
|
||||||
import SectionTitle from '@/components/SectionTitle';
|
import SectionTitle from '@/components/SectionTitle';
|
||||||
import DropdownMenu from '@/components/DropdownMenu';
|
import DropdownMenu from '@/components/DropdownMenu';
|
||||||
|
|||||||
Reference in New Issue
Block a user