Add reset password modal to user management #50

Merged
naeem.ullah merged 1 commits from mazdak/UX-2303 into dev-pending-20-01-2026 1 week ago

@ -13,7 +13,7 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms';
styleUrl: './password-hide-show.component.scss' styleUrl: './password-hide-show.component.scss'
}) })
export class PasswordHideShowComponent { export class PasswordHideShowComponent {
@Output() onEyeClick = new EventEmitter(); @Output() onEyeClick = new EventEmitter();
@Input() showPassword : boolean = false; @Input() showPassword : boolean = false;
inputType : String = ''; inputType : String = '';
constructor() { } constructor() { }
@ -26,4 +26,7 @@ export class PasswordHideShowComponent {
this.showPassword = !this.showPassword; this.showPassword = !this.showPassword;
this.onEyeClick.emit(); this.onEyeClick.emit();
} }
reset() {
this.showPassword = true;
}
} }

@ -0,0 +1,78 @@
<div class="modal fade" id="resetPasswordModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
{{ 'resetPassword' | translate }}
</h5>
<button type="button" class="btn-close" (click)="closeModal()"></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" />
</div>
<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" appNoWhitespaces />
<app-password-hide-show #newPasswordPsh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="togglePasswordType()">
</app-password-hide-show>
</div>
<div class="text-danger" *ngIf="resetPasswordForm.get('newPassword')?.touched ">
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div *ngIf="resetPasswordForm.get('newPassword')?.hasError('pattern')">
{{ 'passwordPattern' | translate }}
</div>
</div>
</div>
<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" appNoWhitespaces />
<app-password-hide-show #confirmPasswordPsh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="toggleConfirmPasswordType()">
</app-password-hide-show>
</div>
<div class="text-danger" *ngIf="resetPasswordForm.get('confirmPassword')?.touched ">
<div *ngIf="resetPasswordForm.get('confirmPassword')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div *ngIf="resetPasswordForm.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 }}
</button>
<button type="button" class="btn btn-primary btn-sm px-4" [disabled]="resetPasswordForm.invalid" (click)="submit()">
{{ 'save' | translate }}
</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ResetPasswordModalComponent } from './reset-password-modal.component';
describe('ResetPasswordModalComponent', () => {
let component: ResetPasswordModalComponent;
let fixture: ComponentFixture<ResetPasswordModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ResetPasswordModalComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ResetPasswordModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,112 @@
import { Component, Input, OnInit, SimpleChanges, 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 { 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;
resetPasswordForm!: FormGroup;
@ViewChild('newPasswordPsh') passwordHideShow?:PasswordHideShowComponent
@ViewChild('confirmPasswordPsh') confirmPasswordHideShow?:PasswordHideShowComponent
constructor(
private fb: FormBuilder,
private httpURIService: HttpURIService,
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.resetPasswordForm = this.fb.group({
userId: [''],
newPassword: ['', [
Validators.required,
Validators.minLength(8),
Validators.maxLength(20),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/)
]],
confirmPassword: ['', Validators.required]
}, { validators: this.passwordMatchValidator });
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['userId'] && this.resetPasswordForm) {
this.resetPasswordForm.reset({
userId: this.userId,
newPassword: '',
confirmPassword: ''
});
this.newPasswordType = 'password';
this.confirmPasswordType = 'password';
this.passwordHideShow?.reset();
this.confirmPasswordHideShow?.reset();
}
}
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword')?.value;
const confirmPassword = group.get('confirmPassword')?.value;
return newPassword === confirmPassword ? null : { passwordMismatch: true };
}
submit() {
if (this.resetPasswordForm.invalid) return;
const payload = {
userId: this.userId,
newPassword: this.resetPasswordForm.get('newPassword')?.value,
porOrgaCode: this.storageService.getItem('POR_ORGACODE')
};
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
.subscribe({
next: (res) => {
if (!(res instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.RESET_PASSWORD_SUCCESS, []);
this.closeModal();
}
}
});
}
closeModal() {
this.resetPasswordForm.reset();
this.newPasswordType = 'password';
this.confirmPasswordType = 'password';
this.passwordHideShow?.reset();
this.confirmPasswordHideShow?.reset();
const modal = document.getElementById('resetPasswordModal');
modal?.classList.remove('show');
modal?.setAttribute('aria-hidden', 'true');
modal!.style.display = 'none';
document.body.classList.remove('modal-open');
document.querySelector('.modal-backdrop')?.remove();
}
}

@ -30,7 +30,7 @@ export class ResetPasswordComponent implements OnInit{
ngOnInit(): void { ngOnInit(): void {
const userIdValue = this.storageService.getItem('USER_ID') const userIdValue = this.storageService.getItem('USER_ID')
this.resetPasswordForm = this.fb.group({ this.resetPasswordForm = this.fb.group({
userId: [{value: userIdValue || '', disabled: true}], userId: [userIdValue],
newPassword: ['', [ newPassword: ['', [
Validators.required, Validators.required,
Validators.minLength(8), Validators.minLength(8),

@ -253,6 +253,9 @@
<button class="btn btn-info btn-sm" title="View" (click)="onView(item.userId)"> <button class="btn btn-info btn-sm" title="View" (click)="onView(item.userId)">
<i class="mdi mdi-eye-outline"></i> <i class="mdi mdi-eye-outline"></i>
</button> </button>
<button class="btn btn-warning btn-sm" 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" <button *ngIf="buttonPermissions?.delete" class="btn btn-danger btn-sm" title="Delete"
(click)="confirmDelete(item.userId)"> (click)="confirmDelete(item.userId)">
<i class="fas fa-trash-alt"></i> <i class="fas fa-trash-alt"></i>
@ -264,6 +267,8 @@
</tbody> </tbody>
</table> </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="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0"> <div class="form-group mb-0">
<ng-select class="form-select-sm" <ng-select class="form-select-sm"

@ -14,12 +14,13 @@ 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 } from '../../utils/enums';
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], 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'
}) })
@ -44,9 +45,9 @@ export class SetupUserComponent implements OnInit {
isLoading: boolean = false; isLoading: boolean = false;
setupUserList: SetupUser[] = []; setupUserList: SetupUser[] = [];
roleOptions = [ roleOptions = [
{ label: 'SuperAdmin', value: 'SUPERADMIN' },
{ label: 'Admin', value: 'ADMIN' }, { label: 'Admin', value: 'ADMIN' },
{ label: 'User', value: 'USER' } { label: 'User', value: 'USER' },
{label: 'Super Admin', value:"SUPERADMIN"}
]; ];
@ -220,4 +221,17 @@ ngOnInit(): void {
}) })
} }
openResetPasswordModal(userId: string) {
this.selectedUserId = userId;
const modal = document.getElementById('resetPasswordModal');
modal?.classList.add('show');
modal!.style.display = 'block';
document.body.classList.add('modal-open');
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
document.body.appendChild(backdrop);
}
} }

Loading…
Cancel
Save