From 23a593dbc77b6f544a17de5a451ff60316f50292 Mon Sep 17 00:00:00 2001 From: Luc SORIGNET Date: Sun, 11 May 2025 14:02:04 +0200 Subject: [PATCH] feat: Messagerie WIP [#17] --- Back-End/Auth/views.py | 4 +- Back-End/GestionMessagerie/urls.py | 3 +- Back-End/GestionMessagerie/views.py | 92 ++++- .../Configuration/application.default.json | 8 + .../mailManager.py | 69 +++- Back-End/N3wtSchool/settings.py | 34 +- Back-End/Settings/views.py | 42 ++- .../Configuration/application.default.json | 4 - .../views/register_form_views.py | 17 +- Front-End/package-lock.json | 325 +++++++++++++----- Front-End/package.json | 2 +- Front-End/src/app/[locale]/admin/layout.js | 4 +- .../src/app/[locale]/admin/messagerie/page.js | 31 +- .../src/app/[locale]/admin/settings/page.js | 108 +++--- .../app/[locale]/parents/messagerie/page.js | 121 +------ Front-End/src/app/actions/messagerieAction.js | 13 +- Front-End/src/app/actions/settingsAction.js | 8 +- .../components/Admin/AnnouncementScheduler.js | 52 +++ Front-End/src/components/Admin/EmailSender.js | 196 ++++++++--- .../src/components/Admin/InstantMessaging.js | 25 ++ Front-End/src/components/AlertMessage.js | 41 ++- Front-End/src/components/Chat.js | 205 +++++++++++ Front-End/src/components/CheckBox.js | 7 +- Front-End/src/components/Footer.js | 2 +- Front-End/src/components/RecipientInput.js | 144 ++++++++ Front-End/src/components/SidebarTabs.js | 4 +- Front-End/src/utils/Url.js | 1 + package.json | 6 +- 28 files changed, 1177 insertions(+), 391 deletions(-) create mode 100644 Back-End/N3wtSchool/Configuration/application.default.json rename Back-End/{Subscriptions => N3wtSchool}/mailManager.py (55%) delete mode 100644 Back-End/Subscriptions/Configuration/application.default.json create mode 100644 Front-End/src/components/Admin/AnnouncementScheduler.js create mode 100644 Front-End/src/components/Admin/InstantMessaging.js create mode 100644 Front-End/src/components/Chat.js create mode 100644 Front-End/src/components/RecipientInput.js diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py index f1730cf..8cd4fd3 100644 --- a/Back-End/Auth/views.py +++ b/Back-End/Auth/views.py @@ -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 @@ -538,7 +538,7 @@ class ProfileRoleView(APIView): profiles_roles_List = profiles_roles_List.filter(role_type=ProfileRole.RoleType.PROFIL_PARENT) elif filter == 'school': profiles_roles_List = profiles_roles_List.filter( - Q(role_type=ProfileRole.RoleType.PROFIL_ECOLE) | + Q(role_type=ProfileRole.RoleType.PROFIL_ECOLE) | Q(role_type=ProfileRole.RoleType.PROFIL_ADMIN) ) else: diff --git a/Back-End/GestionMessagerie/urls.py b/Back-End/GestionMessagerie/urls.py index cb9660a..6cb4da6 100644 --- a/Back-End/GestionMessagerie/urls.py +++ b/Back-End/GestionMessagerie/urls.py @@ -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[0-9]+)$', MessageSimpleView.as_view(), name="messages"), path('send-email/', SendEmailView.as_view(), name='send_email'), + path('search-recipients/', search_recipients, name='search_recipients'), ] \ No newline at end of file diff --git a/Back-End/GestionMessagerie/views.py b/Back-End/GestionMessagerie/views.py index c7c4a7c..64a4047 100644 --- a/Back-End/GestionMessagerie/views.py +++ b/Back-End/GestionMessagerie/views.py @@ -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): @@ -46,21 +52,89 @@ class SendEmailView(APIView): recipients = data.get('recipients', []) 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( + recipients=recipients, + subject=subject, + message=message, + 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) + diff --git a/Back-End/N3wtSchool/Configuration/application.default.json b/Back-End/N3wtSchool/Configuration/application.default.json new file mode 100644 index 0000000..3e9effc --- /dev/null +++ b/Back-End/N3wtSchool/Configuration/application.default.json @@ -0,0 +1,8 @@ +{ + "hostSMTP": "", + "portSMTP": 25, + "username": "", + "password": "", + "useSSL": false, + "useTLS": false +} \ No newline at end of file diff --git a/Back-End/Subscriptions/mailManager.py b/Back-End/N3wtSchool/mailManager.py similarity index 55% rename from Back-End/Subscriptions/mailManager.py rename to Back-End/N3wtSchool/mailManager.py index ddd0648..964d0a1 100644 --- a/Back-End/Subscriptions/mailManager.py +++ b/Back-End/N3wtSchool/mailManager.py @@ -1,8 +1,50 @@ -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(recipients, subject, message, connection=None): + try: + plain_message = strip_tags(message) + from_email = settings.EMAIL_HOST_USER + if connection is None: + send_mail(subject, plain_message, from_email, recipients, html_message=message, fail_silently=False) + else: + send_mail(subject, plain_message, from_email, recipients, html_message=message, connection=connection, 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 +56,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(recipients, subject, html_message) + except Exception as e: errorMessage = str(e) @@ -36,10 +77,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(recipients, subject, html_message) - send_mail(subject, plain_message, from_email, [recipients], html_message=html_message) except Exception as e: errorMessage = str(e) @@ -59,10 +98,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(recipients, subject, html_message) except Exception as e: errorMessage = str(e) @@ -74,13 +110,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(recipients, EMAIL_RELANCE_SUBJECT, EMAIL_RELANCE_CORPUS%str(code)) + except Exception as e: errorMessage = str(e) diff --git a/Back-End/N3wtSchool/settings.py b/Back-End/N3wtSchool/settings.py index 1d9ec1a..072372e 100644 --- a/Back-End/N3wtSchool/settings.py +++ b/Back-End/N3wtSchool/settings.py @@ -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' diff --git a/Back-End/Settings/views.py b/Back-End/Settings/views.py index d3612ce..c13c356 100644 --- a/Back-End/Settings/views.py +++ b/Back-End/Settings/views.py @@ -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 not smtp_settings: - return Response({'error': 'Aucun paramètre SMTP trouvé.'}, status=status.HTTP_404_NOT_FOUND) - serializer = SMTPSettingsSerializer(smtp_settings) - return Response(serializer.data, status=status.HTTP_200_OK) + 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': 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(), diff --git a/Back-End/Subscriptions/Configuration/application.default.json b/Back-End/Subscriptions/Configuration/application.default.json deleted file mode 100644 index 9d2cefb..0000000 --- a/Back-End/Subscriptions/Configuration/application.default.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mailFrom":"", - "password":"" -} \ No newline at end of file diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py index d6a4ddf..fef3e79 100644 --- a/Back-End/Subscriptions/views/register_form_views.py +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -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 @@ -302,7 +302,7 @@ class RegisterFormWithIdView(APIView): initial_pdf = f"{base_dir}/Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf" registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf) registerForm.save() - + # Mise à jour de l'automate # Vérification de la présence du fichier SEPA if registerForm.sepa_file: @@ -331,7 +331,7 @@ class RegisterFormWithIdView(APIView): # Le parent a rempli le dossier d'inscription en sélectionnant "Prélèvement par Mandat SEPA" # L'école doit désormais envoyer le mandat SEPA pour poursuivre l'inscription updateStateMachine(registerForm, 'EVENT_WAITING_FOR_SEPA') - + elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED: # Vérifier si le paramètre fusion est activé via l'URL fusion = data.get('fusionParam', False) @@ -486,15 +486,15 @@ def get_school_file_templates_by_rf(request, id): try: # Récupérer les templates associés au RegistrationForm donné templates = RegistrationSchoolFileTemplate.objects.filter(registration_form=id) - + # Sérialiser les données serializer = RegistrationSchoolFileTemplateSerializer(templates, many=True) - + # Retourner les données sérialisées return JsonResponse(serializer.data, safe=False) except RegistrationSchoolFileTemplate.DoesNotExist: return JsonResponse({'error': 'Aucun template trouvé pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND) - + @swagger_auto_schema( method='get', responses={200: openapi.Response('Success', schema=openapi.Schema( @@ -511,12 +511,11 @@ def get_parent_file_templates_by_rf(request, id): try: # Récupérer les pièces à fournir associés au RegistrationForm donné parent_files = RegistrationParentFileTemplate.objects.filter(registration_form=id) - + # Sérialiser les données serializer = RegistrationParentFileTemplateSerializer(parent_files, many=True) - + # Retourner les données sérialisées 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) - \ No newline at end of file diff --git a/Front-End/package-lock.json b/Front-End/package-lock.json index 3b22305..760648a 100644 --- a/Front-End/package-lock.json +++ b/Front-End/package-lock.json @@ -11,7 +11,6 @@ "@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", @@ -29,6 +28,7 @@ "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": { @@ -1055,25 +1055,6 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, - "node_modules/@tinymce/tinymce-react": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-6.1.0.tgz", - "integrity": "sha512-K0MP3yYVKe8+etUwsg6zyRq+q9TGLaVf005WiBHiB8JZEomAwbBPERGunhU9uOqNQ5gJs8yVOPZ68Xcd1UHclA==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^19.0.0 || ^18.0.0 || ^17.0.1 || ^16.7.0", - "react-dom": "^19.0.0 || ^18.0.0 || ^17.0.1 || ^16.7.0", - "tinymce": "^7.0.0 || ^6.0.0 || ^5.5.1" - }, - "peerDependenciesMeta": { - "tinymce": { - "optional": true - } - } - }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -1110,6 +1091,15 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "license": "MIT", + "dependencies": { + "parchment": "^1.1.2" + } + }, "node_modules/@types/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", @@ -1990,7 +1980,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -2008,7 +1997,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2021,7 +2009,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -2129,6 +2116,15 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2284,6 +2280,26 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2294,7 +2310,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -2311,7 +2326,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -2377,7 +2391,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2480,7 +2493,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2489,7 +2501,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2525,7 +2536,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -3049,11 +3059,29 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3287,7 +3315,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3296,7 +3323,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3328,7 +3354,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3469,7 +3494,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3513,7 +3537,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -3540,7 +3563,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3552,7 +3574,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3668,6 +3689,22 @@ "tslib": "^2.8.0" } }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -3802,7 +3839,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -3923,7 +3959,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -4381,7 +4416,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4706,11 +4740,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4903,6 +4952,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "license": "BSD-3-Clause" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5239,6 +5294,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5283,6 +5339,34 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "license": "BSD-3-Clause", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "license": "MIT", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5369,6 +5453,21 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "license": "MIT", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, "node_modules/react-remove-scroll": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", @@ -5514,7 +5613,6 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -5742,7 +5840,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -5759,7 +5856,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7548,14 +7644,6 @@ "mini-svg-data-uri": "^1.2.3" } }, - "@tinymce/tinymce-react": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-6.1.0.tgz", - "integrity": "sha512-K0MP3yYVKe8+etUwsg6zyRq+q9TGLaVf005WiBHiB8JZEomAwbBPERGunhU9uOqNQ5gJs8yVOPZ68Xcd1UHclA==", - "requires": { - "prop-types": "^15.6.2" - } - }, "@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -7592,6 +7680,14 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "requires": { + "parchment": "^1.1.2" + } + }, "@types/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", @@ -8138,7 +8234,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -8150,7 +8245,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "requires": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -8160,7 +8254,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -8227,6 +8320,11 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -8334,6 +8432,19 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" }, + "deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "requires": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + } + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8344,7 +8455,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "requires": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8355,7 +8465,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "requires": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8409,7 +8518,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -8502,14 +8610,12 @@ "es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" }, "es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "es-iterator-helpers": { "version": "1.2.1", @@ -8539,7 +8645,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "requires": { "es-errors": "^1.3.0" } @@ -8920,11 +9025,26 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, "fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -9084,14 +9204,12 @@ "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "requires": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -9114,7 +9232,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "requires": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -9211,8 +9328,7 @@ "gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "graceful-fs": { "version": "4.2.11", @@ -9241,7 +9357,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "requires": { "es-define-property": "^1.0.0" } @@ -9258,14 +9373,12 @@ "has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "requires": { "has-symbols": "^1.0.3" } @@ -9356,6 +9469,15 @@ "tslib": "^2.8.0" } }, + "is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, "is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9445,7 +9567,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, "requires": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -9521,7 +9642,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, "requires": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -9862,8 +9982,7 @@ "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" }, "merge2": { "version": "1.4.1", @@ -10055,11 +10174,19 @@ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true }, + "object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.7", @@ -10195,6 +10322,11 @@ "p-limit": "^3.0.2" } }, + "parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10397,6 +10529,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -10424,6 +10557,29 @@ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, + "quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "requires": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + } + }, "react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -10482,6 +10638,16 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "requires": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + } + }, "react-remove-scroll": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", @@ -10575,7 +10741,6 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, "requires": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -10711,7 +10876,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "requires": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10725,7 +10889,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, "requires": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", diff --git a/Front-End/package.json b/Front-End/package.json index b865403..18edfab 100644 --- a/Front-End/package.json +++ b/Front-End/package.json @@ -13,7 +13,6 @@ "@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,6 +30,7 @@ "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": { diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js index b6ebcc3..577ef3d 100644 --- a/Front-End/src/app/[locale]/admin/layout.js +++ b/Front-End/src/app/[locale]/admin/layout.js @@ -151,7 +151,7 @@ export default function Layout({ children }) { return ( {/* Topbar */} -
+
+
+
+ setUseTls((prev) => !prev)} // Inverser la valeur booléenne + fieldName="useTls" + itemLabelFunc={() => 'Utiliser TLS'} + /> + setUseSsl((prev) => !prev)} // Inverser la valeur booléenne + fieldName="useSsl" + itemLabelFunc={() => 'Utiliser SSL'} + /> +
+
+ - {statusMessage &&

{statusMessage}

}
diff --git a/Front-End/src/app/[locale]/parents/messagerie/page.js b/Front-End/src/app/[locale]/parents/messagerie/page.js index 1cd7627..9244958 100644 --- a/Front-End/src/app/[locale]/parents/messagerie/page.js +++ b/Front-End/src/app/[locale]/parents/messagerie/page.js @@ -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 ( -
- {' '} - {/* Utilisation de calc pour soustraire la hauteur de l'entête */} -
- {contacts.map((contact) => ( -
setSelectedContact(contact)} - > - {`${contact.name}'s - {contact.name} -
- ))} -
-
-
- {selectedContact && - (messages[selectedContact.id] || []).map((message) => ( -
-
- {`${selectedContact.name}'s - - {selectedContact.name} - - - {new Date(message.date).toLocaleTimeString()} - -
- {message.text} -
- ))} -
-
-
- setNewMessage(e.target.value)} - className="w-full p-2 border border-gray-300 rounded" - placeholder="Écrire un message..." - onKeyDown={handleKeyPress} - /> - -
-
-
- ); + return ; } diff --git a/Front-End/src/app/actions/messagerieAction.js b/Front-End/src/app/actions/messagerieAction.js index 0a25e21..69d6da4 100644 --- a/Front-End/src/app/actions/messagerieAction.js +++ b/Front-End/src/app/actions/messagerieAction.js @@ -1,6 +1,7 @@ import { BE_GESTIONMESSAGERIE_MESSAGES_URL, BE_GESTIONMESSAGERIE_SEND_MESSAGE_URL, + BE_GESTIONMESSAGERIE_SEARCH_RECIPIENTS_URL, } from '@/utils/Url'; const requestResponseHandler = async (response) => { @@ -8,8 +9,6 @@ const requestResponseHandler = async (response) => { 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; @@ -33,3 +32,13 @@ export const sendMessage = (data, csrfToken) => { body: JSON.stringify(data), }).then(requestResponseHandler); }; + +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); +}; diff --git a/Front-End/src/app/actions/settingsAction.js b/Front-End/src/app/actions/settingsAction.js index 8d1229f..e9b24d5 100644 --- a/Front-End/src/app/actions/settingsAction.js +++ b/Front-End/src/app/actions/settingsAction.js @@ -15,8 +15,12 @@ const requestResponseHandler = async (response) => { throw error; }; -export const fetchSmtpSettings = (csrfToken) => { - return fetch(`${BE_SETTINGS_SMTP_URL}/`, { +export const fetchSmtpSettings = (csrfToken, establishment_id = null) => { + let url = `${BE_SETTINGS_SMTP_URL}/`; + if (establishment_id) { + url += `?establishment_id=${establishment_id}`; + } + return fetch(`${url}`, { headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken, diff --git a/Front-End/src/components/Admin/AnnouncementScheduler.js b/Front-End/src/components/Admin/AnnouncementScheduler.js new file mode 100644 index 0000000..be962ab --- /dev/null +++ b/Front-End/src/components/Admin/AnnouncementScheduler.js @@ -0,0 +1,52 @@ +'use client'; +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 + console.log('Annonce planifiée:', { title, date, message }); + }; + + return ( +
+

Planifier une Annonce

+
+ + setTitle(e.target.value)} + className="w-full p-2 border rounded" + /> +
+
+ + setDate(e.target.value)} + className="w-full p-2 border rounded" + /> +
+
+ +