import { Component, inject, OnInit, Output, EventEmitter, ChangeDetectorRef, Input, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { NgIcon } from '@ng-icons/core'; import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; import { Observable, Subject, map, of } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; import { Merchant, ConfigType, Operator, MerchantUtils, } from '@core/models/merchant-config.model'; import { MerchantConfigService } from '../merchant-config.service'; import { RoleManagementService } from '@core/services/hub-users-roles-management.service'; import { AuthService } from '@core/services/auth.service'; import { UiCard } from '@app/components/ui-card'; @Component({ selector: 'app-merchant-config-list', standalone: true, imports: [ CommonModule, FormsModule, NgIcon, UiCard, NgbPaginationModule ], templateUrl: './merchant-config-list.html', }) export class MerchantConfigsList implements OnInit, OnDestroy { private authService = inject(AuthService); private merchantConfigService = inject(MerchantConfigService); protected roleService = inject(RoleManagementService); private cdRef = inject(ChangeDetectorRef); private destroy$ = new Subject(); // Configuration readonly ConfigType = ConfigType; readonly Operator = Operator; readonly MerchantUtils = MerchantUtils; // Inputs @Input() canCreateMerchants: boolean = false; @Input() canDeleteMerchants: boolean = false; // Outputs @Output() merchantSelected = new EventEmitter(); @Output() openCreateMerchantModal = new EventEmitter(); @Output() editMerchantRequested = new EventEmitter(); @Output() deleteMerchantRequested = new EventEmitter(); @Output() activateMerchantRequested = new EventEmitter(); @Output() deactivateMerchantRequested = new EventEmitter(); // Données allMerchants: Merchant[] = []; filteredMerchants: Merchant[] = []; displayedMerchants: Merchant[] = []; // États loading = false; error = ''; // Recherche et filtres searchTerm = ''; operatorFilter: Operator | 'all' = 'all'; // Pagination currentPage = 1; itemsPerPage = 10; totalItems = 0; totalPages = 0; // Tri sortField: keyof Merchant = 'name'; sortDirection: 'asc' | 'desc' = 'asc'; // Filtres disponibles availableOperators: { value: Operator | 'all'; label: string }[] = []; // Permissions currentUserRole: any = null; canViewAllMerchants = false; // ==================== CONVERSION IDS ==================== private convertIdToNumber(id: string): number { const numId = Number(id); if (isNaN(numId)) { throw new Error(`ID invalide pour la conversion en number: ${id}`); } return numId; } private convertIdToString(id: number): string { return id.toString(); } private convertMerchantToFrontend(merchant: any): Merchant { return { ...merchant, id: merchant.id ? this.convertIdToString(merchant.id) : undefined, configs: merchant.configs ? merchant.configs.map((config: any) => ({ ...config, id: config.id ? this.convertIdToString(config.id) : undefined, merchantPartnerId: config.merchantPartnerId ? this.convertIdToString(config.merchantPartnerId) : undefined })) : [], technicalContacts: merchant.technicalContacts ? merchant.technicalContacts.map((contact: any) => ({ ...contact, id: contact.id ? this.convertIdToString(contact.id) : undefined, merchantPartnerId: contact.merchantPartnerId ? this.convertIdToString(contact.merchantPartnerId) : undefined })) : [], users: merchant.users ? merchant.users.map((user: any) => ({ ...user, merchantPartnerId: user.merchantPartnerId ? this.convertIdToString(user.merchantPartnerId) : undefined })) : [] }; } private convertMerchantsToFrontend(merchants: any[]): Merchant[] { return merchants.map(merchant => this.convertMerchantToFrontend(merchant)); } // Getters pour la logique conditionnelle get showCreateButton(): boolean { return this.canCreateMerchants; } get showDeleteButton(): boolean { return this.canDeleteMerchants; } getColumnCount(): number { return 8; // Nombre de colonnes dans le tableau } ngOnInit() { this.initializeAvailableFilters(); } ngAfterViewInit() { this.loadCurrentUserPermissions(); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } private loadCurrentUserPermissions() { this.authService.getUserProfile() .pipe(takeUntil(this.destroy$)) .subscribe({ next: (user) => { this.currentUserRole = this.extractUserRole(user); this.canViewAllMerchants = this.canViewAllMerchantsCheck(this.currentUserRole); this.loadMerchants(); }, error: (error) => { console.error('Error loading current user permissions:', error); this.loadMerchants(); } }); } private extractUserRole(user: any): any { const userRoles = this.authService.getCurrentUserRoles(); return userRoles && userRoles.length > 0 ? userRoles[0] : null; } private canViewAllMerchantsCheck(role: any): boolean { if (!role) return false; const canViewAllRoles = [ 'DCB_ADMIN', 'DCB_SUPPORT', 'DCB_PARTNER_ADMIN' ]; return canViewAllRoles.includes(role); } private initializeAvailableFilters() { this.availableOperators = [ { value: 'all', label: 'Tous les opérateurs' }, { value: Operator.ORANGE_OSN, label: 'Orange' } ]; } loadMerchants() { this.loading = true; this.error = ''; let merchantsObservable: Observable; if (this.canViewAllMerchants) { merchantsObservable = this.getAllMerchants(); } else { merchantsObservable = this.getMyMerchants(); } merchantsObservable .pipe( takeUntil(this.destroy$), catchError(error => { console.error('Error loading merchants:', error); this.error = 'Erreur lors du chargement des marchands'; return of([] as Merchant[]); }) ) .subscribe({ next: (merchants) => { this.allMerchants = merchants || []; this.applyFiltersAndPagination(); this.loading = false; this.cdRef.detectChanges(); }, error: () => { this.error = 'Erreur lors du chargement des marchands'; this.loading = false; this.allMerchants = []; this.filteredMerchants = []; this.displayedMerchants = []; this.cdRef.detectChanges(); } }); } private getAllMerchants(): Observable { return this.merchantConfigService.getMerchants(1, 1000).pipe( map(response => { return this.convertMerchantsToFrontend(response.items); }), catchError(error => { console.error('Error getting all merchants:', error); return of([]); }) ); } private getMyMerchants(): Observable { return this.getAllMerchants(); } // ==================== ACTIONS ==================== viewMerchantProfile(merchant: Merchant) { this.merchantSelected.emit(merchant.id!); } editMerchant(merchant: Merchant) { this.editMerchantRequested.emit(merchant); } deleteMerchant(merchant: Merchant) { this.deleteMerchantRequested.emit(merchant); } activateMerchant(merchant: Merchant) { this.activateMerchantRequested.emit(merchant); } deactivateMerchant(merchant: Merchant) { this.deactivateMerchantRequested.emit(merchant); } // ==================== FILTRES ET RECHERCHE ==================== onSearch() { this.currentPage = 1; this.applyFiltersAndPagination(); } onClearFilters() { this.searchTerm = ''; this.operatorFilter = 'all'; this.currentPage = 1; this.applyFiltersAndPagination(); } filterByOperator(operator: Operator | 'all') { this.operatorFilter = operator; this.currentPage = 1; this.applyFiltersAndPagination(); } applyFiltersAndPagination() { if (!this.allMerchants) { this.allMerchants = []; } // Appliquer les filtres this.filteredMerchants = this.allMerchants.filter(merchant => { const matchesSearch = !this.searchTerm || merchant.name.toLowerCase().includes(this.searchTerm.toLowerCase()) || merchant.adresse.toLowerCase().includes(this.searchTerm.toLowerCase()) || merchant.phone.toLowerCase().includes(this.searchTerm.toLowerCase()) || (merchant.description && merchant.description.toLowerCase().includes(this.searchTerm.toLowerCase())); // Filtrer par opérateur basé sur les configurations const matchesOperator = this.operatorFilter === 'all' || (merchant.configs && merchant.configs.some(config => config.operatorId === this.operatorFilter)); return matchesSearch && matchesOperator; }); // Appliquer le tri this.filteredMerchants.sort((a, b) => { const aValue = a[this.sortField]; const bValue = b[this.sortField]; if (aValue === bValue) return 0; let comparison = 0; if (typeof aValue === 'string' && typeof bValue === 'string') { comparison = aValue.localeCompare(bValue); } else if (aValue instanceof Date && bValue instanceof Date) { comparison = aValue.getTime() - bValue.getTime(); } else if (typeof aValue === 'number' && typeof bValue === 'number') { comparison = aValue - bValue; } return this.sortDirection === 'asc' ? comparison : -comparison; }); // Calculer la pagination this.totalItems = this.filteredMerchants.length; this.totalPages = Math.ceil(this.totalItems / this.itemsPerPage); // Appliquer la pagination const startIndex = (this.currentPage - 1) * this.itemsPerPage; const endIndex = startIndex + this.itemsPerPage; this.displayedMerchants = this.filteredMerchants.slice(startIndex, endIndex); } // ==================== TRI ==================== sort(field: keyof Merchant) { if (this.sortField === field) { this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; } else { this.sortField = field; this.sortDirection = 'asc'; } this.applyFiltersAndPagination(); } getSortIcon(field: string): string { if (this.sortField !== field) return 'lucideArrowUpDown'; return this.sortDirection === 'asc' ? 'lucideArrowUp' : 'lucideArrowDown'; } // ==================== PAGINATION ==================== onPageChange(page: number) { this.currentPage = page; this.applyFiltersAndPagination(); } getStartIndex(): number { return (this.currentPage - 1) * this.itemsPerPage + 1; } getEndIndex(): number { return Math.min(this.currentPage * this.itemsPerPage, this.totalItems); } // ==================== MÉTHODES STATISTIQUES ==================== getTotalMerchantsCount(): number { return this.allMerchants.length; } getTotalConfigsCount(): number { return this.allMerchants.reduce((total, merchant) => total + (merchant.configs ? merchant.configs.length : 0), 0); } getTotalContactsCount(): number { return this.allMerchants.reduce((total, merchant) => total + (merchant.technicalContacts ? merchant.technicalContacts.length : 0), 0); } // ==================== MÉTHODES D'AFFICHAGE ==================== getConfigTypeLabel(configName: ConfigType | string): string { return MerchantUtils.getConfigTypeName(configName); } formatTimestamp(timestamp: string): string { if (!timestamp) return 'Non disponible'; return new Date(timestamp).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // ==================== MÉTHODES POUR LE TEMPLATE ==================== refreshData() { this.loadMerchants(); } getCardTitle(): string { return this.canViewAllMerchants ? 'Tous les Marchands' : 'Mes Marchands'; } getHelperText(): string { return this.canViewAllMerchants ? 'Vue administrative - Gestion de tous les marchands' : 'Vos marchands partenaires'; } getHelperIcon(): string { return this.canViewAllMerchants ? 'lucideShield' : 'lucideStore'; } getLoadingText(): string { return 'Chargement des marchands...'; } getEmptyStateTitle(): string { return 'Aucun marchand trouvé'; } getEmptyStateDescription(): string { return 'Aucun marchand ne correspond à vos critères de recherche.'; } getEmptyStateButtonText(): string { return 'Créer le premier marchand'; } }