mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
Merge pull request 'feat-17-Messagerie_WIP' (#58) from feat-17-Messagerie_WIP into develop
Reviewed-on: https://git.v0id.ovh/n3wt-innov/n3wt-school/pulls/58
This commit is contained in:
@ -0,0 +1 @@
|
||||
cd $(dirname "$0")/../Front-End/ && npm run lint-light
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eslint.workingDirectories": ["./Front-End"]
|
||||
}
|
||||
@ -25,7 +25,7 @@ from django.db.models import Q
|
||||
|
||||
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
||||
from Subscriptions.models import RegistrationForm, Guardian
|
||||
import Subscriptions.mailManager as mailer
|
||||
import N3wtSchool.mailManager as mailer
|
||||
import Subscriptions.util as util
|
||||
import logging
|
||||
from N3wtSchool import bdd, error, settings
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from django.urls import path, re_path
|
||||
from .views import SendEmailView
|
||||
from .views import SendEmailView, search_recipients
|
||||
from GestionMessagerie.views import MessagerieView, MessageView, MessageSimpleView
|
||||
|
||||
urlpatterns = [
|
||||
@ -7,4 +7,5 @@ urlpatterns = [
|
||||
re_path(r'^messages$', MessageView.as_view(), name="messages"),
|
||||
re_path(r'^messages/(?P<id>[0-9]+)$', MessageSimpleView.as_view(), name="messages"),
|
||||
path('send-email/', SendEmailView.as_view(), name='send_email'),
|
||||
path('search-recipients/', search_recipients, name='search_recipients'),
|
||||
]
|
||||
@ -6,12 +6,18 @@ from django.utils.html import strip_tags
|
||||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.db.models import Q
|
||||
from Auth.models import Profile # Assurez-vous que le modèle Profile contient les informations nécessaires
|
||||
|
||||
from .models import *
|
||||
from School.models import Teacher, ProfileRole
|
||||
from Settings.models import SMTPSettings # Assurez-vous que le chemin est correct
|
||||
|
||||
from GestionMessagerie.serializers import MessageSerializer
|
||||
from School.serializers import TeacherSerializer
|
||||
|
||||
from N3wtSchool import bdd
|
||||
import N3wtSchool.mailManager as mailer
|
||||
|
||||
class MessagerieView(APIView):
|
||||
def get(self, request, profile_id):
|
||||
@ -44,23 +50,96 @@ class SendEmailView(APIView):
|
||||
def post(self, request):
|
||||
data = request.data
|
||||
recipients = data.get('recipients', [])
|
||||
cc = data.get('cc', [])
|
||||
bcc = data.get('bcc', [])
|
||||
subject = data.get('subject', 'Notification')
|
||||
message = data.get('message', '')
|
||||
establishment_id = data.get('establishment_id', '')
|
||||
|
||||
if not recipients or not message:
|
||||
return Response({'error': 'Les destinataires et le message sont requis.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
plain_message = strip_tags(message)
|
||||
send_mail(
|
||||
subject,
|
||||
plain_message,
|
||||
settings.EMAIL_HOST_USER,
|
||||
recipients,
|
||||
html_message=message,
|
||||
fail_silently=False,
|
||||
# Récupérer la connexion SMTP
|
||||
connection = mailer.getConnection(establishment_id)
|
||||
|
||||
# Envoyer l'email
|
||||
return mailer.sendMail(
|
||||
subject=subject,
|
||||
message=message,
|
||||
recipients=recipients,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
attachments=[],
|
||||
connection=connection
|
||||
)
|
||||
return Response({'message': 'Email envoyé avec succès.'}, status=status.HTTP_200_OK)
|
||||
except NotFound as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
class ContactsView(APIView):
|
||||
"""
|
||||
API pour récupérer les contacts associés à un établissement.
|
||||
"""
|
||||
def get(self, request, establishment_id):
|
||||
try:
|
||||
# Récupérer les enseignants associés à l'établissement
|
||||
teachers = Teacher.objects.filter(profile_role__establishment_id=establishment_id)
|
||||
teachers_serializer = TeacherSerializer(teachers, many=True)
|
||||
|
||||
# Ajouter un contact pour l'administration
|
||||
admin_contact = {
|
||||
"id": "admin",
|
||||
"name": "Administration",
|
||||
"email": "admin@etablissement.com",
|
||||
"profilePic": "https://www.gravatar.com/avatar/admin"
|
||||
}
|
||||
|
||||
contacts = [admin_contact] + teachers_serializer.data
|
||||
return Response(contacts, status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def search_recipients(request):
|
||||
"""
|
||||
API pour rechercher des destinataires en fonction d'un terme de recherche et d'un établissement.
|
||||
"""
|
||||
query = request.GET.get('q', '').strip() # Récupérer le terme de recherche depuis les paramètres GET
|
||||
establishment_id = request.GET.get('establishment_id', None) # Récupérer l'ID de l'établissement
|
||||
|
||||
if not query:
|
||||
return JsonResponse([], safe=False) # Retourner une liste vide si aucun terme n'est fourni
|
||||
|
||||
if not establishment_id:
|
||||
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Rechercher dans les champs pertinents (nom, prénom, email) et filtrer par establishment_id
|
||||
profiles = Profile.objects.filter(
|
||||
Q(first_name__icontains=query) |
|
||||
Q(last_name__icontains=query) |
|
||||
Q(email__icontains=query),
|
||||
roles__establishment_id=establishment_id, # Utiliser 'roles' au lieu de 'profilerole'
|
||||
roles__is_active=True # Filtrer uniquement les ProfileRole actifs
|
||||
).distinct()
|
||||
|
||||
# Construire la réponse avec les rôles associés
|
||||
results = []
|
||||
for profile in profiles:
|
||||
profile_roles = ProfileRole.objects.filter(
|
||||
profile=profile,
|
||||
establishment_id=establishment_id,
|
||||
is_active=True # Inclure uniquement les ProfileRole actifs
|
||||
).values(
|
||||
'id', 'role_type', 'establishment__name', 'is_active'
|
||||
)
|
||||
results.append({
|
||||
'id': profile.id,
|
||||
'first_name': profile.first_name,
|
||||
'last_name': profile.last_name,
|
||||
'email': profile.email,
|
||||
'roles': list(profile_roles) # Inclure tous les rôles actifs associés pour cet établissement
|
||||
})
|
||||
|
||||
return JsonResponse(results, safe=False)
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"hostSMTP": "",
|
||||
"portSMTP": 25,
|
||||
"username": "",
|
||||
"password": "",
|
||||
"useSSL": false,
|
||||
"useTLS": false
|
||||
}
|
||||
@ -1,8 +1,67 @@
|
||||
from django.core.mail import send_mail, EmailMultiAlternatives, EmailMessage
|
||||
from django.core.mail import send_mail, get_connection, EmailMultiAlternatives, EmailMessage
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
from django.conf import settings
|
||||
import re
|
||||
from N3wtSchool import settings
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import NotFound
|
||||
from Settings.models import SMTPSettings
|
||||
from Establishment.models import Establishment # Importer le modèle Establishment
|
||||
|
||||
def getConnection(id_establishement):
|
||||
try:
|
||||
# Récupérer l'instance de l'établissement
|
||||
establishment = Establishment.objects.get(id=id_establishement)
|
||||
|
||||
# Récupérer les paramètres SMTP associés à l'établissement
|
||||
smtp_settings = SMTPSettings.objects.get(establishment=establishment)
|
||||
|
||||
# Créer une connexion SMTP avec les paramètres récupérés
|
||||
connection = get_connection(
|
||||
host=smtp_settings.smtp_server,
|
||||
port=smtp_settings.smtp_port,
|
||||
username=smtp_settings.smtp_user,
|
||||
password=smtp_settings.smtp_password,
|
||||
use_tls=smtp_settings.use_tls,
|
||||
use_ssl=smtp_settings.use_ssl
|
||||
)
|
||||
return connection
|
||||
|
||||
except Establishment.DoesNotExist:
|
||||
raise NotFound(f"Aucun établissement trouvé avec l'ID {id_establishement}")
|
||||
except SMTPSettings.DoesNotExist:
|
||||
raise NotFound(f"Aucun paramètre SMTP trouvé pour l'établissement {id_establishement}")
|
||||
|
||||
|
||||
def sendMail(subject, message, recipients, cc=[], bcc=[], attachments=[], connection=None):
|
||||
try:
|
||||
plain_message = strip_tags(message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
if connection is not None:
|
||||
from_email = connection.username
|
||||
|
||||
email = EmailMultiAlternatives(
|
||||
subject=subject,
|
||||
body=plain_message,
|
||||
from_email=from_email,
|
||||
to=recipients,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
connection=connection
|
||||
)
|
||||
email.attach_alternative(message, "text/html")
|
||||
|
||||
# Ajout des pièces jointes
|
||||
for attachment in attachments:
|
||||
# attachment doit être un tuple (filename, content, mimetype)
|
||||
# ex: ("document.pdf", fichier.read(), "application/pdf")
|
||||
email.attach(*attachment)
|
||||
|
||||
email.send(fail_silently=False)
|
||||
return Response({'message': 'Email envoyé avec succès.'}, status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def envoieReinitMotDePasse(recipients, code):
|
||||
errorMessage = ''
|
||||
@ -14,9 +73,8 @@ def envoieReinitMotDePasse(recipients, code):
|
||||
}
|
||||
subject = EMAIL_REINIT_SUBJECT
|
||||
html_message = render_to_string('emails/resetPassword.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
send_mail(subject, plain_message, from_email, [recipients], html_message=html_message)
|
||||
sendMail(subject, html_message, recipients)
|
||||
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
@ -36,10 +94,8 @@ def sendRegisterForm(recipients, establishment_id):
|
||||
|
||||
subject = EMAIL_INSCRIPTION_SUBJECT
|
||||
html_message = render_to_string('emails/inscription.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
sendMail(subject, html_message, recipients)
|
||||
|
||||
send_mail(subject, plain_message, from_email, [recipients], html_message=html_message)
|
||||
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
@ -59,10 +115,7 @@ def sendMandatSEPA(recipients, establishment_id):
|
||||
|
||||
subject = EMAIL_INSCRIPTION_SUBJECT
|
||||
html_message = render_to_string('emails/sepa.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
|
||||
send_mail(subject, plain_message, from_email, [recipients], html_message=html_message)
|
||||
sendMail(subject, html_message, recipients)
|
||||
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
@ -74,13 +127,8 @@ def envoieRelanceDossierInscription(recipients, code):
|
||||
EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : ' + BASE_URL + '/users/login\nCordialement'
|
||||
errorMessage = ''
|
||||
try:
|
||||
send_mail(
|
||||
EMAIL_RELANCE_SUBJECT,
|
||||
EMAIL_RELANCE_CORPUS%str(code),
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
sendMail(EMAIL_RELANCE_SUBJECT, EMAIL_RELANCE_CORPUS%str(code), recipients)
|
||||
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
@ -14,6 +14,10 @@ from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
# Configuration du logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
@ -219,23 +223,29 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
#################### Application Settings ##############################
|
||||
########################################################################
|
||||
|
||||
with open('Subscriptions/Configuration/application.json', 'r') as f:
|
||||
jsonObject = json.load(f)
|
||||
|
||||
|
||||
DJANGO_SUPERUSER_PASSWORD='admin'
|
||||
DJANGO_SUPERUSER_USERNAME='admin'
|
||||
DJANGO_SUPERUSER_EMAIL='admin@n3wtschool.com'
|
||||
# Configuration de l'email de l'application
|
||||
smtp_config_file = 'N3wtSchool/Configuration/application.json'
|
||||
|
||||
EMAIL_HOST='smtp.gmail.com'
|
||||
EMAIL_PORT=587
|
||||
EMAIL_HOST_USER=jsonObject['mailFrom']
|
||||
EMAIL_HOST_PASSWORD=jsonObject['password']
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_USE_TLS = True
|
||||
EMAIL_USE_SSL = False
|
||||
|
||||
|
||||
|
||||
if os.path.exists(smtp_config_file):
|
||||
try:
|
||||
with open(smtp_config_file, 'r') as f:
|
||||
smtpSettings = json.load(f)
|
||||
EMAIL_HOST = smtpSettings.get('hostSMTP', '')
|
||||
EMAIL_PORT = smtpSettings.get('portSMTP', 587)
|
||||
EMAIL_HOST_USER = smtpSettings.get('username', '')
|
||||
EMAIL_HOST_PASSWORD = smtpSettings.get('password', '')
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_USE_TLS = smtpSettings.get('useTLS', True)
|
||||
EMAIL_USE_SSL = smtpSettings.get('useSSL', False)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la lecture du fichier de configuration SMTP : {e}")
|
||||
else:
|
||||
logger.error(f"Fichier de configuration SMTP introuvable : {smtp_config_file}")
|
||||
|
||||
DOCUMENT_DIR = 'documents'
|
||||
|
||||
|
||||
@ -12,25 +12,51 @@ class SMTPSettingsView(APIView):
|
||||
"""
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Récupérer les paramètres SMTP",
|
||||
operation_description="Récupérer les paramètres SMTP pour un établissement spécifique ou tous les paramètres si aucun ID n'est fourni",
|
||||
manual_parameters=[
|
||||
openapi.Parameter(
|
||||
'establishment_id',
|
||||
openapi.IN_QUERY,
|
||||
description="ID de l'établissement (facultatif)",
|
||||
type=openapi.TYPE_INTEGER,
|
||||
required=False
|
||||
)
|
||||
],
|
||||
responses={
|
||||
200: SMTPSettingsSerializer(),
|
||||
200: SMTPSettingsSerializer(many=True),
|
||||
404: openapi.Response(description="Aucun paramètre SMTP trouvé."),
|
||||
500: openapi.Response(description="Erreur interne du serveur."),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
establishment_id = request.query_params.get('establishment_id')
|
||||
|
||||
try:
|
||||
smtp_settings = SMTPSettings.objects.first()
|
||||
if establishment_id:
|
||||
# Récupérer les paramètres SMTP pour un établissement spécifique
|
||||
smtp_settings = SMTPSettings.objects.filter(establishment_id=establishment_id).first()
|
||||
if not smtp_settings:
|
||||
return Response({'error': 'Aucun paramètre SMTP trouvé.'}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response(
|
||||
{'error': f"Aucun paramètre SMTP trouvé pour l'établissement {establishment_id}."},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
serializer = SMTPSettingsSerializer(smtp_settings)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
else:
|
||||
# Récupérer tous les paramètres SMTP
|
||||
smtp_settings = SMTPSettings.objects.all()
|
||||
if not smtp_settings.exists():
|
||||
return Response(
|
||||
{'error': "Aucun paramètre SMTP trouvé."},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
serializer = SMTPSettingsSerializer(smtp_settings, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Créer ou mettre à jour les paramètres SMTP",
|
||||
operation_description="Créer ou mettre à jour les paramètres SMTP pour un établissement spécifique",
|
||||
request_body=SMTPSettingsSerializer,
|
||||
responses={
|
||||
200: SMTPSettingsSerializer(),
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"mailFrom":"",
|
||||
"password":""
|
||||
}
|
||||
@ -11,7 +11,7 @@ import json
|
||||
import os
|
||||
from django.core.files import File
|
||||
|
||||
import Subscriptions.mailManager as mailer
|
||||
import N3wtSchool.mailManager as mailer
|
||||
import Subscriptions.util as util
|
||||
|
||||
from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer
|
||||
@ -519,4 +519,3 @@ def get_parent_file_templates_by_rf(request, id):
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
except RegistrationParentFileTemplate.DoesNotExist:
|
||||
return JsonResponse({'error': 'Aucune pièce à fournir trouvée pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
4
Front-End/.babelrc
Normal file
4
Front-End/.babelrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["next/babel"],
|
||||
"plugins": []
|
||||
}
|
||||
3
Front-End/.eslintignore
Normal file
3
Front-End/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
build/
|
||||
public/
|
||||
@ -1,3 +1,10 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
"extends": ["next", "next/core-web-vitals"],
|
||||
"rules": {
|
||||
// Ajoutez vos règles personnalisées ici
|
||||
"react/react-in-jsx-scope": "off", // Désactive l'obligation d'importer React
|
||||
"no-console": "error", // Avertissement pour les console.log
|
||||
"semi": ["error", "always"], // Exige un point-virgule à la fin des lignes
|
||||
"quotes": ["error", "single", { "avoidEscape": true }] // Exige des guillemets simples, sauf si l'on utilise des guillemets doubles à l'intérieur
|
||||
}
|
||||
}
|
||||
|
||||
1958
Front-End/package-lock.json
generated
1958
Front-End/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,13 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint-light": "next lint --quiet",
|
||||
"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",
|
||||
"@tinymce/tinymce-react": "^6.1.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^11.11.11",
|
||||
"ics": "^3.8.1",
|
||||
@ -31,11 +31,10 @@
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18",
|
||||
"react-international-phone": "^4.5.0",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-tooltip": "^5.28.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "^7.26.2",
|
||||
"@babel/traverse": "^7.25.9",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.11",
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
||||
import SelectChoice from '@/components/SelectChoice';
|
||||
import Button from '@/components/Button';
|
||||
import Table from '@/components/Table';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function Page() {
|
||||
const [formData, setFormData] = useState({
|
||||
@ -132,7 +133,7 @@ export default function Page() {
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
text="Enregistrer"
|
||||
onClick={() => console.log('FormData:', formData)}
|
||||
onClick={() => logger.debug('FormData:', formData)}
|
||||
primary
|
||||
className="bg-emerald-500 text-white hover:bg-emerald-600"
|
||||
/>
|
||||
|
||||
@ -151,7 +151,7 @@ export default function Layout({ children }) {
|
||||
return (
|
||||
<ProtectedRoute requiredRight={[RIGHTS.ADMIN, RIGHTS.TEACHER]}>
|
||||
{/* Topbar */}
|
||||
<header className="absolute top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 px-4 md:px-8 flex items-center justify-between z-10 box-border">
|
||||
<header className="absolute top-0 left-64 right-0 h-16 bg-white border-b border-gray-200 px-4 md:px-8 flex items-center justify-between z-10 box-border">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="mr-4 md:hidden text-gray-600 hover:text-gray-900"
|
||||
@ -180,7 +180,7 @@ export default function Layout({ children }) {
|
||||
|
||||
{/* Sidebar */}
|
||||
<div
|
||||
className={`absolute top-16 bottom-16 left-0 z-30 w-64 bg-white border-r border-gray-200 box-border ${
|
||||
className={`absolute top-0 bottom-0 left-0 z-30 w-64 bg-white border-r border-gray-200 box-border ${
|
||||
isSidebarOpen ? 'block' : 'hidden md:block'
|
||||
}`}
|
||||
>
|
||||
|
||||
@ -1,10 +1,36 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import SidebarTabs from '@/components/SidebarTabs';
|
||||
import EmailSender from '@/components/Admin/EmailSender';
|
||||
import InstantMessaging from '@/components/Admin/InstantMessaging';
|
||||
import AnnouncementScheduler from '@/components/Admin/AnnouncementScheduler';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function MessageriePage({ csrfToken }) {
|
||||
const tabs = [
|
||||
{
|
||||
id: 'email',
|
||||
label: 'Envoyer un Mail',
|
||||
content: <EmailSender csrfToken={csrfToken} />,
|
||||
},
|
||||
{
|
||||
id: 'instant',
|
||||
label: 'Messagerie Instantanée',
|
||||
content: <InstantMessaging csrfToken={csrfToken} />,
|
||||
},
|
||||
{
|
||||
id: 'announcement',
|
||||
label: 'Planifier une Annonce',
|
||||
content: <AnnouncementScheduler csrfToken={csrfToken} />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-6">Messagerie Admin</h1>
|
||||
<EmailSender csrfToken={csrfToken} />
|
||||
<div className="flex h-full w-full">
|
||||
<SidebarTabs
|
||||
tabs={tabs}
|
||||
onTabChange={(tabId) => logger.debug(`Onglet actif : ${tabId}`)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import Tab from '@/components/Tab';
|
||||
import TabContent from '@/components/TabContent';
|
||||
import Button from '@/components/Button';
|
||||
import InputText from '@/components/InputText';
|
||||
import CheckBox from '@/components/CheckBox'; // Import du composant CheckBox
|
||||
import logger from '@/utils/logger';
|
||||
import {
|
||||
fetchSmtpSettings,
|
||||
@ -12,6 +13,7 @@ import {
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
import { useCsrfToken } from '@/context/CsrfContext'; // Import du hook pour récupérer le csrfToken
|
||||
import { useNotification } from '@/context/NotificationContext';
|
||||
import { useSearchParams } from 'next/navigation'; // Ajoute cet import
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [activeTab, setActiveTab] = useState('structure');
|
||||
@ -24,18 +26,29 @@ export default function SettingsPage() {
|
||||
const [smtpPassword, setSmtpPassword] = useState('');
|
||||
const [useTls, setUseTls] = useState(true);
|
||||
const [useSsl, setUseSsl] = useState(false);
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
const csrfToken = useCsrfToken(); // Récupération du csrfToken
|
||||
const { showNotification } = useNotification();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const handleTabClick = (tab) => {
|
||||
setActiveTab(tab);
|
||||
};
|
||||
|
||||
// Ajout : sélection automatique de l'onglet via l'ancre ou le paramètre de recherche
|
||||
useEffect(() => {
|
||||
const tabParam = searchParams.get('tab');
|
||||
if (tabParam === 'smtp') {
|
||||
setActiveTab('smtp');
|
||||
} else if (tabParam === 'structure') {
|
||||
setActiveTab('structure');
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
// Charger les paramètres SMTP existants
|
||||
useEffect(() => {
|
||||
if (activeTab === 'smtp') {
|
||||
fetchSmtpSettings(csrfToken) // Passer le csrfToken ici
|
||||
fetchSmtpSettings(csrfToken, selectedEstablishmentId) // Passer le csrfToken ici
|
||||
.then((data) => {
|
||||
setSmtpServer(data.smtp_server || '');
|
||||
setSmtpPort(data.smtp_port || '');
|
||||
@ -45,8 +58,23 @@ export default function SettingsPage() {
|
||||
setUseSsl(data.use_ssl || false);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors du chargement des paramètres SMTP:', error);
|
||||
setStatusMessage('Erreur lors du chargement des paramètres SMTP.');
|
||||
if (error.response && error.response.status === 404) {
|
||||
showNotification(
|
||||
"Les données SMTP n'ont pas été trouvées.",
|
||||
'warning',
|
||||
'Attention'
|
||||
);
|
||||
} else {
|
||||
logger.error(
|
||||
'Erreur lors du chargement des paramètres SMTP:',
|
||||
error
|
||||
);
|
||||
showNotification(
|
||||
'Erreur lors du chargement des paramètres SMTP.',
|
||||
'error',
|
||||
'Erreur'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [activeTab, csrfToken]); // Ajouter csrfToken comme dépendance
|
||||
@ -113,7 +141,11 @@ export default function SettingsPage() {
|
||||
|
||||
editSmtpSettings(smtpData, csrfToken) // Passer le csrfToken ici
|
||||
.then(() => {
|
||||
setStatusMessage('Paramètres SMTP mis à jour avec succès.');
|
||||
showNotification(
|
||||
'Paramètres SMTP mis à jour avec succès.',
|
||||
'success',
|
||||
'Succès'
|
||||
);
|
||||
logger.debug('SMTP Settings Updated:', smtpData);
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -121,7 +153,11 @@ export default function SettingsPage() {
|
||||
'Erreur lors de la mise à jour des paramètres SMTP:',
|
||||
error
|
||||
);
|
||||
setStatusMessage('Erreur lors de la mise à jour des paramètres SMTP.');
|
||||
showNotification(
|
||||
'Erreur lors de la mise à jour des paramètres SMTP.',
|
||||
'error',
|
||||
'Erreur'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -164,6 +200,7 @@ export default function SettingsPage() {
|
||||
</TabContent>
|
||||
<TabContent isActive={activeTab === 'smtp'}>
|
||||
<form onSubmit={handleSmtpSubmit}>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<InputText
|
||||
label="Serveur SMTP"
|
||||
value={smtpServer}
|
||||
@ -185,27 +222,32 @@ export default function SettingsPage() {
|
||||
value={smtpPassword}
|
||||
onChange={handleSmtpPasswordChange}
|
||||
/>
|
||||
<div className="flex items-center space-x-4">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useTls}
|
||||
onChange={handleUseTlsChange}
|
||||
/>
|
||||
Utiliser TLS
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useSsl}
|
||||
onChange={handleUseSslChange}
|
||||
/>
|
||||
Utiliser SSL
|
||||
</label>
|
||||
</div>
|
||||
<Button type="submit" primary text="Mettre à jour"></Button>
|
||||
<div className="mt-6 border-t pt-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
<CheckBox
|
||||
item={{ id: 'useTls' }}
|
||||
formData={{ useTls }}
|
||||
handleChange={() => setUseTls((prev) => !prev)} // Inverser la valeur booléenne
|
||||
fieldName="useTls"
|
||||
itemLabelFunc={() => 'Utiliser TLS'}
|
||||
/>
|
||||
<CheckBox
|
||||
item={{ id: 'useSsl' }}
|
||||
formData={{ useSsl }}
|
||||
handleChange={() => setUseSsl((prev) => !prev)} // Inverser la valeur booléenne
|
||||
fieldName="useSsl"
|
||||
itemLabelFunc={() => 'Utiliser SSL'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
primary
|
||||
text="Mettre à jour"
|
||||
className="mt-6"
|
||||
></Button>
|
||||
</form>
|
||||
{statusMessage && <p className="mt-4 text-sm">{statusMessage}</p>}
|
||||
</TabContent>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -151,7 +151,7 @@ export default function Page() {
|
||||
|
||||
// Envoyer les absences modifiées à une API
|
||||
absencesToUpdate.forEach(([studentId, absenceData]) => {
|
||||
console.log('Modification absence élève : ', studentId);
|
||||
logger.debug('Modification absence élève : ', studentId);
|
||||
saveAbsence(studentId, absenceData);
|
||||
});
|
||||
|
||||
@ -203,7 +203,7 @@ export default function Page() {
|
||||
// Appeler la fonction pour supprimer l'absence
|
||||
deleteAbsences(existingAbsence.id, csrfToken)
|
||||
.then(() => {
|
||||
console.log(
|
||||
logger.debug(
|
||||
`Absence pour l'élève ${studentId} supprimée avec succès.`
|
||||
);
|
||||
// Mettre à jour les absences récupérées
|
||||
@ -214,7 +214,7 @@ export default function Page() {
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
logger.error(
|
||||
`Erreur lors de la suppression de l'absence pour l'élève ${studentId}:`,
|
||||
error
|
||||
);
|
||||
@ -235,7 +235,7 @@ export default function Page() {
|
||||
|
||||
const saveAbsence = (studentId, absenceData) => {
|
||||
if (!absenceData.reason || !studentId || !absenceData.moment) {
|
||||
console.error('Tous les champs requis doivent être fournis.');
|
||||
logger.error('Tous les champs requis doivent être fournis.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -251,7 +251,7 @@ export default function Page() {
|
||||
// Modifier une absence existante
|
||||
editAbsences(absenceData.id, payload, csrfToken)
|
||||
.then(() => {
|
||||
console.log(
|
||||
logger.debug(
|
||||
`Absence pour l'élève ${studentId} modifiée avec succès.`
|
||||
);
|
||||
// Mettre à jour fetchedAbsences et formAbsences localement
|
||||
@ -265,7 +265,7 @@ export default function Page() {
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
logger.error(
|
||||
`Erreur lors de la modification de l'absence pour l'élève ${studentId}:`,
|
||||
error
|
||||
);
|
||||
@ -274,7 +274,7 @@ export default function Page() {
|
||||
// Créer une nouvelle absence
|
||||
createAbsences(payload, csrfToken)
|
||||
.then((response) => {
|
||||
console.log(`Absence pour l'élève ${studentId} créée avec succès.`);
|
||||
logger.debug(`Absence pour l'élève ${studentId} créée avec succès.`);
|
||||
// Mettre à jour fetchedAbsences et formAbsences localement
|
||||
setFetchedAbsences((prev) => ({
|
||||
...prev,
|
||||
@ -286,7 +286,7 @@ export default function Page() {
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
logger.error(
|
||||
`Erreur lors de la création de l'absence pour l'élève ${studentId}:`,
|
||||
error
|
||||
);
|
||||
|
||||
@ -385,14 +385,14 @@ export default function CreateSubscriptionPage() {
|
||||
const guardians = (() => {
|
||||
if (formDataRef.current.selectedGuardians.length > 0) {
|
||||
// Cas 3 : Des guardians sont sélectionnés
|
||||
console.log('Cas 3 : Des guardians sont sélectionnés');
|
||||
logger.debug('Cas 3 : Des guardians sont sélectionnés');
|
||||
return formDataRef.current.selectedGuardians.map((guardianId) => ({
|
||||
id: guardianId,
|
||||
}));
|
||||
} else if (formDataRef.current.isExistingParentProfile) {
|
||||
if (initialGuardianEmail !== existingProfile?.email) {
|
||||
// Cas 2 : Profil existant différent de l'ancien
|
||||
console.log(
|
||||
logger.debug(
|
||||
"Cas 2 : Profil existant différent de l'ancien, mise à jour du profil",
|
||||
{
|
||||
existingProfile,
|
||||
@ -415,14 +415,14 @@ export default function CreateSubscriptionPage() {
|
||||
];
|
||||
} else {
|
||||
// Cas 4 : Profil existant avec le même email
|
||||
console.log('Cas 4 : Profil existant avec le même email', {
|
||||
logger.debug('Cas 4 : Profil existant avec le même email', {
|
||||
existingProfile,
|
||||
});
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
// Cas 1 : Profil inexistant
|
||||
console.log("Cas 1 : Profil inexistant, création d'un nouveau profil");
|
||||
logger.debug("Cas 1 : Profil inexistant, création d'un nouveau profil");
|
||||
return [
|
||||
{
|
||||
profile_role_data: {
|
||||
@ -444,7 +444,7 @@ export default function CreateSubscriptionPage() {
|
||||
}
|
||||
})();
|
||||
|
||||
console.log('test : ', guardians);
|
||||
logger.debug('test : ', guardians);
|
||||
|
||||
const data = {
|
||||
student: {
|
||||
@ -763,10 +763,12 @@ export default function CreateSubscriptionPage() {
|
||||
<div className="mx-auto p-12 space-y-12">
|
||||
{registerFormID ? (
|
||||
<h1 className="text-2xl font-bold">
|
||||
Modifier un dossier d'inscription
|
||||
Modifier un dossier d'inscription
|
||||
</h1>
|
||||
) : (
|
||||
<h1 className="text-2xl font-bold">Créer un dossier d'inscription</h1>
|
||||
<h1 className="text-2xl font-bold">
|
||||
Créer un dossier d'inscription
|
||||
</h1>
|
||||
)}
|
||||
|
||||
{/* Sélection de l'année scolaire */}
|
||||
@ -1047,7 +1049,7 @@ export default function CreateSubscriptionPage() {
|
||||
{/* Montant total */}
|
||||
<div className="flex items-center justify-between bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-300 mt-4">
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
Montant total des frais d'inscription :
|
||||
Montant total des frais d'inscription :
|
||||
</span>
|
||||
<span className="text-lg font-semibold text-gray-800">
|
||||
{totalRegistrationAmount} €
|
||||
|
||||
@ -321,7 +321,7 @@ export default function Page({ params: { locale } }) {
|
||||
.then((data) => {
|
||||
logger.debug('Success:', data);
|
||||
setPopupMessage(
|
||||
`Le dossier d'inscription a été correctement archivé`
|
||||
"Le dossier d'inscription a été correctement archivé"
|
||||
);
|
||||
setPopupVisible(true);
|
||||
setRegistrationForms(
|
||||
@ -332,7 +332,7 @@ export default function Page({ params: { locale } }) {
|
||||
.catch((error) => {
|
||||
logger.error('Error archiving data:', error);
|
||||
setPopupMessage(
|
||||
`Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur.`
|
||||
"Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur."
|
||||
);
|
||||
setPopupVisible(true);
|
||||
});
|
||||
@ -349,14 +349,14 @@ export default function Page({ params: { locale } }) {
|
||||
sendRegisterForm(id)
|
||||
.then((data) => {
|
||||
logger.debug('Success:', data);
|
||||
setPopupMessage(`Le dossier d'inscription a été envoyé avec succès`);
|
||||
setPopupMessage("Le dossier d'inscription a été envoyé avec succès");
|
||||
setPopupVisible(true);
|
||||
setReloadFetch(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Error archiving data:', error);
|
||||
setPopupMessage(
|
||||
`Erreur lors de l'envoi du dossier d'inscription.\nContactez l'administrateur.`
|
||||
"Erreur lors de l'envoi du dossier d'inscription.\nContactez l'administrateur."
|
||||
);
|
||||
setPopupVisible(true);
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { SendHorizontal } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import React from 'react';
|
||||
import Chat from '@/components/Chat';
|
||||
import { getGravatarUrl } from '@/utils/gravatar';
|
||||
|
||||
const contacts = [
|
||||
@ -23,45 +22,7 @@ const contacts = [
|
||||
];
|
||||
|
||||
export default function MessageriePage() {
|
||||
const [selectedContact, setSelectedContact] = useState(null);
|
||||
const [messages, setMessages] = useState({});
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
const handleSendMessage = () => {
|
||||
if (newMessage.trim() && selectedContact) {
|
||||
const contactMessages = messages[selectedContact.id] || [];
|
||||
setMessages({
|
||||
...messages,
|
||||
[selectedContact.id]: [
|
||||
...contactMessages,
|
||||
{
|
||||
id: contactMessages.length + 1,
|
||||
text: newMessage,
|
||||
date: new Date(),
|
||||
},
|
||||
],
|
||||
});
|
||||
setNewMessage('');
|
||||
simulateContactResponse(selectedContact.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const simulateContactResponse = (contactId) => {
|
||||
const simulateResponse = (contactId, setMessages) => {
|
||||
setTimeout(() => {
|
||||
setMessages((prevMessages) => {
|
||||
const contactMessages = prevMessages[contactId] || [];
|
||||
@ -81,79 +42,5 @@ export default function MessageriePage() {
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex" style={{ height: 'calc(100vh - 128px )' }}>
|
||||
{' '}
|
||||
{/* Utilisation de calc pour soustraire la hauteur de l'entête */}
|
||||
<div className="w-1/4 border-r border-gray-200 p-4 overflow-y-auto h-full ">
|
||||
{contacts.map((contact) => (
|
||||
<div
|
||||
key={contact.id}
|
||||
className={`p-2 cursor-pointer ${selectedContact?.id === contact.id ? 'bg-gray-200' : ''}`}
|
||||
onClick={() => setSelectedContact(contact)}
|
||||
>
|
||||
<Image
|
||||
src={contact.profilePic}
|
||||
alt={`${contact.name}'s profile`}
|
||||
className="w-8 h-8 rounded-full inline-block mr-2"
|
||||
width={150}
|
||||
height={150}
|
||||
/>
|
||||
{contact.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col h-full">
|
||||
<div className="flex-1 overflow-y-auto p-4 h-full">
|
||||
{selectedContact &&
|
||||
(messages[selectedContact.id] || []).map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`mb-2 p-2 rounded max-w-xs ${message.isResponse ? 'bg-gray-200 justify-self-end' : 'bg-emerald-200 justify-self-start'}`}
|
||||
style={{
|
||||
borderRadius: message.isResponse
|
||||
? '20px 20px 0 20px'
|
||||
: '20px 20px 20px 0',
|
||||
minWidth: '25%',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center mb-1">
|
||||
<img
|
||||
src={selectedContact.profilePic}
|
||||
alt={`${selectedContact.name}'s profile`}
|
||||
className="w-8 h-8 rounded-full inline-block mr-2"
|
||||
width={150}
|
||||
height={150}
|
||||
/>
|
||||
<span className="text-xs text-gray-600">
|
||||
{selectedContact.name}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400 ml-2">
|
||||
{new Date(message.date).toLocaleTimeString()}
|
||||
</span>
|
||||
</div>
|
||||
{message.text}
|
||||
</div>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 flex">
|
||||
<input
|
||||
type="text"
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
placeholder="Écrire un message..."
|
||||
onKeyDown={handleKeyPress}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
className="p-2 bg-emerald-500 text-white rounded mr-2"
|
||||
>
|
||||
<SendHorizontal />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <Chat contacts={contacts} simulateResponse={simulateResponse} />;
|
||||
}
|
||||
|
||||
31
Front-End/src/app/actions/actionsHandlers.js
Normal file
31
Front-End/src/app/actions/actionsHandlers.js
Normal file
@ -0,0 +1,31 @@
|
||||
import logger from '@/utils/logger';
|
||||
/**
|
||||
*
|
||||
* @param {*} response
|
||||
* @returns
|
||||
*/
|
||||
export const requestResponseHandler = async (response) => {
|
||||
try {
|
||||
const body = await response?.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
error.response = response;
|
||||
throw error;
|
||||
} catch (error) {
|
||||
logger.error('Une erreur est survenue lors du traitement de la réponse', {
|
||||
error,
|
||||
response,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
export const errorHandler = (error) => {
|
||||
logger.error('Error:', { error });
|
||||
// Handle the error here, e.g., show a notification
|
||||
throw error;
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
import { signOut, signIn, getSession } from 'next-auth/react';
|
||||
import { signOut, signIn } from 'next-auth/react';
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
import {
|
||||
BE_AUTH_LOGIN_URL,
|
||||
BE_AUTH_REFRESH_JWT_URL,
|
||||
@ -11,17 +12,6 @@ import {
|
||||
} from '@/utils/Url';
|
||||
import { PARENT_FILTER } from '@/utils/constants';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Login action
|
||||
*/
|
||||
@ -46,7 +36,7 @@ export const getJWT = (data) => {
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
export const refreshJWT = (data) => {
|
||||
const request = new Request(`${BE_AUTH_REFRESH_JWT_URL}`, {
|
||||
@ -57,7 +47,7 @@ export const refreshJWT = (data) => {
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -87,7 +77,9 @@ export const fetchProfileRoles = (
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const updateProfileRoles = (id, data, csrfToken) => {
|
||||
@ -100,7 +92,7 @@ export const updateProfileRoles = (id, data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const deleteProfileRoles = async (id, csrfToken) => {
|
||||
@ -127,7 +119,9 @@ export const deleteProfileRoles = async (id, csrfToken) => {
|
||||
};
|
||||
|
||||
export const fetchProfiles = () => {
|
||||
return fetch(`${BE_AUTH_PROFILES_URL}`).then(requestResponseHandler);
|
||||
return fetch(`${BE_AUTH_PROFILES_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createProfile = (data, csrfToken) => {
|
||||
@ -140,7 +134,7 @@ export const createProfile = (data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const deleteProfile = (id, csrfToken) => {
|
||||
@ -151,7 +145,7 @@ export const deleteProfile = (id, csrfToken) => {
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const updateProfile = (id, data, csrfToken) => {
|
||||
@ -164,7 +158,7 @@ export const updateProfile = (id, data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const sendNewPassword = (data, csrfToken) => {
|
||||
@ -177,7 +171,7 @@ export const sendNewPassword = (data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const subscribe = (data, csrfToken) => {
|
||||
@ -190,7 +184,7 @@ export const subscribe = (data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const resetPassword = (uuid, data, csrfToken) => {
|
||||
@ -203,7 +197,7 @@ export const resetPassword = (uuid, data, csrfToken) => {
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const getResetPassword = (uuid) => {
|
||||
@ -212,5 +206,7 @@ export const getResetPassword = (uuid) => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
@ -1,26 +1,18 @@
|
||||
import {
|
||||
BE_GESTIONMESSAGERIE_MESSAGES_URL,
|
||||
BE_GESTIONMESSAGERIE_SEND_MESSAGE_URL,
|
||||
BE_GESTIONMESSAGERIE_SEARCH_RECIPIENTS_URL,
|
||||
} from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
export const fetchMessages = (id) => {
|
||||
return fetch(`${BE_GESTIONMESSAGERIE_MESSAGES_URL}/${id}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const sendMessage = (data, csrfToken) => {
|
||||
@ -31,5 +23,19 @@ export const sendMessage = (data, csrfToken) => {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const searchRecipients = (establishmentId, query) => {
|
||||
const url = `${BE_GESTIONMESSAGERIE_SEARCH_RECIPIENTS_URL}/?establishment_id=${establishmentId}&q=${encodeURIComponent(query)}`;
|
||||
return fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
@ -1,21 +1,8 @@
|
||||
import { BE_PLANNING_PLANNINGS_URL, BE_PLANNING_EVENTS_URL } from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = response.status !== 204 ? await response?.json() : {};
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(
|
||||
body?.errorMessage ||
|
||||
`Une erreur est survenue code de retour : ${response.status}`
|
||||
);
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
const getData = (url) => {
|
||||
return fetch(`${url}`).then(requestResponseHandler);
|
||||
return fetch(`${url}`).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
const createDatas = (url, newData, csrfToken) => {
|
||||
@ -27,7 +14,9 @@ const createDatas = (url, newData, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(newData),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
const updateDatas = (url, updatedData, csrfToken) => {
|
||||
@ -39,7 +28,9 @@ const updateDatas = (url, updatedData, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(updatedData),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
const removeDatas = (url, csrfToken) => {
|
||||
@ -50,7 +41,9 @@ const removeDatas = (url, csrfToken) => {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchPlannings = (
|
||||
|
||||
@ -8,17 +8,7 @@ import {
|
||||
FE_API_DOCUSEAL_DOWNLOAD_URL,
|
||||
FE_API_DOCUSEAL_GENERATE_TOKEN,
|
||||
} from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
// FETCH requests
|
||||
|
||||
@ -67,7 +57,7 @@ export const fetchRegistrationSchoolFileMasters = (id = null) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationParentFileMasters = (id = null) => {
|
||||
@ -81,7 +71,7 @@ export const fetchRegistrationParentFileMasters = (id = null) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationSchoolFileTemplates = (id = null) => {
|
||||
@ -95,7 +85,7 @@ export const fetchRegistrationSchoolFileTemplates = (id = null) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
// CREATE requests
|
||||
@ -130,7 +120,9 @@ export const createRegistrationSchoolFileMaster = (data, csrfToken) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createRegistrationParentFileMaster = (data, csrfToken) => {
|
||||
@ -142,7 +134,9 @@ export const createRegistrationParentFileMaster = (data, csrfToken) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createRegistrationSchoolFileTemplate = (data, csrfToken) => {
|
||||
@ -154,7 +148,9 @@ export const createRegistrationSchoolFileTemplate = (data, csrfToken) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createRegistrationParentFileTemplate = (data, csrfToken) => {
|
||||
@ -166,7 +162,9 @@ export const createRegistrationParentFileTemplate = (data, csrfToken) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
// EDIT requests
|
||||
@ -207,7 +205,9 @@ export const editRegistrationSchoolFileMaster = (fileId, data, csrfToken) => {
|
||||
},
|
||||
credentials: 'include',
|
||||
}
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editRegistrationParentFileMaster = (id, data, csrfToken) => {
|
||||
@ -222,7 +222,9 @@ export const editRegistrationParentFileMaster = (id, data, csrfToken) => {
|
||||
},
|
||||
credentials: 'include',
|
||||
}
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editRegistrationSchoolFileTemplates = (
|
||||
@ -240,7 +242,9 @@ export const editRegistrationSchoolFileTemplates = (
|
||||
},
|
||||
credentials: 'include',
|
||||
}
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editRegistrationParentFileTemplates = (
|
||||
@ -258,7 +262,9 @@ export const editRegistrationParentFileTemplates = (
|
||||
},
|
||||
credentials: 'include',
|
||||
}
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
// DELETE requests
|
||||
@ -343,7 +349,9 @@ export const cloneTemplate = (templateId, email, is_required) => {
|
||||
email,
|
||||
is_required,
|
||||
}),
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const downloadTemplate = (slug) => {
|
||||
@ -352,7 +360,9 @@ export const downloadTemplate = (slug) => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const generateToken = (email, id = null) => {
|
||||
@ -362,5 +372,7 @@ export const generateToken = (email, id = null) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user_email: email, id }),
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
@ -9,34 +9,28 @@ import {
|
||||
BE_SCHOOL_PAYMENT_MODES_URL,
|
||||
BE_SCHOOL_ESTABLISHMENT_URL,
|
||||
} from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
export const fetchSpecialities = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_SPECIALITIES_URL}?establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchTeachers = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_TEACHERS_URL}?establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
return fetch(`${BE_SCHOOL_TEACHERS_URL}?establishment_id=${establishment}`)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchClasses = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_SCHOOLCLASSES_URL}?establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchClasse = (id) => {
|
||||
@ -46,61 +40,79 @@ export const fetchClasse = (id) => {
|
||||
};
|
||||
|
||||
export const fetchSchedules = () => {
|
||||
return fetch(`${BE_SCHOOL_PLANNINGS_URL}`).then(requestResponseHandler);
|
||||
return fetch(`${BE_SCHOOL_PLANNINGS_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationDiscounts = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_DISCOUNTS_URL}?filter=registration&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchTuitionDiscounts = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_DISCOUNTS_URL}?filter=tuition&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationFees = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_FEES_URL}?filter=registration&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchTuitionFees = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_FEES_URL}?filter=tuition&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationPaymentPlans = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=registration&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchTuitionPaymentPlans = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=tuition&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegistrationPaymentModes = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=registration&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchTuitionPaymentModes = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=tuition&establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchEstablishment = (establishment) => {
|
||||
return fetch(`${BE_SCHOOL_ESTABLISHMENT_URL}/${establishment}`).then(
|
||||
requestResponseHandler
|
||||
);
|
||||
return fetch(`${BE_SCHOOL_ESTABLISHMENT_URL}/${establishment}`)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createDatas = (url, newData, csrfToken) => {
|
||||
@ -112,7 +124,9 @@ export const createDatas = (url, newData, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(newData),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const updateDatas = (url, id, updatedData, csrfToken) => {
|
||||
@ -124,7 +138,9 @@ export const updateDatas = (url, id, updatedData, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(updatedData),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const removeDatas = (url, id, csrfToken) => {
|
||||
@ -135,5 +151,7 @@ export const removeDatas = (url, id, csrfToken) => {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
@ -1,27 +1,23 @@
|
||||
import { BE_SETTINGS_SMTP_URL } from '@/utils/Url';
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
export const PENDING = 'pending';
|
||||
export const SUBSCRIBED = 'subscribed';
|
||||
export const ARCHIVED = 'archived';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
export const fetchSmtpSettings = (csrfToken, establishment_id = null) => {
|
||||
let url = `${BE_SETTINGS_SMTP_URL}/`;
|
||||
if (establishment_id) {
|
||||
url += `?establishment_id=${establishment_id}`;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
throw error;
|
||||
};
|
||||
|
||||
export const fetchSmtpSettings = (csrfToken) => {
|
||||
return fetch(`${BE_SETTINGS_SMTP_URL}/`, {
|
||||
return fetch(`${url}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editSmtpSettings = (data, csrfToken) => {
|
||||
@ -33,5 +29,7 @@ export const editSmtpSettings = (data, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
@ -7,19 +7,7 @@ import {
|
||||
} from '@/utils/Url';
|
||||
|
||||
import { CURRENT_YEAR_FILTER } from '@/utils/constants';
|
||||
import { useNotification } from '@/context/NotificationContext';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error(body?.errorMessage || 'Une erreur est survenue');
|
||||
error.details = body;
|
||||
showNotification('Une erreur inattendue est survenue.', 'error', 'Erreur');
|
||||
throw error;
|
||||
};
|
||||
import { errorHandler, requestResponseHandler } from './actionsHandlers';
|
||||
|
||||
export const fetchRegisterForms = (
|
||||
establishment,
|
||||
@ -36,17 +24,20 @@ export const fetchRegisterForms = (
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchRegisterForm = (id) => {
|
||||
return fetch(`${BE_SUBSCRIPTION_REGISTERFORMS_URL}/${id}`) // Utilisation de studentId au lieu de codeDI
|
||||
.then(requestResponseHandler);
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
export const fetchLastGuardian = () => {
|
||||
return fetch(`${BE_SUBSCRIPTION_LAST_GUARDIAN_ID_URL}`).then(
|
||||
requestResponseHandler
|
||||
);
|
||||
return fetch(`${BE_SUBSCRIPTION_LAST_GUARDIAN_ID_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editRegisterForm = (id, data, csrfToken) => {
|
||||
@ -57,7 +48,9 @@ export const editRegisterForm = (id, data, csrfToken) => {
|
||||
},
|
||||
body: data,
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createRegisterForm = (data, csrfToken) => {
|
||||
@ -70,7 +63,9 @@ export const createRegisterForm = (data, csrfToken) => {
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const sendRegisterForm = (id) => {
|
||||
@ -79,7 +74,9 @@ export const sendRegisterForm = (id) => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const resendRegisterForm = (id) => {
|
||||
@ -88,7 +85,9 @@ export const resendRegisterForm = (id) => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
export const archiveRegisterForm = (id) => {
|
||||
const url = `${BE_SUBSCRIPTION_REGISTERFORMS_URL}/${id}/archive`;
|
||||
@ -97,7 +96,9 @@ export const archiveRegisterForm = (id) => {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchStudents = (establishment, id = null) => {
|
||||
@ -110,7 +111,7 @@ export const fetchStudents = (establishment, id = null) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export const fetchChildren = (id, establishment) => {
|
||||
@ -123,7 +124,7 @@ export const fetchChildren = (id, establishment) => {
|
||||
},
|
||||
}
|
||||
);
|
||||
return fetch(request).then(requestResponseHandler);
|
||||
return fetch(request).then(requestResponseHandler).catch(errorHandler);
|
||||
};
|
||||
|
||||
export async function getRegisterFormFileTemplate(fileId) {
|
||||
@ -206,7 +207,9 @@ export const dissociateGuardian = async (studentId, guardianId) => {
|
||||
export const fetchAbsences = (establishment) => {
|
||||
return fetch(
|
||||
`${BE_SUBSCRIPTION_ABSENCES_URL}?establishment_id=${establishment}`
|
||||
).then(requestResponseHandler);
|
||||
)
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const createAbsences = (data, csrfToken) => {
|
||||
@ -218,7 +221,9 @@ export const createAbsences = (data, csrfToken) => {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
}).then(requestResponseHandler);
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
.catch(errorHandler);
|
||||
};
|
||||
|
||||
export const editAbsences = (absenceId, payload, csrfToken) => {
|
||||
|
||||
53
Front-End/src/components/Admin/AnnouncementScheduler.js
Normal file
53
Front-End/src/components/Admin/AnnouncementScheduler.js
Normal file
@ -0,0 +1,53 @@
|
||||
'use client';
|
||||
import logger from '@/utils/logger';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export default function AnnouncementScheduler({ csrfToken }) {
|
||||
const [title, setTitle] = useState('');
|
||||
const [date, setDate] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
const handleSchedule = () => {
|
||||
// Logique pour planifier une annonce
|
||||
logger.debug('Annonce planifiée:', { title, date, message });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-white rounded shadow">
|
||||
<h2 className="text-xl font-bold mb-4">Planifier une Annonce</h2>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">Titre</label>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">Date</label>
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">Message</label>
|
||||
<textarea
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
className="w-full p-2 border rounded"
|
||||
rows="5"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSchedule}
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded"
|
||||
>
|
||||
Planifier
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,75 +1,158 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import { sendMessage } from '@/app/actions/messagerieAction';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { sendMessage, searchRecipients } from '@/app/actions/messagerieAction';
|
||||
import { fetchSmtpSettings } from '@/app/actions/settingsAction';
|
||||
import { useNotification } from '@/context/NotificationContext';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
import AlertMessage from '@/components/AlertMessage';
|
||||
import RecipientInput from '@/components/RecipientInput';
|
||||
import { useRouter } from 'next/navigation'; // Ajoute cette ligne
|
||||
import WisiwigTextArea from '@/components/WisiwigTextArea';
|
||||
import logger from '@/utils/logger';
|
||||
import InputText from '@/components/InputText';
|
||||
import Button from '@/components/Button';
|
||||
|
||||
export default function EmailSender({ csrfToken }) {
|
||||
const [recipients, setRecipients] = useState('');
|
||||
const [recipients, setRecipients] = useState([]);
|
||||
const [fromEmail, setFromEmail] = useState('');
|
||||
const [cc, setCc] = useState([]);
|
||||
const [bcc, setBcc] = useState([]);
|
||||
const [subject, setSubject] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
const [status, setStatus] = useState('');
|
||||
const [smtpConfigured, setSmtpConfigured] = useState(false); // État pour vérifier si SMTP est configuré
|
||||
const { showNotification } = useNotification();
|
||||
const { selectedEstablishmentId } = useEstablishment(); // Récupérer l'establishment_id depuis le contexte
|
||||
const router = useRouter(); // Ajoute cette ligne
|
||||
|
||||
useEffect(() => {
|
||||
// Vérifier si les paramètres SMTP sont configurés
|
||||
fetchSmtpSettings(csrfToken, selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
if (data.smtp_server && data.smtp_port && data.smtp_user) {
|
||||
setFromEmail(data.smtp_user);
|
||||
setSmtpConfigured(true);
|
||||
} else {
|
||||
setSmtpConfigured(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors de la vérification des paramètres SMTP:', {
|
||||
error,
|
||||
});
|
||||
setSmtpConfigured(false);
|
||||
});
|
||||
}, [csrfToken, selectedEstablishmentId]);
|
||||
|
||||
const handleSendEmail = async () => {
|
||||
const data = {
|
||||
recipients: recipients.split(',').map((email) => email.trim()),
|
||||
recipients,
|
||||
cc,
|
||||
bcc,
|
||||
subject,
|
||||
message,
|
||||
};
|
||||
sendMessage(data);
|
||||
establishment_id: selectedEstablishmentId, // Ajouter l'establishment_id à la payload
|
||||
};
|
||||
|
||||
try {
|
||||
await sendMessage(data);
|
||||
showNotification('Email envoyé avec succès.', 'success', 'Succès');
|
||||
// Réinitialiser les champs après succès
|
||||
setRecipients([]);
|
||||
setCc([]);
|
||||
setBcc([]);
|
||||
setSubject('');
|
||||
setMessage('');
|
||||
} catch (error) {
|
||||
logger.error("Erreur lors de l'envoi de l'email:", { error });
|
||||
showNotification(
|
||||
"Une erreur est survenue lors de l'envoi de l'email.",
|
||||
'error',
|
||||
'Erreur'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (!smtpConfigured) {
|
||||
return (
|
||||
<div className="p-4 bg-white rounded shadow">
|
||||
<h2 className="text-xl font-bold mb-4">Envoyer un Email</h2>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">
|
||||
Destinataires (séparés par des virgules)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={recipients}
|
||||
onChange={(e) => setRecipients(e.target.value)}
|
||||
className="w-full p-2 border rounded"
|
||||
<AlertMessage
|
||||
type="warning"
|
||||
title="Configuration SMTP requise"
|
||||
message="Les paramètres SMTP de cet établissement ne sont pas configurés. Veuillez les configurer dans la page des paramètres."
|
||||
actionLabel="Aller aux paramètres"
|
||||
onAction={() => router.push('/admin/settings?tab=smtp')} // Utilise next/navigation ici
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto bg-white rounded-lg shadow-md">
|
||||
{/* Form */}
|
||||
<div className="p-4 flex flex-col min-h-[600px]">
|
||||
{' '}
|
||||
{/* Ajout flex-col et min-h */}
|
||||
{/* Destinataires */}
|
||||
<RecipientInput
|
||||
label="Destinataires"
|
||||
recipients={recipients}
|
||||
setRecipients={setRecipients}
|
||||
searchRecipients={searchRecipients}
|
||||
establishmentId={selectedEstablishmentId}
|
||||
required
|
||||
/>
|
||||
{/* Cc */}
|
||||
<div className="mt-2">
|
||||
<RecipientInput
|
||||
label="Cc"
|
||||
placeholder="Ajouter Cc"
|
||||
recipients={cc}
|
||||
searchRecipients={searchRecipients}
|
||||
establishmentId={selectedEstablishmentId}
|
||||
setRecipients={setCc}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">Sujet</label>
|
||||
<input
|
||||
type="text"
|
||||
{/* Bcc */}
|
||||
<div className="mt-2">
|
||||
<RecipientInput
|
||||
label="Cci"
|
||||
placeholder="Ajouter Bcc"
|
||||
recipients={bcc}
|
||||
searchRecipients={searchRecipients}
|
||||
establishmentId={selectedEstablishmentId}
|
||||
setRecipients={setBcc}
|
||||
/>
|
||||
</div>
|
||||
{/* Subject */}
|
||||
<InputText
|
||||
name="subject"
|
||||
label="Sujet"
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
className="w-full p-2 border rounded"
|
||||
placeholder="Saisir le sujet"
|
||||
className="mb-4 mt-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block font-medium">Message</label>
|
||||
<Editor
|
||||
apiKey="8ftyao41dcp1et0p409ipyrdtp14wxs0efqdofvrjq1vo2gi" // Remplacez par votre clé API TinyMCE
|
||||
{/* Email Body */}
|
||||
<div className="mb-4 flex flex-col">
|
||||
<WisiwigTextArea
|
||||
label="Mail"
|
||||
value={message}
|
||||
init={{
|
||||
height: 300,
|
||||
menubar: false,
|
||||
plugins: [
|
||||
'advlist autolink lists link image charmap print preview anchor',
|
||||
'searchreplace visualblocks code fullscreen',
|
||||
'insertdatetime media table paste code help wordcount',
|
||||
],
|
||||
toolbar:
|
||||
'undo redo | formatselect | bold italic backcolor | \
|
||||
alignleft aligncenter alignright alignjustify | \
|
||||
bullist numlist outdent indent | removeformat | help',
|
||||
}}
|
||||
onEditorChange={(content) => setMessage(content)}
|
||||
onChange={setMessage}
|
||||
placeholder="Ecrivez votre mail ici..."
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
{/* Footer */}
|
||||
<div className="flex justify-between items-center mt-10">
|
||||
<Button
|
||||
text="Envoyer"
|
||||
onClick={handleSendEmail}
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded"
|
||||
>
|
||||
Envoyer
|
||||
</button>
|
||||
{status && <p className="mt-4 text-sm">{status}</p>}
|
||||
primary
|
||||
className="px-4 py-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
26
Front-End/src/components/Admin/InstantMessaging.js
Normal file
26
Front-End/src/components/Admin/InstantMessaging.js
Normal file
@ -0,0 +1,26 @@
|
||||
// filepath: d:\Dev\n3wt-innov\n3wt-school\Front-End\src\components\Admin\InstantMessaging.js
|
||||
import React from 'react';
|
||||
import Chat from '@/components/Chat';
|
||||
import { getGravatarUrl } from '@/utils/gravatar';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const contacts = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Parent 1',
|
||||
profilePic: getGravatarUrl('parent1@n3wtschool.com'),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Parent 2',
|
||||
profilePic: getGravatarUrl('parent2@n3wtschool.com'),
|
||||
},
|
||||
];
|
||||
|
||||
export default function InstantMessaging({ csrfToken }) {
|
||||
const handleSendMessage = (contact, message) => {
|
||||
logger.debug(`Message envoyé à ${contact.name}: ${message}`);
|
||||
};
|
||||
|
||||
return <Chat contacts={contacts} onSendMessage={handleSendMessage} />;
|
||||
}
|
||||
@ -14,7 +14,7 @@ const AffectationClasseForm = ({ eleve = {}, onSubmit, classes }) => {
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log(formData);
|
||||
logger.debug(formData);
|
||||
/*onSubmit({
|
||||
eleve: {
|
||||
...formData,
|
||||
|
||||
@ -1,21 +1,36 @@
|
||||
import React from 'react';
|
||||
|
||||
const AlertMessage = ({ title, message, buttonText, buttonLink }) => {
|
||||
const AlertMessage = ({
|
||||
type = 'info',
|
||||
title,
|
||||
message,
|
||||
actionLabel,
|
||||
onAction,
|
||||
}) => {
|
||||
// Définir les styles en fonction du type d'alerte
|
||||
const typeStyles = {
|
||||
info: 'bg-blue-100 border-blue-500 text-blue-700',
|
||||
warning: 'bg-yellow-100 border-yellow-500 text-yellow-700',
|
||||
error: 'bg-red-100 border-red-500 text-red-700',
|
||||
success: 'bg-green-100 border-green-500 text-green-700',
|
||||
};
|
||||
|
||||
const alertStyle = typeStyles[type] || typeStyles.info;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="alert centered bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4"
|
||||
role="alert"
|
||||
>
|
||||
<div className={`alert centered border-l-4 p-4 ${alertStyle}`} role="alert">
|
||||
<h3 className="font-bold">{title}</h3>
|
||||
<p className="mt-2">{message}</p>
|
||||
{actionLabel && onAction && (
|
||||
<div className="alert-actions mt-4">
|
||||
<a
|
||||
<button
|
||||
className="btn primary bg-emerald-500 text-white rounded-md px-4 py-2 hover:bg-emerald-600"
|
||||
href={buttonLink}
|
||||
onClick={onAction}
|
||||
>
|
||||
{buttonText} <i className="icon profile-add"></i>
|
||||
</a>
|
||||
{actionLabel}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { usePlanning, PlanningModes } from '@/context/PlanningContext';
|
||||
import { Plus, Edit2, Eye, EyeOff, Check, X } from 'lucide-react';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function ScheduleNavigation({ classes, modeSet = 'event' }) {
|
||||
const {
|
||||
@ -118,7 +119,7 @@ export default function ScheduleNavigation({ classes, modeSet = 'event' }) {
|
||||
>
|
||||
<option value="">Aucune</option>
|
||||
{classes.map((classe) => {
|
||||
console.log({ classe });
|
||||
logger.debug({ classe });
|
||||
return (
|
||||
<option key={classe.id} value={classe.id}>
|
||||
{classe.atmosphere_name}
|
||||
|
||||
205
Front-End/src/components/Chat.js
Normal file
205
Front-End/src/components/Chat.js
Normal file
@ -0,0 +1,205 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { SendHorizontal } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function Chat({
|
||||
discussions,
|
||||
setDiscussions,
|
||||
onSendMessage,
|
||||
simulateResponse,
|
||||
}) {
|
||||
const [selectedDiscussion, setSelectedDiscussion] = useState(null);
|
||||
const [messages, setMessages] = useState({});
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [showCreateForm, setShowCreateForm] = useState(false);
|
||||
const [newDiscussionName, setNewDiscussionName] = useState('');
|
||||
const [newDiscussionProfilePic, setNewDiscussionProfilePic] = useState('');
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
const handleSendMessage = () => {
|
||||
if (newMessage.trim() && selectedDiscussion) {
|
||||
const discussionMessages = messages[selectedDiscussion.id] || [];
|
||||
const newMessages = {
|
||||
...messages,
|
||||
[selectedDiscussion.id]: [
|
||||
...discussionMessages,
|
||||
{
|
||||
id: discussionMessages.length + 1,
|
||||
text: newMessage,
|
||||
date: new Date(),
|
||||
isResponse: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
setMessages(newMessages);
|
||||
setNewMessage('');
|
||||
onSendMessage && onSendMessage(selectedDiscussion, newMessage);
|
||||
simulateResponse && simulateResponse(selectedDiscussion.id, setMessages);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateDiscussion = () => {
|
||||
if (newDiscussionName.trim()) {
|
||||
const newDiscussion = {
|
||||
id: discussions.length + 1,
|
||||
name: newDiscussionName,
|
||||
profilePic: newDiscussionProfilePic || '/default-profile.png', // Image par défaut si aucune n'est fournie
|
||||
lastMessage: '',
|
||||
lastMessageDate: new Date(),
|
||||
};
|
||||
setDiscussions([...discussions, newDiscussion]);
|
||||
setNewDiscussionName('');
|
||||
setNewDiscussionProfilePic('');
|
||||
setShowCreateForm(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
{/* Liste des discussions */}
|
||||
<div className="w-1/4 bg-gray-100 border-r border-gray-300 p-4 overflow-y-auto">
|
||||
<h2 className="text-lg font-bold mb-4">Discussions</h2>
|
||||
<button
|
||||
onClick={() => setShowCreateForm(!showCreateForm)}
|
||||
className="w-full p-2 mb-4 bg-blue-500 text-white rounded-lg"
|
||||
>
|
||||
{showCreateForm ? 'Annuler' : 'Créer une discussion'}
|
||||
</button>
|
||||
{showCreateForm && (
|
||||
<div className="mb-4 p-2 border rounded-lg bg-white">
|
||||
<input
|
||||
type="text"
|
||||
value={newDiscussionName}
|
||||
onChange={(e) => setNewDiscussionName(e.target.value)}
|
||||
placeholder="Nom de la discussion"
|
||||
className="w-full p-2 mb-2 border rounded"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={newDiscussionProfilePic}
|
||||
onChange={(e) => setNewDiscussionProfilePic(e.target.value)}
|
||||
placeholder="URL de la photo de profil (optionnel)"
|
||||
className="w-full p-2 mb-2 border rounded"
|
||||
/>
|
||||
<button
|
||||
onClick={handleCreateDiscussion}
|
||||
className="w-full p-2 bg-green-500 text-white rounded-lg"
|
||||
>
|
||||
Ajouter
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{discussions && discussions.length > 0 ? (
|
||||
discussions.map((discussion) => (
|
||||
<div
|
||||
key={discussion.id}
|
||||
className={`flex items-center p-2 mb-2 cursor-pointer rounded ${
|
||||
selectedDiscussion?.id === discussion.id
|
||||
? 'bg-blue-100'
|
||||
: 'hover:bg-gray-200'
|
||||
}`}
|
||||
onClick={() => setSelectedDiscussion(discussion)}
|
||||
>
|
||||
<Image
|
||||
src={discussion.profilePic}
|
||||
alt={`${discussion.name}'s profile`}
|
||||
className="w-10 h-10 rounded-full mr-3"
|
||||
width={40}
|
||||
height={40}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium">{discussion.name}</p>
|
||||
<p className="text-sm text-gray-500 truncate">
|
||||
{discussion.lastMessage}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-xs text-gray-400">
|
||||
{new Date(discussion.lastMessageDate).toLocaleTimeString()}
|
||||
</span>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500">Aucune discussion disponible.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Zone de chat */}
|
||||
<div className="flex-1 flex flex-col bg-white">
|
||||
{/* En-tête du chat */}
|
||||
{selectedDiscussion && (
|
||||
<div className="flex items-center p-4 border-b border-gray-300">
|
||||
<Image
|
||||
src={selectedDiscussion.profilePic}
|
||||
alt={`${selectedDiscussion.name}'s profile`}
|
||||
className="w-10 h-10 rounded-full mr-3"
|
||||
width={40}
|
||||
height={40}
|
||||
/>
|
||||
<h2 className="text-lg font-bold">{selectedDiscussion.name}</h2>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Messages */}
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{selectedDiscussion &&
|
||||
(messages[selectedDiscussion.id] || []).map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex mb-4 ${
|
||||
message.isResponse ? 'justify-start' : 'justify-end'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`p-3 rounded-lg max-w-xs ${
|
||||
message.isResponse
|
||||
? 'bg-gray-200 text-gray-800'
|
||||
: 'bg-blue-500 text-white'
|
||||
}`}
|
||||
>
|
||||
<p>{message.text}</p>
|
||||
<span className="text-xs text-gray-500 block mt-1">
|
||||
{new Date(message.date).toLocaleTimeString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
{/* Champ de saisie */}
|
||||
{selectedDiscussion && (
|
||||
<div className="p-4 border-t border-gray-300 flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
className="flex-1 p-2 border border-gray-300 rounded-lg mr-2"
|
||||
placeholder="Écrire un message..."
|
||||
onKeyDown={handleKeyPress}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
className="p-2 bg-blue-500 text-white rounded-lg"
|
||||
>
|
||||
<SendHorizontal />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import logger from '@/utils/logger';
|
||||
import React from 'react';
|
||||
|
||||
const CheckBox = ({
|
||||
@ -8,8 +9,13 @@ const CheckBox = ({
|
||||
itemLabelFunc = () => null,
|
||||
horizontal,
|
||||
}) => {
|
||||
console.log(formData);
|
||||
const isChecked = formData[fieldName].includes(parseInt(item.id));
|
||||
logger.debug(formData);
|
||||
|
||||
// Vérifier si formData[fieldName] est un tableau ou une valeur booléenne
|
||||
const isChecked = Array.isArray(formData[fieldName])
|
||||
? formData[fieldName].includes(parseInt(item.id)) // Si c'est un tableau, vérifier si l'élément est inclus
|
||||
: formData[fieldName]; // Si c'est une valeur booléenne, l'utiliser directement
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
import Popup from '@/components/Popup';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const DateTab = ({
|
||||
dates,
|
||||
@ -29,13 +30,13 @@ const DateTab = ({
|
||||
handleEdit(paymentPlanId, dataWithType)
|
||||
.then(() => {
|
||||
setPopupMessage(
|
||||
`Mise à jour de la date d'échéance effectuée avec succès`
|
||||
"Mise à jour de la date d'échéance effectuée avec succès"
|
||||
);
|
||||
setPopupVisible(true);
|
||||
setModifiedDates({});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
logger.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -93,7 +93,6 @@ export default function FileUpload({
|
||||
{typeof existingFile === 'string'
|
||||
? existingFile.split('/').pop() // Si c'est une chaîne, utilisez split
|
||||
: existingFile?.name || 'Nom de fichier inconnu'}{' '}
|
||||
// Sinon, utilisez une propriété ou un fallback
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@ const typeStyles = {
|
||||
};
|
||||
|
||||
export default function FlashNotification({
|
||||
displayPeriod = 3000,
|
||||
title,
|
||||
message,
|
||||
type = 'info',
|
||||
@ -33,9 +34,9 @@ export default function FlashNotification({
|
||||
const timer = setTimeout(() => {
|
||||
setIsVisible(false); // Déclenche la disparition
|
||||
setTimeout(onClose, 300); // Appelle onClose après l'animation
|
||||
}, 3000); // Notification visible pendant 3 secondes
|
||||
}, displayPeriod); // Notification visible pendant 3 secondes par défaut
|
||||
return () => clearTimeout(timer);
|
||||
}, [onClose]);
|
||||
}, [onClose, displayPeriod]);
|
||||
|
||||
if (!message || !isVisible) return null;
|
||||
|
||||
@ -47,14 +48,14 @@ export default function FlashNotification({
|
||||
animate={{ opacity: 1, x: 0 }} // Animation visible
|
||||
exit={{ opacity: 0, x: 50 }} // Animation de sortie
|
||||
transition={{ duration: 0.3 }} // Durée des animations
|
||||
className="fixed top-5 right-5 flex items-stretch w-96 rounded-lg shadow-lg bg-white z-50 border border-gray-200"
|
||||
className="fixed top-5 right-5 flex items-stretch rounded-lg shadow-lg bg-white z-50 border border-gray-200"
|
||||
>
|
||||
{/* Rectangle gauche avec l'icône */}
|
||||
<div className={`flex items-center justify-center w-12 ${bg}`}>
|
||||
<div className={`flex items-center justify-center w-14 ${bg}`}>
|
||||
{icon}
|
||||
</div>
|
||||
{/* Zone de texte */}
|
||||
<div className="flex-1 p-4">
|
||||
<div className="flex-1 w-96 p-4">
|
||||
<p className="font-bold text-black">{title}</p>
|
||||
<p className="text-gray-700">{message}</p>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import Logo from '@/components/Logo';
|
||||
|
||||
export default function Footer({ softwareName, softwareVersion }) {
|
||||
return (
|
||||
<footer className="absolute bottom-0 left-0 right-0 h-16 bg-white border-t border-gray-200 flex items-center justify-center box-border">
|
||||
<footer className="absolute bottom-0 left-64 right-0 h-16 bg-white border-t border-gray-200 flex items-center justify-center box-border">
|
||||
<div className="text-sm font-light">
|
||||
<span>
|
||||
© {new Date().getFullYear()} N3WT-INNOV Tous droits réservés.
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
fetchParentFileTemplatesFromRegistrationFiles,
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const FilesModal = ({
|
||||
isOpen,
|
||||
@ -23,9 +24,7 @@ const FilesModal = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedRegisterForm?.student?.id) {
|
||||
console.error(
|
||||
'selectedRegisterForm.student.id est invalide ou manquant.'
|
||||
);
|
||||
logger.error('selectedRegisterForm.student.id est invalide ou manquant.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -37,7 +36,7 @@ const FilesModal = ({
|
||||
)
|
||||
.then((schoolFiles) => {
|
||||
if (!Array.isArray(schoolFiles)) {
|
||||
console.error(
|
||||
logger.error(
|
||||
'Les fichiers scolaires ne sont pas un tableau :',
|
||||
schoolFiles
|
||||
);
|
||||
@ -84,7 +83,7 @@ const FilesModal = ({
|
||||
setFiles(categorizedFiles);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Erreur lors de la récupération des fichiers :', error);
|
||||
logger.error('Erreur lors de la récupération des fichiers :', error);
|
||||
});
|
||||
}, [selectedRegisterForm]);
|
||||
|
||||
@ -144,7 +143,7 @@ const FilesModal = ({
|
||||
{/* Section Fichiers École */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">
|
||||
Formulaires de l'établissement
|
||||
Formulaires de l'établissement
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
{files.schoolFiles.length > 0 ? (
|
||||
|
||||
@ -138,7 +138,7 @@ export default function InscriptionFormShared({
|
||||
|
||||
// Mettre à jour isPage6Valid en fonction de cette condition
|
||||
setIsPage6Valid(allRequiredUploaded);
|
||||
console.log(allRequiredUploaded);
|
||||
logger.debug(allRequiredUploaded);
|
||||
}, [parentFileTemplates]);
|
||||
|
||||
const handleTemplateSigned = (index) => {
|
||||
@ -420,7 +420,7 @@ export default function InscriptionFormShared({
|
||||
formDataToSend.append('photo', formData.photo);
|
||||
}
|
||||
|
||||
console.log('submit : ', jsonData);
|
||||
logger.debug('submit : ', jsonData);
|
||||
|
||||
// Appeler la fonction onSubmit avec les données FormData
|
||||
onSubmit(formDataToSend);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import SelectChoice from '@/components/SelectChoice';
|
||||
import RadioList from '@/components/RadioList';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function PaymentMethodSelector({
|
||||
formData,
|
||||
@ -18,7 +19,7 @@ export default function PaymentMethodSelector({
|
||||
(field) => getLocalError(field) !== ''
|
||||
);
|
||||
setIsPageValid(isValid);
|
||||
console.log('formdata : ', formData);
|
||||
logger.debug('formdata : ', formData);
|
||||
}, [formData, setIsPageValid]);
|
||||
|
||||
const paymentModesOptions = [
|
||||
@ -68,7 +69,7 @@ export default function PaymentMethodSelector({
|
||||
{/* Frais d'inscription */}
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">
|
||||
Frais d'inscription
|
||||
Frais d'inscription
|
||||
</h2>
|
||||
|
||||
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
|
||||
|
||||
@ -115,8 +115,8 @@ export default function ResponsableInputFields({
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<SectionHeader
|
||||
icon={Users}
|
||||
title={`Responsables légaux`}
|
||||
description={`Remplissez les champs requis`}
|
||||
title={'Responsables légaux'}
|
||||
description={'Remplissez les champs requis'}
|
||||
/>
|
||||
{guardians.map((item, index) => (
|
||||
<div className="p-6 " key={index}>
|
||||
|
||||
@ -97,8 +97,8 @@ export default function SiblingInputFields({
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<SectionHeader
|
||||
icon={Users}
|
||||
title={`Frères et Sœurs`}
|
||||
description={`Ajoutez les informations des frères et sœurs`}
|
||||
title={'Frères et Sœurs'}
|
||||
description={'Ajoutez les informations des frères et sœurs'}
|
||||
/>
|
||||
{siblings.map((item, index) => (
|
||||
<div className="p-6" key={index}>
|
||||
|
||||
@ -154,8 +154,8 @@ export default function StudentInfoForm({
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 space-y-8">
|
||||
<SectionHeader
|
||||
icon={User}
|
||||
title={`Informations de l'élève`}
|
||||
description={`Remplissez les champs requis`}
|
||||
title={"Informations de l'élève"}
|
||||
description={'Remplissez les champs requis'}
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<InputText
|
||||
|
||||
@ -126,7 +126,7 @@ export default function ValidateSubscription({
|
||||
]
|
||||
: []),
|
||||
];
|
||||
console.log(allTemplates);
|
||||
logger.debug(allTemplates);
|
||||
|
||||
return (
|
||||
<div className="mb-4 w-full mx-auto">
|
||||
|
||||
@ -4,6 +4,7 @@ import Table from '@/components/Table';
|
||||
import DateTab from '@/components/DateTab';
|
||||
import InputTextIcon from '@/components/InputTextIcon';
|
||||
import Popup from '@/components/Popup';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const paymentPlansOptions = [
|
||||
{ id: 0, name: '1 fois', frequency: 1 },
|
||||
@ -118,7 +119,7 @@ const PaymentPlanSelector = ({
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
logger.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
@ -222,13 +223,13 @@ const PaymentPlanSelector = ({
|
||||
handleEdit(selectedPlan.id, updatedData)
|
||||
.then(() => {
|
||||
setPopupMessage(
|
||||
`Mise à jour des dates d'échéances effectuée avec succès`
|
||||
"Mise à jour des dates d'échéances effectuée avec succès"
|
||||
);
|
||||
setPopupVisible(true);
|
||||
setIsDefaultDayModified(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
logger.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -8,10 +8,11 @@ import { NotificationProvider } from '@/context/NotificationContext';
|
||||
import { ClassesProvider } from '@/context/ClassesContext';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function Providers({ children, messages, locale, session }) {
|
||||
if (!locale) {
|
||||
console.error('Locale non définie dans Providers');
|
||||
logger.error('Locale non définie dans Providers');
|
||||
locale = 'fr'; // Valeur par défaut
|
||||
}
|
||||
return (
|
||||
|
||||
152
Front-End/src/components/RecipientInput.js
Normal file
152
Front-End/src/components/RecipientInput.js
Normal file
@ -0,0 +1,152 @@
|
||||
import React, { useState } from 'react';
|
||||
import { getGravatarUrl } from '@/utils/gravatar'; // Assurez-vous que cette fonction est définie pour générer les URLs Gravatar
|
||||
import { getRightStr } from '@/utils/rights'; // Fonction existante pour récupérer le nom des rôles
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function RecipientInput({
|
||||
label,
|
||||
recipients,
|
||||
setRecipients,
|
||||
searchRecipients, // Fonction pour effectuer la recherche
|
||||
establishmentId, // ID de l'établissement
|
||||
required = false, // Ajout de la prop required avec valeur par défaut
|
||||
}) {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [suggestions, setSuggestions] = useState([]);
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
|
||||
const handleInputChange = async (e) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value);
|
||||
|
||||
if (value.trim() !== '') {
|
||||
try {
|
||||
const results = await searchRecipients(establishmentId, value);
|
||||
setSuggestions(results);
|
||||
} catch (error) {
|
||||
logger.error('Erreur lors de la recherche des destinataires:', error);
|
||||
setSuggestions([]);
|
||||
}
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex >= 0 && selectedIndex < suggestions.length) {
|
||||
handleSuggestionClick(suggestions[selectedIndex]);
|
||||
} else {
|
||||
const trimmedValue = inputValue.trim();
|
||||
if (trimmedValue && !recipients.includes(trimmedValue)) {
|
||||
setRecipients([...recipients, trimmedValue]);
|
||||
setInputValue('');
|
||||
setSuggestions([]);
|
||||
}
|
||||
}
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
setSelectedIndex((prevIndex) =>
|
||||
prevIndex < suggestions.length - 1 ? prevIndex + 1 : 0
|
||||
);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
setSelectedIndex((prevIndex) =>
|
||||
prevIndex > 0 ? prevIndex - 1 : suggestions.length - 1
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (suggestion) => {
|
||||
if (!recipients.includes(suggestion.email)) {
|
||||
setRecipients([...recipients, suggestion.email]);
|
||||
}
|
||||
setInputValue('');
|
||||
setSuggestions([]);
|
||||
};
|
||||
|
||||
const handleRemoveRecipient = (email) => {
|
||||
setRecipients(recipients.filter((recipient) => recipient !== email));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700">
|
||||
{label}
|
||||
{required && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
<div
|
||||
className={`
|
||||
mt-1 flex flex-wrap items-center gap-2 border rounded-md
|
||||
border-gray-200 hover:border-gray-400 focus-within:border-gray-500
|
||||
transition-colors
|
||||
`}
|
||||
>
|
||||
{recipients.map((email, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center bg-gray-100 text-gray-700 px-2 py-1 rounded-full"
|
||||
>
|
||||
<img
|
||||
src={getGravatarUrl(email)}
|
||||
alt={email}
|
||||
className="w-6 h-6 rounded-full mr-2"
|
||||
/>
|
||||
<span className="mr-2">{email}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveRecipient(email)}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Rechercher des destinataires"
|
||||
className="flex-1 px-3 py-2 block w-full sm:text-sm border-none focus:ring-0 outline-none rounded-md"
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
{suggestions.length > 0 && (
|
||||
<ul className="border rounded mt-2 bg-white shadow">
|
||||
{suggestions.map((suggestion, index) => (
|
||||
<li
|
||||
key={suggestion.id}
|
||||
className={`p-2 cursor-pointer ${
|
||||
index === selectedIndex ? 'bg-gray-200' : ''
|
||||
}`}
|
||||
onClick={() => handleSuggestionClick(suggestion)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={getGravatarUrl(suggestion.email)}
|
||||
alt={suggestion.email}
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
{suggestion.first_name && suggestion.last_name
|
||||
? `${suggestion.first_name} ${suggestion.last_name}`
|
||||
: suggestion.email}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">{suggestion.email}</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
{suggestion.roles
|
||||
.map((role) => getRightStr(role.role_type) || 'Inconnu')
|
||||
.join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -12,7 +12,7 @@ const SidebarTabs = ({ tabs, onTabChange }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-col h-full w-full">
|
||||
{/* Tabs Header */}
|
||||
<div className="flex h-14 bg-gray-50 border-b border-gray-200 shadow-sm">
|
||||
{tabs.map((tab) => (
|
||||
@ -31,7 +31,7 @@ const SidebarTabs = ({ tabs, onTabChange }) => {
|
||||
</div>
|
||||
|
||||
{/* Tabs Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 rounded-b-lg shadow-inner relative">
|
||||
<div className="flex-1 overflow-y-auto rounded-b-lg shadow-inner relative">
|
||||
<AnimatePresence mode="wait">
|
||||
{tabs.map(
|
||||
(tab) =>
|
||||
|
||||
@ -52,7 +52,7 @@ export default function FileUploadDocuSeal({
|
||||
setToken(data.token);
|
||||
})
|
||||
.catch((error) =>
|
||||
console.error('Erreur lors de la génération du token:', error)
|
||||
logger.error('Erreur lors de la génération du token:', error)
|
||||
);
|
||||
}, [fileToEdit]);
|
||||
|
||||
@ -129,7 +129,7 @@ export default function FileUploadDocuSeal({
|
||||
master: templateMaster?.id,
|
||||
registration_form: guardian.registration_form,
|
||||
};
|
||||
console.log('creation : ', data);
|
||||
logger.debug('creation : ', data);
|
||||
createRegistrationSchoolFileTemplate(data, csrfToken)
|
||||
.then((response) => {
|
||||
logger.debug('Template enregistré avec succès:', response);
|
||||
@ -166,13 +166,13 @@ export default function FileUploadDocuSeal({
|
||||
<div className="flex flex-col items-center justify-center h-full text-center space-y-6">
|
||||
{/* Description de l'étape */}
|
||||
<p className="text-gray-700 text-base font-medium mb-4">
|
||||
Étape 1 - Sélectionner au moins un dossier d'inscription
|
||||
Étape 1 - Sélectionner au moins un dossier d'inscription
|
||||
</p>
|
||||
|
||||
{/* Section centrée pour la sélection des groupes */}
|
||||
<div className="bg-gray-50 p-8 rounded-lg shadow-md border border-gray-300 transform transition-transform hover:scale-105">
|
||||
<h3 className="text-xl font-semibold text-gray-800 mb-4 text-center">
|
||||
Dossiers d'inscription
|
||||
Dossiers d'inscription
|
||||
</h3>
|
||||
<MultiSelect
|
||||
name="groups"
|
||||
@ -190,7 +190,7 @@ export default function FileUploadDocuSeal({
|
||||
{/* Section Dossiers d'inscription repositionnée sur le côté */}
|
||||
<div className="col-span-2 bg-white p-4 rounded-lg shadow-md border border-gray-200">
|
||||
<h3 className="text-lg font-medium text-gray-800 mb-4">
|
||||
Dossiers d'inscription
|
||||
Dossiers d'inscription
|
||||
</h3>
|
||||
<MultiSelect
|
||||
name="groups"
|
||||
|
||||
@ -97,7 +97,7 @@ export default function FilesGroupsManagement({
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err.message);
|
||||
logger.debug(err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
setReloadTemplates(false);
|
||||
@ -155,7 +155,7 @@ export default function FilesGroupsManagement({
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error deleting file from database:', error);
|
||||
logger.error('Error deleting file from database:', error);
|
||||
showNotification(
|
||||
`Erreur lors de la suppression du document "${templateMaster.name}".`,
|
||||
'error',
|
||||
@ -175,7 +175,7 @@ export default function FilesGroupsManagement({
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error removing template from DocuSeal:', error);
|
||||
logger.error('Error removing template from DocuSeal:', error);
|
||||
showNotification(
|
||||
`Erreur lors de la suppression du document "${templateMaster.name}".`,
|
||||
'error',
|
||||
@ -207,7 +207,7 @@ export default function FilesGroupsManagement({
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error removing template:', error);
|
||||
logger.error('Error removing template:', error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
@ -235,7 +235,7 @@ export default function FilesGroupsManagement({
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error uploading file:', error);
|
||||
logger.error('Error uploading file:', error);
|
||||
});
|
||||
};
|
||||
|
||||
@ -258,7 +258,7 @@ export default function FilesGroupsManagement({
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error editing file:', error);
|
||||
logger.error('Error editing file:', error);
|
||||
showNotification(
|
||||
'Erreur lors de la modification du fichier',
|
||||
'error',
|
||||
@ -280,7 +280,7 @@ export default function FilesGroupsManagement({
|
||||
setIsGroupModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error handling group:', error);
|
||||
logger.error('Error handling group:', error);
|
||||
showNotification(
|
||||
"Erreur lors de l'opération sur le groupe",
|
||||
'error',
|
||||
@ -300,7 +300,7 @@ export default function FilesGroupsManagement({
|
||||
setIsGroupModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error handling group:', error);
|
||||
logger.error('Error handling group:', error);
|
||||
showNotification(
|
||||
"Erreur lors de l'opération sur le groupe",
|
||||
'error',
|
||||
@ -349,7 +349,7 @@ export default function FilesGroupsManagement({
|
||||
showNotification('Groupe supprimé avec succès.', 'success', 'Succès');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error deleting group:', error);
|
||||
logger.error('Error deleting group:', error);
|
||||
setRemovePopupVisible(false);
|
||||
setIsLoading(false);
|
||||
showNotification(
|
||||
|
||||
@ -54,7 +54,7 @@ const FeesManagement = ({
|
||||
<div className="w-4/5 mx-auto flex items-center mt-8">
|
||||
<hr className="flex-grow border-t-2 border-gray-300" />
|
||||
<span className="mx-4 text-gray-600 font-semibold">
|
||||
Frais d'inscription
|
||||
Frais d'inscription
|
||||
</span>
|
||||
<hr className="flex-grow border-t-2 border-gray-300" />
|
||||
</div>
|
||||
|
||||
@ -257,9 +257,7 @@ const FeesSection = ({
|
||||
onClick={() => {
|
||||
setRemovePopupVisible(true);
|
||||
setRemovePopupMessage(
|
||||
'Attentions ! \nVous êtes sur le point de supprimer un ' +
|
||||
labelTypeFrais +
|
||||
".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?"
|
||||
`Attentions ! \nVous êtes sur le point de supprimer un ${labelTypeFrais} .\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
|
||||
);
|
||||
setRemovePopupOnConfirm(() => () => {
|
||||
handleRemoveFee(fee.id)
|
||||
|
||||
61
Front-End/src/components/WisiwigTextArea.js
Normal file
61
Front-End/src/components/WisiwigTextArea.js
Normal file
@ -0,0 +1,61 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
|
||||
const ReactQuill = dynamic(() => import('react-quill'), { ssr: false });
|
||||
|
||||
export default function WisiwigTextArea({
|
||||
label = 'Mail',
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Ecrivez votre mail ici...',
|
||||
className = 'h-64',
|
||||
required = false,
|
||||
errorMsg,
|
||||
errorLocalMsg,
|
||||
enable = true,
|
||||
}) {
|
||||
return (
|
||||
<div className={`mb-4 ${className}`}>
|
||||
<label className="block text-sm font-medium text-gray-700">
|
||||
{label}
|
||||
{required && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
<div
|
||||
className={`
|
||||
mt-1 border rounded-md
|
||||
${
|
||||
errorMsg || errorLocalMsg
|
||||
? 'border-red-500 hover:border-red-700'
|
||||
: 'border-gray-200 hover:border-gray-400'
|
||||
}
|
||||
${!errorMsg && !errorLocalMsg ? 'focus-within:border-gray-500' : ''}
|
||||
${!enable ? 'bg-gray-100 cursor-not-allowed' : ''}
|
||||
`}
|
||||
>
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={value}
|
||||
onChange={enable ? onChange : undefined}
|
||||
placeholder={placeholder}
|
||||
readOnly={!enable}
|
||||
className={`bg-white rounded-md border-0 shadow-none !border-0 !outline-none ${!enable ? 'bg-gray-100 cursor-not-allowed' : ''}`}
|
||||
style={{ minHeight: 250, border: 'none', boxShadow: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
{errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>}
|
||||
{errorLocalMsg && (
|
||||
<p className="mt-2 text-sm text-red-600">{errorLocalMsg}</p>
|
||||
)}
|
||||
<style jsx global>{`
|
||||
.ql-toolbar.ql-snow {
|
||||
border: none !important;
|
||||
border-bottom: 1px solid #e5e7eb !important; /* gray-200 */
|
||||
border-radius: 0.375rem 0.375rem 0 0 !important; /* rounded-t-md */
|
||||
}
|
||||
.ql-container.ql-snow {
|
||||
border: none !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import { getRequestConfig } from 'next-intl/server';
|
||||
import { routing } from '@/i18n/routing';
|
||||
import { getAvailableNamespaces } from '@/utils/i18n';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default getRequestConfig(async ({ requestLocale }) => {
|
||||
let locale = await requestLocale;
|
||||
@ -22,7 +23,7 @@ export default getRequestConfig(async ({ requestLocale }) => {
|
||||
).default;
|
||||
messages[namespace] = translations;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
logger.warn(
|
||||
`Erreur de chargement pour ${namespace} en ${locale}:`,
|
||||
error
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logger from '@/utils/logger';
|
||||
import { BE_DOCUSEAL_CLONE_TEMPLATE } from '@/utils/Url';
|
||||
|
||||
export default function handler(req, res) {
|
||||
@ -25,11 +26,11 @@ export default function handler(req, res) {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
console.log('Template cloned successfully:', data);
|
||||
logger.debug('Template cloned successfully:', data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error cloning template:', error);
|
||||
logger.error('Error cloning template:', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import logger from '@/utils/logger';
|
||||
import { BE_DOCUSEAL_DOWNLOAD_TEMPLATE } from '@/utils/Url';
|
||||
|
||||
export default function handler(req, res) {
|
||||
if (req.method === 'GET') {
|
||||
const { slug } = req.query;
|
||||
console.log('slug : ', slug);
|
||||
logger.debug('slug : ', slug);
|
||||
|
||||
fetch(`${BE_DOCUSEAL_DOWNLOAD_TEMPLATE}/${slug}`, {
|
||||
method: 'GET',
|
||||
@ -20,11 +21,11 @@ export default function handler(req, res) {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
console.log('Template downloaded successfully:', data);
|
||||
logger.debug('Template downloaded successfully:', data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error downloading template:', error);
|
||||
logger.error('Error downloading template:', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logger from '@/utils/logger';
|
||||
import { BE_DOCUSEAL_GET_JWT } from '@/utils/Url';
|
||||
|
||||
export default function handler(req, res) {
|
||||
@ -11,17 +12,17 @@ export default function handler(req, res) {
|
||||
body: JSON.stringify(req.body),
|
||||
})
|
||||
.then((response) => {
|
||||
console.log('Response status:', response.status);
|
||||
logger.debug('Response status:', response.status);
|
||||
return response
|
||||
.json()
|
||||
.then((data) => ({ status: response.status, data }));
|
||||
})
|
||||
.then(({ status, data }) => {
|
||||
console.log('Response data:', data);
|
||||
logger.debug('Response data:', data);
|
||||
res.status(status).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
logger.error('Error:', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logger from '@/utils/logger';
|
||||
import { BE_DOCUSEAL_REMOVE_TEMPLATE } from '@/utils/Url';
|
||||
|
||||
export default function handler(req, res) {
|
||||
@ -19,11 +20,11 @@ export default function handler(req, res) {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
console.log('Template removed successfully:', data);
|
||||
logger.debug('Template removed successfully:', data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error removing template:', error);
|
||||
logger.error('Error removing template:', error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -54,63 +54,68 @@ export const BE_PLANNING_EVENTS_URL = `${BASE_URL}/Planning/events`;
|
||||
export const BE_GESTIONMESSAGERIE_MESSAGES_URL = `${BASE_URL}/GestionMessagerie/messages`;
|
||||
export const BE_GESTIONMESSAGERIE_MESSAGERIE_URL = `${BASE_URL}/GestionMessagerie/messagerie`;
|
||||
export const BE_GESTIONMESSAGERIE_SEND_MESSAGE_URL = `${BASE_URL}/GestionMessagerie/send-email/`;
|
||||
export const BE_GESTIONMESSAGERIE_SEARCH_RECIPIENTS_URL = `${BASE_URL}/GestionMessagerie/search-recipients`;
|
||||
|
||||
// SETTINGS
|
||||
export const BE_SETTINGS_SMTP_URL = `${BASE_URL}/Settings/smtp-settings`;
|
||||
|
||||
// URL FRONT-END
|
||||
export const FE_HOME_URL = `/`;
|
||||
export const FE_HOME_URL = '/';
|
||||
|
||||
// USERS
|
||||
export const FE_USERS_LOGIN_URL = `/users/login`;
|
||||
export const FE_USERS_SUBSCRIBE_URL = `/users/subscribe`;
|
||||
export const FE_USERS_RESET_PASSWORD_URL = `/users/password/reset`;
|
||||
export const FE_USERS_NEW_PASSWORD_URL = `/users/password/new`;
|
||||
export const FE_USERS_LOGIN_URL = '/users/login';
|
||||
export const FE_USERS_SUBSCRIBE_URL = '/users/subscribe';
|
||||
export const FE_USERS_RESET_PASSWORD_URL = '/users/password/reset';
|
||||
export const FE_USERS_NEW_PASSWORD_URL = '/users/password/new';
|
||||
|
||||
// ADMIN
|
||||
export const FE_ADMIN_HOME_URL = `/admin`;
|
||||
export const FE_ADMIN_HOME_URL = '/admin';
|
||||
|
||||
// ADMIN/SUBSCRIPTIONS URL
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_URL = `/admin/subscriptions`;
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_CREATE_URL = `/admin/subscriptions/createSubscription`;
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_EDIT_URL = `/admin/subscriptions/editSubscription`;
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL = `/admin/subscriptions/validateSubscription`;
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_URL = '/admin/subscriptions';
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_CREATE_URL =
|
||||
'/admin/subscriptions/createSubscription';
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_EDIT_URL =
|
||||
'/admin/subscriptions/editSubscription';
|
||||
export const FE_ADMIN_SUBSCRIPTIONS_VALIDATE_URL =
|
||||
'/admin/subscriptions/validateSubscription';
|
||||
|
||||
//ADMIN/CLASSES URL
|
||||
export const FE_ADMIN_CLASSES_URL = `/admin/classes`;
|
||||
export const FE_ADMIN_CLASSES_URL = '/admin/classes';
|
||||
|
||||
//ADMIN/STRUCTURE URL
|
||||
export const FE_ADMIN_STRUCTURE_URL = `/admin/structure`;
|
||||
export const FE_ADMIN_STRUCTURE_SCHOOLCLASS_MANAGEMENT_URL = `/admin/structure/SchoolClassManagement`;
|
||||
export const FE_ADMIN_STRUCTURE_URL = '/admin/structure';
|
||||
export const FE_ADMIN_STRUCTURE_SCHOOLCLASS_MANAGEMENT_URL =
|
||||
'/admin/structure/SchoolClassManagement';
|
||||
|
||||
//ADMIN/DIRECTORY URL
|
||||
export const FE_ADMIN_DIRECTORY_URL = `/admin/directory`;
|
||||
export const FE_ADMIN_DIRECTORY_URL = '/admin/directory';
|
||||
|
||||
//ADMIN/GRADES URL
|
||||
export const FE_ADMIN_GRADES_URL = `/admin/grades`;
|
||||
export const FE_ADMIN_GRADES_URL = '/admin/grades';
|
||||
|
||||
//ADMIN/TEACHERS URL
|
||||
export const FE_ADMIN_TEACHERS_URL = `/admin/teachers`;
|
||||
export const FE_ADMIN_TEACHERS_URL = '/admin/teachers';
|
||||
|
||||
//ADMIN/PLANNING URL
|
||||
export const FE_ADMIN_PLANNING_URL = `/admin/planning`;
|
||||
export const FE_ADMIN_PLANNING_URL = '/admin/planning';
|
||||
|
||||
//ADMIN/SETTINGS URL
|
||||
export const FE_ADMIN_SETTINGS_URL = `/admin/settings`;
|
||||
export const FE_ADMIN_SETTINGS_URL = '/admin/settings';
|
||||
|
||||
//ADMIN/MESSAGERIE URL
|
||||
export const FE_ADMIN_MESSAGERIE_URL = `/admin/messagerie`;
|
||||
export const FE_ADMIN_MESSAGERIE_URL = '/admin/messagerie';
|
||||
|
||||
// PARENT HOME
|
||||
export const FE_PARENTS_HOME_URL = `/parents`;
|
||||
export const FE_PARENTS_MESSAGERIE_URL = `/parents/messagerie`;
|
||||
export const FE_PARENTS_SETTINGS_URL = `/parents/settings`;
|
||||
export const FE_PARENTS_EDIT_SUBSCRIPTION_URL = `/parents/editSubscription`;
|
||||
export const FE_PARENTS_HOME_URL = '/parents';
|
||||
export const FE_PARENTS_MESSAGERIE_URL = '/parents/messagerie';
|
||||
export const FE_PARENTS_SETTINGS_URL = '/parents/settings';
|
||||
export const FE_PARENTS_EDIT_SUBSCRIPTION_URL = '/parents/editSubscription';
|
||||
|
||||
// API DOCUSEAL
|
||||
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_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';
|
||||
|
||||
/**
|
||||
* Fonction pour obtenir l'URL de redirection en fonction du rôle
|
||||
|
||||
@ -15,6 +15,7 @@ export async function getAvailableNamespaces(locale) {
|
||||
.filter((file) => file.endsWith('.json'))
|
||||
.map((file) => file.replace('.json', ''));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Impossible de lire les namespaces pour ${locale}:`, error);
|
||||
return ['common']; // Namespace par défaut
|
||||
}
|
||||
|
||||
@ -15,21 +15,24 @@ const getCallerInfo = () => {
|
||||
const logger = {
|
||||
debug: (...args) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log.apply(console, ['[DEBUG]', `${getCallerInfo()}`, ...args]);
|
||||
}
|
||||
},
|
||||
error: (...args) => {
|
||||
// Les erreurs sont toujours loguées
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error.apply(console, ['[ERROR]', `${getCallerInfo()}`, ...args]);
|
||||
},
|
||||
warn: (...args) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn.apply(console, ['[WARN]', `${getCallerInfo()}`, ...args]);
|
||||
}
|
||||
},
|
||||
info: (...args) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info.apply(console, ['[INFO]', `${getCallerInfo()}`, ...args]);
|
||||
}
|
||||
},
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
"@commitlint/cli": "^19.5.0",
|
||||
"@commitlint/config-conventional": "^19.5.0",
|
||||
"husky": "^9.1.6",
|
||||
"standard-version": "^9.5.0",
|
||||
"prettier": "^3.5.3"
|
||||
"prettier": "^3.5.3",
|
||||
"standard-version": "^9.5.0"
|
||||
}
|
||||
}
|
||||
@ -1,26 +1,39 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// Chemins vers les fichiers
|
||||
const rootPackageJsonPath = path.resolve(__dirname, '../package.json');
|
||||
const rootPackageJson = JSON.parse(fs.readFileSync(rootPackageJsonPath, 'utf8'));
|
||||
const rootPackageJsonPath = path.resolve(__dirname, "../package.json");
|
||||
const rootPackageJson = JSON.parse(
|
||||
fs.readFileSync(rootPackageJsonPath, "utf8")
|
||||
);
|
||||
// Lire la version actuelle du package.json principal
|
||||
const newVersion = rootPackageJson.version;
|
||||
|
||||
|
||||
const frontendPackageJsonPath = path.resolve(__dirname, '../Front-End/package.json');
|
||||
const frontendPackageJsonPath = path.resolve(
|
||||
__dirname,
|
||||
"../Front-End/package.json"
|
||||
);
|
||||
// Lire et mettre à jour le package.json du sous-module
|
||||
const frontendPackageJson = JSON.parse(fs.readFileSync(frontendPackageJsonPath, 'utf8'));
|
||||
const frontendPackageJson = JSON.parse(
|
||||
fs.readFileSync(frontendPackageJsonPath, "utf8")
|
||||
);
|
||||
frontendPackageJson.version = newVersion;
|
||||
// Écrire les changements dans le fichier frontend/package.json
|
||||
fs.writeFileSync(frontendPackageJsonPath, JSON.stringify(frontendPackageJson, null, 2), 'utf8');
|
||||
fs.writeFileSync(
|
||||
frontendPackageJsonPath,
|
||||
JSON.stringify(frontendPackageJson, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Version mise à jour dans frontend/package.json : ${newVersion}`);
|
||||
|
||||
// Chemin vers le fichier Python de version
|
||||
const versionFilePath = path.resolve(__dirname, '../Back-End/__version__.py');
|
||||
const versionFilePath = path.resolve(__dirname, "../Back-End/__version__.py");
|
||||
|
||||
// Mettre à jour le fichier Python
|
||||
const versionContent = `__version__ = "${newVersion}"\n`;
|
||||
fs.writeFileSync(versionFilePath, versionContent, 'utf8');
|
||||
|
||||
console.log(`Version du backend mise à jour dans ${versionFilePath} : ${newVersion}`);
|
||||
fs.writeFileSync(versionFilePath, versionContent, "utf8");
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`Version du backend mise à jour dans ${versionFilePath} : ${newVersion}`
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user