feat: add DCB User Service API - Authentication system with KEYCLOAK - Modular architecture with services for each feature
This commit is contained in:
parent
d586552944
commit
35f0bcd135
@ -34,7 +34,7 @@ export const routes: Routes = [
|
||||
// ===== REDIRECTIONS POUR LES ERREURS =====
|
||||
{ path: '404', redirectTo: '/error/404' },
|
||||
{ path: '403', redirectTo: '/error/403' },
|
||||
{ path: '401', redirectTo: '/auth/sign-in' },
|
||||
{ path: '401', redirectTo: '/auth/login' },
|
||||
{ path: 'unauthorized', redirectTo: '/error/403' },
|
||||
|
||||
// ===== CATCH-ALL =====
|
||||
|
||||
@ -25,23 +25,32 @@ export class App implements OnInit {
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
try {
|
||||
// Initialiser l'authentification avec gestion d'erreur
|
||||
const isAuthenticated = await this.authService.initialize();
|
||||
|
||||
console.log('Authentication initialized:', isAuthenticated);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
console.log('👤 User not authenticated, may redirect to login');
|
||||
// Note: Votre AuthService gère déjà la redirection dans logout()
|
||||
if (!isAuthenticated && this.router.url === '/') {
|
||||
this.router.navigate(['/auth/login']);
|
||||
} else if (isAuthenticated && this.router.url === '/') {
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during authentication initialization:', error);
|
||||
this.router.navigate(['/auth/login']);
|
||||
}
|
||||
|
||||
// Configurer le titre de la page
|
||||
this.setupTitleListener();
|
||||
}
|
||||
|
||||
private checkPublicRouteRedirection(): void {
|
||||
const currentUrl = this.router.url;
|
||||
const publicRoutes = ['/auth/login', '/auth/reset-password', '/auth/forgot-password'];
|
||||
|
||||
// Si l'utilisateur est authentifié et sur une route publique, rediriger vers la page d'accueil
|
||||
if (publicRoutes.includes(currentUrl)) {
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
}
|
||||
}
|
||||
|
||||
private setupTitleListener(): void {
|
||||
this.router.events
|
||||
.pipe(
|
||||
|
||||
@ -10,14 +10,11 @@ export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: R
|
||||
const roleService = inject(RoleService);
|
||||
const router = inject(Router);
|
||||
|
||||
console.log('🔐 AuthGuard check for:', state.url);
|
||||
|
||||
// Attendre que l'initialisation soit terminée
|
||||
return authService.getInitializedState().pipe(
|
||||
switchMap(initialized => {
|
||||
if (!initialized) {
|
||||
console.log('⏳ AuthService pas encore initialisé, attente...');
|
||||
return of(false); // Bloquer en attendant l'initialisation
|
||||
return of(false);
|
||||
}
|
||||
|
||||
// Vérifier l'authentification
|
||||
@ -28,29 +25,24 @@ export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: R
|
||||
// Tentative de rafraîchissement du token
|
||||
const refreshToken = authService.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
console.log('🔄 Token expiré, tentative de rafraîchissement...');
|
||||
|
||||
return authService.refreshToken().pipe(
|
||||
tap(() => {
|
||||
console.log('✅ Token rafraîchi avec succès');
|
||||
roleService.refreshRoles();
|
||||
}),
|
||||
map(() => checkRoleAccess(route, roleService, router, state.url)),
|
||||
catchError((error) => {
|
||||
console.error('❌ Échec du rafraîchissement du token:', error);
|
||||
authService.logout().subscribe();
|
||||
return of(redirectToLogin(router, state.url, 'session_expired'));
|
||||
return of(redirectToLogin(router, state.url));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Redirection vers login
|
||||
console.log('🔒 Redirection vers login depuis:', state.url);
|
||||
return of(redirectToLogin(router, state.url, 'not_authenticated'));
|
||||
return of(redirectToLogin(router, state.url));
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('❌ Erreur dans le guard d\'auth:', error);
|
||||
return of(redirectToLogin(router, state.url, 'not_authenticated'));
|
||||
return of(redirectToLogin(router, state.url));
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -67,7 +59,6 @@ function checkRoleAccess(
|
||||
const requiredRoles = route.data?.['roles'] as string[];
|
||||
|
||||
if (!requiredRoles || requiredRoles.length === 0) {
|
||||
console.log('✅ Accès autorisé (aucun rôle requis):', currentUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -75,17 +66,9 @@ function checkRoleAccess(
|
||||
const currentUserRoles = roleService.getCurrentUserRoles();
|
||||
|
||||
if (hasRequiredRole) {
|
||||
console.log('✅ Accès autorisé avec rôles:', currentUrl);
|
||||
console.log(' Rôles requis:', requiredRoles);
|
||||
console.log(' Rôles actuels:', currentUserRoles);
|
||||
return true;
|
||||
}
|
||||
|
||||
console.warn('❌ Accès refusé: rôles insuffisants');
|
||||
console.warn(' URL demandée:', currentUrl);
|
||||
console.warn(' Rôles requis:', requiredRoles);
|
||||
console.warn(' Rôles actuels:', currentUserRoles);
|
||||
|
||||
// Rediriger vers la page non autorisée
|
||||
router.navigate(['/unauthorized'], {
|
||||
queryParams: {
|
||||
@ -105,19 +88,13 @@ function checkRoleAccess(
|
||||
function redirectToLogin(
|
||||
router: Router,
|
||||
returnUrl: string,
|
||||
reason: 'not_authenticated' | 'session_expired'
|
||||
): boolean {
|
||||
const queryParams: any = {
|
||||
returnUrl: returnUrl,
|
||||
reason: reason
|
||||
returnUrl: returnUrl
|
||||
};
|
||||
|
||||
// Message spécifique selon la raison
|
||||
if (reason === 'session_expired') {
|
||||
queryParams.message = 'Votre session a expiré. Veuillez vous reconnecter.';
|
||||
}
|
||||
|
||||
router.navigate(['/auth/sign-in'], {
|
||||
router.navigate(['/auth/login'], {
|
||||
queryParams,
|
||||
replaceUrl: true
|
||||
});
|
||||
|
||||
@ -10,7 +10,6 @@ export const publicGuard: CanActivateFn = () => {
|
||||
|
||||
// Si l'utilisateur est déjà authentifié, le rediriger vers le dashboard
|
||||
if (authService.isAuthenticated()) {
|
||||
console.log('🔄 Utilisateur déjà authentifié, redirection vers le dashboard');
|
||||
router.navigate(['/dcb-dashboard'], { replaceUrl: true });
|
||||
return false;
|
||||
}
|
||||
@ -18,16 +17,12 @@ export const publicGuard: CanActivateFn = () => {
|
||||
// Vérifier si un refresh token est disponible
|
||||
const refreshToken = authService.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
console.log('🔄 Token de rafraîchissement détecté, tentative de reconnexion...');
|
||||
|
||||
return authService.refreshToken().pipe(
|
||||
map(() => {
|
||||
console.log('✅ Reconnexion automatique réussie');
|
||||
router.navigate(['/dcb-dashboard'], { replaceUrl: true });
|
||||
return false;
|
||||
}),
|
||||
catchError((error) => {
|
||||
console.error('❌ Échec de la reconnexion automatique:', error);
|
||||
// En cas d'erreur, autoriser l'accès à la page publique
|
||||
return of(true);
|
||||
})
|
||||
@ -35,6 +30,5 @@ export const publicGuard: CanActivateFn = () => {
|
||||
}
|
||||
|
||||
// L'utilisateur n'est pas connecté, autoriser l'accès à la page publique
|
||||
console.log('🌐 Accès public autorisé');
|
||||
return true;
|
||||
};
|
||||
@ -10,7 +10,6 @@ export const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state) =
|
||||
|
||||
// Vérifier d'abord l'authentification
|
||||
if (!authService.isAuthenticated()) {
|
||||
console.log('RoleGuard: User not authenticated, redirecting to login');
|
||||
router.navigate(['/auth/login'], {
|
||||
queryParams: {
|
||||
returnUrl: state.url,
|
||||
@ -24,26 +23,16 @@ export const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state) =
|
||||
const userRoles = authService.getCurrentUserRoles();
|
||||
|
||||
if (!userRoles || userRoles.length === 0) {
|
||||
console.warn('RoleGuard: User has no roles');
|
||||
router.navigate(['/unauthorized'], {
|
||||
queryParams: { reason: 'no_roles' }
|
||||
});
|
||||
router.navigate(['/unauthorized']);
|
||||
return false;
|
||||
}
|
||||
|
||||
const modulePath = getModulePath(route);
|
||||
|
||||
console.log('🔐 RoleGuard check:', {
|
||||
module: modulePath,
|
||||
userRoles: userRoles,
|
||||
url: state.url
|
||||
});
|
||||
|
||||
// Vérifier les permissions
|
||||
const hasAccess = permissionsService.canAccessModule(modulePath, userRoles);
|
||||
|
||||
if (!hasAccess) {
|
||||
console.warn('❌ RoleGuard: Access denied for', modulePath, 'User roles:', userRoles);
|
||||
router.navigate(['/unauthorized'], {
|
||||
queryParams: {
|
||||
module: modulePath,
|
||||
@ -54,7 +43,6 @@ export const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state) =
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('✅ RoleGuard: Access granted for', modulePath);
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -92,8 +80,6 @@ function buildPathFromUrl(route: ActivatedRouteSnapshot): string {
|
||||
}
|
||||
|
||||
function getRequiredRolesForModule(permissionsService: PermissionsService, modulePath: string): string[] {
|
||||
// Cette fonction récupère les rôles requis pour un module donné
|
||||
// Vous devrez peut-être l'adapter selon votre implémentation PermissionsService
|
||||
const [mainModule] = modulePath.split('/');
|
||||
const permission = (permissionsService as any).findPermission?.(mainModule);
|
||||
return permission?.roles || [];
|
||||
|
||||
@ -55,7 +55,7 @@ function handle401Error(
|
||||
}),
|
||||
catchError((refreshError) => {
|
||||
authService.logout().subscribe();
|
||||
router.navigate(['/auth/sign-in']);
|
||||
router.navigate(['/auth/login']);
|
||||
return throwError(() => refreshError);
|
||||
})
|
||||
);
|
||||
|
||||
@ -38,7 +38,14 @@ export interface UserProfileDto {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
roles: string[];
|
||||
enabled: boolean;
|
||||
emailVerified: boolean;
|
||||
merchantPartnerId: string;
|
||||
createdBy: string;
|
||||
createdByUsername: string;
|
||||
createdTimestamp: number;
|
||||
lastLogin?: number;
|
||||
userType: string;
|
||||
}
|
||||
|
||||
export interface TokenValidationResponseDto {
|
||||
@ -74,36 +81,29 @@ export class AuthService {
|
||||
* Initialise l'authentification au démarrage de l'application
|
||||
*/
|
||||
async initialize(): Promise<boolean> {
|
||||
console.log('🔄 Initialisation du service d\'authentification...');
|
||||
|
||||
try {
|
||||
const token = this.getAccessToken();
|
||||
|
||||
if (!token) {
|
||||
console.log('🔍 Aucun token trouvé, utilisateur non authentifié');
|
||||
this.initialized$.next(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isTokenExpired(token)) {
|
||||
console.log('⚠️ Token expiré, tentative de rafraîchissement...');
|
||||
const refreshSuccess = await this.tryRefreshToken();
|
||||
this.initialized$.next(true);
|
||||
return refreshSuccess;
|
||||
}
|
||||
|
||||
// Token valide, vérifier le profil utilisateur
|
||||
console.log('✅ Token valide détecté, vérification du profil...');
|
||||
await this.loadUserProfile().toPromise();
|
||||
|
||||
this.authState$.next(true);
|
||||
this.initialized$.next(true);
|
||||
|
||||
console.log('🎯 Authentification initialisée avec succès');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de l\'initialisation de l\'auth:', error);
|
||||
this.clearAuthData();
|
||||
this.initialized$.next(true);
|
||||
return false;
|
||||
@ -117,17 +117,14 @@ export class AuthService {
|
||||
const refreshToken = this.getRefreshToken();
|
||||
|
||||
if (!refreshToken) {
|
||||
console.log('🔍 Aucun refresh token disponible');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Convertir l'Observable en Promise pour l'initialisation
|
||||
const response = await this.refreshToken().toPromise();
|
||||
console.log('✅ Token rafraîchi avec succès lors de l\'initialisation');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Échec du rafraîchissement du token:', error);
|
||||
this.clearAuthData();
|
||||
return false;
|
||||
}
|
||||
@ -174,10 +171,8 @@ export class AuthService {
|
||||
).pipe(
|
||||
tap(response => {
|
||||
this.handleLoginSuccess(response);
|
||||
console.log('🔄 Token rafraîchi avec succès');
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('❌ Échec du rafraîchissement du token:', error);
|
||||
this.clearAuthData();
|
||||
return throwError(() => error);
|
||||
})
|
||||
@ -194,7 +189,6 @@ export class AuthService {
|
||||
).pipe(
|
||||
tap(() => {
|
||||
this.clearAuthData();
|
||||
console.log('👋 Déconnexion réussie');
|
||||
}),
|
||||
catchError(error => {
|
||||
this.clearAuthData(); // Nettoyer même en cas d'erreur
|
||||
@ -212,10 +206,8 @@ export class AuthService {
|
||||
).pipe(
|
||||
tap(profile => {
|
||||
this.userProfile$.next(profile);
|
||||
console.log('👤 Profil utilisateur chargé:', profile.username);
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('❌ Erreur lors du chargement du profil:', error);
|
||||
return throwError(() => error);
|
||||
})
|
||||
);
|
||||
@ -233,7 +225,6 @@ export class AuthService {
|
||||
}
|
||||
|
||||
this.authState$.next(true);
|
||||
console.log('✅ Connexion réussie');
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +236,6 @@ export class AuthService {
|
||||
localStorage.removeItem(this.refreshTokenKey);
|
||||
this.authState$.next(false);
|
||||
this.userProfile$.next(null);
|
||||
console.log('🧹 Données d\'authentification nettoyées');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -124,6 +124,7 @@ export class MenuService {
|
||||
];
|
||||
}
|
||||
|
||||
// Mettez à jour votre méthode
|
||||
private getFullUserDropdown(): UserDropdownItemType[] {
|
||||
return [
|
||||
{ label: 'Welcome back!', isHeader: true },
|
||||
@ -132,11 +133,10 @@ export class MenuService {
|
||||
{ label: 'Support Center', icon: 'tablerHeadset', url: '/support' },
|
||||
{ isDivider: true },
|
||||
{
|
||||
label: 'Log Out',
|
||||
label: 'Déconnexion',
|
||||
icon: 'tablerLogout2',
|
||||
url: '/auth/logout',
|
||||
class: 'fw-semibold text-danger'
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,18 @@ export class PermissionsService {
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
module: 'auth',
|
||||
roles: [
|
||||
'dcb-admin',
|
||||
'dcb-partner',
|
||||
'dcb-support',
|
||||
'dcb-partner-admin',
|
||||
'dcb-partner-manager',
|
||||
'dcb-partner-support'
|
||||
],
|
||||
},
|
||||
|
||||
// Transactions
|
||||
{
|
||||
module: 'transactions',
|
||||
|
||||
@ -303,6 +303,9 @@ export class RoleManagementService {
|
||||
return 'bg-info';
|
||||
case UserRole.DCB_PARTNER:
|
||||
return 'bg-success';
|
||||
case UserRole.DCB_PARTNER_ADMIN: return 'bg-danger';
|
||||
case UserRole.DCB_PARTNER_MANAGER: return 'bg-success';
|
||||
case UserRole.DCB_PARTNER_SUPPORT: return 'bg-info';
|
||||
default:
|
||||
return 'bg-secondary';
|
||||
}
|
||||
@ -319,6 +322,12 @@ export class RoleManagementService {
|
||||
return 'lucideHeadphones';
|
||||
case UserRole.DCB_PARTNER:
|
||||
return 'lucideBuilding';
|
||||
case UserRole.DCB_PARTNER_ADMIN:
|
||||
return 'lucideShield';
|
||||
case UserRole.DCB_PARTNER_MANAGER:
|
||||
return 'lucideUserCog';
|
||||
case UserRole.DCB_PARTNER_SUPPORT:
|
||||
return 'lucideHeadphones';
|
||||
default:
|
||||
return 'lucideUser';
|
||||
}
|
||||
|
||||
@ -33,10 +33,9 @@ export const userDropdownItems: UserDropdownItemType[] = [
|
||||
isDivider: true,
|
||||
},
|
||||
{
|
||||
label: 'Log Out',
|
||||
label: 'Déconnexion',
|
||||
icon: 'tablerLogout2',
|
||||
url: '/auth/logout',
|
||||
class: 'fw-semibold',
|
||||
},
|
||||
]
|
||||
|
||||
@ -112,16 +111,6 @@ export const menuItems: MenuItemType[] = [
|
||||
{ label: 'Liste des Utilisateurs', url: '/users' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Authentification',
|
||||
icon: 'lucideFingerprint',
|
||||
isCollapsed: true,
|
||||
children: [
|
||||
{ label: 'Login / Logout', url: '/auth/login' },
|
||||
{ label: 'Réinitialisation Mot de Passe', url: '/auth/reset-password' },
|
||||
{ label: 'Gestion des Sessions', url: '/auth/sessions' },
|
||||
],
|
||||
},
|
||||
|
||||
// ---------------------------
|
||||
// Paramètres & Intégrations
|
||||
|
||||
@ -28,20 +28,6 @@
|
||||
|
||||
<!-- Élément normal -->
|
||||
@if (!item.isHeader && !item.isDivider) {
|
||||
@if (item.label === 'Log Out') {
|
||||
<!-- Bouton Logout avec appel de méthode -->
|
||||
<button
|
||||
class="dropdown-item fw-semibold text-danger"
|
||||
(click)="logout()">
|
||||
<ng-icon
|
||||
name="tablerLogout2"
|
||||
size="17"
|
||||
class="align-middle d-inline-flex align-items-center me-2"
|
||||
/>
|
||||
<span class="align-middle">Log Out</span>
|
||||
</button>
|
||||
} @else {
|
||||
<!-- Autres items avec navigation normale -->
|
||||
<a
|
||||
[routerLink]="item.url"
|
||||
class="dropdown-item"
|
||||
@ -56,7 +42,6 @@
|
||||
<span class="align-middle">{{ item.label }}</span>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -45,14 +45,4 @@ export class UserProfile implements OnInit, OnDestroy {
|
||||
private loadDropdownItems() {
|
||||
this.menuItems = this.menuService.getUserDropdownItems()
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.authService.logout()
|
||||
}
|
||||
|
||||
handleItemClick(item: UserDropdownItemType) {
|
||||
if (item.label === 'Log Out') {
|
||||
this.logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,14 @@
|
||||
import { Routes } from '@angular/router'
|
||||
import { SignIn } from '@/app/modules/auth/sign-in'
|
||||
import { Login } from '@/app/modules/auth/login'
|
||||
import { Logout } from '@/app/modules/auth/logout'
|
||||
import { ResetPassword } from '@/app/modules/auth/reset-password'
|
||||
import { NewPassword } from '@/app/modules/auth/new-password'
|
||||
import { publicGuard } from '@core/guards/public.guard';
|
||||
|
||||
export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'sign-in',
|
||||
component: SignIn,
|
||||
path: 'login',
|
||||
component: Login,
|
||||
canActivate: [publicGuard],
|
||||
data: {
|
||||
title: 'Connexion - DCB',
|
||||
@ -35,7 +36,10 @@ export const AUTH_ROUTES: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'logout',
|
||||
redirectTo: 'sign-in',
|
||||
data: { module: 'auth' }
|
||||
component: Logout,
|
||||
data: {
|
||||
title: 'Déconnexion - DCB',
|
||||
module: 'auth'
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -1,4 +1,4 @@
|
||||
// src/app/modules/auth/sign-in/sign-in.ts
|
||||
// src/app/modules/auth/login/sign-in.ts
|
||||
import { Component, inject, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
@ -11,7 +11,7 @@ import { PasswordStrengthBar } from '@app/components/password-strength-bar';
|
||||
import { appName, credits, currentYear } from '@/app/constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sign-in',
|
||||
selector: 'app-login',
|
||||
standalone: true,
|
||||
imports: [FormsModule, CommonModule, RouterLink, AppLogo, PasswordStrengthBar],
|
||||
template: `
|
||||
@ -149,7 +149,7 @@ import { appName, credits, currentYear } from '@/app/constants';
|
||||
`,
|
||||
styles: []
|
||||
})
|
||||
export class SignIn implements OnInit, OnDestroy {
|
||||
export class Login implements OnInit, OnDestroy {
|
||||
protected readonly appName = appName;
|
||||
protected readonly currentYear = currentYear;
|
||||
protected readonly credits = credits;
|
||||
@ -258,16 +258,18 @@ export class SignIn implements OnInit, OnDestroy {
|
||||
const userRoles = this.roleService.getCurrentUserRoles();
|
||||
|
||||
// Logique de redirection selon les rôles
|
||||
if (userRoles.includes('dcb-admin')) {
|
||||
this.router.navigate(['/admin/dcb-dashboard']);
|
||||
/*if (userRoles.includes('dcb-admin')) {
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
} else if (userRoles.includes('dcb-partner-admin')) {
|
||||
this.router.navigate(['/partner/dcb-dashboard']);
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
} else if (userRoles.includes('dcb-support')) {
|
||||
this.router.navigate(['/support/dcb-dashboard']);
|
||||
} else {
|
||||
// Route par défaut
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
}
|
||||
}*/
|
||||
// Route par défaut
|
||||
this.router.navigate(['/dcb-dashboard']);
|
||||
}
|
||||
|
||||
/**
|
||||
51
src/app/modules/auth/logout.ts
Normal file
51
src/app/modules/auth/logout.ts
Normal file
@ -0,0 +1,51 @@
|
||||
// app/modules/auth/logout/logout.component.ts
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthService } from '@core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div class="logout-container">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Déconnexion en cours...</span>
|
||||
</div>
|
||||
<p class="mt-2">Déconnexion en cours...</p>
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.logout-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class Logout implements OnInit {
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.logout();
|
||||
}
|
||||
|
||||
private logout(): void {
|
||||
this.authService.logout().subscribe({
|
||||
next: () => {
|
||||
// Redirection vers la page de login après déconnexion
|
||||
this.router.navigate(['/auth/login'], {
|
||||
queryParams: { logout: 'success' }
|
||||
});
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Erreur lors de la déconnexion:', error);
|
||||
// Rediriger même en cas d'erreur
|
||||
this.router.navigate(['/auth/login']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,7 @@ import { NgOtpInputModule } from 'ng-otp-input'
|
||||
<p class="text-muted text-center mb-0">
|
||||
Return to
|
||||
<a
|
||||
routerLink="/auth/sign-in"
|
||||
routerLink="/auth/login"
|
||||
class="text-decoration-underline link-offset-3 fw-semibold"
|
||||
>Sign in</a
|
||||
>
|
||||
|
||||
@ -63,7 +63,7 @@ import { AppLogo } from '@app/components/app-logo'
|
||||
<p class="text-muted text-center mt-4 mb-0">
|
||||
Return to
|
||||
<a
|
||||
routerLink="/auth/sign-in"
|
||||
routerLink="/auth/login"
|
||||
class="text-decoration-underline link-offset-3 fw-semibold"
|
||||
>Sign in</a
|
||||
>
|
||||
|
||||
@ -66,7 +66,7 @@ import { credits, currentYear } from '@/app/constants'
|
||||
<p class="text-muted text-center mb-0">
|
||||
Return to
|
||||
<a
|
||||
routerLink="/auth/sign-in"
|
||||
routerLink="/auth/login"
|
||||
class="text-decoration-underline link-offset-3 fw-semibold"
|
||||
>Sign in</a
|
||||
>
|
||||
|
||||
@ -251,8 +251,4 @@ const routes: Routes = [
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ModulesRoutes {
|
||||
constructor() {
|
||||
console.log('Modules routes loaded:', routes);
|
||||
}
|
||||
}
|
||||
export class ModulesRoutes {}
|
||||
@ -22,7 +22,7 @@
|
||||
@if (user) {
|
||||
{{ getUserDisplayName() }}
|
||||
} @else {
|
||||
Profil
|
||||
Profile
|
||||
}
|
||||
</li>
|
||||
</ol>
|
||||
@ -178,13 +178,29 @@
|
||||
<div class="card-body">
|
||||
<!-- Rôle actuel -->
|
||||
<div class="text-center mb-3">
|
||||
<span class="badge d-flex align-items-center justify-content-center" [ngClass]="getRoleBadgeClass(user.role)">
|
||||
<ng-icon [name]="getRoleIcon(user.role)" class="me-2"></ng-icon>
|
||||
{{ getRoleLabel(user.role) }}
|
||||
<!-- Afficher le premier rôle ou tous les rôles -->
|
||||
@if (user.roles && user.roles.length > 0) {
|
||||
@for (role of user.roles; track role; let first = $first) {
|
||||
@if (first) { <!-- Afficher seulement le premier rôle pour l'instant -->
|
||||
<span class="badge d-flex align-items-center justify-content-center" [ngClass]="getRoleBadgeClass(role)">
|
||||
<ng-icon [name]="getRoleIcon(role)" class="me-2"></ng-icon>
|
||||
{{ getRoleLabel(role) }}
|
||||
</span>
|
||||
<small class="text-muted d-block mt-1">
|
||||
{{ getRoleDescription(user.role) }}
|
||||
{{ getRoleDescription(role) }}
|
||||
</small>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Indicateur si plusieurs rôles -->
|
||||
@if (user.roles.length > 1) {
|
||||
<small class="text-muted">
|
||||
+ {{ user.roles.length - 1 }} autre(s) rôle(s)
|
||||
</small>
|
||||
}
|
||||
} @else {
|
||||
<span class="badge bg-secondary">Aucun rôle</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Changement de rôle -->
|
||||
@ -193,7 +209,7 @@
|
||||
<label class="form-label fw-semibold">Changer le rôle</label>
|
||||
<select
|
||||
class="form-select"
|
||||
[value]="user.role"
|
||||
[value]="user.roles[0]"
|
||||
(change)="updateUserRole($any($event.target).value)"
|
||||
[disabled]="updatingRoles"
|
||||
>
|
||||
@ -201,12 +217,12 @@
|
||||
@for (role of availableRoles; track role.value) {
|
||||
<option
|
||||
[value]="role.value"
|
||||
[disabled]="!canAssignRole(role.value) || role.value === user.role"
|
||||
[disabled]="!canAssignRole(role.value) || role.value === user.roles[0]"
|
||||
>
|
||||
{{ role.label }}
|
||||
@if (!canAssignRole(role.value)) {
|
||||
(Non autorisé)
|
||||
} @else if (role.value === user.role) {
|
||||
} @else if (role.value === user.roles[0]) {
|
||||
(Actuel)
|
||||
}
|
||||
</option>
|
||||
|
||||
@ -101,7 +101,10 @@ export class MyProfile implements OnInit, OnDestroy {
|
||||
this.availableRoles = [
|
||||
{ value: UserRole.DCB_ADMIN, label: 'DCB Admin', description: 'Administrateur système' },
|
||||
{ value: UserRole.DCB_SUPPORT, label: 'DCB Support', description: 'Support technique' },
|
||||
{ value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' }
|
||||
{ value: UserRole.DCB_PARTNER, label: 'DCB Partner', description: 'Partenaire commercial' },
|
||||
{ value: UserRole.DCB_PARTNER_ADMIN, label: 'DCB Partner Admin', description: 'Admin Partenaire commercial' },
|
||||
{ value: UserRole.DCB_PARTNER_MANAGER, label: 'DCB Partner Manager', description: 'Manager Partenaire commercial' },
|
||||
{ value: UserRole.DCB_PARTNER_SUPPORT, label: 'DCB Partner Support', description: 'Support Partenaire commercial' }
|
||||
];
|
||||
}
|
||||
});
|
||||
@ -111,11 +114,11 @@ export class MyProfile implements OnInit, OnDestroy {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
this.usersService.getUserById(this.user.id)
|
||||
this.authService.getProfile()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (user) => {
|
||||
this.user = user;
|
||||
next: (profile) => {
|
||||
this.user = profile;
|
||||
this.loading = false;
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
|
||||
@ -55,7 +55,10 @@ export interface PaginatedUserResponse {
|
||||
export enum UserRole {
|
||||
DCB_ADMIN = 'dcb-admin',
|
||||
DCB_SUPPORT = 'dcb-support',
|
||||
DCB_PARTNER = 'dcb-partner'
|
||||
DCB_PARTNER = 'dcb-partner',
|
||||
DCB_PARTNER_ADMIN = 'dcb-partner-admin',
|
||||
DCB_PARTNER_MANAGER = 'dcb-partner-manager',
|
||||
DCB_PARTNER_SUPPORT = 'dcb-partner-support'
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
||||
@ -54,13 +54,13 @@ export type MenuItemType = {
|
||||
isCollapsed?: boolean
|
||||
}
|
||||
|
||||
// types/layout.ts - Ajoutez ce type
|
||||
export type UserDropdownItemType = {
|
||||
label?: string;
|
||||
icon?: string;
|
||||
url?: string;
|
||||
isDivider?: boolean;
|
||||
isHeader?: boolean;
|
||||
isLogout?: boolean;
|
||||
class?: string;
|
||||
target?: string;
|
||||
isDisabled?: boolean;
|
||||
|
||||
@ -8,7 +8,6 @@ bootstrapApplication(App, {
|
||||
...appConfig.providers,
|
||||
]
|
||||
}).then(async appRef => {
|
||||
console.log('Application BO Admin completed.');
|
||||
const authService = appRef.injector.get(AuthService);
|
||||
await authService.initialize();
|
||||
}).catch(err => console.error('BO Admin error', err));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user