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

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

@ -98,8 +98,8 @@
<th>{{ "crAccount" | translate }}</th> <th>{{ "crAccount" | translate }}</th>
<th>{{ "drPcaGlacode" | translate }}</th> <th>{{ "drPcaGlacode" | translate }}</th>
<th>{{ "crPcaglacode" | translate }}</th> <th>{{ "crPcaglacode" | translate }}</th>
<th>{{ "transactionCode" | translate }}</th>
<th>{{ "transactionUri" | translate }}</th> <th>{{ "transactionUri" | translate }}</th>
<th>{{ "transactionCode" | translate }}</th>
<th>{{ "paymentMode" | translate }}</th> <th>{{ "paymentMode" | translate }}</th>
<th>{{ "date" | translate }}</th> <th>{{ "date" | translate }}</th>
<th>{{ "channel" | translate }}</th> <th>{{ "channel" | translate }}</th>
@ -141,8 +141,8 @@
<td>{{ log.crMbmbkmsnumber || "-" }}</td> <td>{{ log.crMbmbkmsnumber || "-" }}</td>
<td>{{ log.drPcaGlacode || "-" }}</td> <td>{{ log.drPcaGlacode || "-" }}</td>
<td>{{ log.crPcaglacode || "-" }}</td> <td>{{ log.crPcaglacode || "-" }}</td>
<td>{{log.transactionUri || "N/A"}}</td> <td>{{ log.transactionUri || "N/A"}}</td>
<td>{{log.transactionCode || "N/A"}}</td> <td>{{ log.transactionCode || "N/A"}}</td>
<td>{{ log.ppmPymdcode }}</td> <td>{{ log.ppmPymdcode }}</td>
<td> <td>
{{ log.sgtGntrdate | date: "MMM dd, yyyy" }} {{ 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-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
@ -6,72 +6,144 @@
<h5 class="modal-title"> <h5 class="modal-title">
{{ 'resetPassword' | translate }} {{ 'resetPassword' | translate }}
</h5> </h5>
<button type="button" class="btn-close" (click)="closeModal()"></button> <button type="button" class="btn-close" (click)="closeModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form [formGroup]="resetPasswordForm"> <!-- Show warning if no user is selected -->
<div *ngIf="!userId" class="alert alert-warning mb-3">
<div class="mb-3"> <i class="fas fa-exclamation-triangle me-2"></i>
<label class="form-label">{{ 'userID' | translate }}</label> {{ 'noUserSelected' | translate }}
<input [readonly]="true" class="form-control" formControlName="userId" /> </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> </div>
<small class="text-muted">{{ 'resettingPasswordForThisUser' | translate }}</small>
</div>
<form [formGroup]="resetPasswordForm" *ngIf="userId">
<!-- New Password Field -->
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{{ 'enterNewPassword' | translate }}</label> <label class="form-label fw-bold">
{{ 'enterNewPassword' | translate }} <span class="mandatory">*</span>
<div class="password-wrapper"> </label>
<input class="form-control" formControlName="newPassword" placeholder="{{'enterNewPassword' | translate}}" [type]="newPasswordType" autocomplete="off" appNoWhitespaces /> <div class="password-wrapper input-group">
<input
<app-password-hide-show #newPasswordPsh class="password-eye align-items-stretch" [showPassword]="true" class="form-control"
(onEyeClick)="togglePasswordType()"> formControlName="newPassword"
</app-password-hide-show> 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>
<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')"> <div *ngIf="resetPasswordForm.get('newPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }} {{ 'fieldRequired' | translate }}
</div> </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')"> <div *ngIf="resetPasswordForm.get('newPassword')?.hasError('pattern')">
{{ 'passwordPattern' | translate }} {{ 'passwordPattern' | translate }}
</div> </div>
</div>
<!-- Password requirements hint -->
<div class="form-text text-muted">
<small>{{ 'passwordRequirements' | translate }}</small>
</div> </div>
</div> </div>
<!-- Confirm Password Field -->
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{{ 'confirmPassword' | translate }}</label> <label class="form-label fw-bold">
{{ 'confirmPassword' | translate }} <span class="mandatory">*</span>
<div class="password-wrapper"> </label>
<input class="form-control" formControlName="confirmPassword" placeholder="{{'confirmPassword' | translate}}" [type]="confirmPasswordType" autocomplete="off" appNoWhitespaces /> <div class="password-wrapper input-group">
<input
<app-password-hide-show #confirmPasswordPsh class="password-eye align-items-stretch" [showPassword]="true" class="form-control"
(onEyeClick)="toggleConfirmPasswordType()"> formControlName="confirmPassword"
</app-password-hide-show> 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>
<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')"> <div *ngIf="resetPasswordForm.get('confirmPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }} {{ 'fieldRequired' | translate }}
</div> </div>
<div *ngIf="resetPasswordForm.hasError('passwordMismatch')"> <div *ngIf="resetPasswordForm.get('confirmPassword')?.hasError('passwordMismatch')">
{{ 'passwordsDoNotMatch' | translate }} {{ 'passwordsDoNotMatch' | translate }}
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer d-flex justify-content-end gap-2"> <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()"> <button
{{ 'cancel' | translate }} type="button"
class="btn btn-outline-secondary btn-sm px-4"
(click)="closeModal()"
[attr.disabled]="isSubmitting ? true : null"
>
{{ 'cancel' | translate }}
</button> </button>
<button type="button" class="btn btn-primary btn-sm px-4" [disabled]="resetPasswordForm.invalid" (click)="submit()"> <button
{{ 'save' | translate }} 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> </button>
</div> </div>
</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 { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ReactiveFormsModule } from '@angular/forms';
import { HttpURIService } from '../../app.http.uri.service'; import { HttpURIService } from '../../app.http.uri.service';
import { URIKey } from '../../utils/uri-enums'; import { URIKey } from '../../utils/uri-enums';
import { I18NService } from '../../services/i18n.service'; import { I18NService } from '../../services/i18n.service';
import { StorageService } from '../../shared/services/storage.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 { HttpErrorResponse } from '@angular/common/http';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component'; import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
@Component({ @Component({
selector: 'app-reset-password-modal', selector: 'app-reset-password-modal',
standalone: true, standalone: true,
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent], imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent],
templateUrl: './reset-password-modal.component.html' templateUrl: './reset-password-modal.component.html'
}) })
export class ResetPasswordModalComponent implements OnInit { export class ResetPasswordModalComponent implements OnInit, OnChanges {
newPasswordType: string = 'password' newPasswordType: string = 'password';
confirmPasswordType: string = 'password' confirmPasswordType: string = 'password';
@Input() userId!: string;
@Input() userId: string | null = null;
@Output() modalClosed = new EventEmitter<void>();
resetPasswordForm!: FormGroup; resetPasswordForm!: FormGroup;
@ViewChild('newPasswordPsh') passwordHideShow?:PasswordHideShowComponent isSubmitting: boolean = false;
@ViewChild('confirmPasswordPsh') confirmPasswordHideShow?:PasswordHideShowComponent
@ViewChild('newPasswordPsh') passwordHideShow?: PasswordHideShowComponent;
@ViewChild('confirmPasswordPsh') confirmPasswordHideShow?: PasswordHideShowComponent;
constructor( constructor(
private fb: FormBuilder, private fb: FormBuilder,
@ -32,50 +35,80 @@ export class ResetPasswordModalComponent implements OnInit {
private i18nService: I18NService, private i18nService: I18NService,
private storageService: StorageService private storageService: StorageService
) {} ) {}
togglePasswordType() {
this.newPasswordType = this.passwordHideShow?.showPassword ? 'password' : 'text';
}
toggleConfirmPasswordType() {
this.confirmPasswordType = this.confirmPasswordHideShow?.showPassword ? 'password' : 'text';
}
ngOnInit(): void { ngOnInit(): void {
this.initializeForm();
}
initializeForm(): void {
this.resetPasswordForm = this.fb.group({ this.resetPasswordForm = this.fb.group({
userId: [''],
newPassword: ['', [ newPassword: ['', [
Validators.required, Validators.required,
Validators.minLength(8), Validators.minLength(8),
Validators.maxLength(20), Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/) Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
]], ]],
confirmPassword: ['', Validators.required] 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 { ngOnChanges(changes: SimpleChanges): void {
if (changes['userId'] && this.resetPasswordForm) { if (changes['userId'] && this.resetPasswordForm) {
this.resetPasswordForm.reset({ console.log('ResetPasswordModal: userId changed to:', this.userId);
userId: this.userId,
newPassword: '',
confirmPassword: ''
});
this.newPasswordType = 'password'; // Reset the form when userId changes
this.confirmPasswordType = 'password'; this.resetForm();
this.passwordHideShow?.reset(); // Update the form with new userId if needed
this.confirmPasswordHideShow?.reset(); 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 { passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword')?.value; const newPassword = group.get('newPassword')?.value;
const confirmPassword = group.get('confirmPassword')?.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() { 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 = { const payload = {
userId: this.userId, userId: this.userId,
@ -85,28 +118,56 @@ export class ResetPasswordModalComponent implements OnInit {
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload) this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
.subscribe({ .subscribe({
next: (res) => { next: (response) => {
if (!(res instanceof HttpErrorResponse)) { if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []); this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
this.closeModal(); 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.resetPasswordForm.reset();
this.isSubmitting = false;
this.newPasswordType = 'password'; this.newPasswordType = 'password';
this.confirmPasswordType = 'password'; this.confirmPasswordType = 'password';
// Reset password hide/show components
this.passwordHideShow?.reset(); this.passwordHideShow?.reset();
this.confirmPasswordHideShow?.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'); const modal = document.getElementById('resetPasswordModal');
modal?.classList.remove('show'); if (modal) {
modal?.setAttribute('aria-hidden', 'true'); modal.classList.remove('show');
modal!.style.display = 'none'; modal.setAttribute('aria-hidden', 'true');
modal.style.display = 'none';
}
document.body.classList.remove('modal-open'); 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 { ErrorMessages, SuccessMessages } from '../../utils/enums';
import { HttpErrorResponse, HttpParams } from '@angular/common/http'; import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router'; 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 { SetupUser } from '../../models/user';
import { error } from 'console';
@Component({ @Component({
selector: 'app-reset-password', selector: 'app-reset-password',
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule, NgSelectComponent], imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule, NgSelectModule],
templateUrl: './reset-password.component.html', templateUrl: './reset-password.component.html',
styleUrl: './reset-password.component.scss' styleUrl: './reset-password.component.scss'
}) })
@ -27,46 +26,53 @@ export class ResetPasswordComponent implements OnInit{
isLoading: boolean = false; isLoading: boolean = false;
allUsersDropdown: SetupUser[] = []; allUsersDropdown: SetupUser[] = [];
selectedUserId: string | null = null; selectedUserId: string | null = null;
isSubmitting: boolean = false;
@ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent; @ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent;
@ViewChild('psh2') passwordHideShow2?: 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 { ngOnInit(): void {
const userIdValue = this.storageService.getItem('USER_ID') || null; this.initializeForm();
this.loadUsersForDropdown();
}
initializeForm(): void {
this.resetPasswordForm = this.fb.group({ this.resetPasswordForm = this.fb.group({
userId: [null], userId: ['', Validators.required], // Changed to empty string and required
newPassword: ['', [ newPassword: ['', [
Validators.required, Validators.required,
Validators.minLength(8), Validators.minLength(8),
Validators.maxLength(20), Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
] ]],
],
confirmPassword: ['', [ confirmPassword: ['', [
Validators.required, Validators.required,
Validators.minLength(8), Validators.minLength(8),
Validators.maxLength(20), Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/)
] ]]
] }, {
},
{
validators: this.passwordMatchValidator 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'; this.passwordType1 = this.passwordHideShow1?.showPassword ? 'password' : 'text';
} }
togglePasswordType2() { togglePasswordType2() {
this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text'; this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text';
} }
@ -74,10 +80,13 @@ export class ResetPasswordComponent implements OnInit{
passwordMatchValidator(group: AbstractControl): ValidationErrors | null { passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword'); const newPassword = group.get('newPassword');
const confirmPassword = group.get('confirmPassword'); const confirmPassword = group.get('confirmPassword');
if (!newPassword || !confirmPassword) return null; if (!newPassword || !confirmPassword) return null;
if (confirmPassword.errors && !confirmPassword.errors['passwordMismatch']) { if (confirmPassword.errors && !confirmPassword.errors['passwordMismatch']) {
return null; return null;
} }
if (newPassword.value !== confirmPassword.value) { if (newPassword.value !== confirmPassword.value) {
confirmPassword.setErrors({ passwordMismatch: true }); confirmPassword.setErrors({ passwordMismatch: true });
return { passwordMismatch: true }; return { passwordMismatch: true };
@ -87,48 +96,93 @@ export class ResetPasswordComponent implements OnInit{
} }
} }
onSubmit() { onSubmit() {
if (this.resetPasswordForm.invalid) return; if (this.resetPasswordForm.invalid) {
this.resetPasswordForm.markAllAsTouched();
return;
}
this.isSubmitting = true;
const selectedId = this.resetPasswordForm.get('userId')?.value; const selectedId = this.resetPasswordForm.get('userId')?.value;
console.log("userid.....", selectedId)
console.log("userid.....", selectedId);
const payload = { const payload = {
userId: selectedId, userId: selectedId,
newPassword: this.resetPasswordForm.get('newPassword')?.value, newPassword: this.resetPasswordForm.get('newPassword')?.value,
porOrgaCode: this.storageService.getItem('POR_ORGACODE') porOrgaCode: this.storageService.getItem('POR_ORGACODE')
}; };
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload) this.httpService.requestPOST(URIKey.RESET_PASSWORD_URI, payload).subscribe({
.subscribe({ next: (response) => {
next: (response) => { if (!(response instanceof HttpErrorResponse)) {
if (!(response instanceof HttpErrorResponse)) { this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
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']); this.router.navigate(['/dashboard']);
} }, 1500);
} }
}); this.isSubmitting = false;
} },
error: (error) => {
console.error('Error resetting password:', error);
this.isSubmitting = false;
}
});
}
loadUsersForDropdown(): void{ // Method to properly reset the form
this.isLoading = true; resetForm(): void {
let params = new HttpParams().set('page', '0').set('size', '1000') // 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({ this.httpService.requestGET<SetupUser[]>(URIKey.GET_ALL_USER_URI, params).subscribe({
next: (response)=>{ next: (response) => {
this.allUsersDropdown = response; this.allUsersDropdown = response || [];
this.isLoading = false this.isLoading = false;
}, },
error:(err)=>{ error: (err) => {
console.error('Error fetching users:', err); console.error('Error fetching users:', err);
this.allUsersDropdown = []; this.allUsersDropdown = [];
this.isLoading = false; this.isLoading = false;
} }
}) });
}
} }
}

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

@ -5,7 +5,7 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../../utils/app.constants'; import { pageSizeOptions } from '../../utils/app.constants';
import { SetupUser } from '../../models/user'; 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 { ButtonManagementService } from '../../services/button-management.service';
import { StorageService } from '../../shared/services/storage.service'; import { StorageService } from '../../shared/services/storage.service';
import { TableFilterPipe } from '../../shared/pipes/table-filter.pipe'; 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 { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { HttpURIService } from '../../app.http.uri.service'; import { HttpURIService } from '../../app.http.uri.service';
import { I18NService } from '../../services/i18n.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'; import { ResetPasswordModalComponent } from '../reset-password-modal/reset-password-modal.component';
@Component({ @Component({
selector: 'app-setup-user', selector: 'app-setup-user',
standalone: true, standalone: true,
imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule, TableFilterPipe, ResetPasswordModalComponent ], imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule, TableFilterPipe, ResetPasswordModalComponent],
templateUrl: './setup-user.component.html', templateUrl: './setup-user.component.html',
styleUrl: './setup-user.component.scss' styleUrl: './setup-user.component.scss'
}) })
export class SetupUserComponent implements OnInit { export class SetupUserComponent implements OnInit {
userForm!: FormGroup; userForm!: FormGroup;
selectedUserIdForEdit: string | null = null; // For edit form
selectedUserIdForReset: string | null = null; // For reset password modal
showForm = false; showForm = false;
selectedUserId!: any;
showDeleteModal = false; showDeleteModal = false;
userIdToDelete: any = null; userIdToDelete: any = null;
allItems: SetupUser[] = []; allItems: SetupUser[] = [];
currentPage: number = 1; currentPage: number = 1;
pageSizeOptions = pageSizeOptions pageSizeOptions = pageSizeOptions;
itemsPerPage: number = 10; itemsPerPage: number = 10;
pagedItems: any[] = []; pagedItems: any[] = [];
searchText: any = ''; searchText: any = '';
renewalDataExpanded: boolean = true; renewalDataExpanded: boolean = true;
totalCount: number = 0; totalCount: number = 0;
mode: 'edit' | 'view' = 'view'; mode: 'create' | 'edit' = 'create';
userSetupDataExpanded: boolean = true userSetupDataExpanded: boolean = true;
buttonPermissions: any; buttonPermissions: any;
isLoading: boolean = false; isLoading: boolean = false;
setupUserList: SetupUser[] = []; setupUserList: SetupUser[] = [];
// Store original user data for comparison
originalUserData: SetupUser | null = null;
roleOptions = [ roleOptions = [
{ label: 'Admin', value: 'ADMIN' }, { label: 'Admin', value: 'ADMIN' },
{ label: 'User', value: 'USER' }, { label: 'User', value: 'USER' },
]; ];
constructor( constructor(
private fb: FormBuilder, private fb: FormBuilder,
private buttonManagementService: ButtonManagementService, private buttonManagementService: ButtonManagementService,
private storageService: StorageService, private storageService: StorageService,
private httpService: HttpURIService, private httpService: HttpURIService,
private i18nService: I18NService private i18nService: I18NService
){} ) {}
onSearch(value: string): void { onSearch(value: string): void {
this.searchText = value; this.searchText = value;
@ -82,94 +85,201 @@ export class SetupUserComponent implements OnInit {
this.currentPage = 1; this.currentPage = 1;
this.updatePagedItems(); this.updatePagedItems();
} }
// Submit for creating new user
onSubmit() { onSubmit() {
if (this.userForm.invalid) { if (this.userForm.invalid) {
this.userForm.markAllAsTouched(); this.userForm.markAllAsTouched();
return; return;
} }
const newUser : SetupUser = { const newUser: SetupUser = {
userId: this.userForm.value.userId, userId: this.userForm.value.userId.trim(),
userFullname: this.userForm.value.userFullname, userFullname: this.userForm.value.userFullname.trim(),
email: this.userForm.value.email, email: this.userForm.value.email.trim(),
role: this.userForm.value.userRole, role: this.userForm.value.userRole,
porOrgacode: this.storageService.getItem('POR_ORGACODE'), porOrgacode: this.storageService.getItem('POR_ORGACODE'),
password: this.userForm.value.defaultPassword 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) => { next: (response) => {
if (!(response instanceof HttpErrorResponse)) { if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.USER_CREATED_SUCCESS, []); this.i18nService.success(SuccessMessages.USER_CREATED_SUCCESS, []);
this.userForm.reset(); this.resetForm();
this.mode = 'edit'; this.loadUsersDirect();
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 { updatePagedItems(): void {
const startIndex = (this.currentPage - 1) * this.itemsPerPage; const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage; const endIndex = startIndex + this.itemsPerPage;
this.pagedItems = this.allItems.slice(startIndex, endIndex); this.pagedItems = this.allItems.slice(startIndex, endIndex);
} }
getButtonPermissions(){ getButtonPermissions() {
this.buttonPermissions = this.buttonManagementService.buttonPermissions["setupUser"]; this.buttonPermissions = this.buttonManagementService.buttonPermissions["setupUser"];
} }
toggleTableCard(): void { toggleTableCard(): void {
this.userSetupDataExpanded = !this.userSetupDataExpanded; 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);
} }
}
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: ['', [ userId: ['', [
Validators.required, Validators.required,
Validators.minLength(5), Validators.minLength(5),
Validators.pattern(/^\S+$/) Validators.pattern('^[a-z0-9]*$')
] ]],
],
userFullname: ['', [ userFullname: ['', [
Validators.required, Validators.required,
Validators.minLength(5) Validators.minLength(5),
] Validators.maxLength(500)
], ]],
defaultPassword: ['', [ 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( 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]], email: ['', [Validators.required, Validators.email]],
userRole: [null, Validators.required] 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 { loadUsersDirect(): void {
this.isLoading = false; this.isLoading = true;
let params = new HttpParams() let params = new HttpParams()
.set('page', this.currentPage.toString()) .set('page', (this.currentPage - 1).toString())
.set('size', this.itemsPerPage.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) => { 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.allItems = [...this.setupUserList];
this.updatePagedItems(); this.updatePagedItems();
this.isLoading = false; this.isLoading = false;
@ -182,55 +292,144 @@ ngOnInit(): void {
}); });
} }
onView(userId: any): void{ onView(userId: string): void {
let params = new HttpParams().set('userId', userId); let params = new HttpParams().set('userId', userId);
this.isLoading = true;
this.httpService.requestGET<SetupUser>(URIKey.GET_USER_BY_ID, params).subscribe({ this.httpService.requestGET<SetupUser>(URIKey.GET_USER_BY_ID, params).subscribe({
next: (response: SetupUser) => { next: (response: SetupUser) => {
// Store original data
this.originalUserData = { ...response };
// Patch form values
this.userForm.patchValue({ this.userForm.patchValue({
userId: response.userId, userId: response.userId,
userFullname: response.userFullname, userFullname: response.userFullname,
email: response.email, email: response.email,
userRole: response.role, userRole: response.role,
defaultPassword: '' defaultPassword: '' // Clear password field for security
}); });
this.userForm.disable();
this.selectedUserId = userId; // Enable form for editing
this.isLoading = false; 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) => { error: (err) => {
console.error('Error fetching users:', err); console.error('Error fetching user:', err)
this.allItems = [];
this.isLoading = false; this.isLoading = false;
} }
}); });
} }
onDelete(userId: any){ onDelete(userId: string) {
let params = new HttpParams().set('userId', userId); let params = new HttpParams().set('userId', userId);
this.isLoading = true;
this.httpService.requestDELETE<any>(URIKey.DELETE_USER, params).subscribe({ this.httpService.requestDELETE<any>(URIKey.DELETE_USER, params).subscribe({
next: (response) =>{ next: (response) => {
if (!(response instanceof HttpErrorResponse)) { if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.USER_DELETE_SUCCESS, []); this.i18nService.success(SuccessMessages.USER_DELETE_SUCCESS, []);
this.loadUsersDirect(); this.loadUsersDirect();
this.userForm.reset(); this.resetForm();
this.selectedUserId = null; }
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) { closeResetPasswordModal(): void {
this.selectedUserId = userId; const modal = document.getElementById('resetPasswordModal');
const modal = document.getElementById('resetPasswordModal'); // Remove event listeners
modal?.classList.add('show'); if (modal) {
modal!.style.display = 'block'; const backdrop = document.querySelector('.modal-backdrop');
document.body.classList.add('modal-open'); if (backdrop && (modal as any).__backdropListener) {
backdrop.removeEventListener('click', (modal as any).__backdropListener);
}
const backdrop = document.createElement('div'); if ((modal as any).__escapeListener) {
backdrop.className = 'modal-backdrop fade show'; document.removeEventListener('keydown', (modal as any).__escapeListener);
document.body.appendChild(backdrop); }
} }
// 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", UNAUTHORIZED_REQUEST = "UNAUTHORIZED_REQUEST",
ALREADY_LOGGED_IN = "ALREADY_LOGGED_IN", ALREADY_LOGGED_IN = "ALREADY_LOGGED_IN",
ACCESS_DENIED = "ACCESS_DENIED", 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{ export enum supportedLanguages{
@ -60,7 +67,10 @@ RECORD_DELETED_SUCCESSFULY = "RECORD_DELETED_SUCCESSFULY",
ACCOUNT_CLOSED_SUCCESSFULLY = "ACCOUNT_CLOSED_SUCCESSFULLY", ACCOUNT_CLOSED_SUCCESSFULLY = "ACCOUNT_CLOSED_SUCCESSFULLY",
SUCCESS_MESSAGE = "SUCCESS_MESSAGE", SUCCESS_MESSAGE = "SUCCESS_MESSAGE",
USER_CREATED_SUCCESS = "USER_CREATED_SUCCESS", USER_CREATED_SUCCESS = "USER_CREATED_SUCCESS",
USER_UPDATE_SUCCESS = "USER_UPDATE_SUCCESS",
USER_UPDATED_SUCCESS = "USER_UPDATED_SUCCESS",
USER_DELETE_SUCCESS = "USER_DELETE_SUCCESS" USER_DELETE_SUCCESS = "USER_DELETE_SUCCESS"
} }
export enum MESSAGEKEY { export enum MESSAGEKEY {

@ -14,5 +14,6 @@ export enum URIKey {
FIRST_LOGIN_URI = "FIRST_LOGIN_URI", FIRST_LOGIN_URI = "FIRST_LOGIN_URI",
THIRD_PARTY_REGISTER_URI = "THIRD_PARTY_REGISTER_URI", THIRD_PARTY_REGISTER_URI = "THIRD_PARTY_REGISTER_URI",
TRANSACTION_LOGS = "TRANSACTION_LOGS", 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", "URI": "/user/createUser",
"UUID": "CREATE_USER" "UUID": "CREATE_USER"
}, },
{
"Id": "ENTITY_UPDATE_USER",
"URI": "/user/updateUser",
"UUID": "UPDATE_USER"
},
{ {
"Id": "ENTITY_GET_ALL_USERS", "Id": "ENTITY_GET_ALL_USERS",
"URI": "/user/getAllUsers", "URI": "/user/getAllUsers",

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

Loading…
Cancel
Save