diff --git a/Back-End/Auth/serializers.py b/Back-End/Auth/serializers.py
index 6f1db3e..3846534 100644
--- a/Back-End/Auth/serializers.py
+++ b/Back-End/Auth/serializers.py
@@ -14,7 +14,7 @@ class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
- fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles', 'roleIndexLoginDefault']
+ fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles', 'roleIndexLoginDefault', 'first_name', 'last_name']
extra_kwargs = {'password': {'write_only': True}}
def get_roles(self, obj):
diff --git a/Back-End/GestionEmail/urls.py b/Back-End/GestionEmail/urls.py
index e9ffee7..4798d6d 100644
--- a/Back-End/GestionEmail/urls.py
+++ b/Back-End/GestionEmail/urls.py
@@ -1,9 +1,10 @@
from django.urls import path
from .views import (
- SendEmailView, search_recipients
+ SendEmailView, search_recipients, SendFeedbackView
)
urlpatterns = [
path('send-email/', SendEmailView.as_view(), name='send_email'),
path('search-recipients/', search_recipients, name='search_recipients'),
+ path('send-feedback/', SendFeedbackView.as_view(), name='send_feedback'),
]
\ No newline at end of file
diff --git a/Back-End/GestionEmail/views.py b/Back-End/GestionEmail/views.py
index 9084539..f83ce1e 100644
--- a/Back-End/GestionEmail/views.py
+++ b/Back-End/GestionEmail/views.py
@@ -5,6 +5,7 @@ from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from django.db.models import Q
+from django.conf import settings
from Auth.models import Profile, ProfileRole
import N3wtSchool.mailManager as mailer
@@ -119,3 +120,84 @@ def search_recipients(request):
})
return JsonResponse(results, safe=False)
+
+
+class SendFeedbackView(APIView):
+ """
+ API pour envoyer un feedback au support (EMAIL_HOST_USER).
+ """
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request):
+ data = request.data
+ category = data.get('category', '')
+ subject = data.get('subject', 'Feedback')
+ message = data.get('message', '')
+ user_email = data.get('user_email', '')
+ user_name = data.get('user_name', '')
+ establishment = data.get('establishment', {})
+
+ logger.info(f"Feedback received - Category: {category}, Subject: {subject}")
+
+ if not message or not subject or not category:
+ return Response(
+ {'error': 'La catégorie, le sujet et le message sont requis.'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ try:
+ # Construire le message formaté
+ category_labels = {
+ 'bug': 'Signalement de bug',
+ 'feature': 'Proposition de fonctionnalité',
+ 'question': 'Question',
+ 'other': 'Autre'
+ }
+ category_label = category_labels.get(category, category)
+
+ # Construire les infos établissement
+ establishment_id = establishment.get('id', 'N/A')
+ establishment_name = establishment.get('name', 'N/A')
+ establishment_capacity = establishment.get('total_capacity', 'N/A')
+ establishment_frequency = establishment.get('evaluation_frequency', 'N/A')
+
+ formatted_message = f"""
+
Nouveau Feedback - {category_label}
+ De: {user_name} ({user_email})
+ Établissement
+
+ - ID: {establishment_id}
+ - Nom: {establishment_name}
+ - Capacité: {establishment_capacity}
+ - Fréquence d'évaluation: {establishment_frequency}
+
+
+ Sujet: {subject}
+
+ Message:
+ {message}
+
+ """
+
+ formatted_subject = f"[N3WT School Feedback] [{category_label}] {subject}"
+
+ # Envoyer à EMAIL_HOST_USER avec la configuration SMTP par défaut
+ result = mailer.sendMail(
+ subject=formatted_subject,
+ message=formatted_message,
+ recipients=[settings.EMAIL_HOST_USER],
+ cc=[],
+ bcc=[],
+ attachments=[],
+ connection=None # Utilise la configuration SMTP par défaut
+ )
+
+ logger.info("Feedback envoyé avec succès")
+ return result
+
+ except Exception as e:
+ logger.error(f"Erreur lors de l'envoi du feedback: {str(e)}", exc_info=True)
+ return Response(
+ {'error': "Erreur lors de l'envoi du feedback"},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
diff --git a/Front-End/messages/en/feedback.json b/Front-End/messages/en/feedback.json
new file mode 100644
index 0000000..4b6ed56
--- /dev/null
+++ b/Front-End/messages/en/feedback.json
@@ -0,0 +1,21 @@
+{
+ "title": "Feedback",
+ "description": "Share your feedback, report a bug, or suggest an improvement. We read every message!",
+ "category_label": "Category",
+ "category_placeholder": "Select a category",
+ "category_bug": "Report a bug",
+ "category_feature": "Suggest a feature",
+ "category_question": "Ask a question",
+ "category_other": "Other",
+ "subject_label": "Subject",
+ "subject_placeholder": "Summarize your request",
+ "message_label": "Message",
+ "message_placeholder": "Describe your feedback in detail...",
+ "send": "Send",
+ "sending": "Sending...",
+ "success": "Success",
+ "success_message": "Your feedback has been sent. Thank you!",
+ "error": "Error",
+ "error_required_fields": "Please fill in all required fields.",
+ "error_sending": "An error occurred while sending your feedback."
+}
diff --git a/Front-End/messages/en/sidebar.json b/Front-End/messages/en/sidebar.json
index 16cc0fe..0572e96 100644
--- a/Front-End/messages/en/sidebar.json
+++ b/Front-End/messages/en/sidebar.json
@@ -7,5 +7,6 @@
"educational_monitoring": "Educational Monitoring",
"settings": "Settings",
"schoolAdmin": "School Administration",
- "messagerie": "Messenger"
+ "messagerie": "Messenger",
+ "feedback": "Feedback"
}
diff --git a/Front-End/messages/fr/feedback.json b/Front-End/messages/fr/feedback.json
new file mode 100644
index 0000000..4a8bfcb
--- /dev/null
+++ b/Front-End/messages/fr/feedback.json
@@ -0,0 +1,21 @@
+{
+ "title": "Feedback",
+ "description": "Partagez vos retours, signalez un bug ou proposez une amélioration. Nous lisons chaque message !",
+ "category_label": "Catégorie",
+ "category_placeholder": "Sélectionnez une catégorie",
+ "category_bug": "Signaler un bug",
+ "category_feature": "Proposer une fonctionnalité",
+ "category_question": "Poser une question",
+ "category_other": "Autre",
+ "subject_label": "Sujet",
+ "subject_placeholder": "Résumez votre demande",
+ "message_label": "Message",
+ "message_placeholder": "Décrivez en détail votre retour...",
+ "send": "Envoyer",
+ "sending": "Envoi en cours...",
+ "success": "Succès",
+ "success_message": "Votre feedback a bien été envoyé. Merci !",
+ "error": "Erreur",
+ "error_required_fields": "Veuillez remplir tous les champs obligatoires.",
+ "error_sending": "Une erreur est survenue lors de l'envoi du feedback."
+}
diff --git a/Front-End/messages/fr/sidebar.json b/Front-End/messages/fr/sidebar.json
index aba45e7..5d88c04 100644
--- a/Front-End/messages/fr/sidebar.json
+++ b/Front-End/messages/fr/sidebar.json
@@ -7,5 +7,6 @@
"educational_monitoring": "Suivi pédagogique",
"settings": "Paramètres",
"schoolAdmin": "Administration Scolaire",
- "messagerie": "Messagerie"
+ "messagerie": "Messagerie",
+ "feedback": "Feedback"
}
diff --git a/Front-End/src/app/[locale]/admin/feedback/page.js b/Front-End/src/app/[locale]/admin/feedback/page.js
new file mode 100644
index 0000000..605f167
--- /dev/null
+++ b/Front-End/src/app/[locale]/admin/feedback/page.js
@@ -0,0 +1,136 @@
+'use client';
+
+import React, { useState } from 'react';
+import { sendFeedback } from '@/app/actions/emailAction';
+import { useNotification } from '@/context/NotificationContext';
+import { useEstablishment } from '@/context/EstablishmentContext';
+import { useTranslations } from 'next-intl';
+import WisiwigTextArea from '@/components/Form/WisiwigTextArea';
+import InputText from '@/components/Form/InputText';
+import Button from '@/components/Form/Button';
+import SelectChoice from '@/components/Form/SelectChoice';
+import logger from '@/utils/logger';
+
+export default function FeedbackPage() {
+ const t = useTranslations('feedback');
+ const { showNotification } = useNotification();
+ const { selectedEstablishmentId, establishments, user } = useEstablishment();
+
+ // Récupérer les infos complètes de l'établissement sélectionné
+ const selectedEstablishment = establishments?.find(
+ (e) => e.id === selectedEstablishmentId
+ );
+
+ const [category, setCategory] = useState('');
+ const [subject, setSubject] = useState('');
+ const [message, setMessage] = useState('');
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const categoryChoices = [
+ { value: 'bug', label: t('category_bug') },
+ { value: 'feature', label: t('category_feature') },
+ { value: 'question', label: t('category_question') },
+ { value: 'other', label: t('category_other') },
+ ];
+
+ const handleSubmit = async () => {
+ if (!category || !subject || !message) {
+ showNotification(t('error_required_fields'), 'error', t('error'));
+ return;
+ }
+
+ setIsSubmitting(true);
+
+ // Construire le nom de l'utilisateur (fallback vers l'email si nom indisponible)
+ const userName = user
+ ? user.first_name && user.last_name
+ ? `${user.first_name} ${user.last_name}`
+ : user.username || user.email?.split('@')[0] || ''
+ : '';
+
+ const feedbackData = {
+ category,
+ subject,
+ message,
+ establishment: selectedEstablishment
+ ? {
+ id: selectedEstablishment.id,
+ name: selectedEstablishment.name,
+ total_capacity: selectedEstablishment.total_capacity,
+ evaluation_frequency: selectedEstablishment.evaluation_frequency,
+ }
+ : { id: selectedEstablishmentId },
+ user_email: user?.email || '',
+ user_name: userName,
+ };
+
+ try {
+ await sendFeedback(feedbackData);
+ showNotification(t('success_message'), 'success', t('success'));
+ // Réinitialiser les champs après succès
+ setCategory('');
+ setSubject('');
+ setMessage('');
+ } catch (error) {
+ logger.error("Erreur lors de l'envoi du feedback:", { error });
+ showNotification(t('error_sending'), 'error', t('error'));
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+ {t('title')}
+
+
{t('description')}
+
+
+ {/* Catégorie */}
+
setCategory(e.target.value)}
+ choices={categoryChoices}
+ placeHolder={t('category_placeholder')}
+ required
+ />
+
+ {/* Sujet */}
+ setSubject(e.target.value)}
+ placeholder={t('subject_placeholder')}
+ required
+ className="mb-4 mt-4"
+ />
+
+ {/* Message */}
+
+
+
+
+ {/* Bouton d'envoi */}
+
+
+
+
+
+
+ );
+}
diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js
index 75587bb..3550808 100644
--- a/Front-End/src/app/[locale]/admin/layout.js
+++ b/Front-End/src/app/[locale]/admin/layout.js
@@ -12,6 +12,7 @@ import {
Calendar,
Settings,
MessageSquare,
+ MessageCircleHeart,
} from 'lucide-react';
import Popup from '@/components/Popup';
@@ -24,6 +25,7 @@ import {
FE_ADMIN_PLANNING_URL,
FE_ADMIN_SETTINGS_URL,
FE_ADMIN_MESSAGERIE_URL,
+ FE_ADMIN_FEEDBACK_URL,
} from '@/utils/Url';
import { disconnect } from '@/app/actions/authAction';
@@ -82,6 +84,12 @@ export default function Layout({ children }) {
url: FE_ADMIN_MESSAGERIE_URL,
icon: MessageSquare,
},
+ feedback: {
+ id: 'feedback',
+ name: t('feedback'),
+ url: FE_ADMIN_FEEDBACK_URL,
+ icon: MessageCircleHeart,
+ },
settings: {
id: 'settings',
name: t('settings'),
diff --git a/Front-End/src/app/[locale]/admin/messagerie/page.js b/Front-End/src/app/[locale]/admin/messagerie/page.js
index eeb5b37..f187024 100644
--- a/Front-End/src/app/[locale]/admin/messagerie/page.js
+++ b/Front-End/src/app/[locale]/admin/messagerie/page.js
@@ -1,17 +1,10 @@
'use client';
import React from 'react';
import SidebarTabs from '@/components/SidebarTabs';
-import EmailSender from '@/components/Admin/EmailSender';
import InstantMessaging from '@/components/Admin/InstantMessaging';
-import logger from '@/utils/logger';
export default function MessageriePage({ csrfToken }) {
const tabs = [
- {
- id: 'email',
- label: 'Envoyer un Mail',
- content: ,
- },
{
id: 'instant',
label: 'Messagerie Instantanée',
diff --git a/Front-End/src/app/actions/emailAction.js b/Front-End/src/app/actions/emailAction.js
index 8fdc3bb..8c38a90 100644
--- a/Front-End/src/app/actions/emailAction.js
+++ b/Front-End/src/app/actions/emailAction.js
@@ -1,6 +1,7 @@
import {
BE_GESTIONEMAIL_SEARCH_RECIPIENTS_URL,
BE_GESTIONEMAIL_SEND_EMAIL_URL,
+ BE_GESTIONEMAIL_SEND_FEEDBACK_URL,
} from '@/utils/Url';
import { fetchWithAuth } from '@/utils/fetchWithAuth';
import { getCsrfToken } from '@/utils/getCsrfToken';
@@ -19,3 +20,13 @@ export const sendEmail = async (messageData) => {
body: JSON.stringify(messageData),
});
};
+
+// Envoyer un feedback au support
+export const sendFeedback = async (feedbackData) => {
+ const csrfToken = getCsrfToken();
+ return fetchWithAuth(BE_GESTIONEMAIL_SEND_FEEDBACK_URL, {
+ method: 'POST',
+ headers: { 'X-CSRFToken': csrfToken },
+ body: JSON.stringify(feedbackData),
+ });
+};
diff --git a/Front-End/src/utils/Url.js b/Front-End/src/utils/Url.js
index 78aa9cf..d6763ab 100644
--- a/Front-End/src/utils/Url.js
+++ b/Front-End/src/utils/Url.js
@@ -54,6 +54,7 @@ export const BE_PLANNING_EVENTS_URL = `${BASE_URL}/Planning/events`;
// GESTION EMAIL
export const BE_GESTIONEMAIL_SEND_EMAIL_URL = `${BASE_URL}/GestionEmail/send-email/`;
export const BE_GESTIONEMAIL_SEARCH_RECIPIENTS_URL = `${BASE_URL}/GestionEmail/search-recipients`;
+export const BE_GESTIONEMAIL_SEND_FEEDBACK_URL = `${BASE_URL}/GestionEmail/send-feedback/`;
// GESTION MESSAGERIE
export const BE_GESTIONMESSAGERIE_CONVERSATIONS_URL = `${BASE_URL}/GestionMessagerie/conversations`;
@@ -123,6 +124,9 @@ export const FE_ADMIN_SETTINGS_URL = '/admin/settings';
//ADMIN/MESSAGERIE URL
export const FE_ADMIN_MESSAGERIE_URL = '/admin/messagerie';
+//ADMIN/FEEDBACK URL
+export const FE_ADMIN_FEEDBACK_URL = '/admin/feedback';
+
// PARENT HOME
export const FE_PARENTS_HOME_URL = '/parents';
export const FE_PARENTS_MESSAGERIE_URL = '/parents/messagerie';