From 445cf35382e7efd17f692ad810a071059f66ed1b Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 23 Feb 2025 19:08:13 +0100 Subject: [PATCH] chore: merge conflicts --- Back-End/N3wtSchool/settings.py | 11 +- Back-End/N3wtSchool/urls.py | 1 + Front-End/next.config.mjs | 12 ++ Front-End/package-lock.json | 7 + Front-End/package.json | 1 + Front-End/src/components/InputPhone.js | 7 +- .../Inscription/InscriptionFormShared.js | 180 ++++++------------ .../Inscription/ResponsableInputFields.js | 5 + Front-End/src/components/SelectChoice.js | 56 +++--- .../Structure/Files/FilesManagement.js | 37 +++- docker-compose.yml | 36 ++-- 11 files changed, 187 insertions(+), 166 deletions(-) diff --git a/Back-End/N3wtSchool/settings.py b/Back-End/N3wtSchool/settings.py index 3090976..4232560 100644 --- a/Back-End/N3wtSchool/settings.py +++ b/Back-End/N3wtSchool/settings.py @@ -65,7 +65,8 @@ MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'N3wtSchool.middleware.ContentSecurityPolicyMiddleware' ] @@ -261,7 +262,6 @@ CSRF_COOKIE_HTTPONLY = False CSRF_COOKIE_SECURE = False CSRF_COOKIE_NAME = 'csrftoken' - USE_TZ = True TZ_APPLI = 'Europe/Paris' @@ -328,3 +328,10 @@ SIMPLE_JWT = { 'TOKEN_TYPE_CLAIM': 'token_type', } +# Configuration for DocuSeal JWT +DOCUSEAL_JWT = { + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'EXPIRATION_DELTA': timedelta(hours=1), + 'API_KEY': '1kzHsXqN8P2ezUGT7TjVuBwM1hqtLsztrVSsQ87T7Mz' +} \ No newline at end of file diff --git a/Back-End/N3wtSchool/urls.py b/Back-End/N3wtSchool/urls.py index dfbc490..e336cac 100644 --- a/Back-End/N3wtSchool/urls.py +++ b/Back-End/N3wtSchool/urls.py @@ -44,6 +44,7 @@ urlpatterns = [ path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')), path("GestionNotification/", include(("GestionNotification.urls", 'GestionNotification'), namespace='GestionNotification')), path("School/", include(("School.urls", 'School'), namespace='School')), + path("DocuSeal/", include(("DocuSeal.urls", 'DocuSeal'), namespace='DocuSeal')), # Documentation Api re_path(r'^swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), diff --git a/Front-End/next.config.mjs b/Front-End/next.config.mjs index fe57b5f..bc67c6e 100644 --- a/Front-End/next.config.mjs +++ b/Front-End/next.config.mjs @@ -25,6 +25,18 @@ const nextConfig = { AUTH_SECRET: process.env.AUTH_SECRET || 'false', NEXTAUTH_URL: process.env.NEXTAUTH_URL || "http://localhost:3000", }, + async rewrites() { + return [ + { + source: '/api/documents/:path*', + destination: 'https://api.docuseal.com/v1/documents/:path*', + }, + { + source: '/api/auth/:path*', + destination: '/api/auth/:path*', // Exclure les routes NextAuth des réécritures de proxy + }, + ]; + } }; export default withNextIntl(nextConfig); \ No newline at end of file diff --git a/Front-End/package-lock.json b/Front-End/package-lock.json index 4a9262e..6ab1776 100644 --- a/Front-End/package-lock.json +++ b/Front-End/package-lock.json @@ -8,6 +8,7 @@ "name": "n3wt-school-front-end", "version": "0.0.1", "dependencies": { + "@docuseal/react": "^1.0.56", "@radix-ui/react-dialog": "^1.1.2", "@tailwindcss/forms": "^0.5.9", "date-fns": "^4.1.0", @@ -201,6 +202,12 @@ "kuler": "^2.0.0" } }, + "node_modules/@docuseal/react": { + "version": "1.0.56", + "resolved": "https://registry.npmjs.org/@docuseal/react/-/react-1.0.56.tgz", + "integrity": "sha512-xna62Op4WLIVmgz2U0mi4paFayslxBUk2P8u3D70e1JgVRXsPFwzH6b1WhotedN9PMPS+cG2HP1PmpYoEzdZTQ==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, diff --git a/Front-End/package.json b/Front-End/package.json index 9677262..e05b435 100644 --- a/Front-End/package.json +++ b/Front-End/package.json @@ -10,6 +10,7 @@ "check-strings": "node scripts/check-hardcoded-strings.js" }, "dependencies": { + "@docuseal/react": "^1.0.56", "@radix-ui/react-dialog": "^1.1.2", "@tailwindcss/forms": "^0.5.9", "date-fns": "^4.1.0", diff --git a/Front-End/src/components/InputPhone.js b/Front-End/src/components/InputPhone.js index 4f11a1a..f083008 100644 --- a/Front-End/src/components/InputPhone.js +++ b/Front-End/src/components/InputPhone.js @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; -export default function InputPhone({ name, label, value, onChange, errorMsg, placeholder, className }) { +export default function InputPhone({ name, label, value, onChange, errorMsg, placeholder, className, required }) { const inputRef = useRef(null); useEffect(() => { @@ -18,7 +18,10 @@ export default function InputPhone({ name, label, value, onChange, errorMsg, pla return ( <>
- +
{ @@ -185,6 +182,21 @@ export default function InscriptionFormShared({ return errors?.student?.[field]?.[0]; }; + const handleNextPage = () => { + setCurrentPage(currentPage + 1); + }; + + const handlePreviousPage = () => { + setCurrentPage(currentPage - 1); + }; + + const requiredFileTemplates = fileTemplates.filter(template => template.is_required); + + // Ajout des logs pour débogage + console.log('BASE_URL:', BASE_URL); + console.log('requiredFileTemplates:', requiredFileTemplates); + console.log('currentPage:', currentPage); + // Configuration des colonnes pour le tableau des fichiers const columns = [ { name: 'Nom du fichier', transform: (row) => row.name }, @@ -248,128 +260,54 @@ export default function InscriptionFormShared({
- {/* Section Élève */} -
-

Informations de l'élève

-
- updateFormField('last_name', e.target.value)} - required - errorMsg={getError('last_name')} - /> - updateFormField('first_name', e.target.value)} - errorMsg={getError('first_name')} - required - /> - updateFormField('nationality', e.target.value)} - /> - updateFormField('birth_date', e.target.value)} - required - errorMsg={getError('birth_date')} - /> - updateFormField('birth_place', e.target.value)} - errorMsg={getError('birth_place')} - /> - updateFormField('birth_postal_code', e.target.value)} - required - errorMsg={getError('birth_postal_code')} - /> -
- updateFormField('address', e.target.value)} - errorMsg={getError('address')} - /> -
- updateFormField('attending_physician', e.target.value)} - errorMsg={getError('attending_physician')} - /> - updateFormField('level', e.target.value)} - choices={levels} - required - errorMsg={getError('level')} - /> -
-
- - {/* Section Responsables */} -
-

Responsables

- { - const updatedGuardians = guardians.map(resp => - resp.id === id ? { ...resp, [field]: value } : resp - ); - setGuardians(updatedGuardians); - }} - addGuardian={(e) => { - e.preventDefault(); - setGuardians([...guardians, { id: Date.now() }]); - }} - deleteGuardian={(index) => { - const newArray = [...guardians]; - newArray.splice(index, 1); - setGuardians(newArray); - }} - errors={errors?.student?.guardians || []} + setGuardians={setGuardians} + errors={errors} /> -
+ )} - {/* Section Fichiers d'inscription */} - {fileTemplates.length > 0 && ( + {/* Pages suivantes : Section Fichiers d'inscription */} + {currentPage > 1 && currentPage <= requiredFileTemplates.length + 1 && (
-

Fichiers à remplir

- {}} - /> +

{requiredFileTemplates[currentPage - 2].name}

+ )} + {/* Dernière page : Section Fichiers parents */} + {currentPage === requiredFileTemplates.length + 2 && ( + <> + !template.is_required)} + columns={columns} + /> + + )} + {/* Boutons de contrôle */}
-
{fileTemplates.length > 0 && ( diff --git a/Front-End/src/components/Inscription/ResponsableInputFields.js b/Front-End/src/components/Inscription/ResponsableInputFields.js index 4cdb49b..e4cbc52 100644 --- a/Front-End/src/components/Inscription/ResponsableInputFields.js +++ b/Front-End/src/components/Inscription/ResponsableInputFields.js @@ -58,6 +58,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad label={t('email')} value={item.email} onChange={(event) => {onGuardiansChange(item.id, "email", event.target.value)}} + required errorMsg={getError(index, 'email')} /> {onGuardiansChange(item.id, "phone", event)}} + required errorMsg={getError(index, 'phone')} /> @@ -76,6 +78,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad label={t('birthdate')} value={item.birth_date} onChange={(event) => {onGuardiansChange(item.id, "birth_date", event.target.value)}} + required errorMsg={getError(index, 'birth_date')} /> {onGuardiansChange(item.id, "profession", event.target.value)}} + required errorMsg={getError(index, 'profession')} /> @@ -95,6 +99,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad label={t('address')} value={item.address} onChange={(event) => {onGuardiansChange(item.id, "address", event.target.value)}} + required errorMsg={getError(index, 'address')} /> diff --git a/Front-End/src/components/SelectChoice.js b/Front-End/src/components/SelectChoice.js index b935496..6618396 100644 --- a/Front-End/src/components/SelectChoice.js +++ b/Front-End/src/components/SelectChoice.js @@ -1,34 +1,36 @@ export default function SelectChoice({ type, name, label, required, placeHolder, choices, callback, selected, errorMsg, IconItem, disabled = false }) { return ( <> - -
- {IconItem && - - {} - - } - +
+ +
+ {IconItem && + + {} + + } + +
+ {errorMsg &&

{errorMsg}

}
- {errorMsg &&

{errorMsg}

} ); } \ No newline at end of file diff --git a/Front-End/src/components/Structure/Files/FilesManagement.js b/Front-End/src/components/Structure/Files/FilesManagement.js index d3835bd..204186c 100644 --- a/Front-End/src/components/Structure/Files/FilesManagement.js +++ b/Front-End/src/components/Structure/Files/FilesManagement.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Plus, Download, Edit, Trash2, FolderPlus } from 'lucide-react'; +import { Plus, Download, Edit, Trash2, FolderPlus, Signature } from 'lucide-react'; import Modal from '@/components/Modal'; import Table from '@/components/Table'; import FileUpload from '@/components/FileUpload'; @@ -29,6 +29,8 @@ export default function FilesManagement({ csrfToken }) { const [fileToEdit, setFileToEdit] = useState(null); const [isGroupModalOpen, setIsGroupModalOpen] = useState(false); const [groupToEdit, setGroupToEdit] = useState(null); + const [token, setToken] = useState(null); + const [selectedFile, setSelectedFile] = useState(null); // Fonction pour transformer les données des fichiers avec les informations complètes du groupe const transformFileData = (file, groups) => { @@ -204,6 +206,9 @@ export default function FilesManagement({ csrfToken }) { +
)} ]; @@ -223,6 +228,28 @@ export default function FilesManagement({ csrfToken }) { )} ]; + // Fonction pour gérer la demande de signature + const handleSignatureRequest = (file) => { + fetch('http://localhost:8080/DocuSeal/generateToken', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + document_id: file.id, + user_email: 'anthony.casini.30@gmail.com', + url: file.file + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("token received : ", data.token); + setToken(data.token); + setSelectedFile(file); + }) + .catch((error) => console.error(error)); + }; + return (
)} + {token && selectedFile && ( + + )} ); } diff --git a/docker-compose.yml b/docker-compose.yml index e3fb1d5..770841e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,15 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: school TZ: Europe/Paris + + docuseal: + image: docuseal/docuseal:latest + depends_on: + - database + ports: + - 3001:3000 + environment: + - DATABASE_URL=postgresql://postgres:postgres@database:5432/docuseal backend: build: @@ -37,19 +46,20 @@ services: depends_on: - redis - database + - docuseal command: python start.py - frontend: - build: - context: ./Front-End - args: - - BUILD_MODE=development - ports: - - 3000:3000 - volumes: - - ./Front-End:/app - environment: - - TZ=Europe/Paris - depends_on: - - backend + # frontend: + # build: + # context: ./Front-End + # args: + # - BUILD_MODE=development + # ports: + # - 3000:3000 + # volumes: + # - ./Front-End:/app + # environment: + # - TZ=Europe/Paris + # depends_on: + # - backend