feat: add DCB User Service API - Authentication system with KEYCLOAK - Modular architecture with services for each feature

This commit is contained in:
diallolatoile 2025-10-27 18:36:19 +00:00
parent 3bb7d21a7f
commit 0a7f1c6aa0
10 changed files with 8 additions and 700 deletions

View File

@ -1,10 +1,5 @@
import { Routes } from '@angular/router' import { Routes } from '@angular/router'
import { SignIn } from '@/app/modules/auth/sign-in' import { SignIn } from '@/app/modules/auth/sign-in'
import { SignUp } from '@/app/modules/auth/sign-up'
import { ResetPassword } from '@/app/modules/auth/reset-password'
import { NewPassword } from '@/app/modules/auth/new-password'
import { TwoFactor } from '@/app/modules/auth/two-factor'
import { LockScreen } from '@/app/modules/auth/lock-screen'
export const Auth_ROUTES: Routes = [ export const Auth_ROUTES: Routes = [
{ {
@ -12,29 +7,4 @@ export const Auth_ROUTES: Routes = [
component: SignIn, component: SignIn,
data: { title: 'Sign In' }, data: { title: 'Sign In' },
}, },
{
path: 'auth/sign-up',
component: SignUp,
data: { title: 'Sign Up' },
},
{
path: 'auth/reset-password',
component: ResetPassword,
data: { title: 'Reset Password' },
},
{
path: 'auth/new-password',
component: NewPassword,
data: { title: 'New Password' },
},
{
path: 'auth/two-factor',
component: TwoFactor,
data: { title: 'Two Factor' },
},
{
path: 'auth/lock-screen',
component: LockScreen,
data: { title: 'Lock Screen' },
},
] ]

View File

@ -1,88 +0,0 @@
import { Component } from '@angular/core'
import { appName, credits, currentYear } from '@/app/constants'
import { RouterLink } from '@angular/router'
import { AppLogo } from '@app/components/app-logo'
@Component({
selector: 'app-lock-screen',
imports: [RouterLink, AppLogo],
template: `
<div class="auth-box overflow-hidden align-items-center d-flex">
<div class="container">
<div class="row justify-content-center">
<div class="col-xxl-4 col-md-6 col-sm-8">
<div class="card">
<div class="card-body">
<div class="auth-brand mb-4">
<app-app-logo />
<p class="text-muted w-lg-75 mt-3">
This screen is locked. Enter your password to continue
</p>
</div>
<div class="text-center mb-4">
<img
src="assets/images/users/user-2.jpg"
class="rounded-circle img-thumbnail avatar-xxl mb-2"
alt="thumbnail"
/>
<span>
<h5 class="my-0 fw-semibold">Maxine Kennedy</h5>
<h6 class="my-0 text-muted">Admin Head</h6>
</span>
</div>
<form>
<div class="mb-3">
<label for="userPassword" class="form-label"
>Password <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="password"
class="form-control"
id="userPassword"
placeholder="••••••••"
required
/>
</div>
</div>
<div class="d-grid">
<button
type="submit"
class="btn btn-primary fw-semibold py-2"
>
Unlock
</button>
</div>
</form>
<p class="text-muted text-center mt-4 mb-0">
Not you? Return to
<a
routerLink="/auth/sign-in"
class="text-decoration-underline link-offset-3 fw-semibold"
>Sign in</a
>
</p>
</div>
</div>
<p class="text-center text-muted mt-4 mb-0">
© {{ currentYear }} {{ appName }}. Tous droits réservés. Développé par
<span class="fw-semibold">{{ credits.name }}</span>
</p>
</div>
</div>
</div>
</div>
`,
styles: ``,
})
export class LockScreen {
protected readonly appName = appName
protected readonly currentYear = currentYear
protected readonly credits = credits
}

View File

@ -1,163 +0,0 @@
import { Component } from '@angular/core'
import { appName, credits, currentYear } from '@/app/constants'
import { RouterLink } from '@angular/router'
import { PasswordStrengthBar } from '@app/components/password-strength-bar'
import { FormsModule } from '@angular/forms'
import { AppLogo } from '@app/components/app-logo'
import { NgOtpInputModule } from 'ng-otp-input'
@Component({
selector: 'app-new-password',
imports: [
RouterLink,
PasswordStrengthBar,
FormsModule,
AppLogo,
NgOtpInputModule,
],
template: `
<div class="auth-box overflow-hidden align-items-center d-flex">
<div class="container">
<div class="row justify-content-center">
<div class="col-xxl-4 col-md-6 col-sm-8">
<div class="card">
<div class="card-body">
<div class="auth-brand mb-4">
<app-app-logo />
<p class="text-muted mt-3">
We've emailed you a 6-digit verification code. Please enter
it below to confirm your email address
</p>
</div>
<form>
<div class="mb-3">
<label for="userEmail" class="form-label"
>Email address <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="email"
class="form-control"
id="userEmail"
placeholder="you@example.com"
disabled
/>
</div>
</div>
<div class="mb-3">
<label class="form-label"
>Enter your 6-digit code
<span class="text-danger">*</span></label
>
<ng-otp-input
[config]="{
length: 6,
allowNumbersOnly: true,
inputClass: 'form-control text-center',
}"
>
</ng-otp-input>
</div>
<div class="mb-3" data-password="bar">
<label for="userPassword" class="form-label"
>Password <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="password"
name="password"
[(ngModel)]="password"
class="form-control"
id="userPassword"
placeholder="••••••••"
required
/>
</div>
<app-password-strength-bar [password]="password" />
<div class="password-bar my-2"></div>
</div>
<div class="mb-3">
<label for="userNewPassword" class="form-label"
>Confirm New Password
<span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="password"
class="form-control"
id="userNewPassword"
placeholder="••••••••"
required
/>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input form-check-input-light fs-14"
type="checkbox"
id="termAndPolicy"
/>
<label class="form-check-label" for="termAndPolicy"
>Agree the Terms & Policy</label
>
</div>
</div>
<div class="d-grid">
<button
type="submit"
class="btn btn-primary fw-semibold py-2"
>
Update Password
</button>
</div>
</form>
<p class="mt-4 text-muted text-center mb-4">
Dont have a code?
<a
href="javascript:void(0);"
class="text-decoration-underline link-offset-2 fw-semibold"
>Resend</a
>
or
<a
href="javascript:void(0);"
class="text-decoration-underline link-offset-2 fw-semibold"
>Call Us</a
>
</p>
<p class="text-muted text-center mb-0">
Return to
<a
routerLink="/auth/sign-in"
class="text-decoration-underline link-offset-3 fw-semibold"
>Sign in</a
>
</p>
</div>
</div>
<p class="text-center text-muted mt-4 mb-0">
© {{ currentYear }} {{ appName }}. Tous droits réservés. Développé par
<span class="fw-semibold">{{ credits.name }}</span>
</p>
</div>
</div>
</div>
</div>
`,
styles: ``,
})
export class NewPassword {
password: string = ''
protected readonly appName = appName
protected readonly currentYear = currentYear
protected readonly credits = credits
}

View File

@ -1,190 +0,0 @@
import { Component, inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import { FormsModule } from '@angular/forms'
import { AppLogo } from '@app/components/app-logo'
import { UsersService } from '../users/services/users.service'
import { credits, currentYear } from '@/app/constants'
@Component({
selector: 'app-reset-password',
imports: [RouterLink, AppLogo, FormsModule],
template: `
<div class="auth-box overflow-hidden align-items-center d-flex">
<div class="container">
<div class="row justify-content-center">
<div class="col-xxl-4 col-md-6 col-sm-8">
<div class="card">
<div class="card-body">
<div class="auth-brand mb-4">
<app-app-logo />
<p class="text-muted w-lg-75 mt-3">
Entrez votre adresse email et nous vous enverrons un lien pour réinitialiser votre mot de passe.
</p>
</div>
<!-- Message de succès -->
<div *ngIf="successMessage" class="alert alert-success">
{{ successMessage }}
</div>
<!-- Message d'erreur -->
<div *ngIf="errorMessage" class="alert alert-danger">
{{ errorMessage }}
</div>
<form (ngSubmit)="onSubmit()" #resetForm="ngForm">
<div class="mb-3">
<label for="userEmail" class="form-label">
Adresse email <span class="text-danger">*</span>
</label>
<div class="input-group">
<input
type="email"
class="form-control"
id="userEmail"
placeholder="vous@exemple.com"
[(ngModel)]="email"
name="email"
required
email
[disabled]="loading"
/>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input form-check-input-light fs-14"
type="checkbox"
id="termAndPolicy"
[(ngModel)]="termsAccepted"
name="termsAccepted"
required
[disabled]="loading"
/>
<label class="form-check-label" for="termAndPolicy">
J'accepte les Conditions Générales d'Utilisation
</label>
</div>
</div>
<div class="d-grid">
<button
type="submit"
class="btn btn-primary fw-semibold py-2"
[disabled]="loading || !resetForm.form.valid"
>
<span *ngIf="loading" class="spinner-border spinner-border-sm me-2"></span>
{{ loading ? 'Envoi en cours...' : 'Envoyer la demande' }}
</button>
</div>
</form>
<p class="text-muted text-center mt-4 mb-0">
Retour à
<a
routerLink="/auth/sign-in"
class="text-decoration-underline link-offset-3 fw-semibold"
>
Connexion
</a>
</p>
</div>
</div>
<p class="text-center text-muted mt-4 mb-0">
© {{ currentYear }} Simple par
<span class="fw-semibold">{{ credits.name }}</span>
</p>
</div>
</div>
</div>
</div>
`,
styles: [`
.auth-box {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card {
border: none;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
`]
})
export class ResetPassword {
private usersService = inject(UsersService)
email = ''
termsAccepted = false
loading = false
successMessage = ''
errorMessage = ''
protected readonly currentYear = currentYear
protected readonly credits = credits
onSubmit() {
if (!this.email || !this.termsAccepted) {
this.errorMessage = 'Veuillez remplir tous les champs obligatoires et accepter les conditions.';
return;
}
this.loading = true;
this.successMessage = '';
this.errorMessage = '';
// Simulation d'envoi d'email de réinitialisation
// Note: Cette fonctionnalité nécessite un backend configuré pour envoyer des emails
this.usersService.findUserByEmail(this.email).subscribe({
next: (users) => {
this.loading = false;
if (users && users.length > 0) {
// Si l'utilisateur existe, afficher un message de succès
this.successMessage = 'Un lien de réinitialisation a été envoyé à votre adresse email.';
this.errorMessage = '';
// Ici, vous devriez normalement appeler un service backend
// qui envoie un email de réinitialisation
console.log('Email de réinitialisation envoyé à:', this.email);
} else {
this.errorMessage = 'Aucun utilisateur trouvé avec cette adresse email.';
this.successMessage = '';
}
},
error: (error) => {
this.loading = false;
this.errorMessage = 'Une erreur est survenue lors de la recherche de l\'utilisateur.';
this.successMessage = '';
console.error('Error finding user:', error);
}
});
}
// Alternative: Réinitialisation directe du mot de passe
resetPasswordDirectly(userId: string) {
const newPassword = prompt('Nouveau mot de passe:');
if (newPassword && newPassword.length >= 8) {
const resetDto = {
userId: userId,
newPassword: newPassword,
temporary: false
};
this.usersService.resetPassword(resetDto).subscribe({
next: () => {
alert('Mot de passe réinitialisé avec succès');
},
error: (error) => {
console.error('Error resetting password:', error);
alert('Erreur lors de la réinitialisation du mot de passe');
}
});
} else if (newPassword) {
alert('Le mot de passe doit contenir au moins 8 caractères');
}
}
}

View File

@ -112,15 +112,6 @@ import { appName, credits, currentYear } from '@/app/constants';
</form> </form>
<p class="text-muted text-center mt-4 mb-0">
New here?
<a
routerLink="/auth/sign-up"
class="text-decoration-underline link-offset-3 fw-semibold"
>
Create an account
</a>
</p>
</div> </div>
</div> </div>

View File

@ -1,124 +0,0 @@
import { Component } from '@angular/core'
import { appName, credits, currentYear } from '@/app/constants'
import { RouterLink } from '@angular/router'
import { AppLogo } from '@app/components/app-logo'
import { PasswordStrengthBar } from '@app/components/password-strength-bar'
import { FormsModule } from '@angular/forms'
@Component({
selector: 'app-sign-up',
imports: [RouterLink, AppLogo, FormsModule, PasswordStrengthBar],
template: `
<div class="auth-box overflow-hidden align-items-center d-flex">
<div class="container">
<div class="row justify-content-center">
<div class="col-xxl-4 col-md-6 col-sm-8">
<div class="card">
<div class="card-body">
<div class="auth-brand mb-4">
<app-app-logo />
<p class="text-muted w-lg-75 mt-3">
Lets get you started. Create your account by entering your
details below.
</p>
</div>
<form>
<div class="mb-3">
<label for="userName" class="form-label"
>Name <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="text"
class="form-control"
id="userName"
placeholder="Damian D."
required
/>
</div>
</div>
<div class="mb-3">
<label for="userEmail" class="form-label"
>Email address <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="email"
class="form-control"
id="userEmail"
placeholder="you@example.com"
required
/>
</div>
</div>
<div class="mb-3" data-password="bar">
<label for="userPassword" class="form-label"
>Password <span class="text-danger">*</span></label
>
<div class="input-group">
<input
type="password"
name="password"
class="form-control"
id="userPassword"
placeholder="••••••••"
required
[(ngModel)]="password"
/>
</div>
<app-password-strength-bar [password]="password" />
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input form-check-input-light fs-14 mt-0"
type="checkbox"
id="termAndPolicy"
/>
<label class="form-check-label" for="termAndPolicy"
>Agree the Terms & Policy</label
>
</div>
</div>
<div class="d-grid">
<button
type="submit"
class="btn btn-primary fw-semibold py-2"
>
Create Account
</button>
</div>
</form>
<p class="text-muted text-center mt-4 mb-0">
Already have an account?
<a
routerLink="/auth/sign-in"
class="text-decoration-underline link-offset-3 fw-semibold"
>Login</a
>
</p>
</div>
</div>
<p class="text-center text-muted mt-4 mb-0">
© {{ currentYear }} {{ appName }}. Tous droits réservés. Développé par
<span class="fw-semibold">{{ credits.name }}</span>
</p>
</div>
</div>
</div>
</div>
`,
styles: ``,
})
export class SignUp {
password: string = ''
protected readonly appName = appName
protected readonly currentYear = currentYear
protected readonly credits = credits
}

View File

@ -1,92 +0,0 @@
import { Component } from '@angular/core'
import { AppLogo } from '@app/components/app-logo'
import { NgOtpInputComponent } from 'ng-otp-input'
import { RouterLink } from '@angular/router'
import { appName, credits, currentYear } from '@/app/constants'
@Component({
selector: 'app-two-factor',
imports: [AppLogo, NgOtpInputComponent, RouterLink],
template: `
<div class="auth-box overflow-hidden align-items-center d-flex">
<div class="container">
<div class="row justify-content-center">
<div class="col-xxl-4 col-md-6 col-sm-8">
<div class="card">
<div class="card-body">
<div class="auth-brand mb-4">
<app-app-logo />
<p class="text-muted w-lg-75 mt-3">
We've emailed you a 6-digit verification code we sent to
</p>
</div>
<div class="text-center mb-4">
<div class="fw-bold fs-4">+ (12) ******6789</div>
</div>
<form>
<label class="form-label"
>Enter your 6-digit code
<span class="text-danger">*</span></label
>
<ngx-otp-input
[config]="{
length: 6,
allowNumbersOnly: true,
inputClass: 'form-control text-center mb-3',
}"
>
</ngx-otp-input>
<div class="d-grid">
<button
type="submit"
class="btn btn-primary fw-semibold py-2"
>
Confirm
</button>
</div>
</form>
<p class="mt-4 text-muted text-center mb-4">
Dont have a code?
<a
href="javascript:void(0);"
class="text-decoration-underline link-offset-2 fw-semibold"
>Resend</a
>
or
<a
href="javascript:void(0);"
class="text-decoration-underline link-offset-2 fw-semibold"
>Call Us</a
>
</p>
<p class="text-muted text-center mb-0">
Return to
<a
routerLink="/auth/sign-in"
class="text-decoration-underline link-offset-3 fw-semibold"
>Sign in</a
>
</p>
</div>
</div>
<p class="text-center text-muted mt-4 mb-0">
© {{ currentYear }} {{ appName }}. Tous droits réservés. Développé par
<span class="fw-semibold">{{ credits.name }}</span>
</p>
</div>
</div>
</div>
</div>
`,
styles: ``,
})
export class TwoFactor {
protected readonly appName = appName
protected readonly currentYear = currentYear
protected readonly credits = credits
}

View File

@ -6,7 +6,11 @@ import { MerchantsHistory } from './history/history';
@Component({ @Component({
selector: 'app-merchants', selector: 'app-merchants',
imports: [PageTitle, MerchantsList, MerchantsConfig, MerchantsHistory], imports: [PageTitle,
//MerchantsList,
//MerchantsConfig,
//MerchantsHistory
],
templateUrl: './merchants.html', templateUrl: './merchants.html',
}) })
export class Merchants { export class Merchants {

View File

@ -7,7 +7,7 @@ import { InputTouchspin } from '@/app/modules/components/input-touchspin';
@Component({ @Component({
selector: 'app-config', selector: 'app-config',
imports: [FormsModule, UiCard, InputFields, CheckboxesAndRadios, InputTouchspin], //imports: [FormsModule, UiCard, InputFields, CheckboxesAndRadios, InputTouchspin],
templateUrl: './config.html', templateUrl: './config.html',
}) })
export class OperatorsConfig { export class OperatorsConfig {

View File

@ -278,8 +278,8 @@
<div class="d-flex justify-content-between align-items-center mt-3"> <div class="d-flex justify-content-between align-items-center mt-3">
<div class="text-muted"> <div class="text-muted">
Affichage de {{ (filters.page! - 1) * filters.limit! + 1 }} à Affichage de {{ (filters.page! - 1) * filters.limit! + 1 }} à
{{ (filters.page! * filters.limit!) > (paginatedData?.total || 0) ? (paginatedData?.total || 0) : (filters.page! * filters.limit!) }} {{ (filters.page! * filters.limit!) > (paginatedData.total || 0) ? (paginatedData.total || 0) : (filters.page! * filters.limit!) }}
sur {{ paginatedData?.total || 0 }} transactions sur {{ paginatedData.total || 0 }} transactions
</div> </div>
<nav> <nav>
<ngb-pagination <ngb-pagination