Enhance reset password flow and add permission control

Replaced user ID input with a dropdown in the reset password form and loaded users dynamically. Added 'resetPasswordButton' permission to side menu and conditionally displayed the reset password button
mazdak/UX-2338
Mazdak Gibran 1 week ago
parent 50f1005fd5
commit 79d258b0cf

@ -12,7 +12,7 @@ import { provideAnimations } from '@angular/platform-browser/animations';
import { LoadingInterceptor } from './shared/interceptors/loading.interceptor';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
return new TranslateHttpLoader(http, '/assets/i18n/', '.json');
}
export const appConfig: ApplicationConfig = {

@ -156,19 +156,6 @@ constructor(
} else {
this.changeRegularPassword();
}
const payload = this.getFormPayload();
this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload)
.subscribe({
next: (response) => {
if (!(response instanceof HttpErrorResponse)) {
this.i18nService.success(SuccessMessages.CHANGE_PASSWORD_SUCCESS, []);
this.router.navigate(['/dashboard']);
}
}
});
}
changeFirstTimePassword() {
@ -230,13 +217,6 @@ constructor(
this.i18nService.success(SuccessMessages.CHANGE_PASSWORD_SUCCESS, []);
this.router.navigate(['/home/dashboard']);
}
},
error: (error) => {
console.error('Password change failed:', error);
if (error.status === 401) {
this.authService.logout();
}
}
});
}

@ -21,7 +21,7 @@
<label class="form-label">{{ 'enterNewPassword' | translate }}</label>
<div class="password-wrapper">
<input class="form-control" formControlName="newPassword" placeholder="{{'enterNewPassword' | translate}}" [type]="newPasswordType" appNoWhitespaces />
<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()">
@ -43,7 +43,7 @@
<label class="form-label">{{ 'confirmPassword' | translate }}</label>
<div class="password-wrapper">
<input class="form-control" formControlName="confirmPassword" placeholder="{{'confirmPassword' | translate}}" [type]="confirmPasswordType" appNoWhitespaces />
<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()">

@ -30,20 +30,15 @@
{{ '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"
placeholder="{{ 'userID' | translate }}"
appNoWhitespaces
/>
</div>
<ng-select formControlName="userId" [items]="allUsersDropdown" bindLabel="userFullname" bindValue="userId"
placeholder="{{'SelectUser' | translate}}" [clearable]="false">
</ng-select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">

@ -8,12 +8,15 @@ import { HttpURIService } from '../../app.http.uri.service';
import { StorageService } from '../../shared/services/storage.service';
import { I18NService } from '../../services/i18n.service';
import { ErrorMessages, SuccessMessages } from '../../utils/enums';
import { HttpErrorResponse } from '@angular/common/http';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';
import { NgSelectComponent } from "@ng-select/ng-select";
import { SetupUser } from '../../models/user';
import { error } from 'console';
@Component({
selector: 'app-reset-password',
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule],
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule, NgSelectComponent],
templateUrl: './reset-password.component.html',
styleUrl: './reset-password.component.scss'
})
@ -21,16 +24,20 @@ export class ResetPasswordComponent implements OnInit{
resetPasswordForm!: FormGroup
passwordType1: string = 'password';
passwordType2: string = 'password';
isLoading: boolean = false;
allUsersDropdown: SetupUser[] = [];
selectedUserId: string | null = null;
@ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent;
@ViewChild('psh2') passwordHideShow2?: PasswordHideShowComponent;
constructor(private fb: FormBuilder, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService, private router: Router){}
constructor(private fb: FormBuilder, private httpService: HttpURIService, private httpURIService: HttpURIService, private storageService: StorageService, private i18nService: I18NService, private router: Router){}
ngOnInit(): void {
const userIdValue = this.storageService.getItem('USER_ID')
const userIdValue = this.storageService.getItem('USER_ID') || null;
this.resetPasswordForm = this.fb.group({
userId: [userIdValue],
userId: [null],
newPassword: ['', [
Validators.required,
Validators.minLength(8),
@ -55,7 +62,7 @@ export class ResetPasswordComponent implements OnInit{
this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity();
});
this.loadUsersForDropdown();
}
togglePasswordType1() {
this.passwordType1 = this.passwordHideShow1?.showPassword ? 'password' : 'text';
@ -83,12 +90,14 @@ export class ResetPasswordComponent implements OnInit{
onSubmit() {
if (this.resetPasswordForm.invalid) return;
const selectedId = this.resetPasswordForm.get('userId')?.value;
console.log("userid.....", selectedId)
const payload = {
userId: this.resetPasswordForm.get('userId')?.value,
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) => {
@ -100,6 +109,26 @@ export class ResetPasswordComponent implements OnInit{
});
}
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
},
error:(err)=>{
console.error('Error fetching users:', err);
this.allUsersDropdown = [];
this.isLoading = false;
}
})
}
}

@ -253,7 +253,7 @@
<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" title="Reset Password" (click)="openResetPasswordModal(item.userId)">
<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"

@ -23,6 +23,12 @@
"route": "",
"checked": false,
"expanded": false
},
{
"name": "resetPasswordButton",
"route": "",
"checked": false,
"expanded": false
}
]
},

@ -268,5 +268,6 @@
"USER_DELETE_SUCCESS": "تم حذف المستخدم",
"SAVED_SUCCESSFULLY": "تم الحفظ بنجاح",
"activityLogs": "سجلات النشاط",
"activityLogDetails": "تفاصيل سجل النشاط"
"activityLogDetails": "تفاصيل سجل النشاط",
"resetPasswordButton": "إعادة تعيين كلمة المرور"
}

@ -273,5 +273,6 @@
"activityLogs": "Activity Logs",
"activityLogDetails": "Activity Log Details",
"transactionCode": "Transaction Code",
"transactionUri": "Transaction URI"
"transactionUri": "Transaction URI",
"resetPasswordButton": "Reset Password"
}
Loading…
Cancel
Save