diff --git a/Back-End/Auth/models.py b/Back-End/Auth/models.py
index f148dd7..2728edb 100644
--- a/Back-End/Auth/models.py
+++ b/Back-End/Auth/models.py
@@ -9,7 +9,7 @@ class Profile(AbstractUser):
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ('password', )
-
+ roleIndexLoginDefault = models.IntegerField(default=0)
code = models.CharField(max_length=200, default="", blank=True)
datePeremption = models.CharField(max_length=200, default="", blank=True)
diff --git a/Back-End/Auth/serializers.py b/Back-End/Auth/serializers.py
index 5085eb3..0ba320e 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']
+ fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles', 'roleIndexLoginDefault']
extra_kwargs = {'password': {'write_only': True}}
def get_roles(self, obj):
@@ -53,10 +53,10 @@ class ProfileSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
password = validated_data.pop('password', None)
instance = super().update(instance, validated_data)
-
+
if password:
instance.set_password(password)
-
+
instance.full_clean()
instance.save()
return instance
@@ -114,7 +114,7 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
if obj.profile:
return obj.profile.email
return None
-
+
def get_associated_person(self, obj):
if obj.role_type == ProfileRole.RoleType.PROFIL_PARENT:
guardian = Guardian.objects.filter(profile_role=obj).first()
diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py
index 8955b40..69f6f03 100644
--- a/Back-End/Auth/views.py
+++ b/Back-End/Auth/views.py
@@ -54,6 +54,7 @@ class SessionView(APIView):
'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
'email': openapi.Schema(type=openapi.TYPE_STRING),
+ 'roleIndexLoginDefault': openapi.Schema(type=openapi.TYPE_INTEGER),
'roles': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Items(type=openapi.TYPE_OBJECT, properties={
'role_type': openapi.Schema(type=openapi.TYPE_STRING),
'establishment': openapi.Schema(type=openapi.TYPE_STRING)
@@ -65,18 +66,16 @@ class SessionView(APIView):
)
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', '').split('Bearer ')[-1]
-
try:
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
userid = decoded_token.get('user_id')
user = Profile.objects.get(id=userid)
-
roles = ProfileRole.objects.filter(profile=user).values('role_type', 'establishment__name')
-
response_data = {
'user': {
'id': user.id,
'email': user.email,
+ 'roleIndexLoginDefault': user.roleIndexLoginDefault,
'roles': list(roles)
}
}
@@ -157,11 +156,10 @@ class LoginView(APIView):
operation_description="Connexion utilisateur",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
- required=['email', 'password', 'role_type'],
+ required=['email', 'password'],
properties={
'email': openapi.Schema(type=openapi.TYPE_STRING),
- 'password': openapi.Schema(type=openapi.TYPE_STRING),
- 'role_type': openapi.Schema(type=openapi.TYPE_STRING)
+ 'password': openapi.Schema(type=openapi.TYPE_STRING)
}
),
responses={
@@ -194,38 +192,15 @@ class LoginView(APIView):
password=data.get('password'),
)
if user is not None:
- role_type = data.get('role_type')
- primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
-
- if not primary_role:
+ # Vérifier si l'utilisateur a un role actif
+ has_active_role = ProfileRole.objects.filter(profile=user, is_active=True).first()
+ if not has_active_role:
return JsonResponse({"errorMessage": "Profil inactif"}, status=status.HTTP_401_UNAUTHORIZED)
login(request, user)
user.save()
retour = ''
-
- # Récupérer tous les rôles de l'utilisateur avec le type spécifié
- roles = ProfileRole.objects.filter(profile=user, role_type=role_type).values('role_type', 'establishment__id', 'establishment__name')
-
- # Générer le JWT avec la bonne syntaxe datetime
- access_payload = {
- 'user_id': user.id,
- 'email': user.email,
- 'roles': list(roles),
- 'type': 'access',
- 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
- 'iat': datetime.utcnow(),
- }
-
- access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
- # Générer le Refresh Token (exp: 7 jours)
- refresh_payload = {
- 'user_id': user.id,
- 'type': 'refresh',
- 'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
- 'iat': datetime.utcnow(),
- }
- refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
+ access_token, refresh_token = makeToken(user)
return JsonResponse({
'token': access_token,
@@ -299,35 +274,10 @@ class RefreshJWTView(APIView):
# Récupérer les informations utilisateur
user = Profile.objects.get(id=payload['user_id'])
- role_type = payload.get('role_type')
+ if not user:
+ return JsonResponse({'errorMessage': 'Utilisateur non trouvé'}, status=404)
- # Récupérer le rôle principal de l'utilisateur
- primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
-
- if not primary_role:
- return JsonResponse({'errorMessage': 'Profil inactif'}, status=400)
-
- # Générer un nouveau Access Token avec les informations complètes
- new_access_payload = {
- 'user_id': user.id,
- 'email': user.email,
- 'role_type': primary_role.get_role_type_display(),
- 'establishment': primary_role.establishment.id,
- 'type': 'access',
- 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
- 'iat': datetime.utcnow(),
- }
-
- new_access_token = jwt.encode(new_access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
-
- new_refresh_payload = {
- 'user_id': user.id,
- 'role_type': role_type,
- 'type': 'refresh',
- 'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
- 'iat': datetime.utcnow(),
- }
- new_refresh_token = jwt.encode(new_refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
+ new_access_payload, new_refresh_token = makeToken(user)
return JsonResponse({'token': new_access_token, 'refresh': new_refresh_token}, status=200)
@@ -341,6 +291,38 @@ class RefreshJWTView(APIView):
logger.error(f"Erreur inattendue: {str(e)}")
return JsonResponse({'errorMessage': f'Erreur inattendue: {str(e)}'}, status=400)
+def makeToken(user):
+ """
+ Fonction pour créer un token JWT pour l'utilisateur donné.
+ """
+ try:
+ # Récupérer tous les rôles de l'utilisateur actifs
+ roles = ProfileRole.objects.filter(profile=user, is_active=True).values('role_type', 'establishment__id', 'establishment__name')
+
+ # Générer le JWT avec la bonne syntaxe datetime
+ access_payload = {
+ 'user_id': user.id,
+ 'email': user.email,
+ 'roleIndexLoginDefault':user.roleIndexLoginDefault,
+ 'roles': list(roles),
+ 'type': 'access',
+ 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
+ 'iat': datetime.utcnow(),
+ }
+
+ access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
+ # Générer le Refresh Token (exp: 7 jours)
+ refresh_payload = {
+ 'user_id': user.id,
+ 'type': 'refresh',
+ 'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
+ 'iat': datetime.utcnow(),
+ }
+ refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
+ return access_token, refresh_token
+ except Exception as e:
+ logger.error(f"Erreur lors de la création du token: {str(e)}")
+ return None
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js
index 72d15b8..b3c7f3f 100644
--- a/Front-End/src/app/[locale]/admin/layout.js
+++ b/Front-End/src/app/[locale]/admin/layout.js
@@ -131,7 +131,6 @@ export default function Layout({
if (roleIndex === -1) {
roleIndex = 0;
}
- setCurrentRoleIndex(roleIndex);
const role = session.user.roles[roleIndex].role_type;
setProfileRole(role);
}}
diff --git a/Front-End/src/app/[locale]/admin/page.js b/Front-End/src/app/[locale]/admin/page.js
index dc129ae..db781da 100644
--- a/Front-End/src/app/[locale]/admin/page.js
+++ b/Front-End/src/app/[locale]/admin/page.js
@@ -10,7 +10,7 @@ import logger from '@/utils/logger';
import { fetchRegisterForms } from '@/app/actions/subscriptionAction';
import { fetchUpcomingEvents } from '@/app/actions/planningAction';
import { getSession } from 'next-auth/react';
-import { getCurrentRoleIndex } from '@/store/Store';
+import { useEstablishment } from '@/context/EstablishmentContext';
// Composant EventCard pour afficher les événements
@@ -42,14 +42,11 @@ export default function DashboardPage() {
const [classes, setClasses] = useState([]);
const [establishmentId, setEstablishmentId] = useState(null);
-
+ const { selectedEstablishmentId } = useEstablishment();
useEffect(() => {
getSession()
.then(session => {
- if (session && session.user) {
- const establishmentId = session.user.roles[getCurrentRoleIndex()].establishment__id;
- setEstablishmentId(establishmentId);
- }
+ setEstablishmentId(selectedEstablishmentId);
})
.catch(err => {
logger.error('Error fetching session:', err);
diff --git a/Front-End/src/app/[locale]/parents/layout.js b/Front-End/src/app/[locale]/parents/layout.js
index a94fb6e..3076328 100644
--- a/Front-End/src/app/[locale]/parents/layout.js
+++ b/Front-End/src/app/[locale]/parents/layout.js
@@ -2,6 +2,7 @@
// src/components/Layout.js
import React, { useState, useEffect } from 'react';
import DropdownMenu from '@/components/DropdownMenu';
+import ProfileSelector from '@/components/ProfileSelector';
import { useRouter } from 'next/navigation'; // Ajout de l'importation
import { User, MessageSquare, LogOut, Settings, Home } from 'lucide-react'; // Ajout de l'importation de l'icône Home
import Logo from '@/components/Logo'; // Ajout de l'importation du composant Logo
@@ -11,11 +12,11 @@ import ProtectedRoute from '@/components/ProtectedRoute';
import { disconnect } from '@/app/actions/authAction';
import Popup from '@/components/Popup';
import logger from '@/utils/logger';
-import { useSession } from 'next-auth/react';
-import { FE_USERS_LOGIN_URL } from '@/utils/Url';
import { getRightStr, RIGHTS } from '@/utils/rights';
import { getGravatarUrl } from '@/utils/gravatar';
+import { useEstablishment } from '@/context/EstablishmentContext';
import Image from 'next/image';
+import Footer from '@/components/Footer';
export default function Layout({
children,
@@ -23,11 +24,12 @@ export default function Layout({
const router = useRouter(); // Définition de router
const [messages, setMessages] = useState([]);
- const { data: session, status } = useSession();
- const [userId, setUserId] = useState(null);
- const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isPopupVisible, setIsPopupVisible] = useState(false);
+ const { profileRole, user } = useEstablishment();
+ const softwareName = "N3WT School";
+ const softwareVersion = `${process.env.NEXT_PUBLIC_APP_VERSION}`;
+
const handleDisconnect = () => {
setIsPopupVisible(true);
@@ -36,42 +38,15 @@ export default function Layout({
const confirmDisconnect = () => {
setIsPopupVisible(false);
disconnect();
- };
+ };
- // useEffect(() => {
- // if (status === 'loading') return;
- // if (!session) {
- // router.push(`${FE_USERS_LOGIN_URL}`);
- // }
-
- // const userIdFromSession = session.user.id;
- // setUserId(userIdFromSession);
- // setIsLoading(true);
- // fetchMessages(userId)
- // .then(data => {
- // if (data) {
- // setMessages(data);
- // }
- // logger.debug('Success :', data);
- // })
- // .catch(error => {
- // logger.error('Error fetching data:', error);
- // })
- // .finally(() => {
- // setIsLoading(false);
- // });
- // }, [userId]);
-
- // if (isLoading) {
- // return
Loading...
;
- // }
const dropdownItems = [
{
type: 'info',
content: (
{user?.email || 'Utilisateur'}
-
{getRightStr(user?.roles[0]?.role_type) || ''}
+
{getRightStr(profileRole) || ''}
)
},
@@ -91,11 +66,14 @@ const dropdownItems = [
+ {/* Footer responsive */}
+
{
- if (status === 'loading') return;
-
- if (!session || !session.user) {
- router.push(`${FE_USERS_LOGIN_URL}`);
- } else {
- const userIdFromSession = session.user.user_id;
+ const userIdFromSession = user.user_id;
setUserId(userIdFromSession);
-
- const userEstablishments = session.user.roles.map(role => ({
- id: role.establishment__id,
- name: role.establishment__name,
- role_type: role.role_type
- }));
- setEstablishments(userEstablishments);
-
- if (!selectedEstablishmentId && userEstablishments.length > 0) {
- setSelectedEstablishmentId(userEstablishments[0].id);
- }
console.log(selectedEstablishmentId)
fetchChildren(userIdFromSession, selectedEstablishmentId).then(data => {
setChildren(data);
});
- }
- }, [status, session, selectedEstablishmentId]);
+
+ }, [ selectedEstablishmentId]);
const handleEstablishmentChange = (e) => {
const establishmentId = parseInt(e.target.value, 10);
setSelectedEstablishmentId(establishmentId);
+ const role = establishments.find(est => est.id === establishmentId)?.role_type;
+ setProfileRole(role);
};
-
function handleEdit(eleveId) {
// Logique pour éditer le dossier de l'élève
logger.debug(`Edit dossier for student id: ${eleveId}`);
@@ -107,22 +89,6 @@ export default function ParentHomePage() {
Enfants
- {establishments.length > 1 && (
-
-
-
-
- )}
{
logger.debug('Sign In Result', result);
@@ -58,18 +58,16 @@ export default function Page() {
}
const user = session.user;
logger.debug('User Session:', user);
-
- const roles = user.roles.filter(role => role.role_type === selectedProfile);
- if (roles.length > 0) {
- // Redirection en fonction du rôle
- // Ne pas désactiver le loader avant la redirection
- const currentRoleIndex = 0;
- setCurrentRoleIndex(currentRoleIndex);
- const role = roles[currentRoleIndex].role_type;
- if (role === RIGHTS.ADMIN || role === RIGHTS.TEACHER) {
- router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
- } else if (role === RIGHTS.PARENT) {
- router.push(FE_PARENTS_HOME_URL);
+ setUser(session.user);
+ if (session.user.roles && session.user.roles.length > 0) {
+ let roleIndex = 0;
+ if( session.user.roles.length > session.user.roleIndexLoginDefault){
+ roleIndex = session.user.roleIndexLoginDefault;
+ }
+ const role = session.user.roles[roleIndex].role_type;
+ const url = getRedirectUrlFromRole(role);
+ if (url) {
+ router.push(url);
} else {
setIsLoading(false);
setErrorMessage('Type de rôle non géré');
@@ -104,7 +102,6 @@ export default function Page() {
-
diff --git a/Front-End/src/components/ProfileSelector.js b/Front-End/src/components/ProfileSelector.js
index 5f86e1d..2da1087 100644
--- a/Front-End/src/components/ProfileSelector.js
+++ b/Front-End/src/components/ProfileSelector.js
@@ -1,29 +1,65 @@
-import React from 'react';
+import React, { useState } from 'react';
+import { useEstablishment } from '@/context/EstablishmentContext';
+import DropdownMenu from '@/components/DropdownMenu';
+import { getRightStr } from '@/utils/rights';
+import { ChevronDown } from 'lucide-react'; // Import de l'icône
+
+const ProfileSelector = ({ onEstablishmentChange, className = '' }) => {
+ const { establishments, selectedEstablishmentId, setSelectedEstablishmentId, setProfileRole } = useEstablishment();
+ const [dropdownOpen, setDropdownOpen] = useState(false);
+
+ const handleEstablishmentChange = (establishmentId) => {
+ setSelectedEstablishmentId(establishmentId);
+ const role = establishments.find(est => est.id === establishmentId)?.role_type;
+ setProfileRole(role);
+
+ if (onEstablishmentChange) {
+ onEstablishmentChange(establishmentId);
+ }
+ setDropdownOpen(false); // Fermer le menu après sélection
+ };
+
+ // Si on a pas de rôle ou un seul rôle, on n'affiche pas le sélecteur
+ if (!establishments || establishments.length === 0 || establishments.length === 1) {
+ return null;
+ }
+
+ const selectedEstablishment = establishments.find(est => est.id === selectedEstablishmentId);
-const ProfileSelector = ({ selectedProfile, setSelectedProfile }) => {
return (
-
-
-
-
+
+
+
+
{getRightStr(selectedEstablishment?.role_type) || ''}
+
+ {selectedEstablishment?.name || 'Sélectionnez un établissement'}
+
+
+ {/* Icône ChevronDown avec rotation conditionnelle */}
+
+
+ }
+ items={establishments.map(establishment => ({
+ type: 'item',
+ label: (
+
+
{getRightStr(establishment.role_type)}
+
{establishment.name}
+
+ ),
+ onClick: () => handleEstablishmentChange(establishment.id),
+ }))}
+ buttonClassName="w-full"
+ menuClassName="absolute mt-2 w-full bg-white border border-gray-200 rounded shadow-lg z-10"
+ dropdownOpen={dropdownOpen}
+ setDropdownOpen={setDropdownOpen}
+ />
);
};
diff --git a/Front-End/src/components/ProtectedRoute.js b/Front-End/src/components/ProtectedRoute.js
index fd13f76..fa26ae6 100644
--- a/Front-End/src/components/ProtectedRoute.js
+++ b/Front-End/src/components/ProtectedRoute.js
@@ -1,33 +1,33 @@
-import React, { useEffect } from 'react';
+import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-import Loader from '@/components/Loader'; // Importez le composant Loader
-import { FE_USERS_LOGIN_URL } from '@/utils/Url';
+import { useEstablishment } from '@/context/EstablishmentContext';
+import { FE_USERS_LOGIN_URL,getRedirectUrlFromRole } from '@/utils/Url';
const ProtectedRoute = ({ children, requiredRight }) => {
- const { data: session, status } = useSession({
- required: true,
- onUnauthenticated() {
- router.push(`${FE_USERS_LOGIN_URL}`);
- }
- });
- const router = useRouter();
- // Ne vérifier que si le statut est définitif
- if (status === 'loading') {
- return ;
- }
+ const { user, profileRole } = useEstablishment();
+ const router = useRouter();
+ const hasRequiredRight = (profileRole === requiredRight);
// Vérifier si l'utilisateur a au moins un rôle correspondant au requiredRight
- const hasRequiredRight = session?.user?.roles?.some(role => role.role_type === requiredRight);
+ useEffect(() => {
+ if(user){
+ // Vérifier si l'utilisateur a le droit requis mais pas le bon role on le redirige la page d'accueil associé au role
+ if (!hasRequiredRight) {
+ const redirectUrl = getRedirectUrlFromRole(profileRole);
+ router.push(`${redirectUrl}`);
+ }
+
+ }else{
+ // User non authentifié
+ router.push(`${FE_USERS_LOGIN_URL}`);
+ }
+ }, [profileRole]);
+
- if (session && requiredRight && !hasRequiredRight) {
- router.push(`${FE_USERS_LOGIN_URL}`);
- return null;
- }
// Autoriser l'affichage si authentifié et rôle correct
- return session ? children : null;
+ return hasRequiredRight ? children : null;
};
export default ProtectedRoute;
\ No newline at end of file
diff --git a/Front-End/src/components/Sidebar.js b/Front-End/src/components/Sidebar.js
index 8b52f19..0e46cf8 100644
--- a/Front-End/src/components/Sidebar.js
+++ b/Front-End/src/components/Sidebar.js
@@ -1,7 +1,7 @@
'use client'
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { useEstablishment } from '@/context/EstablishmentContext';
+import ProfileSelector from '@/components/ProfileSelector';
const SidebarItem = ({ icon: Icon, text, active, url, onClick }) => (
(
);
-function Sidebar({ establishments, currentPage, items, onCloseMobile, onEstablishmentChange }) {
+function Sidebar({ currentPage, items, onCloseMobile, onEstablishmentChange }) {
const router = useRouter();
- const { selectedEstablishmentId, setSelectedEstablishmentId, setProfileRole } = useEstablishment();
+
const [selectedItem, setSelectedItem] = useState(currentPage);
useEffect(() => {
@@ -32,31 +32,15 @@ function Sidebar({ establishments, currentPage, items, onCloseMobile, onEstablis
}
};
- const handleEstablishmentChange = (e) => {
- const establishmentId = parseInt(e.target.value, 10);
- setSelectedEstablishmentId(establishmentId);
- const role = establishments.find(est => est.id === establishmentId)?.role_type;
- setProfileRole(role);
- onEstablishmentChange(establishmentId);
- };
-
return (
-
-
-
+
+
-
-