dcb-backoffice/src/app/modules/profile/profile.html

689 lines
25 KiB
HTML

<div class="container-fluid">
<!-- En-tête avec navigation -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4 class="mb-1">
@if (user) {
{{ getUserDisplayName() }}
} @else {
Mon Profil
}
</h4>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item">
<a href="javascript:void(0)" (click)="goBack()" class="text-decoration-none cursor-pointer">
Tableau de bord
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
Mon Profil
</li>
</ol>
</nav>
</div>
<div class="d-flex gap-2">
<!-- Bouton de réinitialisation de mot de passe -->
@if (user && !isEditing) {
<button
class="btn btn-warning"
(click)="openMyProfileResetModal()"
>
<ng-icon name="lucideKey" class="me-1"></ng-icon>
Changer MDP
</button>
<!-- Bouton modification -->
<button
class="btn btn-primary"
(click)="startEditing()"
>
<ng-icon name="lucideEdit" class="me-1"></ng-icon>
Modifier
</button>
}
</div>
</div>
</div>
</div>
<!-- Messages d'alerte -->
@if (error) {
<div class="alert alert-danger">
<div class="d-flex align-items-center">
<ng-icon name="lucideAlertCircle" class="me-2"></ng-icon>
<div>{{ error }}</div>
</div>
</div>
}
@if (success) {
<div class="alert alert-success">
<div class="d-flex align-items-center">
<ng-icon name="lucideCheckCircle" class="me-2"></ng-icon>
<div>{{ success }}</div>
</div>
</div>
}
<div class="row">
<!-- Loading State -->
@if (loading) {
<div class="col-12 text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Chargement...</span>
</div>
<p class="mt-2 text-muted">Chargement de votre profil...</p>
</div>
}
<!-- User Profile -->
@if (user && !loading) {
<!-- Colonne de gauche - Informations de base -->
<div class="col-xl-4 col-lg-5">
<!-- Carte profil -->
<div class="card">
<div class="card-header bg-light">
<h5 class="card-title mb-0">
Mon Profil
</h5>
</div>
<div class="card-body text-center">
<!-- Avatar -->
<div class="avatar-lg mx-auto mb-3">
<div class="avatar-title bg-primary bg-opacity-10 rounded-circle text-primary fs-24">
{{ getUserInitials() }}
</div>
</div>
<h5>{{ getUserDisplayName() }}</h5>
<p class="text-muted mb-2">@{{ user.username }}</p>
<!-- Type d'utilisateur -->
<span class="badge bg-secondary mb-2">
{{ getUserTypeDisplay() }}
</span>
<!-- Statut -->
<span [class]="getStatusBadgeClass()" class="mb-3 d-block">
{{ getStatusText() }}
</span>
<!-- Informations rapides -->
<div class="mt-4 text-start">
<div class="d-flex align-items-center mb-2">
<ng-icon name="lucideMail" class="me-2 text-muted"></ng-icon>
<small>{{ user.email }}</small>
@if (!user.emailVerified) {
<ng-icon name="lucideAlertTriangle" class="ms-1 text-warning" size="14" title="Email non vérifié"></ng-icon>
}
</div>
<div class="d-flex align-items-center">
<ng-icon name="lucideCalendar" class="me-2 text-muted"></ng-icon>
<small>Membre depuis {{ getCreationDate() }}</small>
</div>
@if (isMerchantUser()) {
<div class="d-flex align-items-center mt-2">
<ng-icon name="lucideBuilding" class="me-2 text-muted"></ng-icon>
<small>Partner ID: {{ getMerchantPartnerId() }}</small>
</div>
}
</div>
</div>
</div>
<!-- Carte rôle utilisateur -->
<div class="card mt-3">
<div class="card-header bg-light">
<h5 class="card-title mb-0">
Mon Rôle
</h5>
</div>
<div class="card-body">
<!-- Rôle actuel -->
<div class="text-center mb-3">
@if (getUserRole()) {
<div class="d-flex flex-wrap gap-1 justify-content-center mb-2">
<span class="badge d-flex align-items-center" [ngClass]="getRoleBadgeClass()">
<ng-icon [name]="getRoleIcon(user.role)" class="me-1" size="12"></ng-icon>
{{ getRoleLabel() }}
</span>
</div>
<!-- Description du rôle -->
<small class="text-muted d-block">
{{ getRoleDescription() }}
</small>
} @else {
<span class="badge bg-secondary">Aucun rôle</span>
}
</div>
<!-- Information rôle non modifiable -->
<div class="alert alert-info mt-3">
<small>
<ng-icon name="lucideInfo" class="me-1"></ng-icon>
Votre rôle ne peut pas être modifié depuis cette page
</small>
</div>
</div>
</div>
<!-- Informations de création -->
<div class="card mt-3">
<div class="card-header bg-light">
<h6 class="card-title mb-0">Informations du Compte</h6>
</div>
<div class="card-body">
<div class="row g-2 small">
<div class="col-12">
<strong>ID Utilisateur :</strong>
<div class="text-muted font-monospace small">{{ user.id }}</div>
</div>
<div class="col-12">
<strong>Date de création :</strong>
<div class="text-muted">{{ getCreationDate() }}</div>
</div>
<div class="col-12">
<strong>Type de compte :</strong>
<div class="text-muted">
<span class="badge bg-secondary">{{ getUserTypeDisplay() }}</span>
</div>
</div>
@if (isMerchantUser()) {
<div class="col-12">
<strong>Merchant Partner ID :</strong>
<div class="text-muted font-monospace small">
{{ getMerchantPartnerId() }}
</div>
</div>
}
</div>
</div>
</div>
</div>
<!-- Colonne de droite - Détails et édition -->
<div class="col-xl-8 col-lg-7">
<div class="card">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
@if (isEditing) {
<ng-icon name="lucideEdit" class="me-2"></ng-icon>
Modification de Mon Profil
} @else {
<ng-icon name="lucideUser" class="me-2"></ng-icon>
Mes Informations
}
</h5>
@if (isEditing) {
<div class="d-flex gap-2">
<button
type="button"
class="btn btn-outline-secondary btn-sm"
(click)="cancelEditing()"
[disabled]="saving"
>
<ng-icon name="lucideX" class="me-1"></ng-icon>
Annuler
</button>
<button
type="button"
class="btn btn-success btn-sm"
(click)="saveProfile()"
[disabled]="saving || !isFormValid()"
>
@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>
Enregistrer
</button>
</div>
}
</div>
<div class="card-body">
<div class="row g-3">
<!-- Prénom -->
<div class="col-md-6">
<label class="form-label">Prénom <span class="text-danger">*</span></label>
@if (isEditing) {
<input
type="text"
class="form-control"
[(ngModel)]="editedUser.firstName"
placeholder="Votre prénom"
[disabled]="saving"
required
>
@if (!editedUser.firstName?.trim()) {
<div class="form-text text-danger">
Le prénom est obligatoire
</div>
}
} @else {
<div class="form-control-plaintext">
{{ user.firstName || 'Non renseigné' }}
</div>
}
</div>
<!-- Nom -->
<div class="col-md-6">
<label class="form-label">Nom <span class="text-danger">*</span></label>
@if (isEditing) {
<input
type="text"
class="form-control"
[(ngModel)]="editedUser.lastName"
placeholder="Votre nom"
[disabled]="saving"
required
>
@if (!editedUser.lastName?.trim()) {
<div class="form-text text-danger">
Le nom est obligatoire
</div>
}
} @else {
<div class="form-control-plaintext">
{{ user.lastName || 'Non renseigné' }}
</div>
}
</div>
<!-- Nom d'utilisateur -->
<div class="col-md-6">
<label class="form-label">Nom d'utilisateur</label>
<div class="form-control-plaintext font-monospace">
{{ user.username }}
</div>
<div class="form-text">
Votre identifiant de connexion ne peut pas être modifié
</div>
</div>
<!-- Email -->
<div class="col-md-6">
<label class="form-label">Email</label>
@if (isEditing) {
<input
type="email"
class="form-control"
[(ngModel)]="editedUser.email"
placeholder="votre.email@exemple.com"
[disabled]="saving"
required
>
} @else {
<div class="form-control-plaintext">
{{ user.email }}
@if (!user.emailVerified) {
<span class="badge bg-warning ms-2">Non vérifié</span>
}
</div>
}
</div>
<!-- Statut du compte -->
<div class="col-md-6">
<label class="form-label">Statut du compte</label>
<div class="form-control-plaintext">
<span [class]="getStatusBadgeClass()">
{{ getStatusText() }}
</span>
</div>
<div class="form-text">
@if (!user.enabled) {
Votre compte est actuellement désactivé
} @else if (!user.emailVerified) {
Votre email n'est pas encore vérifié
} @else {
Votre compte est actif
}
</div>
</div>
<!-- Rôle -->
<div class="col-md-6">
<label class="form-label">Rôle</label>
<div class="form-control-plaintext">
<div class="d-flex flex-wrap gap-1">
<span class="badge d-flex align-items-center" [ngClass]="getRoleBadgeClass()">
<ng-icon [name]="getRoleIcon(user.role)" class="me-1" size="12"></ng-icon>
{{ getRoleLabel() }}
</span>
</div>
</div>
<div class="form-text">
{{ getRoleDescription() }}
</div>
</div>
<!-- Informations système -->
@if (!isEditing) {
<div class="col-12">
<hr>
<h6 class="mb-3">
<ng-icon name="lucideSettings" class="me-2"></ng-icon>
Informations Système
</h6>
<div class="row">
<div class="col-md-6">
<label class="form-label">ID Utilisateur</label>
<div class="form-control-plaintext font-monospace small text-truncate">
{{ user.id }}
</div>
</div>
<div class="col-md-6">
<label class="form-label">Date de création</label>
<div class="form-control-plaintext">
{{ getCreationDate() }}
</div>
</div>
<div class="col-md-6">
<label class="form-label">Type de compte</label>
<div class="form-control-plaintext">
<span class="badge bg-secondary">{{ getUserTypeDisplay() }}</span>
</div>
</div>
@if (isMerchantUser()) {
<div class="col-md-6">
<label class="form-label">Merchant Partner ID</label>
<div class="form-control-plaintext font-monospace small">
{{ getMerchantPartnerId() }}
</div>
</div>
}
</div>
</div>
}
</div>
</div>
</div>
<!-- Actions personnelles -->
@if (!isEditing) {
<div class="card mt-3">
<div class="card-header bg-light">
<h6 class="card-title mb-0">Actions Personnelles</h6>
</div>
<div class="card-body">
<div class="row g-2">
<div class="col-md-6">
<button
class="btn btn-outline-warning w-100"
(click)="openMyProfileResetModal()"
>
<ng-icon name="lucideKey" class="me-1"></ng-icon>
Changer le mot de passe
</button>
</div>
<div class="col-md-6">
<button
class="btn btn-outline-primary w-100"
(click)="startEditing()"
>
<ng-icon name="lucideEdit" class="me-1"></ng-icon>
Modifier mon profil
</button>
</div>
</div>
</div>
</div>
}
</div>
}
</div>
</div>
<!-- Modal de réinitialisation de mot de passe personnel - MyProfile -->
<ng-template #myProfileResetPasswordModal let-modal>
<div class="modal-header bg-primary text-white">
<h4 class="modal-title">
<ng-icon name="lucideKeyRound" class="me-2"></ng-icon>
Réinitialiser mon mot de passe
</h4>
<button
type="button"
class="btn-close btn-close-white"
(click)="closeMyProfileResetModal()"
[disabled]="resettingMyPassword"
aria-label="Fermer"
></button>
</div>
<div class="modal-body">
<!-- Informations personnelles -->
<div class="card border-primary mb-4">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="avatar-lg bg-primary bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center me-4">
<span class="text-primary fw-bold fs-4">{{ getMyInitials() }}</span>
</div>
<div>
<h5 class="card-title mb-1">{{ user?.firstName }} {{ user?.lastName }}</h5>
<p class="card-text text-muted mb-1">
<ng-icon name="lucideUser" class="me-1"></ng-icon>
{{ user?.username }}
</p>
<p class="card-text text-muted mb-0">
<ng-icon name="lucideMail" class="me-1"></ng-icon>
{{ user?.email }}
</p>
</div>
</div>
</div>
</div>
<!-- Message de succès -->
@if (myProfileResetSuccess) {
<div class="alert alert-success d-flex align-items-center">
<ng-icon name="lucideCheckCircle" class="me-2"></ng-icon>
<div>
<strong>Succès !</strong> {{ myProfileResetSuccess }}
</div>
</div>
}
<!-- Message d'erreur -->
@if (myProfileResetError) {
<div class="alert alert-danger d-flex align-items-center">
<ng-icon name="lucideAlertCircle" class="me-2"></ng-icon>
<div>
<strong>Erreur :</strong> {{ myProfileResetError }}
</div>
</div>
}
<!-- Formulaire de réinitialisation -->
@if (!myProfileResetSuccess) {
<form (ngSubmit)="confirmMyProfileResetPassword()" #myProfileResetForm="ngForm">
<!-- Mot de passe actuel (pour sécurité) -->
<div class="mb-3">
<label class="form-label">
Mot de passe actuel <span class="text-danger">*</span>
</label>
<div class="input-group">
<input
[type]="showCurrentPassword ? 'text' : 'password'"
class="form-control"
placeholder="Entrez votre mot de passe actuel"
[(ngModel)]="myProfileCurrentPassword"
name="currentPassword"
required
[disabled]="resettingMyPassword"
#currentPasswordInput="ngModel"
>
<button
type="button"
class="btn btn-outline-secondary"
(click)="showCurrentPassword = !showCurrentPassword"
[disabled]="resettingMyPassword"
>
<ng-icon [name]="showCurrentPassword ? 'lucideEyeOff' : 'lucideEye'"></ng-icon>
</button>
</div>
@if (currentPasswordInput.invalid && currentPasswordInput.touched) {
<div class="text-danger small">
Votre mot de passe actuel est requis
</div>
}
</div>
<!-- Nouveau mot de passe -->
<div class="mb-3">
<label class="form-label">
Nouveau mot de passe <span class="text-danger">*</span>
</label>
<div class="input-group">
<input
[type]="showNewMyPassword ? 'text' : 'password'"
class="form-control"
placeholder="Entrez votre nouveau mot de passe"
[(ngModel)]="myProfileNewPassword"
name="newPassword"
required
minlength="8"
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"
[disabled]="resettingMyPassword"
#newMyPasswordInput="ngModel"
>
<button
type="button"
class="btn btn-outline-secondary"
(click)="showNewMyPassword = !showNewMyPassword"
[disabled]="resettingMyPassword"
>
<ng-icon [name]="showNewMyPassword ? 'lucideEyeOff' : 'lucideEye'"></ng-icon>
</button>
</div>
<div class="form-text">
<ul class="small mb-0 ps-3">
<li [class.text-success]="hasMinLength()">Au moins 8 caractères</li>
<li [class.text-success]="hasLowerCase()">Une lettre minuscule</li>
<li [class.text-success]="hasUpperCase()">Une lettre majuscule</li>
<li [class.text-success]="hasNumber()">Un chiffre</li>
<li [class.text-success]="hasSpecialChar()">Un caractère spécial (@$!%*?&)</li>
</ul>
</div>
@if (newMyPasswordInput.invalid && newMyPasswordInput.touched) {
<div class="text-danger small">
@if (newMyPasswordInput.errors?.['required']) {
Le nouveau mot de passe est requis
}
@if (newMyPasswordInput.errors?.['minlength']) {
Le mot de passe doit contenir au moins 8 caractères
}
@if (newMyPasswordInput.errors?.['pattern']) {
Le mot de passe ne respecte pas les critères de sécurité
}
</div>
}
</div>
<!-- Confirmation du nouveau mot de passe -->
<div class="mb-3">
<label class="form-label">
Confirmer le nouveau mot de passe <span class="text-danger">*</span>
</label>
<div class="input-group">
<input
[type]="showConfirmMyPassword ? 'text' : 'password'"
class="form-control"
placeholder="Confirmez votre nouveau mot de passe"
[(ngModel)]="myProfileConfirmPassword"
name="confirmPassword"
required
[disabled]="resettingMyPassword"
#confirmMyPasswordInput="ngModel"
>
<button
type="button"
class="btn btn-outline-secondary"
(click)="showConfirmMyPassword = !showConfirmMyPassword"
[disabled]="resettingMyPassword"
>
<ng-icon [name]="showConfirmMyPassword ? 'lucideEyeOff' : 'lucideEye'"></ng-icon>
</button>
</div>
@if (confirmMyPasswordInput.invalid && confirmMyPasswordInput.touched) {
<div class="text-danger small">
La confirmation du mot de passe est requise
</div>
}
@if (myProfileNewPassword && myProfileConfirmPassword && myProfileNewPassword !== myProfileConfirmPassword) {
<div class="text-danger small">
Les mots de passe ne correspondent pas
</div>
}
</div>
<!-- Indicateur de force du mot de passe -->
@if (myProfileNewPassword) {
<div class="mb-3">
<label class="form-label small">Force du mot de passe</label>
<div class="progress mb-1" style="height: 6px;">
<div
class="progress-bar"
[ngClass]="getPasswordStrengthClass(myProfileNewPassword)"
[style.width.%]="getPasswordStrength(myProfileNewPassword)"
></div>
</div>
<div class="small text-muted text-center">
{{ getPasswordStrengthText(myProfileNewPassword) }}
</div>
</div>
}
</form>
}
</div>
<div class="modal-footer">
@if (myProfileResetSuccess) {
<button
type="button"
class="btn btn-success"
(click)="closeMyProfileResetModal()"
>
<ng-icon name="lucideCheck" class="me-1"></ng-icon>
Terminer
</button>
} @else {
<button
type="button"
class="btn btn-light"
(click)="closeMyProfileResetModal()"
[disabled]="resettingMyPassword"
>
<ng-icon name="lucideX" class="me-1"></ng-icon>
Annuler
</button>
<button
type="button"
class="btn btn-primary"
(click)="confirmMyProfileResetPassword()"
[disabled]="!isMyProfileResetFormValid() || resettingMyPassword"
>
@if (resettingMyPassword) {
<div class="spinner-border spinner-border-sm me-2" role="status">
<span class="visually-hidden">Chargement...</span>
</div>
Réinitialisation...
} @else {
<ng-icon name="lucideKeyRound" class="me-1"></ng-icon>
Changer mon mot de passe
}
</button>
}
</div>
</ng-template>