diff --git a/angular.json b/angular.json index 54d6cf3..6ff7982 100644 --- a/angular.json +++ b/angular.json @@ -38,7 +38,7 @@ "configurations": { "production": { "budgets": [ - { "type": "initial", "maximumWarning": "500kB", "maximumError": "1MB" }, + { "type": "initial", "maximumWarning": "1mb", "maximumError": "5MB" }, { "type": "anyComponentStyle", "maximumWarning": "4kB", "maximumError": "8kB" } ], "outputHashing": "all", diff --git a/package.json b/package.json index ae6d01f..a5d4d50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "aconnect-ux", "version": "0.0.0", + "releaseDate": "2026-01-16", "scripts": { "ng": "ng", "start": "ng serve --configuration=development", diff --git a/src/app/user-management/change-password/change-password.component.html b/src/app/user-management/change-password/change-password.component.html index 359bd5c..52b2965 100644 --- a/src/app/user-management/change-password/change-password.component.html +++ b/src/app/user-management/change-password/change-password.component.html @@ -16,7 +16,19 @@
- +
+
+ + +
+ +
+ {{ 'fieldRequired' | translate }} +
+
-
- {{ 'fieldRequired' | translate }} +
+
+ {{ 'fieldRequired' | translate }} +
+ +
+ {{ 'passwordPattern' | translate }} +
@@ -140,12 +157,21 @@ (onEyeClick)="togglePasswordType1()"> - -
- {{ changePasswordForm.get('newPassword')?.hasError('required') ? ('fieldRequired' | translate) : - changePasswordForm.get('newPassword')?.hasError('minlength') ? ('passwordTooShort' | translate) : '' }} -
+
+
+ {{ 'fieldRequired' | translate }} +
+ +
+ {{ 'passwordPattern' | translate }} +
+
+ Old password and new password cannot be the same +
+ + +
+ @@ -166,11 +192,18 @@ (onEyeClick)="togglePasswordType2()"> -
- {{ changePasswordForm.get('confirmPassword')?.hasError('required') ? ('fieldRequired' | translate) : - changePasswordForm.get('confirmPassword')?.hasError('minlength') ? ('passwordTooShort' | translate) : - changePasswordForm.hasError('passwordMismatch') ? ('passwordsDoNotMatch' | translate) : '' }} +
+
+ {{ 'fieldRequired' | translate }} +
+ +
+ {{ 'passwordPattern' | translate }} +
+ +
+ {{ 'passwordsDoNotMatch' | translate }} +
diff --git a/src/app/user-management/change-password/change-password.component.ts b/src/app/user-management/change-password/change-password.component.ts index 3904489..17d902f 100644 --- a/src/app/user-management/change-password/change-password.component.ts +++ b/src/app/user-management/change-password/change-password.component.ts @@ -43,34 +43,73 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text'; } + ngOnInit(): void { + this.checkIfFirstTimeChangePasswordOrNot(); + + if (this.isFirstLogin) { + this.firstTimeLoginForm = this.fb.group({ + oldPassword: ['', Validators.required], + newPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ]], + confirmPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ]] + }, { validators: this.passwordMatchValidator }); + } + + else { + this.changePasswordForm = this.fb.group({ + oldPassword: ['', Validators.required], + newPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ] + ], + confirmPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ] + ] + }, { validators: [ + this.passwordMatchValidator, + this.oldAndNewPasswordNotSame, + ] + }, + ); + + } +} + passwordMatchValidator(group: AbstractControl): ValidationErrors | null { const newPassword = group.get('newPassword')?.value; const confirmPassword = group.get('confirmPassword')?.value; return newPassword === confirmPassword ? null : { passwordMismatch: true }; } + + oldAndNewPasswordNotSame(group: AbstractControl): ValidationErrors | null { + const oldPassword = group.get('oldPassword')?.value; + const newPassword = group.get('newPassword')?.value; - initForm(): void { - if (this.firstLogin) { - this.changePasswordForm = undefined!; - this.firstTimeLoginForm = this.fb.group({ - newPassword: ['', [Validators.required, Validators.minLength(6)]], - confirmPassword: ['', [Validators.required, Validators.minLength(6)]] - }, { validators: this.passwordMatchValidator }); - } else { - this.firstTimeLoginForm = undefined!; - this.changePasswordForm = this.fb.group({ - oldPassword: ['', Validators.required], - newPassword: ['', [Validators.required, Validators.minLength(6)]], - confirmPassword: ['', [Validators.required, Validators.minLength(6)]] - }, { validators: this.passwordMatchValidator }); - - } + if (!oldPassword || !newPassword) { + return null; } - ngOnInit(): void { - this.checkIfFirstTimeChangePasswordOrNot(); - this.initForm(); - } + return oldPassword === newPassword + ? { oldAndNewPasswordSame: true } + : null; +} + checkIfFirstTimeChangePasswordOrNot(): void { try { @@ -85,17 +124,6 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri } } - initChangePasswordForm(): void { - this.changePasswordForm = this.fb.group( - { - oldPassword: ['', Validators.required], - enterNewPassword: ['', [Validators.required, Validators.minLength(6)]], - confirmPassword: ['', [Validators.required, Validators.minLength(6)]], - }, - { validators: this.passwordMatchValidator } - ); - } - getFormPayload() { const form = this.firstLogin ? this.firstTimeLoginForm : this.changePasswordForm; diff --git a/src/app/user-management/reset-password/reset-password.component.html b/src/app/user-management/reset-password/reset-password.component.html index 0c3ccaf..b6cabee 100644 --- a/src/app/user-management/reset-password/reset-password.component.html +++ b/src/app/user-management/reset-password/reset-password.component.html @@ -39,13 +39,9 @@ formControlName="userId" placeholder="{{ 'userID' | translate }}" appNoWhitespaces - /> - + /> -
- {{ 'fieldRequired' | translate }} -
+ @@ -57,23 +53,26 @@ class="mandatory">*
-
- - - - -
-
- {{ newPasswordError | translate }} +
+ + + + +
+
+
+ {{ 'fieldRequired' | translate }} +
+
+ {{ 'passwordPattern' | translate }} +
+
-
@@ -94,8 +93,20 @@ -
- {{ confirmPasswordError | translate }} +
+
+ {{ 'fieldRequired' | translate }} +
+ +
+ {{ 'passwordPattern' | translate }} +
+ +
+ {{ 'passwordsDoNotMatch' | translate }} +
diff --git a/src/app/user-management/reset-password/reset-password.component.ts b/src/app/user-management/reset-password/reset-password.component.ts index c92d9f8..767a4d3 100644 --- a/src/app/user-management/reset-password/reset-password.component.ts +++ b/src/app/user-management/reset-password/reset-password.component.ts @@ -29,15 +29,27 @@ export class ResetPasswordComponent implements OnInit{ ngOnInit(): void { const userIdValue = this.storageService.getItem('USER_ID') this.resetPasswordForm = this.fb.group({ - - userId: [userIdValue || '', Validators.required], - newPassword: ['', [Validators.required, Validators.minLength(6)]], - confirmPassword: ['', [Validators.required, Validators.minLength(6)]] + userId: [{value: userIdValue || '', disabled: true}], + newPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ] + ], + confirmPassword: ['', [ + Validators.required, + Validators.minLength(8), + Validators.maxLength(20), + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + ] + ] }, { validators: this.passwordMatchValidator } ); + this.resetPasswordForm.get('newPassword')?.valueChanges.subscribe(()=>{ this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity(); }); @@ -50,39 +62,30 @@ export class ResetPasswordComponent implements OnInit{ togglePasswordType2() { 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; + const newPassword = group.get('newPassword'); + const confirmPassword = group.get('confirmPassword'); + if (!newPassword || !confirmPassword) return null; + if (confirmPassword.errors && !confirmPassword.errors['passwordMismatch']) { + return null; + } + if (newPassword.value !== confirmPassword.value) { + confirmPassword.setErrors({ passwordMismatch: true }); + return { passwordMismatch: true }; + } else { + confirmPassword.setErrors(null); + 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, + userId: this.resetPasswordForm.get('userId')?.value, + newPassword: this.resetPasswordForm.get('newPassword')?.value, porOrgaCode: this.storageService.getItem('POR_ORGACODE') }; this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload) @@ -93,7 +96,7 @@ export class ResetPasswordComponent implements OnInit{ } } }); - } + } } diff --git a/src/app/user-management/setup-user/setup-user.component.html b/src/app/user-management/setup-user/setup-user.component.html index 5d4aa2e..7b8dc27 100644 --- a/src/app/user-management/setup-user/setup-user.component.html +++ b/src/app/user-management/setup-user/setup-user.component.html @@ -41,8 +41,28 @@
- {{ 'fieldRequired' | translate }} +
+ {{ 'fieldRequired' | translate }} +
+ +
+
+ {{'userIdMinLength' | translate }}
+ +
+ {{'emptySpaceRestriction' | translate}} +
+ @@ -62,11 +82,28 @@ maxlength="500" placeholder="{{ 'userName' | translate }}" appNoWhitespaces rows="3" /> - - +
- {{ 'fieldRequired' | translate }} +
+ {{ 'fieldRequired' | translate }} +
+
+
+ {{'nameMinLength' | translate }} +
+
+ {{'emptySpaceRestriction' | translate}} +
@@ -113,7 +150,15 @@ placeholder="{{ 'passwordPlaceHolder' | translate }}" appNoWhitespaces/>
- {{ 'fieldRequired' | translate }} +
+ {{ 'fieldRequired' | translate }} +
+
+ {{ 'passwordPattern' | translate }} +
@@ -193,9 +238,10 @@ - - - + + + + @@ -204,6 +250,7 @@ +
{{'userID' | translate}}{{'Name' | translate}}{{'action' | translate}}{{'userID' | translate}}{{'Name' | translate}}{{'Role' | translate}}{{'action' | translate}}
{{ item.userId }} {{ item.userFullname }}{{item.role}} diff --git a/src/app/user-management/setup-user/setup-user.component.ts b/src/app/user-management/setup-user/setup-user.component.ts index 48202df..28bf7fd 100644 --- a/src/app/user-management/setup-user/setup-user.component.ts +++ b/src/app/user-management/setup-user/setup-user.component.ts @@ -10,8 +10,10 @@ import { ButtonManagementService } from '../../services/button-management.servic import { StorageService } from '../../shared/services/storage.service'; import { TableFilterPipe } from '../../shared/pipes/table-filter.pipe'; import { URIKey } from '../../utils/uri-enums'; -import { HttpParams } from '@angular/common/http'; +import { HttpErrorResponse, HttpParams } from '@angular/common/http'; import { HttpURIService } from '../../app.http.uri.service'; +import { I18NService } from '../../services/i18n.service'; +import { SuccessMessages } from '../../utils/enums'; @@ -52,7 +54,8 @@ export class SetupUserComponent implements OnInit { private fb: FormBuilder, private buttonManagementService: ButtonManagementService, private storageService: StorageService, - private httpService: HttpURIService + private httpService: HttpURIService, + private i18nService: I18NService ){} onSearch(value: string): void { @@ -95,13 +98,14 @@ export class SetupUserComponent implements OnInit { } this.httpService.requestPOST(URIKey.CREATE_USER, newUser).subscribe({ - next: () => { - this.userForm.reset(); - this.mode = 'edit'; - this.loadUsersDirect() - }, - - error: (err: any) => console.error(err) + next: (response) => { + if (!(response instanceof HttpErrorResponse)) { + this.i18nService.success(SuccessMessages.USER_CREATED_SUCCESS, []); + this.userForm.reset(); + this.mode = 'edit'; + this.loadUsersDirect() + } + } }); @@ -126,9 +130,25 @@ ngOnInit(): void { this.getButtonPermissions(); this.userForm = this.fb.group({ - userId: ['', [Validators.required]], - userFullname: ['', [Validators.required, Validators.maxLength(500)]], - defaultPassword: ['', Validators.required], + userId: ['', [ + Validators.required, + Validators.minLength(5), + Validators.pattern(/^\S+$/) + ] + ], + userFullname: ['', [ + Validators.required, + Validators.minLength(5), + Validators.pattern(/^\S+$/) + ] + ], + defaultPassword: ['', [ + Validators.required, + Validators.pattern( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,20}$/ + ) + ] + ], email: ['', [Validators.required, Validators.email]], userRole: [null, Validators.required] }); @@ -182,14 +202,13 @@ ngOnInit(): void { let params = new HttpParams().set('userId', userId); this.httpService.requestDELETE(URIKey.DELETE_USER, params).subscribe({ next: (response) =>{ + if (!(response instanceof HttpErrorResponse)) { + this.i18nService.success(SuccessMessages.USER_DELETE_SUCCESS, []); this.loadUsersDirect(); - this.userForm.reset() + this.userForm.reset(); this.selectedUserId = null; - }, - error: (err) =>{ - console.error('Error fetching users:', err); - this.allItems = []; - this.isLoading = false; + } + } }) } diff --git a/src/app/utils/enums.ts b/src/app/utils/enums.ts index 9cc8c4d..b4898ac 100644 --- a/src/app/utils/enums.ts +++ b/src/app/utils/enums.ts @@ -59,7 +59,10 @@ TRANSACTION_SUCCESSFUL = "TRANSACTION_SUCCESSFUL", SAVED_SUCCESSFULLY = "SAVED_SUCCESSFULLY", RECORD_DELETED_SUCCESSFULY = "RECORD_DELETED_SUCCESSFULY", ACCOUNT_CLOSED_SUCCESSFULLY = "ACCOUNT_CLOSED_SUCCESSFULLY", -SUCCESS_MESSAGE = "SUCCESS_MESSAGE" +SUCCESS_MESSAGE = "SUCCESS_MESSAGE", +USER_CREATED_SUCCESS = "USER_CREATED_SUCCESS", +USER_DELETE_SUCCESS = "USER_DELETE_SUCCESS" + } export enum MESSAGEKEY { diff --git a/src/assets/data/sideMenu.json b/src/assets/data/sideMenu.json index c1a3f6d..91e9a1a 100644 --- a/src/assets/data/sideMenu.json +++ b/src/assets/data/sideMenu.json @@ -5,27 +5,6 @@ "checked": false, "expanded": false, "children": [ - { - "name": "thirdPartyRegistration", - "route": "/home/thirdPartyRegistration", - "checked": false, - "expanded": false, - "children": [], - "buttons": [ - { - "name": "edit", - "route": "", - "checked": false, - "expanded": false - }, - { - "name": "delete", - "route": "", - "checked": false, - "expanded": false - } - ] - }, { "name": "setupUser", "route": "/home/setupUser", @@ -113,99 +92,6 @@ } ] }, - { - "name": "SMSBanking", - "route": "", - "checked": false, - "expanded": false, - "children": [ - { - "name": "smsLogger", - "route": "/home/smsLogger", - "checked": false, - "expanded": false, - "children": [] - }, - { - "name": "smsGateway", - "route": "/home/smsGateway", - "checked": false, - "expanded": false, - "children": [], - "buttons": [ - { - "name": "edit", - "route": "", - "checked": false, - "expanded": false - }, - { - "name": "delete", - "route": "", - "checked": false, - "expanded": false - } - ] - } - ] - }, - { - "name": "ibSupport", - "route": "", - "checked": false, - "expanded": false, - "children": [ - { - "name": "ibUnblockUser", - "route": "/home/ibUnblockUser", - "checked": false, - "expanded": false, - "children": [], - "buttons": [ - { - "name": "edit", - "route": "", - "checked": false, - "expanded": false - }, - { - "name": "delete", - "route": "", - "checked": false, - "expanded": false - } - ] - }, - { - "name": "feedbackSetup", - "route": "/home/feedbackSetup", - "checked": false, - "expanded": false, - "children": [] - }, - { - "name": "purposeSetup", - "route": "/home/purposeSetup", - "checked": false, - "expanded": false, - "children": [], - "buttons": [ - { - "name": "edit", - "route": "", - "checked": false, - "expanded": false - }, - { - "name": "delete", - "route": "", - "checked": false, - "expanded": false - } - ] - } - ] - }, { "name": "permissions", "route": "/home/permissions", diff --git a/src/assets/i18n/Arabic.json b/src/assets/i18n/Arabic.json index 309c84e..1f67ef6 100644 --- a/src/assets/i18n/Arabic.json +++ b/src/assets/i18n/Arabic.json @@ -256,6 +256,15 @@ "ERR_SEC_0001": "البريد الإلكتروني موجود بالفعل", "ERR_SEC_0002": "اسم المستخدم موجود بالفعل", "ERR_SEC_0003": "كلمة المرور القديمة غير صحيحة", + "ERR_SEC_0004": "اسم المستخدم أو كلمة المرور غير صحيحة", + "ERR_SEC_0005": "المستخدم غير موجود", + "ERR_SEC_0006": "كلمة المرور التي تم إدخالها غير صحيحة", "toDateGreaterThanToday": "يجب أن يكون التاريخ الحالي أقل من التاريخ الحالي", - "fromDateGreaterThanToday": "يجب أن يكون تاريخ البدء أقل من التاريخ الحالي" + "fromDateGreaterThanToday": "يجب أن يكون تاريخ البدء أقل من التاريخ الحالي", + "userIdMinLength": "يجب أن يكون معرف المستخدم مكوّنًا من 5 أحرف على الأقل", + "nameMinLength": "يجب أن يكون الاسم مكوّنًا من 5 أحرف على الأقل", + "emptySpaceRestriction": "المسافات الفارغة غير مسموح بها", + "USER_CREATED_SUCCESS": "تم إنشاء المستخدم", + "USER_DELETE_SUCCESS": "تم حذف المستخدم" + } \ No newline at end of file diff --git a/src/assets/i18n/English.json b/src/assets/i18n/English.json index e08fdab..c30915b 100644 --- a/src/assets/i18n/English.json +++ b/src/assets/i18n/English.json @@ -112,7 +112,7 @@ "oldPassword":"Old Password", "newPasswordStatic":"New Password", "savePassword":"Save", - "passwordPattern":"Password must be over 8 characters and include an uppercase letter, a lower case letter, a number and a special character", + "passwordPattern":"Password must be 8–20 characters and include uppercase, lowercase, number, and special character", "SMSGatewaySelect":"Select Gateway", "selectIdentValueType": "Select Type", "IdTypeSelect":"Select Select Type", @@ -256,6 +256,14 @@ "ERR_SEC_0001": "Email already exists", "ERR_SEC_0002": "Username already exists", "ERR_SEC_0003": "Old Password is not correct", + "ERR_SEC_0004":"Invalid credentials", + "ERR_SEC_0005": "User not found", + "ERR_SEC_0006": "Incorrect password", "toDateGreaterThanToday": "To Date must be less than Current Date", - "fromDateGreaterThanToday": "From Date must be less than Current Date" + "fromDateGreaterThanToday": "From Date must be less than Current Date", + "userIdMinLength" : "User ID must be at least 5 characters", + "nameMinLength" : "Name must be at least 5 characters", + "emptySpaceRestriction" : "Empty spaces are not allowed", + "USER_CREATED_SUCCESS": "User Created", + "USER_DELETE_SUCCESS": "User Deleted" } \ No newline at end of file diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index 22d8e44..143536d 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -5,6 +5,6 @@ export const environment = { buildDate: '08-01-2026', enableEncryption: true, moduleHost: new Map([ - ["ACONNECT_DOMAIN_URI", "https://api.redmetic.com"] + ["ACONNECT_DOMAIN_URI", "http://localhost:8080"] ]) }; diff --git a/src/index.html b/src/index.html index aa569e3..0095b99 100644 --- a/src/index.html +++ b/src/index.html @@ -2,7 +2,7 @@ - ACONNECTUX + aConnect