diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py
index 45a050b..9594c74 100644
--- a/Back-End/Auth/views.py
+++ b/Back-End/Auth/views.py
@@ -223,7 +223,7 @@ def makeToken(user):
"""
try:
# Récupérer tous les rôles de l'utilisateur actifs
- roles = ProfileRole.objects.filter(profile=user, is_active=True).values('role_type', 'establishment__id', 'establishment__name', 'establishment__evaluation_frequency', 'establishment__total_capacity')
+ roles = ProfileRole.objects.filter(profile=user, is_active=True).values('role_type', 'establishment__id', 'establishment__name', 'establishment__evaluation_frequency', 'establishment__total_capacity', 'establishment__api_docuseal')
# Générer le JWT avec la bonne syntaxe datetime
access_payload = {
diff --git a/Back-End/DocuSeal/views.py b/Back-End/DocuSeal/views.py
index f2b1e03..9a21cb3 100644
--- a/Back-End/DocuSeal/views.py
+++ b/Back-End/DocuSeal/views.py
@@ -1,5 +1,4 @@
from django.conf import settings
-from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
from rest_framework.response import Response
@@ -7,49 +6,67 @@ from rest_framework import status
import jwt
import datetime
import requests
+from Establishment.models import Establishment
@csrf_exempt
@api_view(['POST'])
def generate_jwt_token(request):
- # Vérifier la clé API
+ # Récupérer l'établissement concerné (par ID ou autre info transmise)
+ establishment_id = request.data.get('establishment_id')
+ if not establishment_id:
+ return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ establishment = Establishment.objects.get(id=establishment_id)
+ except Establishment.DoesNotExist:
+ return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND)
+
+ # Vérifier la clé API reçue dans le header
api_key = request.headers.get('X-Auth-Token')
- if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
- return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
+ if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal:
+ return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED)
# Récupérer les données de la requête
user_email = request.data.get('user_email')
documents_urls = request.data.get('documents_urls', [])
- id = request.data.get('id') # Récupérer le id
+ template_id = request.data.get('id')
- # Vérifier les données requises
if not user_email:
return Response({'error': 'User email is required'}, status=status.HTTP_400_BAD_REQUEST)
- # Utiliser la configuration JWT de DocuSeal depuis les settings
- jwt_secret = settings.DOCUSEAL_JWT['API_KEY']
+ # Utiliser la clé API de l'établissement comme secret JWT
+ jwt_secret = establishment.api_docuseal
jwt_algorithm = settings.DOCUSEAL_JWT['ALGORITHM']
expiration_delta = settings.DOCUSEAL_JWT['EXPIRATION_DELTA']
- # Définir le payload
payload = {
'user_email': user_email,
'documents_urls': documents_urls,
- 'template_id': id, # Ajouter le id au payload
- 'exp': datetime.datetime.utcnow() + expiration_delta # Temps d'expiration du token
+ 'template_id': template_id,
+ 'exp': datetime.datetime.utcnow() + expiration_delta
}
- # Générer le token JWT
token = jwt.encode(payload, jwt_secret, algorithm=jwt_algorithm)
-
return Response({'token': token}, status=status.HTTP_200_OK)
@csrf_exempt
@api_view(['POST'])
def clone_template(request):
- # Vérifier la clé API
+ # Récupérer l'établissement concerné
+ establishment_id = request.data.get('establishment_id')
+ print(f"establishment_id : {establishment_id}")
+ if not establishment_id:
+ return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ establishment = Establishment.objects.get(id=establishment_id)
+ except Establishment.DoesNotExist:
+ return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND)
+
+ # Vérifier la clé API reçue dans le header
api_key = request.headers.get('X-Auth-Token')
- if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
- return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
+ if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal:
+ return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED)
# Récupérer les données de la requête
document_id = request.data.get('templateId')
@@ -57,7 +74,7 @@ def clone_template(request):
is_required = request.data.get('is_required')
# Vérifier les données requises
- if not document_id :
+ if not document_id:
return Response({'error': 'template ID is required'}, status=status.HTTP_400_BAD_REQUEST)
# URL de l'API de DocuSeal pour cloner le template
@@ -67,7 +84,7 @@ def clone_template(request):
try:
response = requests.post(clone_url, headers={
'Content-Type': 'application/json',
- 'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
+ 'X-Auth-Token': establishment.api_docuseal
})
if response.status_code != status.HTTP_200_OK:
@@ -79,12 +96,15 @@ def clone_template(request):
# URL de l'API de DocuSeal pour créer une submission
submission_url = f'https://docuseal.com/api/submissions'
- # Faire la requête pour cloner le template
try:
clone_id = data['id']
- response = requests.post(submission_url, json={'template_id':clone_id, 'send_email': False, 'submitters': [{'email': email}]}, headers={
+ response = requests.post(submission_url, json={
+ 'template_id': clone_id,
+ 'send_email': False,
+ 'submitters': [{'email': email}]
+ }, headers={
'Content-Type': 'application/json',
- 'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
+ 'X-Auth-Token': establishment.api_docuseal
})
if response.status_code != status.HTTP_200_OK:
@@ -93,10 +113,10 @@ def clone_template(request):
data = response.json()
data[0]['id'] = clone_id
return Response(data[0], status=status.HTTP_200_OK)
-
+
except requests.RequestException as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
- else :
+ else:
print(f'NOT REQUIRED -> on ne crée pas de submission')
return Response(data, status=status.HTTP_200_OK)
@@ -106,18 +126,28 @@ def clone_template(request):
@csrf_exempt
@api_view(['DELETE'])
def remove_template(request, id):
- # Vérifier la clé API
- api_key = request.headers.get('X-Auth-Token')
- if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
- return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
+ # Récupérer l'établissement concerné
+ establishment_id = request.GET.get('establishment_id')
+ if not establishment_id:
+ return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST)
- # URL de l'API de DocuSeal pour cloner le template
+ try:
+ establishment = Establishment.objects.get(id=establishment_id)
+ except Establishment.DoesNotExist:
+ return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND)
+
+ # Vérifier la clé API reçue dans le header
+ api_key = request.headers.get('X-Auth-Token')
+ if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal:
+ return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED)
+
+ # URL de l'API de DocuSeal pour supprimer le template
+
clone_url = f'https://docuseal.com/api/templates/{id}'
- # Faire la requête pour cloner le template
try:
response = requests.delete(clone_url, headers={
- 'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
+ 'X-Auth-Token': establishment.api_docuseal
})
if response.status_code != status.HTTP_200_OK:
@@ -132,23 +162,32 @@ def remove_template(request, id):
@csrf_exempt
@api_view(['GET'])
def download_template(request, slug):
- # Vérifier la clé API
+ # Récupérer l'établissement concerné
+ establishment_id = request.GET.get('establishment_id')
+ if not establishment_id:
+ return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ establishment = Establishment.objects.get(id=establishment_id)
+ except Establishment.DoesNotExist:
+ return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND)
+
+ # Vérifier la clé API reçue dans le header
api_key = request.headers.get('X-Auth-Token')
- if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
- return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
+ if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal:
+ return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED)
# Vérifier les données requises
- if not slug :
+ if not slug:
return Response({'error': 'slug is required'}, status=status.HTTP_400_BAD_REQUEST)
- # URL de l'API de DocuSeal pour cloner le template
+ # URL de l'API de DocuSeal pour télécharger le template
download_url = f'https://docuseal.com/submitters/{slug}/download'
- # Faire la requête pour cloner le template
try:
response = requests.get(download_url, headers={
'Content-Type': 'application/json',
- 'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
+ 'X-Auth-Token': establishment.api_docuseal
})
if response.status_code != status.HTTP_200_OK:
diff --git a/Back-End/Establishment/models.py b/Back-End/Establishment/models.py
index cbeb4d5..86d8a1d 100644
--- a/Back-End/Establishment/models.py
+++ b/Back-End/Establishment/models.py
@@ -21,6 +21,7 @@ class Establishment(models.Model):
licence_code = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
+ api_docuseal = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.name
\ No newline at end of file
diff --git a/Back-End/N3wtSchool/settings.py b/Back-End/N3wtSchool/settings.py
index 73a97fd..202c926 100644
--- a/Back-End/N3wtSchool/settings.py
+++ b/Back-End/N3wtSchool/settings.py
@@ -363,12 +363,10 @@ SIMPLE_JWT = {
}
# Configuration for DocuSeal JWT
-DOCUSEAL_API_KEY="LRvUTQCbMSSpManYKshdQk9Do6rBQgjHyPrbGfxU3Jg"
DOCUSEAL_JWT = {
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
- 'EXPIRATION_DELTA': timedelta(hours=1),
- 'API_KEY': DOCUSEAL_API_KEY
+ 'EXPIRATION_DELTA': timedelta(hours=1)
}
# Django Channels Configuration
diff --git a/Back-End/start.py b/Back-End/start.py
index 570bdbf..867a684 100644
--- a/Back-End/start.py
+++ b/Back-End/start.py
@@ -14,7 +14,7 @@ test_mode = os.getenv('TEST_MODE', 'False') == 'True'
commands = [
["python", "manage.py", "collectstatic", "--noinput"],
- #["python", "manage.py", "flush", "--noinput"],
+ ["python", "manage.py", "flush", "--noinput"],
["python", "manage.py", "makemigrations", "Common", "--noinput"],
["python", "manage.py", "makemigrations", "Establishment", "--noinput"],
["python", "manage.py", "makemigrations", "Settings", "--noinput"],
diff --git a/Front-End/prod.env b/Front-End/prod.env
index 99015de..d83185a 100644
--- a/Front-End/prod.env
+++ b/Front-End/prod.env
@@ -2,5 +2,4 @@ NEXT_PUBLIC_API_URL=_NEXT_PUBLIC_API_URL_
NEXT_PUBLIC_WSAPI_URL=_NEXT_PUBLIC_WSAPI_URL_
NEXT_PUBLIC_USE_FAKE_DATA=_NEXT_PUBLIC_USE_FAKE_DATA_
AUTH_SECRET=_AUTH_SECRET_
-NEXTAUTH_URL=_NEXTAUTH_URL_
-DOCUSEAL_API_KEY=_DOCUSEAL_API_KEY_
\ No newline at end of file
+NEXTAUTH_URL=_NEXTAUTH_URL_
\ No newline at end of file
diff --git a/Front-End/src/app/[locale]/admin/page.js b/Front-End/src/app/[locale]/admin/page.js
index 9af7345..aadba19 100644
--- a/Front-End/src/app/[locale]/admin/page.js
+++ b/Front-End/src/app/[locale]/admin/page.js
@@ -1,7 +1,7 @@
'use client';
import React, { useState, useEffect } from 'react';
import { useTranslations } from 'next-intl';
-import { Users, Clock, CalendarCheck, School } from 'lucide-react';
+import { Users, Clock, CalendarCheck, School, AlertTriangle, CheckCircle2 } from 'lucide-react';
import Loader from '@/components/Loader';
import StatCard from '@/components/StatCard';
import logger from '@/utils/logger';
@@ -39,7 +39,7 @@ export default function DashboardPage() {
const [upcomingEvents, setUpcomingEvents] = useState([]);
const [absencesToday, setAbsencesToday] = useState([]);
- const { selectedEstablishmentId, selectedEstablishmentTotalCapacity } =
+ const { selectedEstablishmentId, selectedEstablishmentTotalCapacity, apiDocuseal } =
useEstablishment();
const [statusDistribution, setStatusDistribution] = useState([
@@ -168,7 +168,24 @@ export default function DashboardPage() {
return (
-
{t('dashboard')}
+
+
+ {apiDocuseal ? (
+
+ ) : (
+
+ )}
+ {apiDocuseal
+ ? 'Clé API Docuseal renseignée'
+ : 'Clé API Docuseal manquante'}
+
+
{/* Statistiques principales */}
diff --git a/Front-End/src/app/[locale]/admin/structure/page.js b/Front-End/src/app/[locale]/admin/structure/page.js
index 7ba682d..1e38917 100644
--- a/Front-End/src/app/[locale]/admin/structure/page.js
+++ b/Front-End/src/app/[locale]/admin/structure/page.js
@@ -52,7 +52,7 @@ export default function Page() {
);
const csrfToken = useCsrfToken();
- const { selectedEstablishmentId } = useEstablishment();
+ const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
useEffect(() => {
if (selectedEstablishmentId) {
@@ -353,6 +353,7 @@ export default function Page() {
),
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js
index 1b07efd..63735da 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js
@@ -96,7 +96,7 @@ export default function CreateSubscriptionPage() {
const { getNiveauLabel } = useClasses();
const formDataRef = useRef(formData);
- const { selectedEstablishmentId } = useEstablishment();
+ const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
const csrfToken = useCsrfToken();
const router = useRouter();
@@ -473,8 +473,6 @@ export default function CreateSubscriptionPage() {
}
})();
- logger.debug('test : ', guardians);
-
const data = {
student: {
last_name: formDataRef.current.studentLastName,
@@ -532,12 +530,14 @@ export default function CreateSubscriptionPage() {
const clonePromises = masters.map((templateMaster) =>
cloneTemplate(
templateMaster.id,
- formData.guardianEmail,
- templateMaster.is_required
+ formDataRef.current.guardianEmail,
+ templateMaster.is_required,
+ selectedEstablishmentId,
+ apiDocuseal
)
.then((clonedDocument) => {
const cloneData = {
- name: `${templateMaster.name}_${formData.studentFirstName}_${formData.studentLastName}`,
+ name: `${templateMaster.name}_${formDataRef.current.studentFirstName}_${formDataRef.current.studentLastName}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster.id,
@@ -655,6 +655,7 @@ export default function CreateSubscriptionPage() {
return {
...prevData,
selectedGuardians: updatedSelectedGuardians,
+ guardianEmail: guardian.associated_profile_email,
};
});
};
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/editSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/editSubscription/page.js
index b730d75..0f47c0c 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/editSubscription/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/editSubscription/page.js
@@ -17,7 +17,7 @@ export default function Page() {
const [formErrors, setFormErrors] = useState({});
const csrfToken = useCsrfToken();
- const { selectedEstablishmentId } = useEstablishment();
+ const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = (data) => {
@@ -47,6 +47,7 @@ export default function Page() {
studentId={studentId}
csrfToken={csrfToken}
selectedEstablishmentId={selectedEstablishmentId}
+ apiDocuseal = {apiDocuseal}
onSubmit={handleSubmit}
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
errors={formErrors}
diff --git a/Front-End/src/app/[locale]/parents/editSubscription/page.js b/Front-End/src/app/[locale]/parents/editSubscription/page.js
index 7b79610..736bd24 100644
--- a/Front-End/src/app/[locale]/parents/editSubscription/page.js
+++ b/Front-End/src/app/[locale]/parents/editSubscription/page.js
@@ -14,7 +14,7 @@ export default function Page() {
const enable = searchParams.get('enabled') === 'true';
const router = useRouter();
const csrfToken = useCsrfToken();
- const { selectedEstablishmentId } = useEstablishment();
+ const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
const handleSubmit = async (data) => {
try {
@@ -31,6 +31,7 @@ export default function Page() {
studentId={studentId}
csrfToken={csrfToken}
selectedEstablishmentId={selectedEstablishmentId}
+ apiDocuseal = {apiDocuseal}
onSubmit={handleSubmit}
cancelUrl={FE_PARENTS_HOME_URL}
enable={enable}
diff --git a/Front-End/src/app/actions/registerFileGroupAction.js b/Front-End/src/app/actions/registerFileGroupAction.js
index 611b82b..444ceec 100644
--- a/Front-End/src/app/actions/registerFileGroupAction.js
+++ b/Front-End/src/app/actions/registerFileGroupAction.js
@@ -7,6 +7,7 @@ import {
FE_API_DOCUSEAL_CLONE_URL,
FE_API_DOCUSEAL_DOWNLOAD_URL,
FE_API_DOCUSEAL_GENERATE_TOKEN,
+ FE_API_DOCUSEAL_DELETE_URL
} from '@/utils/Url';
import { errorHandler, requestResponseHandler } from './actionsHandlers';
@@ -337,8 +338,23 @@ export const deleteRegistrationParentFileTemplate = (id, csrfToken) => {
};
// API requests
+export const removeTemplate = (templateId, selectedEstablishmentId, apiDocuseal) => {
+ return fetch(`${FE_API_DOCUSEAL_DELETE_URL}`, {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ templateId,
+ establishment_id :selectedEstablishmentId,
+ apiDocuseal
+ }),
+ })
+ .then(requestResponseHandler)
+ .catch(errorHandler);
+};
-export const cloneTemplate = (templateId, email, is_required) => {
+export const cloneTemplate = (templateId, email, is_required, selectedEstablishmentId, apiDocuseal) => {
return fetch(`${FE_API_DOCUSEAL_CLONE_URL}`, {
method: 'POST',
headers: {
@@ -348,14 +364,17 @@ export const cloneTemplate = (templateId, email, is_required) => {
templateId,
email,
is_required,
+ establishment_id :selectedEstablishmentId,
+ apiDocuseal
}),
})
.then(requestResponseHandler)
.catch(errorHandler);
};
-export const downloadTemplate = (slug) => {
- return fetch(`${FE_API_DOCUSEAL_DOWNLOAD_URL}/${slug}`, {
+export const downloadTemplate = (slug, selectedEstablishmentId, apiDocuseal) => {
+ const url = `${FE_API_DOCUSEAL_DOWNLOAD_URL}/${slug}?establishment_id=${selectedEstablishmentId}&apiDocuseal=${apiDocuseal}`;
+ return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
@@ -365,13 +384,13 @@ export const downloadTemplate = (slug) => {
.catch(errorHandler);
};
-export const generateToken = (email, id = null) => {
+export const generateToken = (email, id = null, selectedEstablishmentId, apiDocuseal) => {
return fetch(`${FE_API_DOCUSEAL_GENERATE_TOKEN}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify({ user_email: email, id }),
+ body: JSON.stringify({ user_email: email, id, establishment_id :selectedEstablishmentId, apiDocuseal }),
})
.then(requestResponseHandler)
.catch(errorHandler);
diff --git a/Front-End/src/components/Structure/Files/FileUploadDocuSeal.js b/Front-End/src/components/Structure/Files/FileUploadDocuSeal.js
index 39f946f..da9f70f 100644
--- a/Front-End/src/components/Structure/Files/FileUploadDocuSeal.js
+++ b/Front-End/src/components/Structure/Files/FileUploadDocuSeal.js
@@ -30,7 +30,7 @@ export default function FileUploadDocuSeal({
const csrfToken = useCsrfToken();
- const { selectedEstablishmentId } = useEstablishment();
+ const { selectedEstablishmentId, user, apiDocuseal } = useEstablishment();
useEffect(() => {
fetchRegistrationFileGroups(selectedEstablishmentId).then((data) =>
@@ -44,10 +44,12 @@ export default function FileUploadDocuSeal({
}, [fileToEdit]);
useEffect(() => {
- const email = 'n3wt.school@gmail.com';
+ if (!user && !user?.email) {
+ return;
+ }
const id = fileToEdit ? fileToEdit.id : null;
- generateToken(email, id)
+ generateToken(user?.email, id, selectedEstablishmentId, apiDocuseal)
.then((data) => {
setToken(data.token);
})
@@ -119,7 +121,7 @@ export default function FileUploadDocuSeal({
guardianDetails.forEach((guardian, index) => {
logger.debug('creation du clone avec required : ', is_required);
- cloneTemplate(templateMaster?.id, guardian.email, is_required)
+ cloneTemplate(templateMaster?.id, guardian.email, is_required, selectedEstablishmentId, apiDocuseal)
.then((clonedDocument) => {
// Sauvegarde des schoolFileTemplates clonés dans la base de données
const data = {
diff --git a/Front-End/src/components/Structure/Files/FilesGroupsManagement.js b/Front-End/src/components/Structure/Files/FilesGroupsManagement.js
index 7e7f0ff..ad1ff4c 100644
--- a/Front-End/src/components/Structure/Files/FilesGroupsManagement.js
+++ b/Front-End/src/components/Structure/Files/FilesGroupsManagement.js
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
-import { Download, Edit3, Trash2, FolderPlus, Signature } from 'lucide-react';
+import { Download, Edit3, Trash2, FolderPlus, Signature, AlertTriangle } from 'lucide-react';
import Modal from '@/components/Modal';
import Table from '@/components/Table';
import FileUploadDocuSeal from '@/components/Structure/Files/FileUploadDocuSeal';
@@ -22,6 +22,8 @@ import {
deleteRegistrationFileGroup,
deleteRegistrationSchoolFileMaster,
deleteRegistrationParentFileMaster,
+
+ removeTemplate
} from '@/app/actions/registerFileGroupAction';
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
import logger from '@/utils/logger';
@@ -35,6 +37,7 @@ import AlertMessage from '@/components/AlertMessage';
export default function FilesGroupsManagement({
csrfToken,
selectedEstablishmentId,
+ apiDocuseal
}) {
const [schoolFileMasters, setSchoolFileMasters] = useState([]);
const [schoolFileTemplates, setSchoolFileTemplates] = useState([]);
@@ -114,17 +117,19 @@ export default function FilesGroupsManagement({
setRemovePopupOnConfirm(() => () => {
setIsLoading(true);
// Supprimer les clones associés via l'API DocuSeal
- const removeClonesPromises = schoolFileTemplates
- .filter((template) => template.master === templateMaster.id)
- .map((template) => removeTemplate(template.id));
-
- // Ajouter la suppression du master à la liste des promesses
- removeClonesPromises.push(removeTemplate(templateMaster.id));
+ const removeClonesPromises = [
+ ...schoolFileTemplates
+ .filter((template) => template.master === templateMaster.id)
+ .map((template) =>
+ removeTemplate(template.id, selectedEstablishmentId, apiDocuseal)
+ ),
+ removeTemplate(templateMaster.id, selectedEstablishmentId, apiDocuseal),
+ ];
// Attendre que toutes les suppressions dans DocuSeal soient terminées
Promise.all(removeClonesPromises)
.then((responses) => {
- const allSuccessful = responses.every((response) => response.ok);
+ const allSuccessful = responses.every((response) => response && response.id);
if (allSuccessful) {
logger.debug('Master et clones supprimés avec succès de DocuSeal.');
@@ -188,31 +193,6 @@ export default function FilesGroupsManagement({
});
};
- const removeTemplate = (templateId) => {
- return fetch('/api/docuseal/removeTemplate/', {
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json',
- 'X-CSRFToken': csrfToken,
- },
- body: JSON.stringify({
- templateId,
- }),
- })
- .then((response) => {
- if (!response.ok) {
- return response.json().then((err) => {
- throw new Error(err.message);
- });
- }
- return response;
- })
- .catch((error) => {
- logger.error('Error removing template:', error);
- throw error;
- });
- };
-
const editTemplateMaster = (file) => {
setIsEditing(true);
setFileToEdit(file);
@@ -562,13 +542,25 @@ export default function FilesGroupsManagement({
icon={Signature}
title="Formulaires à remplir"
description="Gérez les formulaires nécessitant une signature électronique."
- button={true}
+ button={apiDocuseal}
buttonOpeningModal={true}
onClick={() => {
setIsModalOpen(true);
setIsEditing(false);
}}
/>
+
+
+ {!apiDocuseal && (
+
+ )}
+ {!apiDocuseal && 'Clé API Docuseal manquante'}
+
+
{
const storedUser = sessionStorage.getItem('user');
return storedUser ? JSON.parse(storedUser) : null;
});
+ const [apiDocuseal, setApiDocusealState] = useState(() => {
+ const storedApiDocuseal = sessionStorage.getItem('apiDocuseal');
+ return storedApiDocuseal ? JSON.parse(storedApiDocuseal) : null;
+ });
// Sauvegarder dans sessionStorage à chaque mise à jour
const setSelectedEstablishmentId = (id) => {
@@ -86,6 +90,11 @@ export const EstablishmentProvider = ({ children }) => {
sessionStorage.setItem('user', JSON.stringify(user));
};
+ const setApiDocuseal = (api) => {
+ setApiDocusealState(api);
+ sessionStorage.setItem('apiDocuseal', JSON.stringify(api));
+ };
+
/**
* Fonction d'initialisation du contexte avec la session (appelée lors du login)
* @param {*} session
@@ -104,6 +113,7 @@ export const EstablishmentProvider = ({ children }) => {
name: role.establishment__name,
evaluation_frequency: role.establishment__evaluation_frequency,
total_capacity: role.establishment__total_capacity,
+ api_docuseal: role.establishment__api_docuseal,
role_id: i,
role_type: role.role_type,
}));
@@ -123,6 +133,9 @@ export const EstablishmentProvider = ({ children }) => {
setSelectedEstablishmentTotalCapacity(
userEstablishments[roleIndexDefault].total_capacity
);
+ setApiDocuseal(
+ userEstablishments[roleIndexDefault].api_docuseal
+ );
setProfileRole(userEstablishments[roleIndexDefault].role_type);
}
if (endInitFunctionHandler) {
@@ -140,6 +153,9 @@ export const EstablishmentProvider = ({ children }) => {
setProfileRoleState(null);
setEstablishmentsState([]);
setUserState(null);
+ setSelectedEstablishmentEvaluationFrequencyState(null);
+ setSelectedEstablishmentTotalCapacityState(null);
+ setApiDocusealState(null);
sessionStorage.clear();
};
@@ -154,6 +170,8 @@ export const EstablishmentProvider = ({ children }) => {
setSelectedEstablishmentEvaluationFrequency,
selectedEstablishmentTotalCapacity,
setSelectedEstablishmentTotalCapacity,
+ apiDocuseal,
+ setApiDocuseal,
selectedRoleId,
setSelectedRoleId,
profileRole,
diff --git a/Front-End/src/pages/api/docuseal/cloneTemplate.js b/Front-End/src/pages/api/docuseal/cloneTemplate.js
index fa2dd54..8f9aeaa 100644
--- a/Front-End/src/pages/api/docuseal/cloneTemplate.js
+++ b/Front-End/src/pages/api/docuseal/cloneTemplate.js
@@ -3,18 +3,19 @@ import { BE_DOCUSEAL_CLONE_TEMPLATE } from '@/utils/Url';
export default function handler(req, res) {
if (req.method === 'POST') {
- const { templateId, email, is_required } = req.body;
+ const { templateId, email, is_required, establishment_id, apiDocuseal } = req.body;
fetch(BE_DOCUSEAL_CLONE_TEMPLATE, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-Auth-Token': process.env.DOCUSEAL_API_KEY,
+ 'X-Auth-Token': apiDocuseal,
},
body: JSON.stringify({
templateId,
email,
is_required,
+ establishment_id,
}),
})
.then((response) => {
diff --git a/Front-End/src/pages/api/docuseal/downloadTemplate/[slug].js b/Front-End/src/pages/api/docuseal/downloadTemplate/[slug].js
index 5d209cc..9ae309c 100644
--- a/Front-End/src/pages/api/docuseal/downloadTemplate/[slug].js
+++ b/Front-End/src/pages/api/docuseal/downloadTemplate/[slug].js
@@ -3,13 +3,13 @@ import { BE_DOCUSEAL_DOWNLOAD_TEMPLATE } from '@/utils/Url';
export default function handler(req, res) {
if (req.method === 'GET') {
- const { slug } = req.query;
+ const { slug, establishment_id, apiDocuseal } = req.query;
logger.debug('slug : ', slug);
- fetch(`${BE_DOCUSEAL_DOWNLOAD_TEMPLATE}/${slug}`, {
+ fetch(`${BE_DOCUSEAL_DOWNLOAD_TEMPLATE}/${slug}?establishment_id=${establishment_id}`, {
method: 'GET',
headers: {
- 'X-Auth-Token': process.env.DOCUSEAL_API_KEY,
+ 'X-Auth-Token': apiDocuseal,
},
})
.then((response) => {
diff --git a/Front-End/src/pages/api/docuseal/generateToken.js b/Front-End/src/pages/api/docuseal/generateToken.js
index 7f263d6..2069abe 100644
--- a/Front-End/src/pages/api/docuseal/generateToken.js
+++ b/Front-End/src/pages/api/docuseal/generateToken.js
@@ -3,13 +3,15 @@ import { BE_DOCUSEAL_GET_JWT } from '@/utils/Url';
export default function handler(req, res) {
if (req.method === 'POST') {
+ const { apiDocuseal, ...rest } = req.body;
+
fetch(BE_DOCUSEAL_GET_JWT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-Auth-Token': process.env.DOCUSEAL_API_KEY,
+ 'X-Auth-Token': apiDocuseal,
},
- body: JSON.stringify(req.body),
+ body: JSON.stringify(rest),
})
.then((response) => {
logger.debug('Response status:', response.status);
diff --git a/Front-End/src/pages/api/docuseal/removeTemplate.js b/Front-End/src/pages/api/docuseal/removeTemplate.js
index 0725bce..b3a9467 100644
--- a/Front-End/src/pages/api/docuseal/removeTemplate.js
+++ b/Front-End/src/pages/api/docuseal/removeTemplate.js
@@ -3,12 +3,12 @@ import { BE_DOCUSEAL_REMOVE_TEMPLATE } from '@/utils/Url';
export default function handler(req, res) {
if (req.method === 'DELETE') {
- const { templateId } = req.body;
+ const { templateId, establishment_id, apiDocuseal } = req.body;
- fetch(`${BE_DOCUSEAL_REMOVE_TEMPLATE}/${templateId}`, {
+ fetch(`${BE_DOCUSEAL_REMOVE_TEMPLATE}/${templateId}?establishment_id=${establishment_id}`, {
method: 'DELETE',
headers: {
- 'X-Auth-Token': process.env.DOCUSEAL_API_KEY,
+ 'X-Auth-Token': apiDocuseal,
},
})
.then((response) => {
diff --git a/Front-End/src/utils/Url.js b/Front-End/src/utils/Url.js
index cdac09e..5d5881a 100644
--- a/Front-End/src/utils/Url.js
+++ b/Front-End/src/utils/Url.js
@@ -136,6 +136,7 @@ export const FE_PARENTS_EDIT_SUBSCRIPTION_URL = '/parents/editSubscription';
export const FE_API_DOCUSEAL_GENERATE_TOKEN = '/api/docuseal/generateToken';
export const FE_API_DOCUSEAL_CLONE_URL = '/api/docuseal/cloneTemplate';
export const FE_API_DOCUSEAL_DOWNLOAD_URL = '/api/docuseal/downloadTemplate';
+export const FE_API_DOCUSEAL_DELETE_URL = '/api/docuseal/removeTemplate';
/**
* Fonction pour obtenir l'URL de redirection en fonction du rôle
diff --git a/docker-compose.yml b/docker-compose.yml
index a8a3d99..42f1e08 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -16,15 +16,40 @@ services:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: school
TZ: Europe/Paris
+ # docuseal_db:
+ # image: postgres:latest
+ # environment:
+ # POSTGRES_USER: postgres
+ # POSTGRES_PASSWORD: postgres
+ # DOCUSEAL_DB_HOST: docuseal_db
+ # POSTGRES_DB: docuseal
+ # ports:
+ # - 5433:5432 # port différent si besoin d'accès direct depuis l'hôte
- docuseal:
- image: docuseal/docuseal:latest
- depends_on:
- - database
- ports:
- - 3001:3000
- environment:
- - DATABASE_URL=postgresql://postgres:postgres@database:5432/docuseal
+ # docuseal:
+ # image: docuseal/docuseal:latest
+ # container_name: docuseal_app
+ # depends_on:
+ # - docuseal_db
+ # ports:
+ # - "3001:3000"
+ # environment:
+ # DATABASE_URL: postgresql://postgres:postgres@docuseal_db:5432/docuseal
+ # volumes:
+ # - ./docuseal:/data/docuseal
+
+ # caddy:
+ # image: caddy:2
+ # container_name: caddy
+ # restart: unless-stopped
+ # ports:
+ # - "4000:4443"
+ # volumes:
+ # - ./Caddyfile:/etc/caddy/Caddyfile
+ # - caddy_data:/data
+ # - caddy_config:/config
+ # depends_on:
+ # - docuseal
backend:
build:
@@ -44,25 +69,26 @@ services:
depends_on:
- redis
- database
- - docuseal
+ #- docuseal
command: python start.py
- init_docuseal_users:
- build:
- context: .
- dockerfile: Dockerfile
- depends_on:
- - docuseal
- environment:
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
- USER_FIRST_NAME: n3wt
- USER_LAST_NAME: school
- USER_COMPANY: n3wt.innov
- USER_EMAIL: n3wt.school@gmail.com
- USER_PASSWORD: n3wt1234
- volumes:
- - ./initDocusealUsers.sh:/docker-entrypoint-initdb.d/initDocusealUsers.sh
+ # init_docuseal_users:
+ # build:
+ # context: .
+ # dockerfile: Dockerfile
+ # depends_on:
+ # - docuseal
+ # environment:
+ # DOCUSEAL_DB_HOST: docuseal_db
+ # POSTGRES_USER: postgres
+ # POSTGRES_PASSWORD: postgres
+ # USER_FIRST_NAME: n3wt
+ # USER_LAST_NAME: school
+ # USER_COMPANY: n3wt.innov
+ # USER_EMAIL: n3wt.school@gmail.com
+ # USER_PASSWORD: n3wt1234
+ # volumes:
+ # - ./initDocusealUsers.sh:/docker-entrypoint-initdb.d/initDocusealUsers.sh
# frontend:
# build:
@@ -79,3 +105,6 @@ services:
# - TZ=Europe/Paris
# depends_on:
# - backend
+volumes:
+ caddy_data:
+ caddy_config: