dcb-backoffice/src/app/modules/users/list/list.html

342 lines
13 KiB
HTML

<app-ui-card title="Liste des Utilisateurs Hub">
<a
helper-text
href="javascript:void(0);"
class="icon-link icon-link-hover link-primary fw-semibold"
>Gérez les accès utilisateurs de votre plateforme DCB
</a>
<div card-body>
<!-- Barre d'actions supérieure -->
<div class="row mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<!-- Statistiques rapides -->
<div class="btn-group btn-group-sm">
<button
type="button"
class="btn btn-outline-primary"
[class.active]="roleFilter === 'all'"
(click)="filterByRole('all')"
>
Tous ({{ allUsers.length }})
</button>
<button
type="button"
class="btn btn-outline-danger"
[class.active]="roleFilter === UserRole.DCB_ADMIN"
(click)="filterByRole(UserRole.DCB_ADMIN)"
>
Admins ({{ getUsersCountByRole(UserRole.DCB_ADMIN) }})
</button>
<button
type="button"
class="btn btn-outline-info"
[class.active]="roleFilter === UserRole.DCB_SUPPORT"
(click)="filterByRole(UserRole.DCB_SUPPORT)"
>
Support ({{ getUsersCountByRole(UserRole.DCB_SUPPORT) }})
</button>
<button
type="button"
class="btn btn-outline-success"
[class.active]="roleFilter === UserRole.DCB_PARTNER"
(click)="filterByRole(UserRole.DCB_PARTNER)"
>
Partenaires ({{ getUsersCountByRole(UserRole.DCB_PARTNER) }})
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex justify-content-end gap-2">
@if (canCreateUsers) {
<button
class="btn btn-primary"
(click)="openCreateModal.emit()"
>
<ng-icon name="lucideUserPlus" class="me-1"></ng-icon>
Nouvel Utilisateur
</button>
}
</div>
</div>
</div>
<!-- Barre de recherche et filtres -->
<div class="row mb-3">
<div class="col-md-3">
<div class="input-group">
<span class="input-group-text">
<ng-icon name="lucideSearch"></ng-icon>
</span>
<input
type="text"
class="form-control"
placeholder="Nom, email, username..."
[(ngModel)]="searchTerm"
(keyup.enter)="onSearch()"
>
</div>
</div>
<div class="col-md-2">
<select class="form-select" [(ngModel)]="statusFilter" (change)="onSearch()">
<option value="all">Tous les statuts</option>
<option value="enabled">Activés ({{ getEnabledUsersCount() }})</option>
<option value="disabled">Désactivés ({{ getDisabledUsersCount() }})</option>
</select>
</div>
<div class="col-md-2">
<select class="form-select" [(ngModel)]="emailVerifiedFilter" (change)="onSearch()">
<option value="all">Tous les emails</option>
<option value="verified">Email vérifié</option>
<option value="not-verified">Email non vérifié</option>
</select>
</div>
<div class="col-md-2">
<select class="form-select" [(ngModel)]="roleFilter" (change)="onSearch()">
@for (role of availableRoles; track role.value) {
<option [value]="role.value">{{ role.label }}</option>
}
</select>
</div>
<div class="col-md-3">
<div class="d-flex gap-2">
<button class="btn btn-outline-primary" (click)="onSearch()">
<ng-icon name="lucideFilter" class="me-1"></ng-icon>
Appliquer
</button>
<button class="btn btn-outline-secondary" (click)="onClearFilters()">
<ng-icon name="lucideX" class="me-1"></ng-icon>
Réinitialiser
</button>
</div>
</div>
</div>
<!-- Loading State -->
@if (loading) {
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Chargement...</span>
</div>
<p class="mt-2 text-muted">Chargement des utilisateurs...</p>
</div>
}
<!-- Error State -->
@if (error && !loading) {
<div class="alert alert-danger" role="alert">
<div class="d-flex align-items-center">
<ng-icon name="lucideAlertCircle" class="me-2"></ng-icon>
<div>{{ error }}</div>
</div>
</div>
}
<!-- Users Table -->
@if (!loading && !error) {
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead class="table-light">
<tr>
<th (click)="sort('username')" class="cursor-pointer">
<div class="d-flex align-items-center">
<span>Utilisateur</span>
<ng-icon [name]="getSortIcon('username')" class="ms-1 fs-12"></ng-icon>
</div>
</th>
<th (click)="sort('email')" class="cursor-pointer">
<div class="d-flex align-items-center">
<span>Email</span>
<ng-icon [name]="getSortIcon('email')" class="ms-1 fs-12"></ng-icon>
</div>
</th>
<th (click)="sort('role')" class="cursor-pointer">
<div class="d-flex align-items-center">
<span>Rôle</span>
<ng-icon [name]="getSortIcon('role')" class="ms-1 fs-12"></ng-icon>
</div>
</th>
<th (click)="sort('enabled')" class="cursor-pointer">
<div class="d-flex align-items-center">
<span>Statut</span>
<ng-icon [name]="getSortIcon('enabled')" class="ms-1 fs-12"></ng-icon>
</div>
</th>
<th (click)="sort('createdTimestamp')" class="cursor-pointer">
<div class="d-flex align-items-center">
<span>Créé le</span>
<ng-icon [name]="getSortIcon('createdTimestamp')" class="ms-1 fs-12"></ng-icon>
</div>
</th>
<th width="180">Actions</th>
</tr>
</thead>
<tbody>
@for (user of displayedUsers; track user.id) {
<tr>
<td>
<div class="d-flex align-items-center">
<div class="avatar-sm bg-primary bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center me-2">
<span class="text-primary fw-semibold small">
{{ getUserInitials(user) }}
</span>
</div>
<div>
<strong class="d-block">{{ getUserDisplayName(user) }}</strong>
<small class="text-muted">@{{ user.username }}</small>
</div>
</div>
</td>
<td>
<div class="d-flex align-items-center">
{{ user.email }}
@if (!user.emailVerified) {
<ng-icon
name="lucideAlertTriangle"
class="ms-1 text-warning"
size="16"
title="Email non vérifié"
></ng-icon>
}
</div>
</td>
<td>
<span class="badge d-flex align-items-center" [ngClass]="getRoleBadgeClass(user.role)">
<ng-icon [name]="getRoleIcon(user.role)" class="me-1" size="14"></ng-icon>
{{ getRoleLabel(user.role) }}
</span>
</td>
<td>
<span [class]="getStatusBadgeClass(user)">
{{ getStatusText(user) }}
</span>
</td>
<td>
<small class="text-muted">
{{ formatTimestamp(user.createdTimestamp) }}
</small>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<button
class="btn btn-outline-primary btn-sm"
(click)="viewUserProfile(user.id)"
title="Voir le profil"
>
<ng-icon name="lucideEye"></ng-icon>
</button>
<button
class="btn btn-outline-warning btn-sm"
(click)="resetPassword(user)"
title="Réinitialiser le mot de passe"
>
<ng-icon name="lucideKey"></ng-icon>
</button>
@if (user.enabled) {
<button
class="btn btn-outline-secondary btn-sm"
(click)="disableUser(user)"
title="Désactiver l'utilisateur"
>
<ng-icon name="lucideUserX"></ng-icon>
</button>
} @else {
<button
class="btn btn-outline-success btn-sm"
(click)="enableUser(user)"
title="Activer l'utilisateur"
>
<ng-icon name="lucideUserCheck"></ng-icon>
</button>
}
@if (canDeleteUsers) {
<button
class="btn btn-outline-danger btn-sm"
(click)="deleteUser(user)"
title="Supprimer l'utilisateur"
>
<ng-icon name="lucideTrash2"></ng-icon>
</button>
}
</div>
</td>
</tr>
}
@empty {
<tr>
<td colspan="6" class="text-center py-4">
<div class="text-muted">
<ng-icon name="lucideUsers" class="fs-1 mb-3 opacity-50"></ng-icon>
<h5 class="mb-2">Aucun utilisateur trouvé</h5>
<p class="mb-3">Aucun utilisateur ne correspond à vos critères de recherche.</p>
@if (canCreateUsers) {
<button class="btn btn-primary" (click)="openCreateModal.emit()">
<ng-icon name="lucideUserPlus" class="me-1"></ng-icon>
Créer le premier utilisateur
</button>
}
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Pagination -->
@if (totalPages > 1) {
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="text-muted">
Affichage de {{ getStartIndex() }} à {{ getEndIndex() }} sur {{ totalItems }} utilisateurs
</div>
<nav>
<ngb-pagination
[collectionSize]="totalItems"
[page]="currentPage"
[pageSize]="itemsPerPage"
[maxSize]="5"
[rotate]="true"
[boundaryLinks]="true"
(pageChange)="onPageChange($event)"
/>
</nav>
</div>
}
<!-- Résumé des résultats -->
@if (displayedUsers.length > 0) {
<div class="mt-3 pt-3 border-top">
<div class="row text-center">
<div class="col">
<small class="text-muted">
<strong>Total :</strong> {{ allUsers.length }} utilisateurs
</small>
</div>
<div class="col">
<small class="text-muted">
<strong>Actifs :</strong> {{ getEnabledUsersCount() }}
</small>
</div>
<div class="col">
<small class="text-muted">
<strong>Admins :</strong> {{ getUsersCountByRole(UserRole.DCB_ADMIN) }}
</small>
</div>
<div class="col">
<small class="text-muted">
<strong>Support :</strong> {{ getUsersCountByRole(UserRole.DCB_SUPPORT) }}
</small>
</div>
<div class="col">
<small class="text-muted">
<strong>Partenaires :</strong> {{ getUsersCountByRole(UserRole.DCB_PARTNER) }}
</small>
</div>
</div>
</div>
}
}
</div>
</app-ui-card>