feat: Gestion des documents nécessitant des signatures électroniques et

ceux ne nécessitant pas les signatures électroniques [#22]
This commit is contained in:
N3WT DE COMPET
2025-03-02 12:35:53 +01:00
parent 2ac4832985
commit e3879f516b
10 changed files with 103 additions and 69 deletions

View File

@ -19,7 +19,7 @@ def generate_jwt_token(request):
# Récupérer les données de la requête # Récupérer les données de la requête
user_email = request.data.get('user_email') user_email = request.data.get('user_email')
documents_urls = request.data.get('documents_urls', []) documents_urls = request.data.get('documents_urls', [])
template_id = request.data.get('template_id') # Récupérer le template_id id = request.data.get('id') # Récupérer le id
# Vérifier les données requises # Vérifier les données requises
if not user_email: if not user_email:
@ -34,7 +34,7 @@ def generate_jwt_token(request):
payload = { payload = {
'user_email': user_email, 'user_email': user_email,
'documents_urls': documents_urls, 'documents_urls': documents_urls,
'template_id': template_id, # Ajouter le template_id au payload 'template_id': id, # Ajouter le id au payload
'exp': datetime.datetime.utcnow() + expiration_delta # Temps d'expiration du token 'exp': datetime.datetime.utcnow() + expiration_delta # Temps d'expiration du token
} }
@ -54,6 +54,8 @@ def clone_template(request):
# Récupérer les données de la requête # Récupérer les données de la requête
document_id = request.data.get('templateId') document_id = request.data.get('templateId')
email = request.data.get('email') email = request.data.get('email')
is_required = request.data.get('is_required')
print(f'test is required = {is_required}')
# Vérifier les données requises # Vérifier les données requises
if not document_id : if not document_id :
@ -74,26 +76,32 @@ def clone_template(request):
data = response.json() data = response.json()
# URL de l'API de DocuSeal pour créer une submission if is_required:
submission_url = f'https://docuseal.com/api/submissions' print(f'REQUIRED -> création dune submission')
# 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 # Faire la requête pour cloner le template
try: try:
clone_id = data['id'] 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', 'Content-Type': 'application/json',
'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY'] 'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
}) })
if response.status_code != status.HTTP_200_OK: if response.status_code != status.HTTP_200_OK:
return Response({'error': 'Failed to create submission'}, status=response.status_code) return Response({'error': 'Failed to create submission'}, status=response.status_code)
data = response.json() data = response.json()
data[0]['template_id'] = clone_id data[0]['id'] = clone_id
return Response(data[0], status=status.HTTP_200_OK) print(f'DATA RESPONSE : {data[0]}')
return Response(data[0], status=status.HTTP_200_OK)
except requests.RequestException as e: except requests.RequestException as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else :
print(f'NOT REQUIRED -> on ne crée pas de submission')
return Response(data, status=status.HTTP_200_OK)
except requests.RequestException as e: except requests.RequestException as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

View File

@ -173,12 +173,13 @@ def registration_file_path(instance, filename):
return f'registration_files/dossier_rf_{instance.student_id}/{filename}' return f'registration_files/dossier_rf_{instance.student_id}/{filename}'
class RegistrationTemplateMaster(models.Model): class RegistrationTemplateMaster(models.Model):
groups = models.ManyToManyField(RegistrationFileGroup, related_name='template_masters') groups = models.ManyToManyField(RegistrationFileGroup, related_name='template_masters', blank=True)
template_id = models.IntegerField(primary_key=True) id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255, default="") name = models.CharField(max_length=255, default="")
is_required = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return f'{self.group.name} - {self.template_id}' return f'{self.group.name} - {self.id}'
class RegistrationForm(models.Model): class RegistrationForm(models.Model):
class RegistrationFormStatus(models.IntegerChoices): class RegistrationFormStatus(models.IntegerChoices):
@ -220,14 +221,14 @@ class RegistrationForm(models.Model):
return "RF_" + self.student.last_name + "_" + self.student.first_name return "RF_" + self.student.last_name + "_" + self.student.first_name
def registration_file_upload_to(instance, filename): def registration_file_upload_to(instance, filename):
return f"registration_files/dossier_rf_{instance.register_form.pk}/{filename}" return f"registration_files/dossier_rf_{instance.registration_form.pk}/{filename}"
class RegistrationTemplate(models.Model): class RegistrationTemplate(models.Model):
master = models.ForeignKey(RegistrationTemplateMaster, on_delete=models.CASCADE, related_name='templates') master = models.ForeignKey(RegistrationTemplateMaster, on_delete=models.CASCADE, related_name='templates', blank=True)
template_id = models.IntegerField(primary_key=True) id = models.IntegerField(primary_key=True)
slug = models.CharField(max_length=255, default="") slug = models.CharField(max_length=255, default="")
name = models.CharField(max_length=255, default="") name = models.CharField(max_length=255, default="")
registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='templates') registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='templates', blank=True)
file = models.FileField(null=True,blank=True, upload_to=registration_file_upload_to) file = models.FileField(null=True,blank=True, upload_to=registration_file_upload_to)
def __str__(self): def __str__(self):

View File

@ -12,11 +12,13 @@ import pytz
from datetime import datetime from datetime import datetime
class RegistrationTemplateMasterSerializer(serializers.ModelSerializer): class RegistrationTemplateMasterSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta: class Meta:
model = RegistrationTemplateMaster model = RegistrationTemplateMaster
fields = '__all__' fields = '__all__'
class RegistrationTemplateSerializer(serializers.ModelSerializer): class RegistrationTemplateSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta: class Meta:
model = RegistrationTemplate model = RegistrationTemplate
fields = '__all__' fields = '__all__'

View File

@ -44,7 +44,7 @@ class RegistrationTemplateMasterSimpleView(APIView):
} }
) )
def get(self, request, id): def get(self, request, id):
master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='template_id', _value=id) master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='id', _value=id)
if master is None: if master is None:
return JsonResponse({"errorMessage":'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) return JsonResponse({"errorMessage":'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationTemplateMasterSerializer(master) serializer = RegistrationTemplateMasterSerializer(master)
@ -60,7 +60,7 @@ class RegistrationTemplateMasterSimpleView(APIView):
} }
) )
def put(self, request, id): def put(self, request, id):
master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='template_id', _value=id) master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='id', _value=id)
if master is None: if master is None:
return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationTemplateMasterSerializer(master, data=request.data) serializer = RegistrationTemplateMasterSerializer(master, data=request.data)
@ -77,7 +77,7 @@ class RegistrationTemplateMasterSimpleView(APIView):
} }
) )
def delete(self, request, id): def delete(self, request, id):
master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='template_id', _value=id) master = bdd.getObject(_objectName=RegistrationTemplateMaster, _columnName='id', _value=id)
if master is not None: if master is not None:
master.delete() master.delete()
return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
@ -118,7 +118,7 @@ class RegistrationTemplateSimpleView(APIView):
} }
) )
def get(self, request, id): def get(self, request, id):
template = bdd.getObject(_objectName=RegistrationTemplate, _columnName='template_id', _value=id) template = bdd.getObject(_objectName=RegistrationTemplate, _columnName='id', _value=id)
if template is None: if template is None:
return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationTemplateSerializer(template) serializer = RegistrationTemplateSerializer(template)
@ -134,7 +134,7 @@ class RegistrationTemplateSimpleView(APIView):
} }
) )
def put(self, request, id): def put(self, request, id):
template = bdd.getObject(_objectName=RegistrationTemplate, _columnName='template_id', _value=id) template = bdd.getObject(_objectName=RegistrationTemplate, _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 = RegistrationTemplateSerializer(template, data=request.data) serializer = RegistrationTemplateSerializer(template, data=request.data)
@ -151,7 +151,7 @@ class RegistrationTemplateSimpleView(APIView):
} }
) )
def delete(self, request, id): def delete(self, request, id):
template = bdd.getObject(_objectName=RegistrationTemplate, _columnName='template_id', _value=id) template = bdd.getObject(_objectName=RegistrationTemplate, _columnName='id', _value=id)
if template is not None: if template is not None:
template.delete() template.delete()
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)

View File

@ -386,14 +386,14 @@ useEffect(()=>{
// Cloner les templates pour chaque templateMaster du fileGroup // Cloner les templates pour chaque templateMaster du fileGroup
const masters = templateMasters.filter(file => file.groups.includes(selectedFileGroup)); const masters = templateMasters.filter(file => file.groups.includes(selectedFileGroup));
const clonePromises = masters.map((templateMaster, index) => { const clonePromises = masters.map((templateMaster, index) => {
return cloneTemplate(templateMaster.template_id, guardianEmail) return cloneTemplate(templateMaster.id, guardianEmail, templateMaster.is_required)
.then(clonedDocument => { .then(clonedDocument => {
// Sauvegarde des templates clonés dans la base de données // Sauvegarde des templates clonés dans la base de données
const cloneData = { const cloneData = {
name: `clone_${clonedDocument.id}`, name: `clone_${clonedDocument.id}`,
slug: clonedDocument.slug, slug: clonedDocument.slug,
template_id: clonedDocument.id, id: clonedDocument.id,
master: templateMaster.template_id, master: templateMaster.id,
registration_form: data.student.id registration_form: data.student.id
}; };
@ -464,14 +464,14 @@ useEffect(()=>{
// Cloner les templates pour chaque templateMaster du fileGroup // Cloner les templates pour chaque templateMaster du fileGroup
const masters = templateMasters.filter(file => file.groups.includes(selectedFileGroup)); const masters = templateMasters.filter(file => file.groups.includes(selectedFileGroup));
const clonePromises = masters.map((templateMaster, index) => { const clonePromises = masters.map((templateMaster, index) => {
return cloneTemplate(templateMaster.template_id, updatedData.guardianEmail) return cloneTemplate(templateMaster.id, updatedData.guardianEmail, templateMaster.is_required)
.then(clonedDocument => { .then(clonedDocument => {
// Sauvegarde des templates clonés dans la base de données // Sauvegarde des templates clonés dans la base de données
const cloneData = { const cloneData = {
name: `clone_${clonedDocument.id}`, name: `clone_${clonedDocument.id}`,
slug: clonedDocument.slug, slug: clonedDocument.slug,
template_id: clonedDocument.template_id, id: clonedDocument.id,
master: templateMaster.template_id, master: templateMaster.id,
registration_form: data.student.id registration_form: data.student.id
}; };

View File

@ -197,7 +197,7 @@ export const editRegistrationTemplateMaster = (fileId, data, csrfToken) => {
.then(requestResponseHandler) .then(requestResponseHandler)
} }
export const cloneTemplate = (templateId, email) => { export const cloneTemplate = (templateId, email, is_required) => {
return fetch(`${FE_API_DOCUSEAL_CLONE_URL}`, { return fetch(`${FE_API_DOCUSEAL_CLONE_URL}`, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -205,7 +205,8 @@ export const cloneTemplate = (templateId, email) => {
}, },
body: JSON.stringify({ body: JSON.stringify({
templateId, templateId,
email email,
is_required
}) })
}) })
.then(requestResponseHandler) .then(requestResponseHandler)

View File

@ -8,7 +8,13 @@ import Button from '@/components/Button';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'; import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import Table from '@/components/Table'; import Table from '@/components/Table';
import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction'; import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
import { fetchRegistrationFileFromGroup, fetchRegistrationTemplateMaster, downloadTemplate, createRegistrationTemplates, deleteRegistrationTemplates } from '@/app/actions/registerFileGroupAction'; import { fetchRegistrationFileFromGroup,
fetchRegistrationTemplateMaster,
downloadTemplate,
createRegistrationTemplates,
editRegistrationTemplates,
deleteRegistrationTemplates
} from '@/app/actions/registerFileGroupAction';
import { Download, Upload, Trash2, Eye } from 'lucide-react'; import { Download, Upload, Trash2, Eye } from 'lucide-react';
import { BASE_URL } from '@/utils/Url'; import { BASE_URL } from '@/utils/Url';
import DraggableFileUpload from '@/components/DraggableFileUpload'; import DraggableFileUpload from '@/components/DraggableFileUpload';
@ -277,10 +283,21 @@ export default function InscriptionFormShared({
withDownloadButton={false} withDownloadButton={false}
onComplete={() => { onComplete={() => {
downloadTemplate(requiredFileTemplates[currentPage - 2].slug) downloadTemplate(requiredFileTemplates[currentPage - 2].slug)
.then((data) => { .then((data) => fetch(data))
logger.debug("PDF URL : ", data) .then((response) => response.blob())
.then((blob) => {
const file = new File([blob], `${requiredFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
const updateData = new FormData();
updateData.append('file', file);
return editRegistrationTemplates(requiredFileTemplates[currentPage - 2].id, updateData, csrfToken);
}) })
.catch((error) => console.error(error)); .then((data) => {
logger.debug("EDIT TEMPLATE : ", data);
})
.catch((error) => {
logger.error("error editing template : ", error);
});
}} }}
> >
</DocusealForm> </DocusealForm>

View File

@ -33,7 +33,7 @@ export default function FileUpload({ handleCreateTemplateMaster, handleEditTempl
const body = fileToEdit const body = fileToEdit
? JSON.stringify({ ? JSON.stringify({
user_email: 'n3wt.school@gmail.com', user_email: 'n3wt.school@gmail.com',
template_id: fileToEdit.template_id id: fileToEdit.id
}) })
: JSON.stringify({ : JSON.stringify({
user_email: 'n3wt.school@gmail.com' user_email: 'n3wt.school@gmail.com'
@ -75,6 +75,7 @@ export default function FileUpload({ handleCreateTemplateMaster, handleEditTempl
const handleLoad = (detail) => { const handleLoad = (detail) => {
const templateId = detail?.id; const templateId = detail?.id;
logger.debug('loading template id : ', detail)
setTemplateMaster(detail); setTemplateMaster(detail);
} }
@ -88,30 +89,34 @@ export default function FileUpload({ handleCreateTemplateMaster, handleEditTempl
setUploadedFileName(detail.name); setUploadedFileName(detail.name);
} }
const handleSubmit = () => { const handleSubmit = (data) => {
const is_required = (data.fields.length > 0)
if (fileToEdit) { if (fileToEdit) {
logger.debug('Modification du template master:', templateMaster?.id); logger.debug('Modification du template master:', templateMaster?.id);
handleEditTemplateMaster({ handleEditTemplateMaster({
name: uploadedFileName, name: uploadedFileName,
group_ids: selectedGroups.map(group => group.id), group_ids: selectedGroups.map(group => group.id),
template_id: templateMaster?.id id: templateMaster?.id,
is_required: is_required
}); });
} else { } else {
logger.debug('Création du template master:', templateMaster?.id); logger.debug('Création du template master:', templateMaster?.id);
handleCreateTemplateMaster({ handleCreateTemplateMaster({
name: uploadedFileName, name: uploadedFileName,
group_ids: selectedGroups.map(group => group.id), group_ids: selectedGroups.map(group => group.id),
template_id: templateMaster?.id id: templateMaster?.id,
is_required: is_required
}); });
guardianDetails.forEach((guardian, index) => { guardianDetails.forEach((guardian, index) => {
cloneTemplate(templateMaster?.id, guardian.email) logger.debug('creation du clone avec required : ', is_required)
cloneTemplate(templateMaster?.id, guardian.email, is_required)
.then(clonedDocument => { .then(clonedDocument => {
// Sauvegarde des templates clonés dans la base de données // Sauvegarde des templates clonés dans la base de données
const data = { const data = {
name: `${uploadedFileName}_${guardian.first_name}_${guardian.last_name}`, name: `${uploadedFileName}_${guardian.first_name}_${guardian.last_name}`,
slug: clonedDocument.slug, slug: clonedDocument.slug,
template_id: clonedDocument.template_id, id: clonedDocument.id,
master: templateMaster?.id, master: templateMaster?.id,
registration_form: guardian.registration_form registration_form: guardian.registration_form
}; };

View File

@ -50,10 +50,6 @@ export default function FilesGroupsManagement({ csrfToken }) {
]).then(([filesTemplateMasters, groupsData, filesTemplates]) => { ]).then(([filesTemplateMasters, groupsData, filesTemplates]) => {
setGroups(groupsData); setGroups(groupsData);
setTemplates(filesTemplates); setTemplates(filesTemplates);
// Sélectionner automatiquement le premier groupe s'il existe
if (groupsData.length > 0) {
setSelectedGroup(groupsData[0].id.toString());
}
// Transformer chaque fichier pour inclure les informations complètes du groupe // Transformer chaque fichier pour inclure les informations complètes du groupe
const transformedFiles = filesTemplateMasters.map(file => transformFileData(file, groupsData)); const transformedFiles = filesTemplateMasters.map(file => transformFileData(file, groupsData));
setTemplateMasters(transformedFiles); setTemplateMasters(transformedFiles);
@ -67,11 +63,11 @@ export default function FilesGroupsManagement({ csrfToken }) {
const deleteTemplateMaster = (templateMaster) => { const deleteTemplateMaster = (templateMaster) => {
// Supprimer les clones associés via l'API DocuSeal // Supprimer les clones associés via l'API DocuSeal
const removeClonesPromises = templates const removeClonesPromises = templates
.filter(template => template.master === templateMaster.template_id) .filter(template => template.master === templateMaster.id)
.map(template => removeTemplate(template.template_id)); .map(template => removeTemplate(template.id));
// Ajouter la suppression du master à la liste des promesses // Ajouter la suppression du master à la liste des promesses
removeClonesPromises.push(removeTemplate(templateMaster.template_id)); removeClonesPromises.push(removeTemplate(templateMaster.id));
// Attendre que toutes les suppressions dans DocuSeal soient terminées // Attendre que toutes les suppressions dans DocuSeal soient terminées
Promise.all(removeClonesPromises) Promise.all(removeClonesPromises)
@ -81,10 +77,10 @@ export default function FilesGroupsManagement({ csrfToken }) {
logger.debug('Master et clones supprimés avec succès de DocuSeal.'); logger.debug('Master et clones supprimés avec succès de DocuSeal.');
// Supprimer le template master de la base de données // Supprimer le template master de la base de données
deleteRegistrationTemplateMaster(templateMaster.template_id, csrfToken) deleteRegistrationTemplateMaster(templateMaster.id, csrfToken)
.then(response => { .then(response => {
if (response.ok) { if (response.ok) {
setTemplateMasters(templateMasters.filter(fichier => fichier.template_id !== templateMaster.template_id)); setTemplateMasters(templateMasters.filter(fichier => fichier.id !== templateMaster.id));
alert('Fichier supprimé avec succès.'); alert('Fichier supprimé avec succès.');
} else { } else {
alert('Erreur lors de la suppression du fichier dans la base de données.'); alert('Erreur lors de la suppression du fichier dans la base de données.');
@ -133,11 +129,12 @@ export default function FilesGroupsManagement({ csrfToken }) {
setIsModalOpen(true); setIsModalOpen(true);
}; };
const handleCreateTemplateMaster = ({name, group_ids, template_id}) => { const handleCreateTemplateMaster = ({name, group_ids, id, is_required}) => {
const data = { const data = {
name: name, name: name,
template_id: template_id, id: id,
groups: group_ids groups: group_ids,
is_required: is_required
}; };
logger.debug(data); logger.debug(data);
@ -153,20 +150,21 @@ export default function FilesGroupsManagement({ csrfToken }) {
}); });
}; };
const handleEditTemplateMaster = ({name, group_ids, template_id}) => { const handleEditTemplateMaster = ({name, group_ids, id, is_required}) => {
const data = { const data = {
name: name, name: name,
template_id: template_id, id: id,
groups: group_ids groups: group_ids,
is_required: is_required
}; };
logger.debug(data); logger.debug(data);
editRegistrationTemplateMaster(template_id, data, csrfToken) editRegistrationTemplateMaster(id, data, csrfToken)
.then(data => { .then(data => {
// Transformer le fichier mis à jour avec les informations du groupe // Transformer le fichier mis à jour avec les informations du groupe
const transformedFile = transformFileData(data, groups); const transformedFile = transformFileData(data, groups);
setTemplateMasters(prevFichiers => setTemplateMasters(prevFichiers =>
prevFichiers.map(f => f.template_id === template_id ? transformedFile : f) prevFichiers.map(f => f.id === id ? transformedFile : f)
); );
setIsModalOpen(false); setIsModalOpen(false);
}) })

View File

@ -2,7 +2,8 @@ import { BE_DOCUSEAL_CLONE_TEMPLATE } from '@/utils/Url';
export default function handler(req, res) { export default function handler(req, res) {
if (req.method === 'POST') { if (req.method === 'POST') {
const { templateId, email } = req.body; const { templateId, email, is_required } = req.body;
console.log('coucou : ', req.body)
fetch(BE_DOCUSEAL_CLONE_TEMPLATE, { fetch(BE_DOCUSEAL_CLONE_TEMPLATE, {
method: 'POST', method: 'POST',
@ -12,7 +13,8 @@ export default function handler(req, res) {
}, },
body: JSON.stringify({ body: JSON.stringify({
templateId, templateId,
email email,
is_required
}) })
}) })
.then(response => { .then(response => {