feat: add DCB User Service API - Authentication system with KEYCLOAK - Modular architecture with services for each feature
This commit is contained in:
parent
d2a5ffcebb
commit
8d7fdb27ea
@ -58,7 +58,7 @@ export interface TechnicalContact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MerchantUser {
|
export interface MerchantUser {
|
||||||
userId: number;
|
userId: string;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
username?: string;
|
username?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
@ -134,8 +134,8 @@ export interface CreateMerchantDto {
|
|||||||
description?: string;
|
description?: string;
|
||||||
adresse: string;
|
adresse: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
configs: Omit<MerchantConfig, 'id' | 'merchantPartnerId' | 'createdAt' | 'updatedAt'>[];
|
configs?: Omit<MerchantConfig, 'id' | 'merchantPartnerId' | 'createdAt' | 'updatedAt'>[];
|
||||||
technicalContacts: Omit<TechnicalContact, 'id' | 'merchantPartnerId' | 'createdAt' | 'updatedAt'>[];
|
technicalContacts?: Omit<TechnicalContact, 'id' | 'merchantPartnerId' | 'createdAt' | 'updatedAt'>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateMerchantDto extends Partial<CreateMerchantDto> {}
|
export interface UpdateMerchantDto extends Partial<CreateMerchantDto> {}
|
||||||
@ -227,14 +227,6 @@ export class MerchantUtils {
|
|||||||
errors.push('Le téléphone est requis');
|
errors.push('Le téléphone est requis');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!merchant.technicalContacts || merchant.technicalContacts.length === 0) {
|
|
||||||
errors.push('Au moins un contact technique est requis');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!merchant.configs || merchant.configs.length === 0) {
|
|
||||||
errors.push('Au moins une configuration est requise');
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -233,6 +233,7 @@ export class AuthService {
|
|||||||
emailVerified: apiUser.emailVerified ?? apiUser.email_verified ?? false,
|
emailVerified: apiUser.emailVerified ?? apiUser.email_verified ?? false,
|
||||||
userType: userType,
|
userType: userType,
|
||||||
merchantPartnerId: apiUser.merchantPartnerId || apiUser.partnerId || apiUser.merchantId || null,
|
merchantPartnerId: apiUser.merchantPartnerId || apiUser.partnerId || apiUser.merchantId || null,
|
||||||
|
merchantConfigId: apiUser.merchantConfigId || apiUser.configId || apiUser.merchantId || null,
|
||||||
role: apiUser.clientRoles || apiUser.clientRoles?.[0] || '', // Gérer rôle unique ou tableau
|
role: apiUser.clientRoles || apiUser.clientRoles?.[0] || '', // Gérer rôle unique ou tableau
|
||||||
createdBy: apiUser.createdBy || apiUser.creatorId || null,
|
createdBy: apiUser.createdBy || apiUser.creatorId || null,
|
||||||
createdByUsername: apiUser.createdByUsername || apiUser.creatorUsername || null,
|
createdByUsername: apiUser.createdByUsername || apiUser.creatorUsername || null,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { MerchantUsersService } from '@modules/hub-users-management/merchant-use
|
|||||||
import { BehaviorSubject, Observable, map, tap, of, catchError } from 'rxjs';
|
import { BehaviorSubject, Observable, map, tap, of, catchError } from 'rxjs';
|
||||||
import { UserRole, UserType, AvailableRole } from '@core/models/dcb-bo-hub-user.model';
|
import { UserRole, UserType, AvailableRole } from '@core/models/dcb-bo-hub-user.model';
|
||||||
|
|
||||||
|
// Interfaces
|
||||||
export interface RolePermission {
|
export interface RolePermission {
|
||||||
canCreateUsers: boolean;
|
canCreateUsers: boolean;
|
||||||
canEditUsers: boolean;
|
canEditUsers: boolean;
|
||||||
@ -21,6 +22,196 @@ export interface AvailableRolesWithPermissions {
|
|||||||
roles: (AvailableRole & { permissions: RolePermission })[];
|
roles: (AvailableRole & { permissions: RolePermission })[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RoleConfig {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
badgeClass: string;
|
||||||
|
icon: string;
|
||||||
|
permissions: RolePermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions par défaut
|
||||||
|
const DEFAULT_PERMISSIONS: RolePermission = {
|
||||||
|
canCreateUsers: false,
|
||||||
|
canEditUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageRoles: false,
|
||||||
|
canViewStats: false,
|
||||||
|
canManageMerchants: false,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: false,
|
||||||
|
canAccessPartner: false,
|
||||||
|
assignableRoles: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configuration des rôles
|
||||||
|
const ROLE_CONFIG: Record<UserRole, RoleConfig> = {
|
||||||
|
[UserRole.DCB_ADMIN]: {
|
||||||
|
label: 'Administrateur DCB',
|
||||||
|
description: 'Administrateur système avec tous les accès',
|
||||||
|
badgeClass: 'bg-danger',
|
||||||
|
icon: 'lucideShield',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: true,
|
||||||
|
canEditUsers: true,
|
||||||
|
canDeleteUsers: true,
|
||||||
|
canManageRoles: true,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: true,
|
||||||
|
canAccessAdmin: true,
|
||||||
|
canAccessSupport: true,
|
||||||
|
canAccessPartner: true,
|
||||||
|
assignableRoles: Object.values(UserRole)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.DCB_SUPPORT]: {
|
||||||
|
label: 'Support DCB',
|
||||||
|
description: 'Support technique avec accès étendus',
|
||||||
|
badgeClass: 'bg-info',
|
||||||
|
icon: 'lucideHeadphones',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: true,
|
||||||
|
canEditUsers: true,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageRoles: true,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: true,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: true,
|
||||||
|
canAccessPartner: true,
|
||||||
|
assignableRoles: [
|
||||||
|
UserRole.DCB_SUPPORT,
|
||||||
|
UserRole.DCB_PARTNER,
|
||||||
|
UserRole.DCB_PARTNER_ADMIN,
|
||||||
|
UserRole.DCB_PARTNER_MANAGER,
|
||||||
|
UserRole.DCB_PARTNER_SUPPORT
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.DCB_PARTNER]: {
|
||||||
|
label: 'Partenaire DCB',
|
||||||
|
description: 'Partenaire commercial principal',
|
||||||
|
badgeClass: 'bg-primary',
|
||||||
|
icon: 'lucideBuilding',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: true,
|
||||||
|
canEditUsers: true,
|
||||||
|
canDeleteUsers: true,
|
||||||
|
canManageRoles: true,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: false,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: false,
|
||||||
|
canAccessPartner: false,
|
||||||
|
assignableRoles: [
|
||||||
|
UserRole.DCB_PARTNER_ADMIN,
|
||||||
|
UserRole.DCB_PARTNER_MANAGER,
|
||||||
|
UserRole.DCB_PARTNER_SUPPORT
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.DCB_PARTNER_ADMIN]: {
|
||||||
|
label: 'Admin Partenaire',
|
||||||
|
description: 'Administrateur de partenaire marchand',
|
||||||
|
badgeClass: 'bg-warning',
|
||||||
|
icon: 'lucideShieldCheck',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: true,
|
||||||
|
canEditUsers: true,
|
||||||
|
canDeleteUsers: true,
|
||||||
|
canManageRoles: true,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: false,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: false,
|
||||||
|
canAccessPartner: false,
|
||||||
|
assignableRoles: [UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.DCB_PARTNER_MANAGER]: {
|
||||||
|
label: 'Manager Partenaire',
|
||||||
|
description: 'Manager opérationnel partenaire',
|
||||||
|
badgeClass: 'bg-success',
|
||||||
|
icon: 'lucideUserCog',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: false,
|
||||||
|
canEditUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageRoles: false,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: true,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: false,
|
||||||
|
canAccessPartner: true,
|
||||||
|
assignableRoles: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.DCB_PARTNER_SUPPORT]: {
|
||||||
|
label: 'Support Partenaire',
|
||||||
|
description: 'Support technique partenaire',
|
||||||
|
badgeClass: 'bg-secondary',
|
||||||
|
icon: 'lucideHeadphones',
|
||||||
|
permissions: {
|
||||||
|
canCreateUsers: false,
|
||||||
|
canEditUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageRoles: false,
|
||||||
|
canViewStats: true,
|
||||||
|
canManageMerchants: false,
|
||||||
|
canAccessAdmin: false,
|
||||||
|
canAccessSupport: false,
|
||||||
|
canAccessPartner: true,
|
||||||
|
assignableRoles: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[UserRole.MERCHANT_CONFIG_ADMIN]: {
|
||||||
|
label: 'Admin Marchand',
|
||||||
|
description: 'Administrateur de configuration marchand',
|
||||||
|
badgeClass: 'bg-warning',
|
||||||
|
icon: 'lucideSettings',
|
||||||
|
permissions: DEFAULT_PERMISSIONS
|
||||||
|
},
|
||||||
|
[UserRole.MERCHANT_CONFIG_MANAGER]: {
|
||||||
|
label: 'Manager Marchand',
|
||||||
|
description: 'Manager de configuration marchand',
|
||||||
|
badgeClass: 'bg-success',
|
||||||
|
icon: 'lucideUserCog',
|
||||||
|
permissions: DEFAULT_PERMISSIONS
|
||||||
|
},
|
||||||
|
[UserRole.MERCHANT_CONFIG_TECHNICAL]: {
|
||||||
|
label: 'Technique Marchand',
|
||||||
|
description: 'Support technique configuration marchand',
|
||||||
|
badgeClass: 'bg-secondary',
|
||||||
|
icon: 'lucideWrench',
|
||||||
|
permissions: DEFAULT_PERMISSIONS
|
||||||
|
},
|
||||||
|
[UserRole.MERCHANT_CONFIG_VIEWER]: {
|
||||||
|
label: 'Visualiseur Marchand',
|
||||||
|
description: 'Visualiseur de configuration marchand',
|
||||||
|
badgeClass: 'bg-light',
|
||||||
|
icon: 'lucideEye',
|
||||||
|
permissions: DEFAULT_PERMISSIONS
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Rôles Hub (pour les filtres)
|
||||||
|
const HUB_ROLES = [
|
||||||
|
UserRole.DCB_ADMIN,
|
||||||
|
UserRole.DCB_SUPPORT,
|
||||||
|
UserRole.DCB_PARTNER
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
// Rôles Marchands (pour les filtres)
|
||||||
|
const MERCHANT_ROLES = [
|
||||||
|
UserRole.DCB_PARTNER_ADMIN,
|
||||||
|
UserRole.DCB_PARTNER_MANAGER,
|
||||||
|
UserRole.DCB_PARTNER_SUPPORT,
|
||||||
|
UserRole.MERCHANT_CONFIG_ADMIN,
|
||||||
|
UserRole.MERCHANT_CONFIG_MANAGER,
|
||||||
|
UserRole.MERCHANT_CONFIG_TECHNICAL,
|
||||||
|
UserRole.MERCHANT_CONFIG_VIEWER
|
||||||
|
] as const;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@ -35,28 +226,30 @@ export class RoleManagementService {
|
|||||||
* Charge les rôles Hub disponibles
|
* Charge les rôles Hub disponibles
|
||||||
*/
|
*/
|
||||||
loadAvailableHubRoles(): Observable<AvailableRolesWithPermissions> {
|
loadAvailableHubRoles(): Observable<AvailableRolesWithPermissions> {
|
||||||
return this.hubUsersService.getAvailableHubRoles().pipe(
|
return this.loadRoles(
|
||||||
map(apiResponse => ({
|
() => this.hubUsersService.getAvailableHubRoles(),
|
||||||
roles: apiResponse.roles.map(role => ({
|
'hub'
|
||||||
...role,
|
|
||||||
permissions: this.getPermissionsForRole(role.value)
|
|
||||||
}))
|
|
||||||
})),
|
|
||||||
tap(roles => this.availableRoles$.next(roles)),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('Error loading hub roles:', error);
|
|
||||||
// On renvoie un observable valide pour éviter le crash
|
|
||||||
return of({ roles: [] } as AvailableRolesWithPermissions);
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Charge les rôles Marchands disponibles
|
* Charge les rôles Marchands disponibles
|
||||||
*/
|
*/
|
||||||
loadAvailableMerchantRoles(): Observable<AvailableRolesWithPermissions> {
|
loadAvailableMerchantRoles(): Observable<AvailableRolesWithPermissions> {
|
||||||
return this.merchantUsersService.getAvailableMerchantRoles().pipe(
|
return this.loadRoles(
|
||||||
|
() => this.merchantUsersService.getAvailableMerchantRoles(),
|
||||||
|
'merchant'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthode générique pour charger les rôles
|
||||||
|
*/
|
||||||
|
private loadRoles(
|
||||||
|
fetchFn: () => Observable<{ roles: AvailableRole[] }>,
|
||||||
|
type: 'hub' | 'merchant'
|
||||||
|
): Observable<AvailableRolesWithPermissions> {
|
||||||
|
return fetchFn().pipe(
|
||||||
map(apiResponse => ({
|
map(apiResponse => ({
|
||||||
roles: apiResponse.roles.map(role => ({
|
roles: apiResponse.roles.map(role => ({
|
||||||
...role,
|
...role,
|
||||||
@ -65,8 +258,7 @@ export class RoleManagementService {
|
|||||||
})),
|
})),
|
||||||
tap(roles => this.availableRoles$.next(roles)),
|
tap(roles => this.availableRoles$.next(roles)),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error('Error loading merchant roles:', error);
|
console.error(`Error loading ${type} roles:`, error);
|
||||||
// On renvoie un observable valide pour éviter le crash
|
|
||||||
return of({ roles: [] } as AvailableRolesWithPermissions);
|
return of({ roles: [] } as AvailableRolesWithPermissions);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -98,119 +290,9 @@ export class RoleManagementService {
|
|||||||
*/
|
*/
|
||||||
getPermissionsForRole(role: UserRole | null): RolePermission {
|
getPermissionsForRole(role: UserRole | null): RolePermission {
|
||||||
if (!role) {
|
if (!role) {
|
||||||
return this.getDefaultPermissions();
|
return DEFAULT_PERMISSIONS;
|
||||||
}
|
}
|
||||||
|
return ROLE_CONFIG[role]?.permissions || DEFAULT_PERMISSIONS;
|
||||||
const allRoles = this.getAllRoles();
|
|
||||||
const hubRoles = [UserRole.DCB_SUPPORT, UserRole.DCB_PARTNER, UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT];
|
|
||||||
const merchantRoles = [UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT];
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case UserRole.DCB_ADMIN:
|
|
||||||
return {
|
|
||||||
canCreateUsers: true,
|
|
||||||
canEditUsers: true,
|
|
||||||
canDeleteUsers: true,
|
|
||||||
canManageRoles: true,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: true,
|
|
||||||
canAccessAdmin: true,
|
|
||||||
canAccessSupport: true,
|
|
||||||
canAccessPartner: true,
|
|
||||||
assignableRoles: allRoles
|
|
||||||
};
|
|
||||||
|
|
||||||
case UserRole.DCB_SUPPORT:
|
|
||||||
return {
|
|
||||||
canCreateUsers: true,
|
|
||||||
canEditUsers: true,
|
|
||||||
canDeleteUsers: false,
|
|
||||||
canManageRoles: true,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: true,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: true,
|
|
||||||
canAccessPartner: true,
|
|
||||||
assignableRoles: hubRoles,
|
|
||||||
};
|
|
||||||
|
|
||||||
case UserRole.DCB_PARTNER:
|
|
||||||
return {
|
|
||||||
canCreateUsers: true,
|
|
||||||
canEditUsers: true,
|
|
||||||
canDeleteUsers: true,
|
|
||||||
canManageRoles: true,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: false,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: false,
|
|
||||||
canAccessPartner: false,
|
|
||||||
assignableRoles: merchantRoles
|
|
||||||
};
|
|
||||||
|
|
||||||
case UserRole.DCB_PARTNER_ADMIN:
|
|
||||||
return {
|
|
||||||
canCreateUsers: true,
|
|
||||||
canEditUsers: true,
|
|
||||||
canDeleteUsers: true,
|
|
||||||
canManageRoles: true,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: false,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: false,
|
|
||||||
canAccessPartner: false,
|
|
||||||
assignableRoles: [UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT]
|
|
||||||
};
|
|
||||||
|
|
||||||
case UserRole.DCB_PARTNER_MANAGER:
|
|
||||||
return {
|
|
||||||
canCreateUsers: false,
|
|
||||||
canEditUsers: false,
|
|
||||||
canDeleteUsers: false,
|
|
||||||
canManageRoles: false,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: true,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: false,
|
|
||||||
canAccessPartner: true,
|
|
||||||
assignableRoles: []
|
|
||||||
};
|
|
||||||
|
|
||||||
case UserRole.DCB_PARTNER_SUPPORT:
|
|
||||||
return {
|
|
||||||
canCreateUsers: false,
|
|
||||||
canEditUsers: false,
|
|
||||||
canDeleteUsers: false,
|
|
||||||
canManageRoles: false,
|
|
||||||
canViewStats: true,
|
|
||||||
canManageMerchants: false,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: false,
|
|
||||||
canAccessPartner: true,
|
|
||||||
assignableRoles: []
|
|
||||||
};
|
|
||||||
|
|
||||||
default:
|
|
||||||
return this.getDefaultPermissions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permissions par défaut (rôle inconnu ou non défini)
|
|
||||||
*/
|
|
||||||
private getDefaultPermissions(): RolePermission {
|
|
||||||
return {
|
|
||||||
canCreateUsers: false,
|
|
||||||
canEditUsers: false,
|
|
||||||
canDeleteUsers: false,
|
|
||||||
canManageRoles: false,
|
|
||||||
canViewStats: false,
|
|
||||||
canManageMerchants: false,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
canAccessSupport: false,
|
|
||||||
canAccessPartner: false,
|
|
||||||
assignableRoles: []
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -219,7 +301,6 @@ export class RoleManagementService {
|
|||||||
canAssignRole(currentUserRole: UserRole | null, targetRole: UserRole): boolean {
|
canAssignRole(currentUserRole: UserRole | null, targetRole: UserRole): boolean {
|
||||||
if (!currentUserRole) return false;
|
if (!currentUserRole) return false;
|
||||||
|
|
||||||
// Rôles qui peuvent attribuer tous les rôles
|
|
||||||
const fullPermissionRoles = [
|
const fullPermissionRoles = [
|
||||||
UserRole.DCB_ADMIN,
|
UserRole.DCB_ADMIN,
|
||||||
UserRole.DCB_SUPPORT,
|
UserRole.DCB_SUPPORT,
|
||||||
@ -230,287 +311,163 @@ export class RoleManagementService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pour les autres rôles, utiliser les permissions définies
|
|
||||||
const permissions = this.getPermissionsForRole(currentUserRole);
|
const permissions = this.getPermissionsForRole(currentUserRole);
|
||||||
return permissions.assignableRoles.includes(targetRole);
|
return permissions.assignableRoles.includes(targetRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Méthodes d'utilité pour les permissions
|
||||||
* Vérifie si l'utilisateur courant peut créer des utilisateurs
|
|
||||||
*/
|
|
||||||
canCreateUsers(currentUserRole: UserRole | null): boolean {
|
canCreateUsers(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canCreateUsers : false;
|
return this.getPermission(currentUserRole, 'canCreateUsers');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut éditer des utilisateurs
|
|
||||||
*/
|
|
||||||
canEditUsers(currentUserRole: UserRole | null): boolean {
|
canEditUsers(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canEditUsers : false;
|
return this.getPermission(currentUserRole, 'canEditUsers');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut supprimer des utilisateurs
|
|
||||||
*/
|
|
||||||
canDeleteUsers(currentUserRole: UserRole | null): boolean {
|
canDeleteUsers(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canDeleteUsers : false;
|
return this.getPermission(currentUserRole, 'canDeleteUsers');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut gérer les rôles
|
|
||||||
*/
|
|
||||||
canManageRoles(currentUserRole: UserRole | null): boolean {
|
canManageRoles(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canManageRoles : false;
|
return this.getPermission(currentUserRole, 'canManageRoles');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut accéder aux statistiques
|
|
||||||
*/
|
|
||||||
canViewStats(currentUserRole: UserRole | null): boolean {
|
canViewStats(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canViewStats : false;
|
return this.getPermission(currentUserRole, 'canViewStats');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut gérer les merchants
|
|
||||||
*/
|
|
||||||
canManageMerchants(currentUserRole: UserRole | null): boolean {
|
canManageMerchants(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canManageMerchants : false;
|
return this.getPermission(currentUserRole, 'canManageMerchants');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut accéder à l'admin
|
|
||||||
*/
|
|
||||||
canAccessAdmin(currentUserRole: UserRole | null): boolean {
|
canAccessAdmin(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canAccessAdmin : false;
|
return this.getPermission(currentUserRole, 'canAccessAdmin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut accéder au support
|
|
||||||
*/
|
|
||||||
canAccessSupport(currentUserRole: UserRole | null): boolean {
|
canAccessSupport(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canAccessSupport : false;
|
return this.getPermission(currentUserRole, 'canAccessSupport');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur courant peut accéder à l'espace partenaire
|
|
||||||
*/
|
|
||||||
canAccessPartner(currentUserRole: UserRole | null): boolean {
|
canAccessPartner(currentUserRole: UserRole | null): boolean {
|
||||||
return currentUserRole ? this.getPermissionsForRole(currentUserRole).canAccessPartner : false;
|
return this.getPermission(currentUserRole, 'canAccessPartner');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère le libellé d'un rôle
|
* Méthode helper générique pour les permissions
|
||||||
|
*/
|
||||||
|
private getPermission(
|
||||||
|
role: UserRole | null,
|
||||||
|
permissionKey: keyof RolePermission
|
||||||
|
): boolean {
|
||||||
|
if (!role) return false;
|
||||||
|
const permissions = this.getPermissionsForRole(role);
|
||||||
|
return Boolean(permissions[permissionKey]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthodes d'utilité pour les rôles
|
||||||
*/
|
*/
|
||||||
getRoleLabel(role: string): string {
|
getRoleLabel(role: string): string {
|
||||||
const userRole = role as UserRole;
|
const userRole = role as UserRole;
|
||||||
switch (userRole) {
|
return ROLE_CONFIG[userRole]?.label || role;
|
||||||
case UserRole.DCB_ADMIN:
|
|
||||||
return 'Administrateur DCB';
|
|
||||||
case UserRole.DCB_SUPPORT:
|
|
||||||
return 'Support DCB';
|
|
||||||
case UserRole.DCB_PARTNER:
|
|
||||||
return 'Partenaire DCB';
|
|
||||||
case UserRole.DCB_PARTNER_ADMIN:
|
|
||||||
return 'Admin Partenaire';
|
|
||||||
case UserRole.DCB_PARTNER_MANAGER:
|
|
||||||
return 'Manager Partenaire';
|
|
||||||
case UserRole.DCB_PARTNER_SUPPORT:
|
|
||||||
return 'Support Partenaire';
|
|
||||||
default:
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère la description d'un rôle
|
|
||||||
*/
|
|
||||||
getRoleDescription(role: string | UserRole): string {
|
getRoleDescription(role: string | UserRole): string {
|
||||||
const userRole = role as UserRole;
|
const userRole = role as UserRole;
|
||||||
const roleDescriptions: { [key in UserRole]: string } = {
|
return ROLE_CONFIG[userRole]?.description || 'Description non disponible';
|
||||||
[UserRole.DCB_ADMIN]: 'Administrateur système avec tous les accès',
|
|
||||||
[UserRole.DCB_SUPPORT]: 'Support technique avec accès étendus',
|
|
||||||
[UserRole.DCB_PARTNER]: 'Partenaire commercial principal',
|
|
||||||
[UserRole.DCB_PARTNER_ADMIN]: 'Administrateur de partenaire marchand',
|
|
||||||
[UserRole.DCB_PARTNER_MANAGER]: 'Manager opérationnel partenaire',
|
|
||||||
[UserRole.DCB_PARTNER_SUPPORT]: 'Support technique partenaire',
|
|
||||||
[UserRole.MERCHANT_CONFIG_ADMIN]: 'Administrateur de partenaire marchand',
|
|
||||||
[UserRole.MERCHANT_CONFIG_MANAGER]: 'Manager opérationnel partenaire',
|
|
||||||
[UserRole.MERCHANT_CONFIG_TECHNICAL]: 'Support technique partenaire',
|
|
||||||
[UserRole.MERCHANT_CONFIG_VIEWER]: 'Support technique partenaire'
|
|
||||||
};
|
|
||||||
return roleDescriptions[userRole] || 'Description non disponible';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère la classe CSS pour un badge de rôle
|
|
||||||
*/
|
|
||||||
getRoleBadgeClass(role: string): string {
|
getRoleBadgeClass(role: string): string {
|
||||||
const userRole = role as UserRole;
|
const userRole = role as UserRole;
|
||||||
switch (userRole) {
|
return ROLE_CONFIG[userRole]?.badgeClass || 'bg-secondary';
|
||||||
case UserRole.DCB_ADMIN:
|
|
||||||
return 'bg-danger';
|
|
||||||
case UserRole.DCB_SUPPORT:
|
|
||||||
return 'bg-info';
|
|
||||||
case UserRole.DCB_PARTNER:
|
|
||||||
return 'bg-primary';
|
|
||||||
case UserRole.DCB_PARTNER_ADMIN:
|
|
||||||
return 'bg-warning';
|
|
||||||
case UserRole.DCB_PARTNER_MANAGER:
|
|
||||||
return 'bg-success';
|
|
||||||
case UserRole.DCB_PARTNER_SUPPORT:
|
|
||||||
return 'bg-secondary';
|
|
||||||
default:
|
|
||||||
return 'bg-secondary';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère l'icône pour un rôle
|
|
||||||
*/
|
|
||||||
getRoleIcon(role: string): string {
|
getRoleIcon(role: string): string {
|
||||||
const userRole = role as UserRole;
|
const userRole = role as UserRole;
|
||||||
switch (userRole) {
|
return ROLE_CONFIG[userRole]?.icon || 'lucideUser';
|
||||||
case UserRole.DCB_ADMIN:
|
|
||||||
return 'lucideShield';
|
|
||||||
case UserRole.DCB_SUPPORT:
|
|
||||||
return 'lucideHeadphones';
|
|
||||||
case UserRole.DCB_PARTNER:
|
|
||||||
return 'lucideBuilding';
|
|
||||||
case UserRole.DCB_PARTNER_ADMIN:
|
|
||||||
return 'lucideShieldCheck';
|
|
||||||
case UserRole.DCB_PARTNER_MANAGER:
|
|
||||||
return 'lucideUserCog';
|
|
||||||
case UserRole.DCB_PARTNER_SUPPORT:
|
|
||||||
return 'lucideHeadphones';
|
|
||||||
default:
|
|
||||||
return 'lucideUser';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie si un rôle est un rôle administrateur
|
* Vérifications de type de rôle
|
||||||
*/
|
*/
|
||||||
isAdminRole(role: UserRole): boolean {
|
isAdminRole(role: UserRole): boolean {
|
||||||
return role === UserRole.DCB_ADMIN;
|
return role === UserRole.DCB_ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si un rôle est un rôle support
|
|
||||||
*/
|
|
||||||
isSupportRole(role: UserRole): boolean {
|
isSupportRole(role: UserRole): boolean {
|
||||||
return role === UserRole.DCB_SUPPORT;
|
return role === UserRole.DCB_SUPPORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si un rôle est un rôle partenaire
|
|
||||||
*/
|
|
||||||
isPartnerRole(role: UserRole): boolean {
|
isPartnerRole(role: UserRole): boolean {
|
||||||
return role === UserRole.DCB_PARTNER;
|
return role === UserRole.DCB_PARTNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
isMerchantUserRole(role: UserRole): boolean {
|
||||||
* Vérifie si un rôle est un rôle marchand
|
return role === UserRole.DCB_PARTNER_ADMIN
|
||||||
*/
|
|| role === UserRole.DCB_PARTNER_MANAGER
|
||||||
isMerchantRole(role: UserRole): boolean {
|
|| role === UserRole.DCB_PARTNER_SUPPORT
|
||||||
const merchantRoles = [
|
|| role === UserRole.MERCHANT_CONFIG_ADMIN
|
||||||
UserRole.DCB_PARTNER_ADMIN,
|
|| role === UserRole.MERCHANT_CONFIG_MANAGER
|
||||||
UserRole.DCB_PARTNER_MANAGER,
|
|| role === UserRole.MERCHANT_CONFIG_TECHNICAL
|
||||||
UserRole.DCB_PARTNER_SUPPORT
|
|| role === UserRole.MERCHANT_CONFIG_VIEWER;
|
||||||
];
|
|
||||||
return merchantRoles.includes(role);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère tous les rôles disponibles sous forme de tableau
|
* Gestion des listes de rôles
|
||||||
*/
|
*/
|
||||||
getAllRoles(): UserRole[] {
|
getAllRoles(): UserRole[] {
|
||||||
return Object.values(UserRole);
|
return Object.values(UserRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getHubRoles(): UserRole[] {
|
||||||
* Récupère les rôles que l'utilisateur courant peut attribuer
|
return [...HUB_ROLES];
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
getMerchantRoles(): UserRole[] {
|
||||||
|
return [...MERCHANT_ROLES];
|
||||||
|
}
|
||||||
|
|
||||||
getAssignableRoles(currentUserRole: UserRole | null): UserRole[] {
|
getAssignableRoles(currentUserRole: UserRole | null): UserRole[] {
|
||||||
if (!currentUserRole) return [];
|
if (!currentUserRole) return [];
|
||||||
return this.getPermissionsForRole(currentUserRole).assignableRoles;
|
return this.getPermissionsForRole(currentUserRole).assignableRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère les rôles Hub assignables (pour le composant unifié)
|
|
||||||
*/
|
|
||||||
getAssignableHubRoles(currentUserRole: UserRole | null): UserRole[] {
|
getAssignableHubRoles(currentUserRole: UserRole | null): UserRole[] {
|
||||||
if (!currentUserRole) return [];
|
return this.filterAssignableRoles(currentUserRole, HUB_ROLES);
|
||||||
|
|
||||||
const allHubRoles = this.getHubRoles();
|
|
||||||
const permissions = this.getPermissionsForRole(currentUserRole);
|
|
||||||
|
|
||||||
return allHubRoles.filter(role =>
|
|
||||||
permissions.assignableRoles.includes(role)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère les rôles Marchands assignables (pour le composant unifié)
|
|
||||||
*/
|
|
||||||
getAssignableMerchantRoles(currentUserRole: UserRole | null): UserRole[] {
|
getAssignableMerchantRoles(currentUserRole: UserRole | null): UserRole[] {
|
||||||
|
return this.filterAssignableRoles(currentUserRole, MERCHANT_ROLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private filterAssignableRoles(
|
||||||
|
currentUserRole: UserRole | null,
|
||||||
|
roleList: readonly UserRole[]
|
||||||
|
): UserRole[] {
|
||||||
if (!currentUserRole) return [];
|
if (!currentUserRole) return [];
|
||||||
|
|
||||||
const allMerchantRoles = this.getMerchantRoles();
|
|
||||||
const permissions = this.getPermissionsForRole(currentUserRole);
|
const permissions = this.getPermissionsForRole(currentUserRole);
|
||||||
|
return roleList.filter(role => permissions.assignableRoles.includes(role));
|
||||||
return allMerchantRoles.filter(role =>
|
|
||||||
permissions.assignableRoles.includes(role)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère uniquement les rôles Hub
|
* Vérifications de rôles
|
||||||
*/
|
|
||||||
getHubRoles(): UserRole[] {
|
|
||||||
return [
|
|
||||||
UserRole.DCB_ADMIN,
|
|
||||||
UserRole.DCB_SUPPORT,
|
|
||||||
UserRole.DCB_PARTNER
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère uniquement les rôles Marchands
|
|
||||||
*/
|
|
||||||
getMerchantRoles(): UserRole[] {
|
|
||||||
return [
|
|
||||||
UserRole.DCB_PARTNER_ADMIN,
|
|
||||||
UserRole.DCB_PARTNER_MANAGER,
|
|
||||||
UserRole.DCB_PARTNER_SUPPORT
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur a un rôle spécifique
|
|
||||||
*/
|
*/
|
||||||
hasRole(userRole: UserRole | null, targetRole: UserRole): boolean {
|
hasRole(userRole: UserRole | null, targetRole: UserRole): boolean {
|
||||||
return userRole === targetRole;
|
return userRole === targetRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si l'utilisateur a au moins un des rôles spécifiés
|
|
||||||
*/
|
|
||||||
hasAnyRole(userRole: UserRole | null, targetRoles: UserRole[]): boolean {
|
hasAnyRole(userRole: UserRole | null, targetRoles: UserRole[]): boolean {
|
||||||
return userRole ? targetRoles.includes(userRole) : false;
|
return userRole ? targetRoles.includes(userRole) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Réinitialise le cache des rôles
|
* Gestion du cache
|
||||||
*/
|
*/
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.availableRoles$.next(null);
|
this.availableRoles$.next(null);
|
||||||
this.currentUserRole$.next(null);
|
this.currentUserRole$.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère les rôles disponibles (observable)
|
|
||||||
*/
|
|
||||||
getAvailableRoles(): Observable<AvailableRolesWithPermissions | null> {
|
getAvailableRoles(): Observable<AvailableRolesWithPermissions | null> {
|
||||||
return this.availableRoles$.asObservable();
|
return this.availableRoles$.asObservable();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -316,6 +316,103 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Informations de base du marchand (seulement pour DCB_PARTNER) -->
|
||||||
|
@if (newUser.role === UserRole.DCB_PARTNER) {
|
||||||
|
<div class="col-12 mt-4">
|
||||||
|
<h6 class="border-bottom pb-2">
|
||||||
|
<ng-icon name="lucideStore" class="me-2"></ng-icon>
|
||||||
|
Informations de base du marchand
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">
|
||||||
|
Nom du marchand <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Ex: Boutique ABC"
|
||||||
|
[(ngModel)]="newMerchant.name"
|
||||||
|
name="merchantName"
|
||||||
|
required
|
||||||
|
[disabled]="creatingUser"
|
||||||
|
#merchantName="ngModel"
|
||||||
|
>
|
||||||
|
@if (merchantName.invalid && merchantName.touched) {
|
||||||
|
<div class="text-danger small">
|
||||||
|
Le nom du marchand est requis
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Logo URL</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="https://exemple.com/logo.png"
|
||||||
|
[(ngModel)]="newMerchant.logo"
|
||||||
|
name="merchantLogo"
|
||||||
|
[disabled]="creatingUser"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Description</label>
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Description du marchand"
|
||||||
|
[(ngModel)]="newMerchant.description"
|
||||||
|
name="merchantDescription"
|
||||||
|
[disabled]="creatingUser"
|
||||||
|
rows="2"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">
|
||||||
|
Adresse <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Adresse complète"
|
||||||
|
[(ngModel)]="newMerchant.adresse"
|
||||||
|
name="merchantAdresse"
|
||||||
|
required
|
||||||
|
[disabled]="creatingUser"
|
||||||
|
#merchantAdresse="ngModel"
|
||||||
|
>
|
||||||
|
@if (merchantAdresse.invalid && merchantAdresse.touched) {
|
||||||
|
<div class="text-danger small">
|
||||||
|
L'adresse est requise
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">
|
||||||
|
Téléphone <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="+XX X XX XX XX XX"
|
||||||
|
[(ngModel)]="newMerchant.phone"
|
||||||
|
name="merchantPhone"
|
||||||
|
required
|
||||||
|
[disabled]="creatingUser"
|
||||||
|
#merchantPhone="ngModel"
|
||||||
|
>
|
||||||
|
@if (merchantPhone.invalid && merchantPhone.touched) {
|
||||||
|
<div class="text-danger small">
|
||||||
|
Le téléphone est requis
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Avertissement pour les non-DCB_PARTNER -->
|
<!-- Avertissement pour les non-DCB_PARTNER -->
|
||||||
@if (!canManageRoles) {
|
@if (!canManageRoles) {
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@ -401,6 +498,9 @@
|
|||||||
• Type d'utilisateur : HUB<br>
|
• Type d'utilisateur : HUB<br>
|
||||||
• Créé par : Utilisateur courant<br>
|
• Créé par : Utilisateur courant<br>
|
||||||
• Votre rôle : {{ currentUserRole || 'Non défini' }}
|
• Votre rôle : {{ currentUserRole || 'Non défini' }}
|
||||||
|
@if (newUser.role === UserRole.DCB_PARTNER) {
|
||||||
|
<br>• Un marchand sera créé avec cet utilisateur
|
||||||
|
}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Subject, takeUntil } from 'rxjs';
|
|||||||
import { HubUsersService } from './hub-users.service';
|
import { HubUsersService } from './hub-users.service';
|
||||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||||
import { AuthService } from '@core/services/auth.service';
|
import { AuthService } from '@core/services/auth.service';
|
||||||
|
import { MerchantSyncService } from './merchant-sync-orchestrator.service';
|
||||||
import { PageTitle } from '@app/components/page-title/page-title';
|
import { PageTitle } from '@app/components/page-title/page-title';
|
||||||
import { HubUsersList } from './hub-users-list/hub-users-list';
|
import { HubUsersList } from './hub-users-list/hub-users-list';
|
||||||
import { HubUserProfile } from './hub-users-profile/hub-users-profile';
|
import { HubUserProfile } from './hub-users-profile/hub-users-profile';
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
UserRole,
|
UserRole,
|
||||||
UserType
|
UserType
|
||||||
} from '@core/models/dcb-bo-hub-user.model';
|
} from '@core/models/dcb-bo-hub-user.model';
|
||||||
|
import { CreateMerchantDto } from '@core/models/merchant-config.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hub-users',
|
selector: 'app-hub-users',
|
||||||
@ -37,6 +39,7 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
private modalService = inject(NgbModal);
|
private modalService = inject(NgbModal);
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
private hubUsersService = inject(HubUsersService);
|
private hubUsersService = inject(HubUsersService);
|
||||||
|
private merchantSyncService = inject(MerchantSyncService);
|
||||||
protected roleService = inject(RoleManagementService);
|
protected roleService = inject(RoleManagementService);
|
||||||
private cdRef = inject(ChangeDetectorRef);
|
private cdRef = inject(ChangeDetectorRef);
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
@ -60,7 +63,7 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
canDeleteUsers = false;
|
canDeleteUsers = false;
|
||||||
canManageRoles = false;
|
canManageRoles = false;
|
||||||
|
|
||||||
// Formulaire de création
|
// Formulaire de création utilisateur
|
||||||
newUser: {
|
newUser: {
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
@ -70,12 +73,17 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
role: UserRole;
|
role: UserRole;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
emailVerified: boolean;
|
emailVerified: boolean;
|
||||||
userType: UserType
|
userType: UserType;
|
||||||
} = this.getDefaultUserForm();
|
} = this.getDefaultUserForm();
|
||||||
|
|
||||||
|
// Formulaire de création marchand (pour DCB_PARTNER)
|
||||||
|
newMerchant: CreateMerchantDto = this.getDefaultMerchantForm();
|
||||||
|
|
||||||
// États des opérations
|
// États des opérations
|
||||||
creatingUser = false;
|
creatingUser = false;
|
||||||
createUserError = '';
|
createUserError = '';
|
||||||
|
creatingMerchant = false;
|
||||||
|
|
||||||
resettingPassword = false;
|
resettingPassword = false;
|
||||||
resetPasswordError = '';
|
resetPasswordError = '';
|
||||||
resetPasswordSuccess = '';
|
resetPasswordSuccess = '';
|
||||||
@ -218,6 +226,18 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getDefaultMerchantForm(): CreateMerchantDto {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
logo: '',
|
||||||
|
description: '',
|
||||||
|
adresse: '',
|
||||||
|
phone: '',
|
||||||
|
configs:[],
|
||||||
|
technicalContacts: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== MÉTHODES D'INTERFACE ====================
|
// ==================== MÉTHODES D'INTERFACE ====================
|
||||||
|
|
||||||
userProfiles: { [userId: string]: any } = {}; // Stocker les profils par userId
|
userProfiles: { [userId: string]: any } = {}; // Stocker les profils par userId
|
||||||
@ -313,17 +333,8 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resetUserForm() {
|
private resetUserForm() {
|
||||||
this.newUser = {
|
this.newUser = this.getDefaultUserForm();
|
||||||
username: '',
|
this.newMerchant = this.getDefaultMerchantForm();
|
||||||
email: '',
|
|
||||||
firstName: '',
|
|
||||||
lastName: '',
|
|
||||||
password: '',
|
|
||||||
role: UserRole.DCB_SUPPORT,
|
|
||||||
enabled: true,
|
|
||||||
emailVerified: false,
|
|
||||||
userType: UserType.HUB
|
|
||||||
};
|
|
||||||
console.log('🔄 Hub user form reset');
|
console.log('🔄 Hub user form reset');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,10 +397,11 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = this.validateUserForm();
|
// Validation du formulaire utilisateur
|
||||||
if (!validation.isValid) {
|
const userValidation = this.validateUserForm();
|
||||||
this.createUserError = validation.error!;
|
if (!userValidation.isValid) {
|
||||||
console.error('❌ Form validation failed:', validation.error);
|
this.createUserError = userValidation.error!;
|
||||||
|
console.error('❌ User form validation failed:', userValidation.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +414,95 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
this.creatingUser = true;
|
this.creatingUser = true;
|
||||||
this.createUserError = '';
|
this.createUserError = '';
|
||||||
|
|
||||||
console.log('📤 Creating hub user with data:', this.newUser);
|
console.log('📤 Creating hub user:', {
|
||||||
|
username: this.newUser.username,
|
||||||
|
role: this.newUser.role,
|
||||||
|
type: this.newUser.role === UserRole.DCB_PARTNER ? 'DCB Partner (avec marchand)' : 'Utilisateur Hub standard'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pour DCB_PARTNER: créer d'abord le marchand
|
||||||
|
if (this.newUser.role === UserRole.DCB_PARTNER) {
|
||||||
|
// Validation du formulaire marchand pour DCB_PARTNER
|
||||||
|
const merchantValidation = this.validateMerchantForm();
|
||||||
|
if (!merchantValidation.isValid) {
|
||||||
|
this.createUserError = merchantValidation.error!;
|
||||||
|
console.error('❌ Merchant form validation failed:', merchantValidation.error);
|
||||||
|
this.creatingUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createMerchantForDcbPartner();
|
||||||
|
} else {
|
||||||
|
// Pour les autres rôles: créer uniquement l'utilisateur Hub
|
||||||
|
this.createHubUserOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un marchand avec un DCB Partner (ordre: merchantConfig -> Keycloak)
|
||||||
|
*/
|
||||||
|
private createMerchantForDcbPartner() {
|
||||||
|
this.creatingMerchant = true;
|
||||||
|
console.log('📤 Creating merchant for DCB_PARTNER:', this.newMerchant);
|
||||||
|
|
||||||
|
// Données du DCB Partner
|
||||||
|
const dcbPartnerData = {
|
||||||
|
username: this.newUser.username,
|
||||||
|
email: this.newUser.email,
|
||||||
|
password: this.newUser.password,
|
||||||
|
firstName: this.newUser.firstName,
|
||||||
|
lastName: this.newUser.lastName,
|
||||||
|
enabled: this.newUser.enabled,
|
||||||
|
emailVerified: this.newUser.emailVerified
|
||||||
|
};
|
||||||
|
|
||||||
|
// Données du marchand
|
||||||
|
const merchantData: CreateMerchantDto = {
|
||||||
|
...this.newMerchant
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('🔧 Données envoyées:');
|
||||||
|
console.log(' Merchant:', merchantData);
|
||||||
|
console.log(' DCB Partner:', { ...dcbPartnerData, password: '***' });
|
||||||
|
|
||||||
|
// Utiliser le MerchantSyncService pour créer d'abord dans merchantConfig puis dans Keycloak
|
||||||
|
this.merchantSyncService.createMerchant(merchantData, dcbPartnerData)
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (createResult) => {
|
||||||
|
console.log('✅ Merchant and DCB Partner created successfully:', createResult);
|
||||||
|
this.creatingUser = false;
|
||||||
|
this.creatingMerchant = false;
|
||||||
|
|
||||||
|
console.log('📊 Résultat création:');
|
||||||
|
console.log(' Merchant Config ID:', createResult.merchantConfig?.id);
|
||||||
|
console.log(' Keycloak Merchant ID:', createResult.keycloakMerchant?.id);
|
||||||
|
console.log(' Merchant name:', createResult.merchantConfig?.name);
|
||||||
|
console.log(' DCB Partner email:', createResult.keycloakMerchant?.email);
|
||||||
|
|
||||||
|
this.modalService.dismissAll();
|
||||||
|
this.refreshUsersList();
|
||||||
|
this.cdRef.detectChanges();
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('❌ Error creating merchant and DCB Partner:', error);
|
||||||
|
this.creatingUser = false;
|
||||||
|
this.creatingMerchant = false;
|
||||||
|
|
||||||
|
this.createUserError = this.getMerchantErrorMessage(error);
|
||||||
|
console.error('❌ Détails de l\'erreur:', error);
|
||||||
|
|
||||||
|
this.cdRef.detectChanges();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée uniquement un utilisateur Hub (pas de marchand)
|
||||||
|
*/
|
||||||
|
private createHubUserOnly() {
|
||||||
|
console.log('📤 Creating hub user only (no merchant)');
|
||||||
|
|
||||||
this.hubUsersService.createHubUser(this.newUser)
|
this.hubUsersService.createHubUser(this.newUser)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
@ -556,6 +656,25 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
return 'Erreur lors de la création de l\'utilisateur. Veuillez réessayer.';
|
return 'Erreur lors de la création de l\'utilisateur. Veuillez réessayer.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getMerchantErrorMessage(error: any): string {
|
||||||
|
if (error.error?.message) {
|
||||||
|
return error.error.message;
|
||||||
|
}
|
||||||
|
if (error.status === 400) {
|
||||||
|
return 'Données du marchand ou du DCB Partner invalides.';
|
||||||
|
}
|
||||||
|
if (error.status === 409) {
|
||||||
|
return 'Un marchand avec ce nom ou un utilisateur avec ces identifiants existe déjà.';
|
||||||
|
}
|
||||||
|
if (error.status === 403) {
|
||||||
|
return 'Vous n\'avez pas les permissions pour créer un marchand ou un DCB Partner.';
|
||||||
|
}
|
||||||
|
if (error.status === 404) {
|
||||||
|
return 'Service de création de marchand non disponible.';
|
||||||
|
}
|
||||||
|
return 'Erreur lors de la création du marchand et du DCB Partner.';
|
||||||
|
}
|
||||||
|
|
||||||
private getResetPasswordErrorMessage(error: any): string {
|
private getResetPasswordErrorMessage(error: any): string {
|
||||||
if (error.error?.message) {
|
if (error.error?.message) {
|
||||||
return error.error.message;
|
return error.error.message;
|
||||||
@ -588,7 +707,7 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
return 'Erreur lors de la suppression de l\'utilisateur. Veuillez réessayer.';
|
return 'Erreur lors de la suppression de l\'utilisateur. Veuillez réessayer.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== VALIDATION DU FORMULAIRE ====================
|
// ==================== VALIDATION DES FORMULAIRES ====================
|
||||||
|
|
||||||
private validateUserForm(): { isValid: boolean; error?: string } {
|
private validateUserForm(): { isValid: boolean; error?: string } {
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
@ -625,4 +744,20 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
return { isValid: true };
|
return { isValid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateMerchantForm(): { isValid: boolean; error?: string } {
|
||||||
|
const requiredFields = [
|
||||||
|
{ field: this.newMerchant.name?.trim(), name: 'Nom du marchand' },
|
||||||
|
{ field: this.newMerchant.adresse?.trim(), name: 'Adresse' },
|
||||||
|
{ field: this.newMerchant.phone?.trim(), name: 'Téléphone' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { field, name } of requiredFields) {
|
||||||
|
if (!field) {
|
||||||
|
return { isValid: false, error: `${name} est requis pour un DCB Partner` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isValid: true };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ export interface MerchantSyncStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class MerchantSyncCrudService {
|
export class MerchantSyncService {
|
||||||
private hubUsersService = inject(HubUsersService);
|
private hubUsersService = inject(HubUsersService);
|
||||||
private merchantUsersService = inject(MerchantUsersService);
|
private merchantUsersService = inject(MerchantUsersService);
|
||||||
private merchantConfigService = inject(MerchantConfigService);
|
private merchantConfigService = inject(MerchantConfigService);
|
||||||
@ -302,7 +302,7 @@ export class MerchantSyncCrudService {
|
|||||||
|
|
||||||
const users = keycloakUsers.map(keycloakUser => {
|
const users = keycloakUsers.map(keycloakUser => {
|
||||||
const merchantConfigUser = merchantConfigUsers.find(
|
const merchantConfigUser = merchantConfigUsers.find(
|
||||||
mcUser => mcUser.email === keycloakUser.email
|
mcUser => mcUser.userId === keycloakUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -382,7 +382,7 @@ export class MerchantSyncCrudService {
|
|||||||
*/
|
*/
|
||||||
updateMerchantUser(
|
updateMerchantUser(
|
||||||
keycloakUserId: string,
|
keycloakUserId: string,
|
||||||
merchantConfigUserId: number,
|
merchantConfigUserId: string,
|
||||||
merchantConfigId: string,
|
merchantConfigId: string,
|
||||||
updates: {
|
updates: {
|
||||||
keycloak?: UpdateUserDto;
|
keycloak?: UpdateUserDto;
|
||||||
@ -519,7 +519,7 @@ export class MerchantSyncCrudService {
|
|||||||
*/
|
*/
|
||||||
deleteMerchantUser(
|
deleteMerchantUser(
|
||||||
keycloakUserId: string,
|
keycloakUserId: string,
|
||||||
merchantConfigUserId: number,
|
merchantConfigUserId: string,
|
||||||
merchantConfigId: string,
|
merchantConfigId: string,
|
||||||
): Observable<{ success: boolean; message: string }> {
|
): Observable<{ success: boolean; message: string }> {
|
||||||
console.log('🗑️ DELETE Merchant User...');
|
console.log('🗑️ DELETE Merchant User...');
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import {
|
|||||||
UserType
|
UserType
|
||||||
} from '@core/models/dcb-bo-hub-user.model';
|
} from '@core/models/dcb-bo-hub-user.model';
|
||||||
import { HubUsersService } from './hub-users.service';
|
import { HubUsersService } from './hub-users.service';
|
||||||
import { MerchantSyncCrudService } from './merchant-sync-orchestrator.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-merchant-users',
|
selector: 'app-merchant-users',
|
||||||
@ -226,7 +225,7 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get showMerchantPartnerField(): boolean {
|
get showMerchantPartnerField(): boolean {
|
||||||
if (this.isMerchantRole(this.newUser.role)) {
|
if (this.isMerchantRole(this.newUser.role) && !this.isPartnerUser) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -264,7 +263,13 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
.subscribe({
|
.subscribe({
|
||||||
next: (user) => {
|
next: (user) => {
|
||||||
this.currentUserRole = this.extractUserRole(user);
|
this.currentUserRole = this.extractUserRole(user);
|
||||||
|
|
||||||
|
if(user?.role.includes(UserRole.DCB_PARTNER)){
|
||||||
|
this.currentMerchantPartnerId = user.id;
|
||||||
|
} else {
|
||||||
this.currentMerchantPartnerId = this.extractMerchantPartnerId(user);
|
this.currentMerchantPartnerId = this.extractMerchantPartnerId(user);
|
||||||
|
}
|
||||||
|
|
||||||
this.currentUserType = this.extractUserType(user);
|
this.currentUserType = this.extractUserType(user);
|
||||||
|
|
||||||
console.log(`MERCHANT User ROLE: ${this.currentUserRole}`);
|
console.log(`MERCHANT User ROLE: ${this.currentUserRole}`);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
ConfigType,
|
ConfigType,
|
||||||
Operator,
|
Operator,
|
||||||
MerchantUtils,
|
MerchantUtils,
|
||||||
|
UserRole,
|
||||||
} from '@core/models/merchant-config.model';
|
} from '@core/models/merchant-config.model';
|
||||||
|
|
||||||
import { MerchantConfigService } from '../merchant-config.service';
|
import { MerchantConfigService } from '../merchant-config.service';
|
||||||
@ -81,6 +82,7 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
// Permissions
|
// Permissions
|
||||||
currentUserRole: any = null;
|
currentUserRole: any = null;
|
||||||
canViewAllMerchants = false;
|
canViewAllMerchants = false;
|
||||||
|
currentMerchantConfigId: string | undefined;
|
||||||
|
|
||||||
// ==================== CONVERSION IDS ====================
|
// ==================== CONVERSION IDS ====================
|
||||||
|
|
||||||
@ -131,7 +133,7 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumnCount(): number {
|
getColumnCount(): number {
|
||||||
return 8; // Nombre de colonnes dans le tableau
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -153,7 +155,19 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
.subscribe({
|
.subscribe({
|
||||||
next: (user) => {
|
next: (user) => {
|
||||||
this.currentUserRole = this.extractUserRole(user);
|
this.currentUserRole = this.extractUserRole(user);
|
||||||
|
|
||||||
|
if(user?.role.includes(UserRole.DCB_PARTNER)){
|
||||||
|
this.currentMerchantConfigId = user.merchantConfigId;
|
||||||
|
} else {
|
||||||
|
this.currentMerchantConfigId = this.extractMerchantConfigId(user);
|
||||||
|
}
|
||||||
|
|
||||||
this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole);
|
this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole);
|
||||||
|
|
||||||
|
console.log(`MERCHANT User ROLE: ${this.currentUserRole}`);
|
||||||
|
console.log(`Merchant Config ID: ${this.currentMerchantConfigId}`);
|
||||||
|
console.log(`canViewAllMerchants: ${this.canViewAllMerchants}`);
|
||||||
|
|
||||||
this.loadMerchants();
|
this.loadMerchants();
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
@ -163,6 +177,17 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extraire le merchantPartnerId
|
||||||
|
*/
|
||||||
|
private extractMerchantConfigId(user: any): string {
|
||||||
|
if (user?.merchantConfigId) {
|
||||||
|
return user.merchantConfigId;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private extractUserRole(user: any): any {
|
private extractUserRole(user: any): any {
|
||||||
const userRoles = this.authService.getCurrentUserRoles();
|
const userRoles = this.authService.getCurrentUserRoles();
|
||||||
return userRoles && userRoles.length > 0 ? userRoles[0] : null;
|
return userRoles && userRoles.length > 0 ? userRoles[0] : null;
|
||||||
@ -172,9 +197,8 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
if (!role) return false;
|
if (!role) return false;
|
||||||
|
|
||||||
const canViewAllRoles = [
|
const canViewAllRoles = [
|
||||||
'DCB_ADMIN',
|
UserRole.DCB_ADMIN,
|
||||||
'DCB_SUPPORT',
|
UserRole.DCB_SUPPORT
|
||||||
'DCB_PARTNER_ADMIN'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return canViewAllRoles.includes(role);
|
return canViewAllRoles.includes(role);
|
||||||
@ -239,7 +263,17 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getMyMerchants(): Observable<Merchant[]> {
|
private getMyMerchants(): Observable<Merchant[]> {
|
||||||
return this.getAllMerchants();
|
const merchantConfigId = Number(this.currentMerchantConfigId);
|
||||||
|
|
||||||
|
return this.merchantConfigService.getMerchantUsers(merchantConfigId).pipe(
|
||||||
|
map(response => {
|
||||||
|
return this.convertMerchantsToFrontend(response);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('Error getting all merchants:', error);
|
||||||
|
return of([]);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -399,91 +399,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Formulaire d'ajout de configuration -->
|
|
||||||
@if (addingConfig) {
|
|
||||||
<div class="section-card mb-4">
|
|
||||||
<div class="section-header">
|
|
||||||
<h6 class="mb-0">
|
|
||||||
<ng-icon name="lucidePlus" class="me-2"></ng-icon>
|
|
||||||
Nouvelle configuration
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="section-content">
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label">Type</label>
|
|
||||||
<select
|
|
||||||
class="form-select"
|
|
||||||
[(ngModel)]="newConfig.name"
|
|
||||||
>
|
|
||||||
@for (type of getAvailableConfigTypes(); track type.value) {
|
|
||||||
<option [value]="type.value">{{ type.label }}</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label">Opérateur</label>
|
|
||||||
<select
|
|
||||||
class="form-select"
|
|
||||||
[(ngModel)]="newConfig.operatorId"
|
|
||||||
>
|
|
||||||
<option [value]="null">Aucun opérateur spécifique</option>
|
|
||||||
@for (operator of getAvailableOperators(); track operator.value) {
|
|
||||||
<option [ngValue]="operator.value">{{ operator.label }}</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12">
|
|
||||||
<label class="form-label">Valeur</label>
|
|
||||||
<textarea
|
|
||||||
class="form-control font-monospace"
|
|
||||||
[(ngModel)]="newConfig.value"
|
|
||||||
placeholder="Valeur de la configuration"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
@if (isNewConfigSensitive()) {
|
|
||||||
<div class="alert alert-warning mt-2 mb-0">
|
|
||||||
<small>
|
|
||||||
<ng-icon name="lucideAlertTriangle" class="me-1"></ng-icon>
|
|
||||||
Cette valeur contient des informations sensibles. Soyez prudent.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
(click)="cancelAddingConfig()"
|
|
||||||
[disabled]="saving"
|
|
||||||
>
|
|
||||||
Annuler
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-success"
|
|
||||||
(click)="addConfig()"
|
|
||||||
[disabled]="saving || !validateNewConfig().isValid"
|
|
||||||
>
|
|
||||||
@if (saving) {
|
|
||||||
<div class="spinner-border spinner-border-sm me-1" role="status">
|
|
||||||
<span class="visually-hidden">Chargement...</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<ng-icon name="lucideCheck" class="me-1"></ng-icon>
|
|
||||||
Ajouter la configuration
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<!-- Formulaire d'édition -->
|
<!-- Formulaire d'édition -->
|
||||||
@if (isEditingConfig(config.id!)) {
|
@if (isEditingConfig(config.id!)) {
|
||||||
<div class="border-top pt-3 mt-3">
|
<div class="border-top pt-3 mt-3">
|
||||||
@ -572,6 +487,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
@ -596,6 +512,91 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<!-- Formulaire d'ajout de configuration -->
|
||||||
|
@if (addingConfig) {
|
||||||
|
<div class="section-card mb-4">
|
||||||
|
<div class="section-header">
|
||||||
|
<h6 class="mb-0">
|
||||||
|
<ng-icon name="lucidePlus" class="me-2"></ng-icon>
|
||||||
|
Nouvelle configuration
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Type</label>
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
[(ngModel)]="newConfig.name"
|
||||||
|
>
|
||||||
|
@for (type of getAvailableConfigTypes(); track type.value) {
|
||||||
|
<option [value]="type.value">{{ type.label }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Opérateur</label>
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
[(ngModel)]="newConfig.operatorId"
|
||||||
|
>
|
||||||
|
<option [value]="null">Aucun opérateur spécifique</option>
|
||||||
|
@for (operator of getAvailableOperators(); track operator.value) {
|
||||||
|
<option [ngValue]="operator.value">{{ operator.label }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Valeur</label>
|
||||||
|
<textarea
|
||||||
|
class="form-control font-monospace"
|
||||||
|
[(ngModel)]="newConfig.value"
|
||||||
|
placeholder="Valeur de la configuration"
|
||||||
|
rows="3"
|
||||||
|
></textarea>
|
||||||
|
@if (isNewConfigSensitive()) {
|
||||||
|
<div class="alert alert-warning mt-2 mb-0">
|
||||||
|
<small>
|
||||||
|
<ng-icon name="lucideAlertTriangle" class="me-1"></ng-icon>
|
||||||
|
Cette valeur contient des informations sensibles. Soyez prudent.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
(click)="cancelAddingConfig()"
|
||||||
|
[disabled]="saving"
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-success"
|
||||||
|
(click)="addConfig()"
|
||||||
|
[disabled]="saving || !validateNewConfig().isValid"
|
||||||
|
>
|
||||||
|
@if (saving) {
|
||||||
|
<div class="spinner-border spinner-border-sm me-1" role="status">
|
||||||
|
<span class="visually-hidden">Chargement...</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<ng-icon name="lucideCheck" class="me-1"></ng-icon>
|
||||||
|
Ajouter la configuration
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -198,7 +198,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
success = '';
|
success = '';
|
||||||
|
|
||||||
// Navigation par onglets
|
// Navigation par onglets
|
||||||
activeTab: 'overview' | 'configs' | 'contacts' | 'users' = 'overview';
|
activeTab: 'overview' | 'configs' | 'contacts' = 'overview';
|
||||||
|
|
||||||
// Gestion des permissions
|
// Gestion des permissions
|
||||||
currentUserRole: UserRole | null = null;
|
currentUserRole: UserRole | null = null;
|
||||||
@ -314,7 +314,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== GESTION DES ONGLETS ====================
|
// ==================== GESTION DES ONGLETS ====================
|
||||||
|
|
||||||
setActiveTab(tab: 'overview' | 'configs' | 'contacts' | 'users') {
|
setActiveTab(tab: 'overview' | 'configs' | 'contacts') {
|
||||||
this.activeTab = tab;
|
this.activeTab = tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +414,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// États pour la modification de rôle
|
// États pour la modification de rôle
|
||||||
editingUserId: number | null = null;
|
editingUserId: string | null = null;
|
||||||
newUserRole: UserRole | null = null;
|
newUserRole: UserRole | null = null;
|
||||||
|
|
||||||
// ==================== GESTION DES CONFIGURATIONS ====================
|
// ==================== GESTION DES CONFIGURATIONS ====================
|
||||||
@ -663,7 +663,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditingUserRole(userId: number): boolean {
|
isEditingUserRole(userId: string): boolean {
|
||||||
return this.editingUserId === userId;
|
return this.editingUserId === userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +950,6 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
switch (this.activeTab) {
|
switch (this.activeTab) {
|
||||||
case 'configs': return this.merchant?.configs || [];
|
case 'configs': return this.merchant?.configs || [];
|
||||||
case 'contacts': return this.merchant?.technicalContacts || [];
|
case 'contacts': return this.merchant?.technicalContacts || [];
|
||||||
case 'users': return this.merchant?.users || [];
|
|
||||||
default: return [];
|
default: return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1042,7 +1041,8 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
return contact.id || index;
|
return contact.id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByUserId(index: number, user: MerchantUser): number {
|
trackByUserId(index: number, user: MerchantUser): string {
|
||||||
return user.userId || index;
|
return user?.userId ?? `user-${index}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -24,9 +24,9 @@
|
|||||||
Permissions limitées
|
Permissions limitées
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
@if (selectedMerchantPartnerId) {
|
@if (selectedMerchantConfigId) {
|
||||||
<span class="ms-2">
|
<span class="ms-2">
|
||||||
<strong>Marchand :</strong> {{ selectedMerchantPartnerId }}
|
<strong>Marchand :</strong> {{ selectedMerchantConfigId }}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</small>
|
</small>
|
||||||
@ -65,7 +65,13 @@
|
|||||||
<li [ngbNavItem]="'list'">
|
<li [ngbNavItem]="'list'">
|
||||||
<a ngbNavLink (click)="showTab('list')">
|
<a ngbNavLink (click)="showTab('list')">
|
||||||
<ng-icon name="lucideList" class="fs-lg me-md-1 d-inline-flex align-middle" />
|
<ng-icon name="lucideList" class="fs-lg me-md-1 d-inline-flex align-middle" />
|
||||||
<span class="d-none d-md-inline-block align-middle">Marchands</span>
|
<span class="d-none d-md-inline-block align-middle">
|
||||||
|
@if (isMerchantUser) {
|
||||||
|
Utilisateurs
|
||||||
|
} @else {
|
||||||
|
Marchands
|
||||||
|
}
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<app-merchant-config-list
|
<app-merchant-config-list
|
||||||
@ -80,13 +86,29 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li [ngbNavItem]="'profile'" [hidden]="activeTab !== 'profile'">
|
<li [ngbNavItem]="'profile'" [hidden]="!showProfileTab">
|
||||||
<a ngbNavLink (click)="showTab('profile')">
|
<a ngbNavLink (click)="showTab('profile')">
|
||||||
<ng-icon name="lucideSettings" class="fs-lg me-md-1 d-inline-flex align-middle" />
|
<ng-icon name="lucideSettings" class="fs-lg me-md-1 d-inline-flex align-middle" />
|
||||||
<span class="d-none d-md-inline-block align-middle">Détails Marchand</span>
|
<span class="d-none d-md-inline-block align-middle">
|
||||||
|
@if (isMerchantUser) {
|
||||||
|
Mon Marchand
|
||||||
|
} @else {
|
||||||
|
Détails Marchand
|
||||||
|
}
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
@if (selectedMerchantId) {
|
@if (showProfileTab) {
|
||||||
|
<!-- Pour les merchant users, utiliser userMerchantId, pour les autres selectedMerchantId -->
|
||||||
|
@if (isMerchantUser && userMerchantId) {
|
||||||
|
<app-merchant-config-view
|
||||||
|
[merchantId]="userMerchantId"
|
||||||
|
(openCreateMerchantModal)="openCreateMerchantModal()"
|
||||||
|
(editMerchantRequested)="onEditMerchantRequested($event)"
|
||||||
|
(editConfigRequested)="onEditConfigRequested($event)"
|
||||||
|
(back)="backToList()"
|
||||||
|
/>
|
||||||
|
} @else if (!isMerchantUser && selectedMerchantId) {
|
||||||
<app-merchant-config-view
|
<app-merchant-config-view
|
||||||
[merchantId]="selectedMerchantId"
|
[merchantId]="selectedMerchantId"
|
||||||
(openCreateMerchantModal)="openCreateMerchantModal()"
|
(openCreateMerchantModal)="openCreateMerchantModal()"
|
||||||
@ -94,6 +116,7 @@
|
|||||||
(editConfigRequested)="onEditConfigRequested($event)"
|
(editConfigRequested)="onEditConfigRequested($event)"
|
||||||
(back)="backToList()"
|
(back)="backToList()"
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
} @else {
|
} @else {
|
||||||
<div class="alert alert-warning text-center">
|
<div class="alert alert-warning text-center">
|
||||||
<ng-icon name="lucideAlertCircle" class="me-2"></ng-icon>
|
<ng-icon name="lucideAlertCircle" class="me-2"></ng-icon>
|
||||||
@ -107,7 +130,6 @@
|
|||||||
<div class="tab-content" [ngbNavOutlet]="configsNav"></div>
|
<div class="tab-content" [ngbNavOutlet]="configsNav"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal de création de marchand -->
|
<!-- Modal de création de marchand -->
|
||||||
<ng-template #createMerchantModal let-modal>
|
<ng-template #createMerchantModal let-modal>
|
||||||
@ -239,6 +261,8 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@if(newMerchant.technicalContacts) {
|
||||||
<!-- Liste des contacts -->
|
<!-- Liste des contacts -->
|
||||||
@for (contact of newMerchant.technicalContacts; track $index; let i = $index) {
|
@for (contact of newMerchant.technicalContacts; track $index; let i = $index) {
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
@ -306,6 +330,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -334,6 +359,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if(newMerchant.configs) {
|
||||||
<!-- Liste des configurations -->
|
<!-- Liste des configurations -->
|
||||||
@for (config of newMerchant.configs; track $index; let i = $index) {
|
@for (config of newMerchant.configs; track $index; let i = $index) {
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
@ -399,6 +425,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -426,9 +453,7 @@
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
[disabled]="!merchantForm.form.valid || creatingMerchant ||
|
[disabled]="!merchantForm.form.valid || creatingMerchant"
|
||||||
!newMerchant.technicalContacts.length ||
|
|
||||||
!newMerchant.configs.length"
|
|
||||||
>
|
>
|
||||||
@if (creatingMerchant) {
|
@if (creatingMerchant) {
|
||||||
<div class="spinner-border spinner-border-sm me-2" role="status">
|
<div class="spinner-border spinner-border-sm me-2" role="status">
|
||||||
|
|||||||
@ -3,9 +3,6 @@ import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http'
|
|||||||
import { environment } from '@environments/environment';
|
import { environment } from '@environments/environment';
|
||||||
import { Observable, map, catchError, throwError, retry, timeout } from 'rxjs';
|
import { Observable, map, catchError, throwError, retry, timeout } from 'rxjs';
|
||||||
|
|
||||||
// Import de vos modèles existants
|
|
||||||
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Merchant,
|
Merchant,
|
||||||
CreateMerchantDto,
|
CreateMerchantDto,
|
||||||
@ -114,19 +111,19 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMerchantById(id: number): Observable<Merchant> {
|
getMerchantById(userId: number): Observable<Merchant> {
|
||||||
const numericId = this.convertIdToNumber(id);
|
//const numericId = this.convertIdToNumber(id);
|
||||||
|
|
||||||
console.log(`📥 Loading merchant ${id}`);
|
console.log(`📥 Loading merchant ${userId}`);
|
||||||
|
|
||||||
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${numericId}`).pipe(
|
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${userId}`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
retry(this.MAX_RETRIES),
|
retry(this.MAX_RETRIES),
|
||||||
map(apiMerchant => {
|
map(apiMerchant => {
|
||||||
console.log(`✅ Merchant ${id} loaded successfully`);
|
console.log(`✅ Merchant ${userId} loaded successfully`);
|
||||||
return this.dataAdapter.convertApiMerchantToFrontend(apiMerchant);
|
return this.dataAdapter.convertApiMerchantToFrontend(apiMerchant);
|
||||||
}),
|
}),
|
||||||
catchError(error => this.handleError('getMerchantById', error, { merchantId: id }))
|
catchError(error => this.handleError('getMerchantById', error, { merchantId: userId }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,11 +178,10 @@ export class MerchantConfigService {
|
|||||||
getMerchantUsers(merchantId: number): Observable<MerchantUser[]> {
|
getMerchantUsers(merchantId: number): Observable<MerchantUser[]> {
|
||||||
//const merchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${merchantId}`).pipe(
|
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${merchantId}/users`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiMerchant => {
|
map(apiMerchant => {
|
||||||
return (apiMerchant.users || []).map(user =>
|
return (apiMerchant.users || []).map(user =>
|
||||||
// ✅ UTILISATION DE L'ADAPTER
|
|
||||||
this.dataAdapter.convertApiUserToFrontend(user)
|
this.dataAdapter.convertApiUserToFrontend(user)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -193,7 +189,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUserRole(merchantId: number, userId: number, updateRoleDto: UpdateUserRoleDto): Observable<MerchantUser> {
|
updateUserRole(merchantId: number, userId: string, updateRoleDto: UpdateUserRoleDto): Observable<MerchantUser> {
|
||||||
//const merchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.patch<ApiMerchantUser>(
|
return this.http.patch<ApiMerchantUser>(
|
||||||
@ -210,7 +206,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUserFromMerchant(merchantId: number, userId: number): Observable<void> {
|
removeUserFromMerchant(merchantId: number, userId: string): Observable<void> {
|
||||||
//const merchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.delete<void>(`${this.baseApiUrl}/${merchantId}/users/${userId}`).pipe(
|
return this.http.delete<void>(`${this.baseApiUrl}/${merchantId}/users/${userId}`).pipe(
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { catchError, finalize, map, of, Subject, takeUntil } from 'rxjs';
|
|||||||
import { MerchantConfigService } from './merchant-config.service';
|
import { MerchantConfigService } from './merchant-config.service';
|
||||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||||
import { AuthService } from '@core/services/auth.service';
|
import { AuthService } from '@core/services/auth.service';
|
||||||
|
import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
||||||
import { PageTitle } from '@app/components/page-title/page-title';
|
import { PageTitle } from '@app/components/page-title/page-title';
|
||||||
import { MerchantConfigsList } from './merchant-config-list/merchant-config-list';
|
import { MerchantConfigsList } from './merchant-config-list/merchant-config-list';
|
||||||
import { MerchantConfigView } from './merchant-config-view/merchant-config-view';
|
import { MerchantConfigView } from './merchant-config-view/merchant-config-view';
|
||||||
@ -23,7 +24,6 @@ import {
|
|||||||
TechnicalContact
|
TechnicalContact
|
||||||
} from '@core/models/merchant-config.model';
|
} from '@core/models/merchant-config.model';
|
||||||
import { UserRole, UserType, PaginatedUserResponse, User } from '@core/models/dcb-bo-hub-user.model';
|
import { UserRole, UserType, PaginatedUserResponse, User } from '@core/models/dcb-bo-hub-user.model';
|
||||||
import { HubUsersService } from '../hub-users-management/hub-users.service';
|
|
||||||
|
|
||||||
import { MerchantDataAdapter } from './merchant-data-adapter.service';
|
import { MerchantDataAdapter } from './merchant-data-adapter.service';
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
private modalService = inject(NgbModal);
|
private modalService = inject(NgbModal);
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
private merchantConfigService = inject(MerchantConfigService);
|
private merchantConfigService = inject(MerchantConfigService);
|
||||||
|
private merchantSyncService = inject(MerchantSyncService);
|
||||||
private dataAdapter = inject(MerchantDataAdapter);
|
private dataAdapter = inject(MerchantDataAdapter);
|
||||||
protected roleService = inject(RoleManagementService);
|
protected roleService = inject(RoleManagementService);
|
||||||
private cdRef = inject(ChangeDetectorRef);
|
private cdRef = inject(ChangeDetectorRef);
|
||||||
@ -70,12 +71,16 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
// Gestion des permissions
|
// Gestion des permissions
|
||||||
currentUserRole: UserRole | null = null;
|
currentUserRole: UserRole | null = null;
|
||||||
currentUserType: UserType | null = null;
|
currentUserType: UserType | null = null;
|
||||||
currentMerchantPartnerId: string = '';
|
currentMerchantConfigId: string | undefined;
|
||||||
userPermissions: any = null;
|
userPermissions: any = null;
|
||||||
canCreateMerchants = false;
|
canCreateMerchants = false;
|
||||||
canDeleteMerchants = false;
|
canDeleteMerchants = false;
|
||||||
canManageMerchants = false;
|
canManageMerchants = false;
|
||||||
|
|
||||||
|
// Déterminer le type d'utilisateur
|
||||||
|
isMerchantUser = false;
|
||||||
|
isHubUser = false;
|
||||||
|
|
||||||
// Formulaire de création
|
// Formulaire de création
|
||||||
newMerchant: CreateMerchantDto = this.getDefaultMerchantForm();
|
newMerchant: CreateMerchantDto = this.getDefaultMerchantForm();
|
||||||
|
|
||||||
@ -123,16 +128,169 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
merchantPartners: User[] = [];
|
merchantPartners: User[] = [];
|
||||||
loadingMerchantPartners = false;
|
loadingMerchantPartners = false;
|
||||||
merchantPartnersError = '';
|
merchantPartnersError = '';
|
||||||
selectedMerchantPartnerId: string = '';
|
selectedMerchantConfigId: string | undefined;
|
||||||
|
|
||||||
// Cache des marchands
|
// Cache des marchands
|
||||||
merchantProfiles: { [merchantId: number]: Merchant } = {};
|
merchantProfiles: { [merchantId: number]: Merchant } = {};
|
||||||
loadingMerchants: { [merchantId: number]: boolean } = {};
|
loadingMerchants: { [merchantId: number]: boolean } = {};
|
||||||
|
|
||||||
|
// Marchand de l'utilisateur (pour les merchant users)
|
||||||
|
userMerchantId: number | null = null;
|
||||||
|
userMerchant: Merchant | null = null;
|
||||||
|
loadingUserMerchant = false;
|
||||||
|
|
||||||
|
// Gestion des utilisateurs du marchand (pour les merchant users)
|
||||||
|
merchantUsers: User[] = [];
|
||||||
|
loadingMerchantUsers = false;
|
||||||
|
merchantUsersError = '';
|
||||||
|
currentMerchantPartnerId: string = '';
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.activeTab = 'list';
|
|
||||||
this.loadCurrentUserPermissions();
|
this.loadCurrentUserPermissions();
|
||||||
|
this.initializeUserType();
|
||||||
this.initializeMerchantPartnerContext();
|
this.initializeMerchantPartnerContext();
|
||||||
|
|
||||||
|
// Déterminer l'onglet initial en fonction du type d'utilisateur
|
||||||
|
this.setInitialTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise le type d'utilisateur
|
||||||
|
*/
|
||||||
|
private initializeUserType(): void {
|
||||||
|
this.currentUserType = this.authService.getCurrentUserType();
|
||||||
|
this.currentUserRole = this.authService.getCurrentUserRole();
|
||||||
|
|
||||||
|
// Déterminer si c'est un utilisateur marchand
|
||||||
|
if (this.currentUserRole === UserRole.DCB_PARTNER ||
|
||||||
|
this.currentUserRole === UserRole.DCB_PARTNER_ADMIN ||
|
||||||
|
this.currentUserRole === UserRole.DCB_PARTNER_MANAGER ||
|
||||||
|
this.currentUserRole === UserRole.DCB_PARTNER_SUPPORT ||
|
||||||
|
this.currentUserRole === UserRole.MERCHANT_CONFIG_MANAGER ||
|
||||||
|
this.currentUserRole === UserRole.MERCHANT_CONFIG_TECHNICAL ||
|
||||||
|
this.currentUserRole === UserRole.MERCHANT_CONFIG_VIEWER) {
|
||||||
|
this.isMerchantUser = true;
|
||||||
|
} else {
|
||||||
|
this.isHubUser = this.currentUserType === UserType.HUB;
|
||||||
|
this.isMerchantUser = this.currentUserType === UserType.MERCHANT_PARTNER;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`User Type: ${this.currentUserType}`);
|
||||||
|
console.log(`User Role: ${this.currentUserRole}`);
|
||||||
|
console.log(`Is Merchant User: ${this.isMerchantUser}`);
|
||||||
|
console.log(`Is Hub User: ${this.isHubUser}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Définit l'onglet initial en fonction du type d'utilisateur
|
||||||
|
*/
|
||||||
|
private setInitialTab(): void {
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
// Pour un utilisateur marchand, charger directement son marchand ET les utilisateurs
|
||||||
|
this.activeTab = 'profile'; // On change pour afficher d'abord la liste des utilisateurs
|
||||||
|
this.loadUserMerchantAndUsers();
|
||||||
|
} else {
|
||||||
|
// Pour un utilisateur Hub, afficher la liste des marchands
|
||||||
|
this.activeTab = 'list';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge le marchand ET les utilisateurs pour un merchant user
|
||||||
|
*/
|
||||||
|
private loadUserMerchantAndUsers(): void {
|
||||||
|
if (!this.isMerchantUser || !this.currentMerchantConfigId) return;
|
||||||
|
|
||||||
|
// Charger le marchand de l'utilisateur
|
||||||
|
this.loadUserMerchant();
|
||||||
|
|
||||||
|
// Charger les utilisateurs du marchand
|
||||||
|
this.loadMerchantUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge le marchand de l'utilisateur (pour les merchant users)
|
||||||
|
*/
|
||||||
|
private loadUserMerchant(): void {
|
||||||
|
if (!this.isMerchantUser || !this.currentMerchantConfigId) return;
|
||||||
|
|
||||||
|
this.loadingUserMerchant = true;
|
||||||
|
|
||||||
|
const merchantConfigId = Number(this.currentMerchantConfigId);
|
||||||
|
|
||||||
|
this.merchantConfigService.getMerchantById(merchantConfigId)
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe({
|
||||||
|
next: (merchant) => {
|
||||||
|
if (merchant) {
|
||||||
|
const frontendMerchant = this.convertMerchantToFrontend(merchant);
|
||||||
|
this.userMerchantId = frontendMerchant.id!;
|
||||||
|
this.userMerchant = frontendMerchant;
|
||||||
|
|
||||||
|
// Afficher automatiquement le profil du marchand
|
||||||
|
this.showTab('profile', merchantConfigId);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.warn('No merchant found for current user');
|
||||||
|
}
|
||||||
|
this.loadingUserMerchant = false;
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.error('Error loading user merchant:', error);
|
||||||
|
this.loadingUserMerchant = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge les utilisateurs du marchand (pour les merchant users)
|
||||||
|
*/
|
||||||
|
private loadMerchantUsers(): void {
|
||||||
|
if (!this.isMerchantUser || !this.currentMerchantConfigId) return;
|
||||||
|
|
||||||
|
this.loadingMerchantUsers = true;
|
||||||
|
this.merchantUsersError = '';
|
||||||
|
|
||||||
|
this.merchantSyncService.getMerchantUsers(
|
||||||
|
this.currentMerchantConfigId,
|
||||||
|
this.currentMerchantPartnerId
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('Error loading merchant users:', error);
|
||||||
|
this.merchantUsersError = 'Erreur lors du chargement des utilisateurs';
|
||||||
|
this.loadingMerchantUsers = false;
|
||||||
|
return of({ users: [] });
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: (response: any) => {
|
||||||
|
// Transformer les données des utilisateurs
|
||||||
|
this.merchantUsers = response.users.map((userData: any) => {
|
||||||
|
const keycloakUser = userData.keycloakUser || {};
|
||||||
|
const merchantConfigUser = userData.merchantConfigUser || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: keycloakUser.id || merchantConfigUser.userId,
|
||||||
|
username: keycloakUser.username,
|
||||||
|
email: keycloakUser.email,
|
||||||
|
firstName: keycloakUser.firstName,
|
||||||
|
lastName: keycloakUser.lastName,
|
||||||
|
role: keycloakUser.role || merchantConfigUser.role,
|
||||||
|
enabled: keycloakUser.enabled,
|
||||||
|
emailVerified: keycloakUser.emailVerified,
|
||||||
|
merchantPartnerId: this.currentMerchantPartnerId,
|
||||||
|
merchantConfigId: this.currentMerchantConfigId,
|
||||||
|
createdDate: keycloakUser.createdDate || merchantConfigUser.createdDate,
|
||||||
|
lastModifiedDate: keycloakUser.lastModifiedDate || merchantConfigUser.lastModifiedDate
|
||||||
|
} as unknown as User;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Loaded ${this.merchantUsers.length} users for merchant`);
|
||||||
|
this.loadingMerchantUsers = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== MÉTHODES MANQUANTES POUR LE TEMPLATE ====================
|
// ==================== MÉTHODES MANQUANTES POUR LE TEMPLATE ====================
|
||||||
@ -260,8 +418,15 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
.subscribe({
|
.subscribe({
|
||||||
next: (user) => {
|
next: (user) => {
|
||||||
this.currentUserRole = this.extractUserRole(user);
|
this.currentUserRole = this.extractUserRole(user);
|
||||||
this.currentMerchantPartnerId = this.extractMerchantPartnerId(user);
|
|
||||||
this.currentUserType = this.extractUserType(user);
|
if (user?.role.includes(UserRole.DCB_PARTNER)) {
|
||||||
|
this.currentMerchantPartnerId = user.id;
|
||||||
|
this.currentMerchantConfigId = user.merchantConfigId;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.currentMerchantPartnerId = user?.merchantPartnerId || '';
|
||||||
|
this.currentMerchantConfigId = this.extractMerchantConfigId(user);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.currentUserRole) {
|
if (this.currentUserRole) {
|
||||||
this.userPermissions = this.roleService.getPermissionsForRole(this.currentUserRole);
|
this.userPermissions = this.roleService.getPermissionsForRole(this.currentUserRole);
|
||||||
@ -271,7 +436,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialiser le contexte marchand
|
// Initialiser le contexte marchand
|
||||||
this.selectedMerchantPartnerId = this.currentMerchantPartnerId;
|
this.selectedMerchantConfigId = this.currentMerchantConfigId;
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
console.error('Error loading user profile:', error);
|
console.error('Error loading user profile:', error);
|
||||||
@ -284,12 +449,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
return userRoles && userRoles.length > 0 ? userRoles[0] : null;
|
return userRoles && userRoles.length > 0 ? userRoles[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private extractUserType(user: any): UserType | null {
|
private extractMerchantConfigId(user: any): string {
|
||||||
return this.authService.getCurrentUserType();
|
return user?.merchantConfigId || '';
|
||||||
}
|
|
||||||
|
|
||||||
private extractMerchantPartnerId(user: any): string {
|
|
||||||
return user?.merchantPartnerId || this.authService.getCurrentMerchantPartnerId() || '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== PERMISSIONS SPÉCIFIQUES MARCHAND ====================
|
// ==================== PERMISSIONS SPÉCIFIQUES MARCHAND ====================
|
||||||
@ -304,12 +465,32 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
canModifyMerchant(merchant: Merchant): boolean {
|
canModifyMerchant(merchant: Merchant): boolean {
|
||||||
if (this.canManageMerchants) return true;
|
if (this.canManageMerchants) return true;
|
||||||
|
|
||||||
return true;
|
// Pour les utilisateurs marchands, ils ne peuvent modifier que leur propre marchand
|
||||||
|
if (this.isMerchantUser && this.userMerchantId) {
|
||||||
|
return merchant.id === this.userMerchantId;
|
||||||
|
}
|
||||||
|
|
||||||
// PARTNER ne peut modifier que ses propres marchands
|
return true;
|
||||||
// Vérifier via les configs ou users
|
}
|
||||||
//return merchant.configs?.some(config => config.merchantPartnerId === this.currentMerchantPartnerId) ||
|
|
||||||
// merchant.users?.some(user => user.merchantPartnerId === this.currentMerchantPartnerId);
|
/**
|
||||||
|
* Vérifie si l'utilisateur peut voir la liste des marchands
|
||||||
|
*/
|
||||||
|
get canViewMerchantList(): boolean {
|
||||||
|
// Les utilisateurs marchands ne voient pas la liste des marchands, mais la liste des utilisateurs
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Les utilisateurs Hub avec les rôles appropriés peuvent voir la liste
|
||||||
|
return this.isHubUser && (this.canManageMerchants || this.currentUserRole === UserRole.DCB_SUPPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si l'utilisateur peut voir la liste des utilisateurs du marchand
|
||||||
|
*/
|
||||||
|
get canViewMerchantUsers(): boolean {
|
||||||
|
return this.isMerchantUser && !!this.currentMerchantConfigId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== GESTION DES MARCHANDS ====================
|
// ==================== GESTION DES MARCHANDS ====================
|
||||||
@ -317,14 +498,12 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
private initializeMerchantPartnerContext(): void {
|
private initializeMerchantPartnerContext(): void {
|
||||||
if ((this.currentUserRole === UserRole.DCB_PARTNER ||
|
if ((this.currentUserRole === UserRole.DCB_PARTNER ||
|
||||||
this.currentUserRole === UserRole.DCB_PARTNER_ADMIN) &&
|
this.currentUserRole === UserRole.DCB_PARTNER_ADMIN) &&
|
||||||
this.currentMerchantPartnerId) {
|
this.currentMerchantConfigId) {
|
||||||
this.selectedMerchantPartnerId = this.currentMerchantPartnerId;
|
this.selectedMerchantConfigId = this.currentMerchantConfigId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMerchantSelectionChange(selectedMerchantId: number): void {
|
onMerchantSelectionChange(selectedMerchantId: number): void {
|
||||||
//this.selectedMerchantPartnerId = selectedMerchantId;
|
|
||||||
|
|
||||||
// Recharger les marchands pour le partenaire sélectionné
|
// Recharger les marchands pour le partenaire sélectionné
|
||||||
if (this.merchantConfigsList) {
|
if (this.merchantConfigsList) {
|
||||||
this.merchantConfigsList.refreshData();
|
this.merchantConfigsList.refreshData();
|
||||||
@ -335,19 +514,57 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
showTab(tab: 'list' | 'profile', merchantId?: number): void {
|
showTab(tab: 'list' | 'profile', merchantId?: number): void {
|
||||||
console.log(`Switching to tab: ${tab}`, merchantId ? `for merchant ${merchantId}` : '');
|
console.log(`Switching to tab: ${tab}`, merchantId ? `for merchant ${merchantId}` : '');
|
||||||
|
|
||||||
|
// Pour tous les utilisateurs, permettre de basculer entre les onglets
|
||||||
this.activeTab = tab;
|
this.activeTab = tab;
|
||||||
|
|
||||||
if (merchantId) {
|
if (tab === 'profile') {
|
||||||
this.selectedMerchantId = merchantId;
|
// Déterminer l'ID du marchand à afficher
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
// Pour les merchant users, toujours utiliser leur propre marchand
|
||||||
|
this.selectedMerchantId = this.userMerchantId || null;
|
||||||
|
} else {
|
||||||
|
// Pour les Hub users, utiliser l'ID fourni ou null
|
||||||
|
this.selectedMerchantId = merchantId || null;
|
||||||
|
}
|
||||||
|
|
||||||
// Charger le profil si pas déjà chargé
|
// Charger le profil si pas déjà chargé et si un ID est disponible
|
||||||
if (!this.merchantProfiles[merchantId]) {
|
if (this.selectedMerchantId && !this.merchantProfiles[this.selectedMerchantId]) {
|
||||||
this.loadMerchantProfile(merchantId);
|
this.loadMerchantProfile(this.selectedMerchantId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Pour l'onglet 'list', pas besoin d'ID de marchand sélectionné
|
||||||
this.selectedMerchantId = null;
|
this.selectedMerchantId = null;
|
||||||
|
|
||||||
|
// Si c'est un merchant user et qu'on va sur l'onglet liste, recharger les utilisateurs
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
this.refreshMerchantUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si l'utilisateur peut créer des utilisateurs dans son marchand
|
||||||
|
*/
|
||||||
|
get canCreateMerchantUsers(): boolean {
|
||||||
|
if (!this.isMerchantUser) return false;
|
||||||
|
|
||||||
|
// Seuls certains rôles marchands peuvent créer des utilisateurs
|
||||||
|
return this.currentUserRole === UserRole.DCB_PARTNER ||
|
||||||
|
this.currentUserRole === UserRole.DCB_PARTNER_ADMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si l'utilisateur peut éditer son marchand
|
||||||
|
*/
|
||||||
|
get canEditMerchant(): boolean {
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
// Les merchant users peuvent éditer leur propre marchand
|
||||||
|
return this.canModifyMerchant(this.userMerchant || { id: this.userMerchantId } as Merchant);
|
||||||
|
}
|
||||||
|
return this.canManageMerchants;
|
||||||
|
}
|
||||||
|
|
||||||
private loadMerchantProfile(merchantId: number): void {
|
private loadMerchantProfile(merchantId: number): void {
|
||||||
if (this.loadingMerchants[merchantId]) return;
|
if (this.loadingMerchants[merchantId]) return;
|
||||||
@ -370,10 +587,11 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
backToList(): void {
|
backToList(): void {
|
||||||
console.log('🔙 Returning to list view');
|
console.log('🔙 Returning to list view');
|
||||||
this.activeTab = 'list';
|
this.showTab('list');
|
||||||
this.selectedMerchantId = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================== ÉVÉNEMENTS DES COMPOSANTS ENFANTS ====================
|
// ==================== ÉVÉNEMENTS DES COMPOSANTS ENFANTS ====================
|
||||||
|
|
||||||
onMerchantSelected(merchantId: number): void {
|
onMerchantSelected(merchantId: number): void {
|
||||||
@ -556,6 +774,11 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
this.merchantProfiles[this.selectedMerchantId] = frontendMerchant;
|
this.merchantProfiles[this.selectedMerchantId] = frontendMerchant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le marchand de l'utilisateur si nécessaire
|
||||||
|
if (this.isMerchantUser && this.userMerchantId === merchantId) {
|
||||||
|
this.userMerchant = frontendMerchant;
|
||||||
|
}
|
||||||
|
|
||||||
this.successMessage = 'Marchand modifié avec succès';
|
this.successMessage = 'Marchand modifié avec succès';
|
||||||
|
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
@ -663,6 +886,12 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
this.backToList();
|
this.backToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Si l'utilisateur marchand supprime son propre marchand
|
||||||
|
if (this.isMerchantUser && this.userMerchantId === merchantId) {
|
||||||
|
this.userMerchant = null;
|
||||||
|
this.userMerchantId = null;
|
||||||
|
}
|
||||||
|
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
@ -730,6 +959,15 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rafraîchit la liste des utilisateurs du marchand
|
||||||
|
*/
|
||||||
|
refreshMerchantUsers(): void {
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
this.loadMerchantUsers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== GESTION DES ERREURS ====================
|
// ==================== GESTION DES ERREURS ====================
|
||||||
|
|
||||||
private getCreateErrorMessage(error: any): string {
|
private getCreateErrorMessage(error: any): string {
|
||||||
@ -800,25 +1038,58 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== GETTERS TEMPLATE ====================
|
// ==================== GETTERS TEMPLATE ====================
|
||||||
|
|
||||||
get currentMerchantProfile(): Merchant | null {
|
get showListTab(): boolean {
|
||||||
return this.selectedMerchantId ? this.merchantProfiles[this.selectedMerchantId] : null;
|
// Pour les merchant users, toujours montrer l'onglet liste (pour les utilisateurs)
|
||||||
|
// Pour les Hub users, montrer la liste si autorisé
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.canViewMerchantList;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLoadingMerchant(): boolean {
|
get showProfileTab(): boolean {
|
||||||
return this.selectedMerchantId ? this.loadingMerchants[this.selectedMerchantId] : false;
|
// Pour les merchant users, toujours montrer l'onglet profile (pour leur marchand)
|
||||||
|
// Pour les Hub users, montrer l'onglet profile si un marchand est sélectionné
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
return !!this.userMerchantId; // Montrer seulement si le marchand est chargé
|
||||||
|
}
|
||||||
|
return !!this.selectedMerchantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showMerchantPartnerField(): boolean {
|
get pageTitleForUser(): string {
|
||||||
return this.canManageMerchants;
|
if (this.isMerchantUser) {
|
||||||
|
return this.activeTab === 'list' ? 'Utilisateurs du Marchand' : 'Mon Marchand';
|
||||||
|
}
|
||||||
|
return this.pageTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
get requireMerchantPartnerSelection(): boolean {
|
get pageSubtitleForUser(): string {
|
||||||
return this.canManageMerchants;
|
if (this.isMerchantUser) {
|
||||||
|
return this.activeTab === 'list'
|
||||||
|
? 'Gérez les utilisateurs de votre marchand'
|
||||||
|
: 'Gérez la configuration technique de votre marchand';
|
||||||
|
}
|
||||||
|
return this.pageSubtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPartnerUser(): boolean {
|
get badgeForUser(): any {
|
||||||
return this.currentUserRole === UserRole.DCB_PARTNER ||
|
if (this.isMerchantUser) {
|
||||||
this.currentUserRole === UserRole.DCB_PARTNER_ADMIN;
|
return this.activeTab === 'list'
|
||||||
|
? { icon: 'lucideUsers', text: 'Users' }
|
||||||
|
: { icon: 'lucideStore', text: 'My Merchant' };
|
||||||
|
}
|
||||||
|
return this.badge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Détermine ce qui doit être affiché dans la liste
|
||||||
|
*/
|
||||||
|
get shouldDisplayMerchantUsers(): boolean {
|
||||||
|
return this.isMerchantUser && this.activeTab === 'list';
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldDisplayMerchantList(): boolean {
|
||||||
|
return !this.isMerchantUser && this.activeTab === 'list';
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
|||||||
@ -186,30 +186,33 @@ export class MerchantDataAdapter {
|
|||||||
errors.push('Le téléphone est requis');
|
errors.push('Le téléphone est requis');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dto.technicalContacts || dto.technicalContacts.length === 0) {
|
if (dto.technicalContacts && dto.technicalContacts.length > 0) {
|
||||||
errors.push('Au moins un contact technique est requis');
|
|
||||||
} else {
|
|
||||||
dto.technicalContacts.forEach((contact, index) => {
|
dto.technicalContacts.forEach((contact, index) => {
|
||||||
|
|
||||||
if (!contact.firstName?.trim()) {
|
if (!contact.firstName?.trim()) {
|
||||||
errors.push(`Le prénom du contact ${index + 1} est requis`);
|
errors.push(`Le prénom du contact ${index + 1} est requis`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contact.lastName?.trim()) {
|
if (!contact.lastName?.trim()) {
|
||||||
errors.push(`Le nom du contact ${index + 1} est requis`);
|
errors.push(`Le nom du contact ${index + 1} est requis`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contact.phone?.trim()) {
|
if (!contact.phone?.trim()) {
|
||||||
errors.push(`Le téléphone du contact ${index + 1} est requis`);
|
errors.push(`Le téléphone du contact ${index + 1} est requis`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contact.email?.trim()) {
|
if (!contact.email?.trim()) {
|
||||||
errors.push(`L'email du contact ${index + 1} est requis`);
|
errors.push(`L'email du contact ${index + 1} est requis`);
|
||||||
} else if (!this.isValidEmail(contact.email)) {
|
} else if (!this.isValidEmail(contact.email)) {
|
||||||
errors.push(`L'email du contact ${index + 1} est invalide`);
|
errors.push(`L'email du contact ${index + 1} est invalide`);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dto.configs || dto.configs.length === 0) {
|
if (dto.configs && dto.configs.length > 0) {
|
||||||
errors.push('Au moins une configuration est requise');
|
|
||||||
} else {
|
|
||||||
dto.configs.forEach((config, index) => {
|
dto.configs.forEach((config, index) => {
|
||||||
if (!config.name?.trim()) {
|
if (!config.name?.trim()) {
|
||||||
errors.push(`Le type de configuration ${index + 1} est requis`);
|
errors.push(`Le type de configuration ${index + 1} est requis`);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { MerchantSyncCrudService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
||||||
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export class Support implements OnInit {
|
|||||||
testMerchantConfigUserId:''
|
testMerchantConfigUserId:''
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(private merchantCrudService: MerchantSyncCrudService) {}
|
constructor(private merchantCrudService: MerchantSyncService) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
console.log('🚀 Support Component - Tests CRUD Merchants');
|
console.log('🚀 Support Component - Tests CRUD Merchants');
|
||||||
@ -276,7 +276,7 @@ export class Support implements OnInit {
|
|||||||
const userUpdateResult = await firstValueFrom(
|
const userUpdateResult = await firstValueFrom(
|
||||||
this.merchantCrudService.updateMerchantUser(
|
this.merchantCrudService.updateMerchantUser(
|
||||||
this.testData.testUserId,
|
this.testData.testUserId,
|
||||||
Number(this.testData.testMerchantConfigUserId),
|
this.testData.testMerchantConfigUserId,
|
||||||
this.testData.merchantConfigId,
|
this.testData.merchantConfigId,
|
||||||
{
|
{
|
||||||
keycloak: {
|
keycloak: {
|
||||||
@ -393,7 +393,7 @@ export class Support implements OnInit {
|
|||||||
const deleteUserResult = await firstValueFrom(
|
const deleteUserResult = await firstValueFrom(
|
||||||
this.merchantCrudService.deleteMerchantUser(
|
this.merchantCrudService.deleteMerchantUser(
|
||||||
this.testData.testUserId,
|
this.testData.testUserId,
|
||||||
Number(this.testData.testMerchantConfigUserId),
|
this.testData.testMerchantConfigUserId,
|
||||||
this.testData.merchantConfigId
|
this.testData.merchantConfigId
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user