osho-marka
Naeem Ullah 6 days ago
parent 91ed9032d5
commit 0b54f82063

@ -125,6 +125,7 @@ export class LoginComponent {
}
onLangChange() {
const selectedLang = this.currentLanguage.value;
this.translateService.setDefaultLang(selectedLang);

@ -98,8 +98,8 @@
<th>{{ "crAccount" | translate }}</th>
<th>{{ "drPcaGlacode" | translate }}</th>
<th>{{ "crPcaglacode" | translate }}</th>
<th>{{ "transactionCode" | translate }}</th>
<th>{{ "transactionUri" | translate }}</th>
<th>{{ "transactionCode" | translate }}</th>
<th>{{ "paymentMode" | translate }}</th>
<th>{{ "date" | translate }}</th>
<th>{{ "channel" | translate }}</th>
@ -141,8 +141,8 @@
<td>{{ log.crMbmbkmsnumber || "-" }}</td>
<td>{{ log.drPcaGlacode || "-" }}</td>
<td>{{ log.crPcaglacode || "-" }}</td>
<td>{{log.transactionUri || "N/A"}}</td>
<td>{{log.transactionCode || "N/A"}}</td>
<td>{{ log.transactionUri || "N/A"}}</td>
<td>{{ log.transactionCode || "N/A"}}</td>
<td>{{ log.ppmPymdcode }}</td>
<td>
{{ log.sgtGntrdate | date: "MMM dd, yyyy" }}

@ -1,4 +1,4 @@
<div class="modal fade" id="resetPasswordModal" tabindex="-1">
<div class="modal fade" id="resetPasswordModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
@ -6,72 +6,144 @@
<h5 class="modal-title">
{{ 'resetPassword' | translate }}
</h5>
<button type="button" class="btn-close" (click)="closeModal()"></button>
<button type="button" class="btn-close" (click)="closeModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
<form [formGroup]="resetPasswordForm">
<div class="mb-3">
<label class="form-label">{{ 'userID' | translate }}</label>
<input [readonly]="true" class="form-control" formControlName="userId" />
<!-- Show warning if no user is selected -->
<div *ngIf="!userId" class="alert alert-warning mb-3">
<i class="fas fa-exclamation-triangle me-2"></i>
{{ 'noUserSelected' | translate }}
</div>
<!-- User ID display -->
<div class="mb-3" *ngIf="userId">
<label class="form-label fw-bold">
{{ 'userID' | translate }} <span class="mandatory">*</span>
</label>
<div class="input-group">
<input
type="text"
class="form-control bg-light"
[value]="userId"
readonly
placeholder="{{ 'userID' | translate }}"
/>
<span class="input-group-text">
<i class="fas fa-user"></i>
</span>
</div>
<small class="text-muted">{{ 'resettingPasswordForThisUser' | translate }}</small>
</div>
<form [formGroup]="resetPasswordForm" *ngIf="userId">
<!-- New Password Field -->
<div class="mb-3">
<label class="form-label">{{ 'enterNewPassword' | translate }}</label>
<div class="password-wrapper">
<input class="form-control" formControlName="newPassword" placeholder="{{'enterNewPassword' | translate}}" [type]="newPasswordType" autocomplete="off" appNoWhitespaces />
<app-password-hide-show #newPasswordPsh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="togglePasswordType()">
</app-password-hide-show>
<label class="form-label fw-bold">
{{ 'enterNewPassword' | translate }} <span class="mandatory">*</span>
</label>
<div class="password-wrapper input-group">
<input
class="form-control"
formControlName="newPassword"
placeholder="{{'enterNewPassword' | translate}}"
[type]="newPasswordType"
autocomplete="new-password"
appNoWhitespaces
[attr.disabled]="isSubmitting ? true : null"
/>
<button
class="btn btn-outline-secondary"
type="button"
(click)="togglePasswordType()"
[attr.disabled]="isSubmitting ? true : null"
>
<i class="fas" [class.fa-eye]="newPasswordType === 'password'"
[class.fa-eye-slash]="newPasswordType === 'text'"></i>
</button>
</div>
<div class="text-danger" *ngIf="resetPasswordForm.get('newPassword')?.touched ">
<!-- Validation messages -->
<div class="text-danger mt-1" *ngIf="resetPasswordForm.get('newPassword')?.touched">
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('minlength')">
{{ 'passwordMinLength' | translate }}
</div>
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('maxlength')">
{{ 'passwordMaxLength' | translate }}
</div>
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('pattern')">
{{ 'passwordPattern' | translate }}
</div>
</div>
<!-- Password requirements hint -->
<div class="form-text text-muted">
<small>{{ 'passwordRequirements' | translate }}</small>
</div>
</div>
<!-- Confirm Password Field -->
<div class="mb-3">
<label class="form-label">{{ 'confirmPassword' | translate }}</label>
<div class="password-wrapper">
<input class="form-control" formControlName="confirmPassword" placeholder="{{'confirmPassword' | translate}}" [type]="confirmPasswordType" autocomplete="off" appNoWhitespaces />
<app-password-hide-show #confirmPasswordPsh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="toggleConfirmPasswordType()">
</app-password-hide-show>
<label class="form-label fw-bold">
{{ 'confirmPassword' | translate }} <span class="mandatory">*</span>
</label>
<div class="password-wrapper input-group">
<input
class="form-control"
formControlName="confirmPassword"
placeholder="{{'confirmPassword' | translate}}"
[type]="confirmPasswordType"
autocomplete="new-password"
appNoWhitespaces
[attr.disabled]="isSubmitting ? true : null"
/>
<button
class="btn btn-outline-secondary"
type="button"
(click)="toggleConfirmPasswordType()"
[attr.disabled]="isSubmitting ? true : null"
>
<i class="fas" [class.fa-eye]="confirmPasswordType === 'password'"
[class.fa-eye-slash]="confirmPasswordType === 'text'"></i>
</button>
</div>
<div class="text-danger" *ngIf="resetPasswordForm.get('confirmPassword')?.touched ">
<!-- Validation messages -->
<div class="text-danger mt-1" *ngIf="resetPasswordForm.get('confirmPassword')?.touched">
<div *ngIf="resetPasswordForm.get('confirmPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div *ngIf="resetPasswordForm.hasError('passwordMismatch')">
<div *ngIf="resetPasswordForm.get('confirmPassword')?.hasError('passwordMismatch')">
{{ 'passwordsDoNotMatch' | translate }}
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer d-flex justify-content-end gap-2">
<button type="button" class="btn btn-outline-secondary btn-sm px-4" (click)="closeModal()">
{{ 'cancel' | translate }}
<div class="modal-footer d-flex justify-content-end gap-2">
<button
type="button"
class="btn btn-outline-secondary btn-sm px-4"
(click)="closeModal()"
[attr.disabled]="isSubmitting ? true : null"
>
{{ 'cancel' | translate }}
</button>
<button type="button" class="btn btn-primary btn-sm px-4" [disabled]="resetPasswordForm.invalid" (click)="submit()">
{{ 'save' | translate }}
<button
type="button"
class="btn btn-primary btn-sm px-4"
[disabled]="resetPasswordForm.invalid || !userId || isSubmitting"
(click)="submit()"
>
<span *ngIf="isSubmitting" class="spinner-border spinner-border-sm me-2"></span>
{{ isSubmitting ? ('saving' | translate) : ('save' | translate) }}
</button>
</div>
</div>
</div>
</div>

@ -1,30 +1,33 @@
import { Component, Input, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { Component, Input, OnInit, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ReactiveFormsModule } from '@angular/forms';
import { HttpURIService } from '../../app.http.uri.service';
import { URIKey } from '../../utils/uri-enums';
import { I18NService } from '../../services/i18n.service';
import { StorageService } from '../../shared/services/storage.service';
import { SuccessMessages } from '../../utils/enums';
import { SuccessMessages, ErrorMessages } from '../../utils/enums';
import { HttpErrorResponse } from '@angular/common/http';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
@Component({
selector: 'app-reset-password-modal',
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent],
templateUrl: './reset-password-modal.component.html'
})
export class ResetPasswordModalComponent implements OnInit {
newPasswordType: string = 'password'
confirmPasswordType: string = 'password'
@Input() userId!: string;
export class ResetPasswordModalComponent implements OnInit, OnChanges {
newPasswordType: string = 'password';
confirmPasswordType: string = 'password';
@Input() userId: string | null = null;
@Output() modalClosed = new EventEmitter<void>();
resetPasswordForm!: FormGroup;
@ViewChild('newPasswordPsh') passwordHideShow?:PasswordHideShowComponent
@ViewChild('confirmPasswordPsh') confirmPasswordHideShow?:PasswordHideShowComponent
isSubmitting: boolean = false;
@ViewChild('newPasswordPsh') passwordHideShow?: PasswordHideShowComponent;
@ViewChild('confirmPasswordPsh') confirmPasswordHideShow?: PasswordHideShowComponent;
constructor(
private fb: FormBuilder,
@ -32,50 +35,80 @@ export class ResetPasswordModalComponent implements OnInit {
private i18nService: I18NService,
private storageService: StorageService
) {}
togglePasswordType() {
this.newPasswordType = this.passwordHideShow?.showPassword ? 'password' : 'text';
}
toggleConfirmPasswordType() {
this.confirmPasswordType = this.confirmPasswordHideShow?.showPassword ? 'password' : 'text';
}
ngOnInit(): void {
this.initializeForm();
}
initializeForm(): void {
this.resetPasswordForm = this.fb.group({
userId: [''],
newPassword: ['', [
Validators.required,
Validators.minLength(8),
Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/)
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
]],
confirmPassword: ['', Validators.required]
}, { validators: this.passwordMatchValidator });
}, {
validators: this.passwordMatchValidator,
updateOn: 'blur'
});
// Update confirm password validation when new password changes
this.resetPasswordForm.get('newPassword')?.valueChanges.subscribe(() => {
this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity();
});
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['userId'] && this.resetPasswordForm) {
this.resetPasswordForm.reset({
userId: this.userId,
newPassword: '',
confirmPassword: ''
});
if (changes['userId'] && this.resetPasswordForm) {
console.log('ResetPasswordModal: userId changed to:', this.userId);
this.newPasswordType = 'password';
this.confirmPasswordType = 'password';
// Reset the form when userId changes
this.resetForm();
this.passwordHideShow?.reset();
this.confirmPasswordHideShow?.reset();
// Update the form with new userId if needed
if (this.userId) {
// You might want to fetch user details here if needed
console.log('Preparing reset password for user:', this.userId);
}
}
}
}
togglePasswordType() {
this.newPasswordType = this.newPasswordType === 'password' ? 'text' : 'password';
}
toggleConfirmPasswordType() {
this.confirmPasswordType = this.confirmPasswordType === 'password' ? 'text' : 'password';
}
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword')?.value;
const confirmPassword = group.get('confirmPassword')?.value;
return newPassword === confirmPassword ? null : { passwordMismatch: true };
if (!newPassword || !confirmPassword) return null;
// If passwords don't match, set error on confirmPassword field
if (newPassword !== confirmPassword) {
group.get('confirmPassword')?.setErrors({ passwordMismatch: true });
return { passwordMismatch: true };
} else {
group.get('confirmPassword')?.setErrors(null);
return null;
}
}
submit() {
if (this.resetPasswordForm.invalid) return;
if (this.resetPasswordForm.invalid || !this.userId) {
console.error('Form is invalid or no userId available');
this.resetPasswordForm.markAllAsTouched();
return;
}
console.log('Submitting reset password for userId:', this.userId);
this.isSubmitting = true;
const payload = {
userId: this.userId,
@ -85,28 +118,56 @@ export class ResetPasswordModalComponent implements OnInit {
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
.subscribe({
next: (res) => {
if (!(res instanceof HttpErrorResponse)) {
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
this.closeModal();
} else {
this.i18nService.error(ErrorMessages.RESET_PASSWORD_FAILED, []);
}
this.isSubmitting = false;
},
error: (error) => {
console.error('Error resetting password:', error);
this.isSubmitting = false;
}
});
}
closeModal() {
resetForm(): void {
this.resetPasswordForm.reset();
this.isSubmitting = false;
this.newPasswordType = 'password';
this.confirmPasswordType = 'password';
// Reset password hide/show components
this.passwordHideShow?.reset();
this.confirmPasswordHideShow?.reset();
// Reset form validation state
Object.keys(this.resetPasswordForm.controls).forEach(key => {
const control = this.resetPasswordForm.get(key);
control?.markAsPristine();
control?.markAsUntouched();
});
}
closeModal() {
this.resetForm();
this.modalClosed.emit();
// Remove the modal backdrop and reset modal state
const modal = document.getElementById('resetPasswordModal');
modal?.classList.remove('show');
modal?.setAttribute('aria-hidden', 'true');
modal!.style.display = 'none';
if (modal) {
modal.classList.remove('show');
modal.setAttribute('aria-hidden', 'true');
modal.style.display = 'none';
}
document.body.classList.remove('modal-open');
document.querySelector('.modal-backdrop')?.remove();
const backdrop = document.querySelector('.modal-backdrop');
if (backdrop) {
backdrop.remove();
}
}
}

@ -10,13 +10,12 @@ import { I18NService } from '../../services/i18n.service';
import { ErrorMessages, SuccessMessages } from '../../utils/enums';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';
import { NgSelectComponent } from "@ng-select/ng-select";
import { NgSelectModule } from '@ng-select/ng-select';
import { SetupUser } from '../../models/user';
import { error } from 'console';
@Component({
selector: 'app-reset-password',
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule, NgSelectComponent],
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule, NgSelectModule],
templateUrl: './reset-password.component.html',
styleUrl: './reset-password.component.scss'
})
@ -27,46 +26,53 @@ export class ResetPasswordComponent implements OnInit{
isLoading: boolean = false;
allUsersDropdown: SetupUser[] = [];
selectedUserId: string | null = null;
isSubmitting: boolean = false;
@ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent;
@ViewChild('psh2') passwordHideShow2?: PasswordHideShowComponent;
constructor(private fb: FormBuilder, private httpService: HttpURIService, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService, private router: Router){}
constructor(
private fb: FormBuilder,
private httpService: HttpURIService,
private storageService: StorageService,
private i18nService: I18NService,
private router: Router
){}
ngOnInit(): void {
const userIdValue = this.storageService.getItem('USER_ID') || null;
this.initializeForm();
this.loadUsersForDropdown();
}
initializeForm(): void {
this.resetPasswordForm = this.fb.group({
userId: [null],
userId: ['', Validators.required], // Changed to empty string and required
newPassword: ['', [
Validators.required,
Validators.minLength(8),
Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
]
],
]],
confirmPassword: ['', [
Validators.required,
Validators.minLength(8),
Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
]
]
},
{
]]
}, {
validators: this.passwordMatchValidator
}
);
this.resetPasswordForm.get('newPassword')?.valueChanges.subscribe(()=>{
this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity();
});
});
this.loadUsersForDropdown();
// Update confirm password validation when new password changes
this.resetPasswordForm.get('newPassword')?.valueChanges.subscribe(() => {
this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity();
});
}
togglePasswordType1() {
togglePasswordType1() {
this.passwordType1 = this.passwordHideShow1?.showPassword ? 'password' : 'text';
}
togglePasswordType2() {
this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text';
}
@ -74,10 +80,13 @@ export class ResetPasswordComponent implements OnInit{
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword');
const confirmPassword = group.get('confirmPassword');
if (!newPassword || !confirmPassword) return null;
if (confirmPassword.errors && !confirmPassword.errors['passwordMismatch']) {
return null;
}
if (newPassword.value !== confirmPassword.value) {
confirmPassword.setErrors({ passwordMismatch: true });
return { passwordMismatch: true };
@ -87,48 +96,93 @@ export class ResetPasswordComponent implements OnInit{
}
}
onSubmit() {
if (this.resetPasswordForm.invalid) return;
if (this.resetPasswordForm.invalid) {
this.resetPasswordForm.markAllAsTouched();
return;
}
this.isSubmitting = true;
const selectedId = this.resetPasswordForm.get('userId')?.value;
console.log("userid.....", selectedId)
console.log("userid.....", selectedId);
const payload = {
userId: selectedId,
newPassword: this.resetPasswordForm.get('newPassword')?.value,
porOrgaCode: this.storageService.getItem('POR_ORGACODE')
};
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
.subscribe({
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
this.httpService.requestPOST(URIKey.RESET_PASSWORD_URI, payload).subscribe({
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
this.resetForm(); // Reset form after successful submission
// Optional: Navigate to dashboard or stay on page
setTimeout(() => {
this.router.navigate(['/dashboard']);
}
}, 1500);
}
});
}
this.isSubmitting = false;
},
error: (error) => {
console.error('Error resetting password:', error);
this.isSubmitting = false;
}
});
}
loadUsersForDropdown(): void{
this.isLoading = true;
let params = new HttpParams().set('page', '0').set('size', '1000')
// Method to properly reset the form
resetForm(): void {
// Reset the form values
this.resetPasswordForm.reset();
// Reset password visibility
this.passwordType1 = 'password';
this.passwordType2 = 'password';
// Reset password hide/show components if available
if (this.passwordHideShow1) {
this.passwordHideShow1.showPassword = false;
}
if (this.passwordHideShow2) {
this.passwordHideShow2.showPassword = false;
}
// Mark all controls as untouched and pristine
Object.keys(this.resetPasswordForm.controls).forEach(key => {
const control = this.resetPasswordForm.get(key);
control?.markAsUntouched();
control?.markAsPristine();
});
// Reset ng-select by clearing the value
setTimeout(() => {
this.resetPasswordForm.patchValue({
userId: '',
newPassword: '',
confirmPassword: ''
});
}, 0);
}
loadUsersForDropdown(): void {
this.isLoading = true;
let params = new HttpParams()
.set('page', '0')
.set('size', '1000');
this.httpService.requestGET<SetupUser[]>(URIKey.GET_ALL_USER_URI, params).subscribe({
next: (response)=>{
this.allUsersDropdown = response;
this.isLoading = false
next: (response) => {
this.allUsersDropdown = response || [];
this.isLoading = false;
},
error:(err)=>{
error: (err) => {
console.error('Error fetching users:', err);
this.allUsersDropdown = [];
this.isLoading = false;
}
})
}
});
}
}

@ -3,222 +3,374 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
<div
class="d-sm-flex align-items-center justify-content-between navbar-header p-0"
></div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center"
>
{{ "setupUser" | translate }}
</div>
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'setupUser' | translate}}
</div>
<div class="card-body">
<form [formGroup]="userForm">
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userId" class="text-nowrap">
{{ 'userId' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<input type="text" id="userId"
class="form-control"
formControlName="userId"
name="userId"
placeholder="{{ 'userID' | translate }}" appNoWhitespaces
/>
</div>
<div class="text-danger" *ngIf="userForm.get('userId')?.touched && userForm.get('userId')?.invalid">
<div *ngIf="
userForm.get('userId')?.errors?.['required'] &&
!userForm.get('userId')?.value
">
{{ 'fieldRequired' | translate }}
</div>
</div>
<div class="text-danger" *ngIf="
userForm.get('userId')?.errors?.['minlength'] &&
userForm.get('userId')?.value
">
{{'userIdMinLength' | translate }}
</div>
<div class="text-danger" *ngIf="
userForm.get('userId')?.errors?.['pattern'] &&
userForm.get('userId')?.value
">
{{'emptySpaceRestriction' | translate}}
</div >
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="name"
class="text-nowrap mt-2">
{{ 'name' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<form [formGroup]="userForm">
<div class="row g-3 mb-3">
<!-- User ID -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userId" class="text-nowrap">
{{ "userId" | translate
}}<span class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100"
>
<input
type="text"
id="userId"
class="form-control"
formControlName="userId"
name="userId"
placeholder="{{ 'userID' | translate }}"
appNoWhitespaces
[readonly]="mode === 'edit'"
[class.bg-light]="mode === 'edit'"
/>
<input id="userFullname"
class="form-control"
formControlName="userFullname"
name="userFullname"
maxlength="500"
placeholder="{{ 'Full Name' | translate }}"
rows="3" />
<div class="text-danger" *ngIf="userForm.get('userFullname')?.touched && userForm.get('userFullname')?.invalid">
<div *ngIf="
userForm.get('userFullname')?.errors?.['required'] &&
!userForm.get('userFullname')?.value
">
{{ 'fieldRequired' | translate }}
</div>
</div>
<div class="text-danger" *ngIf="
userForm.get('userFullname')?.errors?.['minlength'] &&
userForm.get('userFullname')?.value
">
{{'nameMinLength' | translate }}
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="email" class="text-nowrap">
{{ 'email' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="email"
class="form-control"
formControlName="email"
name="email"
placeholder="{{ 'email' | translate }}" appNoWhitespaces/>
<div
class="text-danger"
*ngIf="
userForm.get('userId')?.touched &&
userForm.get('userId')?.invalid
"
>
<div
*ngIf="
userForm.get('userId')?.errors?.[
'required'
]
"
>
{{ "fieldRequired" | translate }}
</div>
<div
*ngIf="
userForm.get('userId')?.errors?.[
'minlength'
]
"
>
{{ "userIdMinLength" | translate }}
</div>
<div
*ngIf="
userForm.get('userId')?.errors?.[
'pattern'
]
"
>
{{
"emptySpaceRestriction" | translate
}}
</div>
</div>
</div>
</div>
</div>
<div class="text-danger" *ngIf="userForm.get('email')?.errors?.['required']
&& userForm.get('email')?.touched">
{{ 'fieldRequired' | translate }}
</div>
<div class="text-danger" *ngIf="userForm.get('email')?.errors?.['email']
&& userForm.get('email')?.touched">
{{"invalidEmail" | translate}}
</div>
</div>
<!-- Full Name -->
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label
for="userFullname"
class="text-nowrap mt-2"
>
{{ "name" | translate
}}<span class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100"
>
<input
id="userFullname"
class="form-control"
formControlName="userFullname"
name="userFullname"
maxlength="500"
placeholder="{{
'Full Name' | translate
}}"
/>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="defaultPassword" class="text-nowrap">
{{ 'defaultPassword' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="defaultPassword"
class="form-control"
formControlName="defaultPassword"
name="defaultPassword"
placeholder="{{ 'passwordPlaceHolder' | translate }}" appNoWhitespaces/>
<div
class="text-danger"
*ngIf="
userForm.get('userFullname')?.touched &&
userForm.get('userFullname')?.invalid
"
>
<div
*ngIf="
userForm.get('userFullname')
?.errors?.['required']
"
>
{{ "fieldRequired" | translate }}
</div>
<div
*ngIf="
userForm.get('userFullname')
?.errors?.['minlength']
"
>
{{ "nameMinLength" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="text-danger" *ngIf="userForm.get('defaultPassword')?.touched && userForm.get('defaultPassword')?.invalid">
<div *ngIf="userForm.get('defaultPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div *ngIf="
!userForm.get('defaultPassword')?.hasError('required') &&
userForm.get('defaultPassword')?.hasError('pattern')
">
{{ 'passwordPattern' | translate }}
</div>
</div>
</div>
<div class="row g-3 mb-3">
<!-- Email -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="email" class="text-nowrap">
{{ "email" | translate
}}<span class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100"
>
<input
id="email"
class="form-control"
formControlName="email"
name="email"
placeholder="{{ 'email' | translate }}"
appNoWhitespaces
/>
</div>
</div>
<div
class="text-danger"
*ngIf="
userForm.get('email')?.touched &&
userForm.get('email')?.invalid
"
>
<div
*ngIf="
userForm.get('email')?.errors?.[
'required'
]
"
>
{{ "fieldRequired" | translate }}
</div>
<div
*ngIf="
userForm.get('email')?.errors?.[
'email'
]
"
>
{{ "invalidEmail" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userRole" class="text-nowrap">
{{ 'SelectRole' | translate }}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<ng-select id="userRole" class="form-select form-select-narrow" formControlName="userRole" [items]="roleOptions" bindLabel="label"
bindValue="value" placeholder="{{ 'SelectRole' | translate }}" >
</ng-select>
<!-- Password -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label
for="defaultPassword"
class="text-nowrap"
>
{{ "password" | translate
}}<span class="mandatory" *ngIf="mode === 'create'">*</span>
<span class="text-muted small" *ngIf="mode === 'edit'"></span>
</label>
<div
class="password-wrapper position-relative w-100"
>
<input
id="defaultPassword"
type="password"
class="form-control"
formControlName="defaultPassword"
name="defaultPassword"
placeholder="{{
mode === 'create' ? ('passwordPlaceHolder' | translate) : ('newPasswordOptional' | translate)
}}"
appNoWhitespaces
/>
<div class="text-danger" *ngIf="userForm.get('userRole')?.touched && userForm.get('userRole')?.invalid">
{{ 'fieldRequired' | translate }}
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button type="button" class="btn btn-primary waves-effect waves-light" (click)="onSubmit()" [disabled]="userForm.invalid"
>
{{ 'save' | translate }}
</button>
<div
class="text-danger"
*ngIf="
userForm.get('defaultPassword')
?.touched &&
userForm.get('defaultPassword')?.invalid
"
>
<div
*ngIf="
userForm.get('defaultPassword')
?.errors?.['required'] &&
mode === 'create'
"
>
{{ "fieldRequired" | translate }}
</div>
<div
*ngIf="
!userForm.get('defaultPassword')
?.errors?.['required'] &&
userForm.get('defaultPassword')
?.errors?.['pattern']
"
>
{{ "passwordPattern" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<!-- User Role -->
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userRole" class="text-nowrap">
{{ "SelectRole" | translate
}}<span class="mandatory">*</span>
</label>
<div class="position-relative w-100">
<ng-select
id="userRole"
class="form-select form-select-narrow"
formControlName="userRole"
[items]="roleOptions"
bindLabel="label"
bindValue="value"
placeholder="{{
'SelectRole' | translate
}}"
>
</ng-select>
<div
class="text-danger"
*ngIf="
userForm.get('userRole')?.touched &&
userForm.get('userRole')?.invalid
"
>
{{ "fieldRequired" | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<!-- Save button for create mode -->
<button
*ngIf="mode === 'create'"
type="button"
class="btn btn-primary waves-effect waves-light"
(click)="onSubmit()"
[disabled]="userForm.invalid"
>
{{ "save" | translate }}
</button>
<!-- Update button for edit mode -->
<button
*ngIf="mode === 'edit'"
type="button"
class="btn btn-primary waves-effect waves-light me-2"
(click)="onUpdate()"
[disabled]="userForm.invalid || !isFormDirty()"
>
{{ "update" | translate }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Cancel button for edit mode -->
<button
*ngIf="mode === 'edit'"
type="button"
class="btn btn-secondary waves-effect waves-light"
(click)="cancelEdit()"
>
{{ "cancel" | translate }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body" *ngIf="renewalDataExpanded && allItems.length; else noRecordsFound">
<div
class="card-body"
*ngIf="
renewalDataExpanded && allItems.length;
else noRecordsFound
"
>
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'SetupUserDetails' | translate}}
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center"
>
{{ "SetupUserDetails" | translate }}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm"
<input
type="text"
class="form-control form-control-sm"
[(ngModel)]="searchText"
placeholder="{{ 'search' | translate }}">
placeholder="{{ 'search' | translate }}"
/>
<i class="fas fa-search search-icon"></i>
</div>
<i class="materialdesignicons" (click)="toggleTableCard()">
<ng-container *ngIf="userSetupDataExpanded; else collapsedIcon">
<i
class="materialdesignicons"
(click)="toggleTableCard()"
>
<ng-container
*ngIf="
userSetupDataExpanded;
else collapsedIcon
"
>
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
@ -227,79 +379,139 @@
</i>
</div>
</div>
<div class="card-body" *ngIf="userSetupDataExpanded && allItems.length; else noRecordsFound">
<div
class="card-body"
*ngIf="
userSetupDataExpanded && allItems.length;
else noRecordsFound
"
>
<div class="table-responsive">
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th style="width: 30%">{{'userID' | translate}}</th>
<th style="width: 30%">{{'Name' | translate}}</th>
<th style="width: 30%">{{'Role' | translate}}</th>
<th style="width: 10%">{{'action' | translate}}</th>
<th style="width: 25%">
{{ "userID" | translate }}
</th>
<th style="width: 25%">
{{ "Name" | translate }}
</th>
<th style="width: 25%">
{{ "Email" | translate }}
</th>
<th style="width: 15%">
{{ "Role" | translate }}
</th>
<th style="width: 10%">
{{ "action" | translate }}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of (allItems | tableFilter: searchText : ['userId', 'userFullname']).slice((currentPage-1)*itemsPerPage, currentPage*itemsPerPage)">
<td>{{ item.userId }}</td>
<td>{{ item.userFullname }}</td>
<td>{{item.role}}</td>
<tr
*ngFor="
let item of (
allItems
| tableFilter
: searchText
: ['userId', 'userFullname', 'email']
).slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
)
"
>
<td>{{ item.userId }}</td>
<td>{{ item.userFullname }}</td>
<td>{{ item.email }}</td>
<td>{{ getRoleLabel(item.role) }}</td>
<td>
<div class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm" title="View" (click)="onView(item.userId)">
<i class="mdi mdi-eye-outline"></i>
</button>
<button class="btn btn-warning btn-sm" *ngIf="buttonPermissions?.resetPasswordButton" title="Reset Password" (click)="openResetPasswordModal(item.userId)">
<i class="mdi mdi-lock-reset"></i>
</button>
<button *ngIf="buttonPermissions?.delete" class="btn btn-danger btn-sm" title="Delete"
(click)="confirmDelete(item.userId)">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
<td>
<div
class="d-flex justify-content-center gap-2"
>
<button
class="btn btn-info btn-sm"
title="View/Edit"
(click)="onView(item.userId)"
>
<i class="mdi mdi-eye-outline"></i>
</button>
<button
class="btn btn-warning btn-sm"
*ngIf="
buttonPermissions?.resetPasswordButton
"
title="Reset Password"
(click)="
openResetPasswordModal(item.userId)
"
>
<i class="mdi mdi-lock-reset"></i>
</button>
<button
*ngIf="buttonPermissions?.delete"
class="btn btn-danger btn-sm"
title="Delete"
(click)="confirmDelete(item.userId)"
>
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<app-reset-password-modal [userId]="selectedUserId">
</app-reset-password-modal>
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm"
[items]="pageSizeOptions"
bindLabel="label"
bindValue="value"
[(ngModel)]="itemsPerPage"
(change)="itemsPerPageChanged()"
[searchable]="false"
[clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted" *ngIf="allItems.length > 1">
{{'page' | translate}} {{currentPage}} {{'of' |
translate}} {{totalPages()}} ({{allItems.length}}
{{'totalItems' | translate}})
</div>
<app-reset-password-modal
[userId]="selectedUserIdForReset ?? ''"
(modalClosed)="closeResetPasswordModal()">
</app-reset-password-modal>
<div
class="d-flex justify-content-between align-items-center mt-3"
>
<div class="form-group mb-0">
<ng-select
class="form-select-sm"
[items]="pageSizeOptions"
bindLabel="label"
bindValue="value"
[(ngModel)]="itemsPerPage"
(change)="itemsPerPageChanged()"
[searchable]="false"
[clearable]="false"
[dropdownPosition]="'top'"
>
</ng-select>
</div>
<div class="btn-group">
<button class="btn btn-primary waves-effect waves-light" (click)="previousPage()">
{{ 'previous' | translate }}
</button>
<button class="btn btn-primary waves-effect waves-light" (click)="nextPage()">
{{ 'next' | translate }}
</button>
</div>
</div>
<div
class="text-muted"
*ngIf="allItems.length > 1"
>
{{ "page" | translate }} {{ currentPage }}
{{ "of" | translate }} {{ totalPages() }} ({{
allItems.length
}}
{{ "totalItems" | translate }})
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light"
(click)="previousPage()"
[disabled]="currentPage === 1"
>
{{ "previous" | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light"
(click)="nextPage()"
[disabled]="currentPage === totalPages()"
>
{{ "next" | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
@ -315,7 +527,10 @@
</div>
</div>
<ng-template #noRecordsFound>
<div *ngIf="!isLoading && allItems.length === 0" class="text-center text-muted mt-3">
<p>{{'noUserDetailsFound' | translate}}</p>
</div>
<div
*ngIf="!isLoading && allItems.length === 0"
class="text-center text-muted mt-3"
>
<p>{{ "noUserDetailsFound" | translate }}</p>
</div>
</ng-template>

@ -5,7 +5,7 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../../utils/app.constants';
import { SetupUser } from '../../models/user';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { FormBuilder, Validators, FormGroup, AbstractControl } from '@angular/forms';
import { ButtonManagementService } from '../../services/button-management.service';
import { StorageService } from '../../shared/services/storage.service';
import { TableFilterPipe } from '../../shared/pipes/table-filter.pipe';
@ -13,50 +13,53 @@ import { URIKey } from '../../utils/uri-enums';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { HttpURIService } from '../../app.http.uri.service';
import { I18NService } from '../../services/i18n.service';
import { SuccessMessages } from '../../utils/enums';
import { SuccessMessages, ErrorMessages } from '../../utils/enums';
import { ResetPasswordModalComponent } from '../reset-password-modal/reset-password-modal.component';
@Component({
selector: 'app-setup-user',
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule, TableFilterPipe, ResetPasswordModalComponent ],
imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule, TableFilterPipe, ResetPasswordModalComponent],
templateUrl: './setup-user.component.html',
styleUrl: './setup-user.component.scss'
})
export class SetupUserComponent implements OnInit {
userForm!: FormGroup;
selectedUserIdForEdit: string | null = null; // For edit form
selectedUserIdForReset: string | null = null; // For reset password modal
showForm = false;
selectedUserId!: any;
showDeleteModal = false;
userIdToDelete: any = null;
allItems: SetupUser[] = [];
currentPage: number = 1;
pageSizeOptions = pageSizeOptions
pageSizeOptions = pageSizeOptions;
itemsPerPage: number = 10;
pagedItems: any[] = [];
searchText: any = '';
renewalDataExpanded: boolean = true;
totalCount: number = 0;
mode: 'edit' | 'view' = 'view';
mode: 'create' | 'edit' = 'create';
userSetupDataExpanded: boolean = true
userSetupDataExpanded: boolean = true;
buttonPermissions: any;
isLoading: boolean = false;
setupUserList: SetupUser[] = [];
isLoading: boolean = false;
setupUserList: SetupUser[] = [];
// Store original user data for comparison
originalUserData: SetupUser | null = null;
roleOptions = [
{ label: 'Admin', value: 'ADMIN' },
{ label: 'User', value: 'USER' },
];
constructor(
private fb: FormBuilder,
private buttonManagementService: ButtonManagementService,
private storageService: StorageService,
private httpService: HttpURIService,
private i18nService: I18NService
){}
) {}
onSearch(value: string): void {
this.searchText = value;
@ -82,94 +85,201 @@ export class SetupUserComponent implements OnInit {
this.currentPage = 1;
this.updatePagedItems();
}
// Submit for creating new user
onSubmit() {
if (this.userForm.invalid) {
this.userForm.markAllAsTouched();
return;
}
const newUser : SetupUser = {
userId: this.userForm.value.userId,
userFullname: this.userForm.value.userFullname,
email: this.userForm.value.email,
const newUser: SetupUser = {
userId: this.userForm.value.userId.trim(),
userFullname: this.userForm.value.userFullname.trim(),
email: this.userForm.value.email.trim(),
role: this.userForm.value.userRole,
porOrgacode: this.storageService.getItem('POR_ORGACODE'),
password: this.userForm.value.defaultPassword
}
};
this.httpService.requestPOST<SetupUser[]>(URIKey.CREATE_USER, newUser).subscribe({
this.isLoading = true;
this.httpService.requestPOST<SetupUser>(URIKey.CREATE_USER, newUser).subscribe({
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.USER_CREATED_SUCCESS, []);
this.userForm.reset();
this.mode = 'edit';
this.loadUsersDirect()
this.resetForm();
this.loadUsersDirect();
}
this.isLoading = false;
},
error: (error) => {
console.error('Error creating user:', error);
this.isLoading = false;
}
});
}
// Update existing user
onUpdate() {
if (this.userForm.invalid) {
this.userForm.markAllAsTouched();
return;
}
const updatedUser: any = {
userId: this.selectedUserIdForEdit,
userFullname: this.userForm.value.userFullname.trim(),
email: this.userForm.value.email.trim(),
role: this.userForm.value.userRole,
porOrgacode: this.storageService.getItem('POR_ORGACODE')
};
// Only include password if it was provided
if (this.userForm.value.defaultPassword && this.userForm.value.defaultPassword.trim()) {
updatedUser.password = this.userForm.value.defaultPassword;
}
let params = new HttpParams().set('userId', this.selectedUserIdForEdit!);
this.isLoading = true;
this.httpService.requestPUT<SetupUser>(URIKey.UPDATE_USER, updatedUser, undefined, params).subscribe({
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.USER_UPDATED_SUCCESS, []);
this.resetForm();
this.loadUsersDirect();
}
this.isLoading = false;
},
error: (error) => {
console.error('Error updating user:', error);
this.isLoading = false;
}
});
}
// Cancel edit mode
cancelEdit() {
this.resetForm();
}
// Reset form to create mode
resetForm() {
this.userForm.reset();
this.userForm.enable();
this.mode = 'create';
this.selectedUserIdForEdit = null;
this.originalUserData = null;
// Reset form validation state
Object.keys(this.userForm.controls).forEach(key => {
const control = this.userForm.get(key);
control?.markAsPristine();
control?.markAsUntouched();
});
}
// Check if form has been modified
isFormDirty(): boolean {
if (!this.originalUserData || this.mode !== 'edit') {
return true;
}
const currentValues = this.userForm.value;
// Check if any field has changed (excluding password)
return (
currentValues.userFullname !== this.originalUserData.userFullname ||
currentValues.email !== this.originalUserData.email ||
currentValues.userRole !== this.originalUserData.role ||
(currentValues.defaultPassword && currentValues.defaultPassword.trim() !== '')
);
}
// Get role label for display
getRoleLabel(roleValue: string): string {
const role = this.roleOptions.find(r => r.value === roleValue);
return role ? role.label : roleValue;
}
updatePagedItems(): void {
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
this.pagedItems = this.allItems.slice(startIndex, endIndex);
}
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
this.pagedItems = this.allItems.slice(startIndex, endIndex);
}
getButtonPermissions(){
getButtonPermissions() {
this.buttonPermissions = this.buttonManagementService.buttonPermissions["setupUser"];
}
toggleTableCard(): void {
this.userSetupDataExpanded = !this.userSetupDataExpanded;
}
confirmDelete(userId: string) {
const confirmed = window.confirm('Are you sure you want to delete this user?');
if (confirmed) {
this.onDelete(userId);
this.userSetupDataExpanded = !this.userSetupDataExpanded;
}
}
ngOnInit(): void {
confirmDelete(userId: string) {
const confirmed = window.confirm('Are you sure you want to delete this user?');
if (confirmed) {
this.onDelete(userId);
}
}
this.getButtonPermissions();
ngOnInit(): void {
this.getButtonPermissions();
this.initializeForm();
this.loadUsersDirect();
}
this.userForm = this.fb.group({
initializeForm(): void {
this.userForm = this.fb.group({
userId: ['', [
Validators.required,
Validators.minLength(5),
Validators.pattern(/^\S+$/)
]
],
Validators.pattern('^[a-z0-9]*$')
]],
userFullname: ['', [
Validators.required,
Validators.minLength(5)
]
],
Validators.minLength(5),
Validators.maxLength(500)
]],
defaultPassword: ['', [
Validators.required,
// Make password optional for edit mode, required for create
(control: AbstractControl) => {
if (this.mode === 'create' && !control.value) {
return { required: true };
}
return null;
},
Validators.pattern(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,20}$/
)
]
],
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,20}$/
)
]],
email: ['', [Validators.required, Validators.email]],
userRole: [null, Validators.required]
});
this.loadUsersDirect();
// Update password validation when mode changes
this.userForm.get('defaultPassword')?.valueChanges.subscribe(() => {
const passwordControl = this.userForm.get('defaultPassword');
if (passwordControl) {
passwordControl.updateValueAndValidity();
}
});
}
loadUsersDirect(): void {
this.isLoading = false;
let params = new HttpParams()
.set('page', this.currentPage.toString())
.set('size', this.itemsPerPage.toString());
this.isLoading = true;
let params = new HttpParams()
.set('page', (this.currentPage - 1).toString())
.set('size', this.itemsPerPage.toString());
this.httpService.requestGET<any[]>(URIKey.GET_ALL_USER_URI, params).subscribe({
this.httpService.requestGET<any>(URIKey.GET_ALL_USER_URI, params).subscribe({
next: (response) => {
this.setupUserList = response
if (response && response.content) {
this.setupUserList = response.content;
this.totalCount = response.totalElements || response.content.length;
} else {
this.setupUserList = response;
}
this.allItems = [...this.setupUserList];
this.updatePagedItems();
this.isLoading = false;
@ -182,55 +292,144 @@ ngOnInit(): void {
});
}
onView(userId: any): void{
onView(userId: string): void {
let params = new HttpParams().set('userId', userId);
this.isLoading = true;
this.httpService.requestGET<SetupUser>(URIKey.GET_USER_BY_ID, params).subscribe({
next: (response: SetupUser) => {
// Store original data
this.originalUserData = { ...response };
// Patch form values
this.userForm.patchValue({
userId: response.userId,
userFullname: response.userFullname,
email: response.email,
userRole: response.role,
defaultPassword: ''
});
this.userForm.disable();
this.selectedUserId = userId;
this.isLoading = false;
userId: response.userId,
userFullname: response.userFullname,
email: response.email,
userRole: response.role,
defaultPassword: '' // Clear password field for security
});
// Enable form for editing
this.userForm.enable();
// Disable userId field (it shouldn't be changed)
this.userForm.get('userId')?.disable();
// Set the edit userId
this.selectedUserIdForEdit = userId;
this.mode = 'edit'; // Set mode to edit
this.isLoading = false;
// Scroll to form
this.scrollToForm();
},
error: (err) => {
console.error('Error fetching users:', err);
this.allItems = [];
console.error('Error fetching user:', err)
this.isLoading = false;
}
});
}
onDelete(userId: any){
onDelete(userId: string) {
let params = new HttpParams().set('userId', userId);
this.isLoading = true;
this.httpService.requestDELETE<any>(URIKey.DELETE_USER, params).subscribe({
next: (response) =>{
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.USER_DELETE_SUCCESS, []);
this.loadUsersDirect();
this.userForm.reset();
this.selectedUserId = null;
this.i18nService.success(SuccessMessages.USER_DELETE_SUCCESS, []);
this.loadUsersDirect();
this.resetForm();
}
this.isLoading = false;
},
error: (error) => {
console.error('Error deleting user:', error);
this.i18nService.error(ErrorMessages.USER_DELETE_FAILED, []);
this.isLoading = false;
}
});
}
openResetPasswordModal(userId: string) {
console.log('Opening reset password modal for userId:', userId);
this.selectedUserIdForReset = userId;
// Show the modal
const modal = document.getElementById('resetPasswordModal');
if (modal) {
modal.classList.add('show');
modal.style.display = 'block';
document.body.classList.add('modal-open');
// Add backdrop
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
document.body.appendChild(backdrop);
// Listen for modal close event
const closeModal = () => {
this.closeResetPasswordModal();
};
// Add click event to backdrop
backdrop.addEventListener('click', closeModal);
// Add event listener for escape key
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
closeModal();
}
};
document.addEventListener('keydown', handleEscape);
// Store event listeners for cleanup
(modal as any).__backdropListener = closeModal;
(modal as any).__escapeListener = handleEscape;
}
}
// Helper method to scroll to form
private scrollToForm(): void {
setTimeout(() => {
const formElement = document.querySelector('.card.border');
if (formElement) {
formElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
})
}, 100);
}
openResetPasswordModal(userId: string) {
this.selectedUserId = userId;
closeResetPasswordModal(): void {
const modal = document.getElementById('resetPasswordModal');
const modal = document.getElementById('resetPasswordModal');
modal?.classList.add('show');
modal!.style.display = 'block';
document.body.classList.add('modal-open');
// Remove event listeners
if (modal) {
const backdrop = document.querySelector('.modal-backdrop');
if (backdrop && (modal as any).__backdropListener) {
backdrop.removeEventListener('click', (modal as any).__backdropListener);
}
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
document.body.appendChild(backdrop);
}
if ((modal as any).__escapeListener) {
document.removeEventListener('keydown', (modal as any).__escapeListener);
}
}
// Hide modal
if (modal) {
modal.classList.remove('show');
modal.style.display = 'none';
}
// Remove backdrop
const backdrop = document.querySelector('.modal-backdrop');
if (backdrop) {
backdrop.remove();
}
document.body.classList.remove('modal-open');
// Clear the reset userId
this.selectedUserIdForReset = null;
}
}

@ -6,6 +6,13 @@ export enum ErrorMessages{
UNAUTHORIZED_REQUEST = "UNAUTHORIZED_REQUEST",
ALREADY_LOGGED_IN = "ALREADY_LOGGED_IN",
ACCESS_DENIED = "ACCESS_DENIED",
USER_CREATE_FAILED = "USER_CREATE_FAILED",
USER_UPDATE_FAILED = "USER_UPDATE_FAILED",
USER_DELETE_FAILED = "USER_DELETE_FAILED",
USER_FETCH_FAILED = "USER_FETCH_FAILED",
RESET_PASSWORD_FAILED = "RESET_PASSWORD_FAILED",
CHANGE_PASSWORD_FAILED = "ERR_SEC_0007"
}
export enum supportedLanguages{
@ -60,7 +67,10 @@ RECORD_DELETED_SUCCESSFULY = "RECORD_DELETED_SUCCESSFULY",
ACCOUNT_CLOSED_SUCCESSFULLY = "ACCOUNT_CLOSED_SUCCESSFULLY",
SUCCESS_MESSAGE = "SUCCESS_MESSAGE",
USER_CREATED_SUCCESS = "USER_CREATED_SUCCESS",
USER_UPDATE_SUCCESS = "USER_UPDATE_SUCCESS",
USER_UPDATED_SUCCESS = "USER_UPDATED_SUCCESS",
USER_DELETE_SUCCESS = "USER_DELETE_SUCCESS"
}
export enum MESSAGEKEY {

@ -14,5 +14,6 @@ export enum URIKey {
FIRST_LOGIN_URI = "FIRST_LOGIN_URI",
THIRD_PARTY_REGISTER_URI = "THIRD_PARTY_REGISTER_URI",
TRANSACTION_LOGS = "TRANSACTION_LOGS",
LOGGER_MANAGER_URI = "LOGGER_MANAGER_URI"
LOGGER_MANAGER_URI = "LOGGER_MANAGER_URI",
UPDATE_USER = 'UPDATE_USER'
}

@ -27,6 +27,11 @@
"URI": "/user/createUser",
"UUID": "CREATE_USER"
},
{
"Id": "ENTITY_UPDATE_USER",
"URI": "/user/updateUser",
"UUID": "UPDATE_USER"
},
{
"Id": "ENTITY_GET_ALL_USERS",
"URI": "/user/getAllUsers",

@ -256,6 +256,7 @@
"noUserDetailsFound":"No User Details found",
"noThirdPartyRegFound":"No Third Party Registration Details Found",
"ERR_SEC_0001": "Email already exists",
"ERR_SEC_0007": "New password cannot be same as old password",
"ERR_SEC_0002": "Username already exists",
"ERR_SEC_0003": "Old Password is not correct",
"ERR_SEC_0004":"Invalid credentials",
@ -267,6 +268,7 @@
"nameMinLength" : "Name must be at least 5 characters",
"emptySpaceRestriction" : "Empty spaces are not allowed",
"USER_CREATED_SUCCESS": "User Created",
"USER_UPDATE_SUCCESS": "User Updated",
"USER_DELETE_SUCCESS": "User Deleted",
"SAVED_SUCCESSFULLY" :"Saved Successfully",
"oldAndNewPass":"Old password and new password cannot be the same",

Loading…
Cancel
Save