Merge pull request 'creating payload for reset and change password' (#22) from mazdak/UX-1570 into dev-pending-09-12-2025

Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/22
dev-pending-09-12-2025
Naeem Ullah 1 month ago
commit 822f4860d9

@ -88,7 +88,7 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<form> <form [formGroup]="changePasswordForm">
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<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">
@ -100,14 +100,16 @@
<div class="d-flex flex-row align-items-stretch"> <div class="d-flex flex-row align-items-stretch">
<input type="text" id="oldPassword" <input type="text" id="oldPassword"
class="form-control" class="form-control"
formControlName="oldPassword"
type="{{passwordType}}" type="{{passwordType}}"
placeholder="{{ 'oldPassword' | translate }}" appNoWhitespaces placeholder="{{ 'oldPassword' | translate }}" appNoWhitespaces
/> />
<app-password-hide-show #psh class="password-eye align-items-stretch" [showPassword]="true" (onEyeClick)="togglePasswordType()"></app-password-hide-show> <app-password-hide-show #psh class="password-eye align-items-stretch" [showPassword]="true" (onEyeClick)="togglePasswordType()"></app-password-hide-show>
</div> </div>
<!-- <div class="text-danger"> <div class="text-danger" *ngIf="changePasswordForm.get('oldPassword')?.touched &&
{{ 'requiredField' | translate }} changePasswordForm.get('oldPassword')?.invalid">
</div> --> {{ 'fieldRequired' | translate }}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -122,6 +124,7 @@
<input id="enterNewPassword" <input id="enterNewPassword"
class="form-control" class="form-control"
formControlName="enterNewPassword"
type="{{passwordType1}}" type="{{passwordType1}}"
maxlength="500" maxlength="500"
placeholder="{{ 'enterNewPassword' | translate }}" appNoWhitespaces placeholder="{{ 'enterNewPassword' | translate }}" appNoWhitespaces
@ -130,6 +133,9 @@
</div> </div>
<div class="text-danger" *ngIf="newPasswordError$">
{{ newPasswordError$ | translate }}
</div>
</div> </div>
</div> </div>
@ -145,6 +151,7 @@
<input id="confirmPassword" <input id="confirmPassword"
class="form-control" class="form-control"
formControlName="confirmPassword"
type="{{passwordType2}}" type="{{passwordType2}}"
maxlength="500" maxlength="500"
placeholder="{{ 'confirmPassword' | translate }}" appNoWhitespaces placeholder="{{ 'confirmPassword' | translate }}" appNoWhitespaces
@ -153,6 +160,9 @@
</div> </div>
<div class="text-danger" *ngIf="confirmPasswordError$">
{{ confirmPasswordError$ | translate }}
</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -163,7 +173,8 @@
<div class="col-md-6 ms-auto text-end"> <div class="col-md-6 ms-auto text-end">
<button <button
class="btn btn-primary waves-effect waves-light" class="btn btn-primary waves-effect waves-light"
(click)="onSubmit()"
[disabled]="changePasswordForm.invalid"
>{{'save' | translate}}</button> >{{'save' | translate}}</button>

@ -1,10 +1,10 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AbstractControl, FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component'; import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
import { StorageService } from '../../shared/services/storage.service'; import { HttpURIService } from '../../app.http.uri.service';
import { Router } from '@angular/router'; import { URIKey } from '../../utils/uri-enums';
@Component({ @Component({
selector: 'app-change-password', selector: 'app-change-password',
@ -13,15 +13,12 @@ import { Router } from '@angular/router';
styleUrl: './change-password.component.scss' styleUrl: './change-password.component.scss'
}) })
export class ChangePasswordComponent{ export class ChangePasswordComponent implements OnInit{
isFirstLogin = false; isFirstLogin = false;
loginForm!: FormGroup; changePasswordForm!: FormGroup;
currentLanguage = new FormControl(); currentLanguage = new FormControl();
httpService: any; httpService: any;
constructor(private storageService: StorageService, private router: Router){}
onLangChange() {
throw new Error('Method not implemented.');
}
passwordType: string = 'password'; passwordType: string = 'password';
passwordType1: string = 'password'; passwordType1: string = 'password';
passwordType2: string = 'password'; passwordType2: string = 'password';
@ -29,6 +26,7 @@ passwordType2: string = 'password';
@ViewChild('psh') passwordHideShow?: PasswordHideShowComponent; @ViewChild('psh') passwordHideShow?: PasswordHideShowComponent;
@ViewChild('psh1') passwordHideShow1 ?: PasswordHideShowComponent; @ViewChild('psh1') passwordHideShow1 ?: PasswordHideShowComponent;
@ViewChild('psh2') passwordHideShow2 ?: PasswordHideShowComponent; @ViewChild('psh2') passwordHideShow2 ?: PasswordHideShowComponent;
constructor(private fb: FormBuilder, private httpURIService: HttpURIService){}
togglePasswordType() { togglePasswordType() {
this.passwordType = this.passwordHideShow?.showPassword ? 'password' : 'text'; this.passwordType = this.passwordHideShow?.showPassword ? 'password' : 'text';
@ -40,31 +38,53 @@ passwordType2: string = 'password';
this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text'; this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text';
} }
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('enterNewPassword')?.value;
const confirmPassword = group.get('confirmPassword')?.value;
return newPassword === confirmPassword ? null : { passwordMismatch: true };
}
ngOnInit(): void { ngOnInit(): void {
// Call the method to check if first-time login this.changePasswordForm = this.fb.group({
this.checkIfFirstTimeChangePasswordOrNot(); oldPassword: ['', Validators.required],
enterNewPassword: ['',[ Validators.required, Validators.minLength(6)]],
confirmPassword: ['', [Validators.required, Validators.minLength(6)]]
},
{
validators: this.passwordMatchValidator
}
)
} }
get newPasswordError$() {
const control = this.changePasswordForm.get('newPassword');
if (!control || !control.touched) return null;
checkIfFirstTimeChangePasswordOrNot() { if (control.hasError('required')) return 'fieldRequired';
const fromMenu = history.state?.['fromMenu']; if (control.hasError('minlength')) return 'passwordTooShort';
return null;
}
if (fromMenu) { get confirmPasswordError$() {
this.isFirstLogin = false; const control = this.changePasswordForm.get('confirmPassword');
} else { if (!control || !control.touched) return null;
try {
const currentUser: any = JSON.parse(this.storageService.getItem('user') || '{}');
// Check if user exists and has isFirstLogin flag if (control.hasError('required')) return 'fieldRequired';
if (currentUser?.user?.isFirstLogin) { if (control.hasError('minlength')) return 'passwordTooShort';
this.isFirstLogin = true; if (this.changePasswordForm.hasError('passwordMismatch')) return 'passwordsDoNotMatch';
} else {
this.isFirstLogin = false; return null;
} }
} catch (error) {
console.error('Error parsing user data:', error); onSubmit(){
this.isFirstLogin = false; if(this.changePasswordForm.invalid){return}
}
const payload = {
oldPassword: this.changePasswordForm.value.oldPassword,
newPassword: this.changePasswordForm.value.enterNewPassword
} }
this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload)
.subscribe();
} }
} }

@ -22,27 +22,30 @@
{{'resetPassword' | translate}} {{'resetPassword' | translate}}
</div> </div>
<div class="card-body"> <div class="card-body">
<form > <form [formGroup]="resetPasswordForm">
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<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="userID" class="text-nowrap"> <label for="userID" class="text-nowrap" >
{{ 'userID' | translate }}<span {{ 'userID' | translate }}<span
class="mandatory">*</span> class="mandatory">*</span>
</label> </label>
<div class="password-wrapper position-relative w-100"> <div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch"> <div class="d-flex flex-row align-items-stretch">
<input type="text" id="userID" <input
type="text"
id="userID"
class="form-control" class="form-control"
formControlName="userId"
placeholder="{{ 'userID' | translate }}" appNoWhitespaces placeholder="{{ 'userID' | translate }}"
appNoWhitespaces
/> />
</div> </div>
<!-- <div class="text-danger"> <div class="text-danger" *ngIf="resetPasswordForm.get('userId')?.touched &&
{{ 'requiredField' | translate }} resetPasswordForm.get('userId')?.invalid">
</div> --> {{ 'fieldRequired' | translate }}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -53,9 +56,10 @@
{{ 'enterNewPassword' | translate }}<span {{ 'enterNewPassword' | translate }}<span
class="mandatory">*</span> class="mandatory">*</span>
</label> </label>
<div class="password-wrapper position-relative w-100"> <div class="w-100">
<div class="password-wrapper">
<input id="enterNewPassword" <input id="enterNewPassword"
formControlName="newPassword"
class="form-control" class="form-control"
autocomplete="new-password" autocomplete="new-password"
type="{{passwordType1}}" type="{{passwordType1}}"
@ -66,9 +70,12 @@
</div> </div>
<div class="text-danger mt-1" *ngIf="newPasswordError">
{{ newPasswordError | translate }}
</div>
</div> </div>
</div> </div>
</div>
</div> </div>
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<div class="col-md-6"> <div class="col-md-6">
@ -77,22 +84,20 @@
{{ 'confirmPassword' | translate }}<span {{ 'confirmPassword' | translate }}<span
class="mandatory">*</span> class="mandatory">*</span>
</label> </label>
<div class="password-wrapper position-relative w-100"> <div class="w-100">
<div class="password-wrapper">
<input id="confirmPassword" <input id="confirmPassword"
class="form-control" class="form-control"
type="{{passwordType2}}" type="{{passwordType2}}"
formControlName="confirmPassword"
placeholder="{{ 'confirmPassword' | translate }}" appNoWhitespaces/> placeholder="{{ 'confirmPassword' | translate }}" appNoWhitespaces/>
<app-password-hide-show class="password-eye align-items-stretch" #psh2 [showPassword]="true" (onEyeClick)="togglePasswordType2()"></app-password-hide-show> <app-password-hide-show class="password-eye align-items-stretch" #psh2 [showPassword]="true" (onEyeClick)="togglePasswordType2()"></app-password-hide-show>
<!-- <div class="text-danger">
<div>
{{ 'requiredField' | translate }}
</div>
</div>
<div>
{{ 'expiryBeforeRenewal' | translate }}
</div> -->
</div> </div>
<div class="text-danger" *ngIf="confirmPasswordError">
{{ confirmPasswordError | translate }}
</div>
</div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -103,14 +108,12 @@
<div class="col-md-6 ms-auto text-end"> <div class="col-md-6 ms-auto text-end">
<button <button
class="btn btn-primary waves-effect waves-light" class="btn btn-primary waves-effect waves-light"
(click)="onSubmit()"
[disabled]="resetPasswordForm.invalid"
>{{'save' | translate}}</button> >{{'save' | translate}}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>

@ -1,26 +1,88 @@
import { Component, ViewChild } from '@angular/core'; import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
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';
import { URIKey } from '../../utils/uri-enums';
import { HttpURIService } from '../../app.http.uri.service';
@Component({ @Component({
selector: 'app-reset-password', selector: 'app-reset-password',
imports: [TranslateModule, PasswordHideShowComponent], imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule],
templateUrl: './reset-password.component.html', templateUrl: './reset-password.component.html',
styleUrl: './reset-password.component.scss' styleUrl: './reset-password.component.scss'
}) })
export class ResetPasswordComponent { export class ResetPasswordComponent implements OnInit{
passwordType1: string = 'password'; resetPasswordForm!: FormGroup
passwordType2: string = 'password'; passwordType1: string = 'password';
passwordType2: string = 'password';
@ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent;
@ViewChild('psh2') passwordHideShow2?: PasswordHideShowComponent;
@ViewChild('psh1') passwordHideShow1 ?: PasswordHideShowComponent; constructor(private fb: FormBuilder, private httpURIService: HttpURIService){}
@ViewChild('psh2') passwordHideShow2 ?: PasswordHideShowComponent;
ngOnInit(): void {
this.resetPasswordForm = this.fb.group({
userId: ['', Validators.required],
newPassword: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', [Validators.required, Validators.minLength(6)]]
},
{
validators: this.passwordMatchValidator
}
);
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';
} }
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('newPassword')?.value;
const confirmPassword = group.get('confirmPassword')?.value;
return newPassword === confirmPassword ? null : { passwordMismatch: true };
}
get newPasswordError() {
const control = this.resetPasswordForm.get('newPassword');
if (!control || !control.touched) return null;
if (control.hasError('required')) return 'fieldRequired';
if (control.hasError('minlength')) return 'passwordTooShort';
return null;
}
get confirmPasswordError() {
const control = this.resetPasswordForm.get('confirmPassword');
if (!control || !control.touched) return null;
if (control.hasError('required')) return 'fieldRequired';
if (control.hasError('minlength')) return 'passwordTooShort';
if (this.resetPasswordForm.hasError('passwordMismatch')) return 'passwordsDoNotMatch';
return null;
}
onSubmit() {
if (this.resetPasswordForm.invalid) return;
const payload = {
userId: this.resetPasswordForm.value.userId,
newPassword: this.resetPasswordForm.value.newPassword
};
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
.subscribe();
}
} }

@ -8,5 +8,7 @@ export enum URIKey {
DELETE_USER = 'DELETE_USER', DELETE_USER = 'DELETE_USER',
USER_SAVE_PERMISSION = "USER_SAVE_PERMISSION", USER_SAVE_PERMISSION = "USER_SAVE_PERMISSION",
USER_GET_PERMISSIONS = "USER_GET_PERMISSIONS", USER_GET_PERMISSIONS = "USER_GET_PERMISSIONS",
GET_ALL_USER_URI = "GET_ALL_USER_URI" GET_ALL_USER_URI = "GET_ALL_USER_URI",
RESET_PASSWORD_URI = "RESET_PASSWORD_URI",
CHANGE_PASSWORD_URI = "CHANGE_PASSWORD_URI"
} }

@ -48,9 +48,14 @@
"UUID": "USER_GET_PERMISSIONS" "UUID": "USER_GET_PERMISSIONS"
}, },
{ {
"Id" : "ENTITY_USER_SAVE_PERMISSION", "Id" : "ENTITY_RESET_PASSWORD_URI",
"URI": "/user/updatePermissions", "URI": "/user/reset-password",
"UUID": "USER_SAVE_PERMISSION" "UUID": "RESET_PASSWORD_URI"
},
{
"Id" : "ENTITY_CHANGE_PASSWORD_URI",
"URI": "/user/change-password",
"UUID": "CHANGE_PASSWORD_URI"
} }
] ]
} }

@ -7,6 +7,8 @@
"defaultPassword": "كلمة المرور الافتراضية", "defaultPassword": "كلمة المرور الافتراضية",
"rememberMe":"تذكرنى", "rememberMe":"تذكرنى",
"forgotPassword":"هل نسيت كلمة السر؟", "forgotPassword":"هل نسيت كلمة السر؟",
"passwordTooShort": "كلمة المرور قصيرة جدًا.",
"passwordsDoNotMatch": "كلمتا المرور غير متطابقتين.",
"login":"تسجيل الدخول", "login":"تسجيل الدخول",
"dashboardTitle":"لوحة القيادة", "dashboardTitle":"لوحة القيادة",
"passwordChangeRequired": "تغيير كلمة المرور مطلوب", "passwordChangeRequired": "تغيير كلمة المرور مطلوب",

@ -136,6 +136,8 @@
"passwordPatternNotMatched":"Password Pattern Not Matched", "passwordPatternNotMatched":"Password Pattern Not Matched",
"successDeleted":"Successfully Deleted", "successDeleted":"Successfully Deleted",
"passwordNotSame":"Password Cannot be same as Old Password", "passwordNotSame":"Password Cannot be same as Old Password",
"passwordTooShort": "Password is too short.",
"passwordsDoNotMatch": "Passwords do not match.",
"SuccessSave":"Successfully Saved", "SuccessSave":"Successfully Saved",
"SuccessFind":"Successfully Find", "SuccessFind":"Successfully Find",
"customerAlreadyUnblocked": "Customer Already UnBlocked", "customerAlreadyUnblocked": "Customer Already UnBlocked",

Loading…
Cancel
Save