Merge branch 'dev-pending-01-01-2026' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1814

mazdak/UX-1814
Mazdak Gibran 4 weeks ago
commit 1126264d7a

@ -96,7 +96,6 @@ export class LoginComponent {
login() { login() {
if (this.loginForm.valid) { if (this.loginForm.valid) {
this.ucred.porOrgacode = HiddenValues.POR_ORGACODE;
this.ucred.password = this.loginForm.get(FormConstants.PASSWORD)?.value; this.ucred.password = this.loginForm.get(FormConstants.PASSWORD)?.value;
this.ucred.userId = this.loginForm.get(FormConstants.USER_ID)?.value; this.ucred.userId = this.loginForm.get(FormConstants.USER_ID)?.value;
this.authService.authenticate(this.ucred).subscribe( (res: any) => { this.authService.authenticate(this.ucred).subscribe( (res: any) => {

@ -31,10 +31,10 @@ export class AuthenticationService {
this.i18nService.error(ErrorMessages.ALREADY_LOGGED_IN,[]); this.i18nService.error(ErrorMessages.ALREADY_LOGGED_IN,[]);
return; return;
} }
this.credentialService.setPorOrgacode(uCreds.porOrgacode); this.credentialService.setPorOrgacode(HiddenValues.POR_ORGACODE);
this.credentialService.setUserId(uCreds.userId); this.credentialService.setUserId(uCreds.userId);
this.credentialService.setPassword(uCreds.password); this.credentialService.setPassword(uCreds.password);
this.storageService.setItem(FormConstants.POR_ORGACODE, uCreds.porOrgacode); this.storageService.setItem(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE);
this.storageService.setItem(FormConstants.USER_ID, uCreds.userId); this.storageService.setItem(FormConstants.USER_ID, uCreds.userId);
this.storageService.setItem(FormConstants.PASSWORD, uCreds.password); this.storageService.setItem(FormConstants.PASSWORD, uCreds.password);
this.httpService.requestPOST(URIKey.USER_LOGIN_URI, uCreds).subscribe((data: any) => { this.httpService.requestPOST(URIKey.USER_LOGIN_URI, uCreds).subscribe((data: any) => {

@ -1,8 +1,14 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({ @Component({
selector: 'app-password-hide-show', selector: 'app-password-hide-show',
imports: [], imports: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PasswordHideShowComponent),
multi: true
}],
templateUrl: './password-hide-show.component.html', templateUrl: './password-hide-show.component.html',
styleUrl: './password-hide-show.component.scss' styleUrl: './password-hide-show.component.scss'
}) })

@ -3,7 +3,7 @@
<div class="inner-pg-sp"> <div class="inner-pg-sp">
<div class="container-fluid"> <div class="container-fluid">
<div *ngIf="isFirstLogin" class="auth-page d-flex align-items-center"> <div *ngIf="isFirstLogin && firstTimeLoginForm" class="auth-page d-flex align-items-center">
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6 col-lg-5 col-xl-4"> <div class="col-md-6 col-lg-5 col-xl-4">
@ -15,7 +15,7 @@
<h5 class="mb-0 text-dark">{{'changePassword' | translate}}</h5> <h5 class="mb-0 text-dark">{{'changePassword' | translate}}</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<form [formGroup]="changePasswordForm" > <form [formGroup]="firstTimeLoginForm">
<!-- New Password --> <!-- New Password -->
<div class="mb-3"> <div class="mb-3">
@ -47,7 +47,7 @@
<!-- Submit Button --> <!-- Submit Button -->
<div class="mt-3 d-grid"> <div class="mt-3 d-grid">
<button class="btn btn-primary waves-effect waves-light" type="button" (click)="onSubmit()"> <button class="btn btn-primary waves-effect waves-light" (click)="onSubmit()" type="button">
{{'save' | translate}} {{'save' | translate}}
</button> </button>
</div> </div>
@ -60,7 +60,7 @@
</div> </div>
</div> </div>
<div *ngIf="!isFirstLogin" class="full-width-page"> <div *ngIf="!isFirstLogin && changePasswordForm" class="full-width-page">
<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">
@ -115,16 +115,16 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="d-flex align-items-start gap-2"> <div class="d-flex align-items-start gap-2">
<label for="enterNewPassword" <label for="newPassword"
class="text-nowrap mt-2"> class="text-nowrap mt-2">
{{ 'enterNewPassword' | translate }}<span {{ 'enterNewPassword' | 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">
<input id="enterNewPassword" <input id="newPassword"
class="form-control" class="form-control"
formControlName="enterNewPassword" formControlName="newPassword"
type="{{passwordType1}}" type="{{passwordType1}}"
maxlength="500" maxlength="500"
placeholder="{{ 'enterNewPassword' | translate }}" appNoWhitespaces placeholder="{{ 'enterNewPassword' | translate }}" appNoWhitespaces

@ -16,6 +16,7 @@ import { StorageService } from '../../shared/services/storage.service';
export class ChangePasswordComponent implements OnInit{ export class ChangePasswordComponent implements OnInit{
isFirstLogin = false; isFirstLogin = false;
firstTimeLoginForm!: FormGroup;
changePasswordForm!: FormGroup; changePasswordForm!: FormGroup;
currentLanguage = new FormControl(); currentLanguage = new FormControl();
httpService: any; httpService: any;
@ -40,7 +41,7 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri
} }
passwordMatchValidator(group: AbstractControl): ValidationErrors | null { passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
const newPassword = group.get('enterNewPassword')?.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 }; return newPassword === confirmPassword ? null : { passwordMismatch: true };
} }
@ -48,18 +49,19 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri
ngOnInit(): void { ngOnInit(): void {
this.checkIfFirstTimeChangePasswordOrNot(); this.checkIfFirstTimeChangePasswordOrNot();
if (!this.isFirstLogin) { if (this.isFirstLogin) {
this.initChangePasswordForm(); this.firstTimeLoginForm = this.fb.group({
} newPassword: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', [Validators.required, Validators.minLength(6)]]
}, { validators: this.passwordMatchValidator });
} else {
this.changePasswordForm = this.fb.group({ this.changePasswordForm = this.fb.group({
oldPassword: ['', Validators.required], oldPassword: ['', Validators.required],
enterNewPassword: ['',[ Validators.required, Validators.minLength(6)]], newPassword: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', [Validators.required, Validators.minLength(6)]] confirmPassword: ['', [Validators.required, Validators.minLength(6)]]
}, }, { validators: this.passwordMatchValidator });
{
validators: this.passwordMatchValidator
} }
)
} }
checkIfFirstTimeChangePasswordOrNot(): void { checkIfFirstTimeChangePasswordOrNot(): void {
@ -113,14 +115,18 @@ constructor(private fb: FormBuilder, private httpURIService: HttpURIService, pri
return null; return null;
} }
getFormPayload() {
const form = this.isFirstLogin ? this.firstTimeLoginForm : this.changePasswordForm;
onSubmit(){ return {
if(this.changePasswordForm.invalid){return} oldPassword: form.get('oldPassword')?.value || null,
// confirmPassword: form.get('confirmPassword')?.value || null,
const payload = { newPassword: form.get('newPassword')?.value,
oldPassword: this.changePasswordForm.value.oldPassword, userId: this.storageService.getItem('USER_ID')
newPassword: this.changePasswordForm.value.enterNewPassword, };
} }
onSubmit(){
const payload = this.getFormPayload();
this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload) this.httpURIService.requestPOST(URIKey.CHANGE_PASSWORD_URI, payload)
.subscribe(); .subscribe();

@ -1,95 +1,126 @@
<div class="page-content"> <div class="page-content">
<div class="container-fluid"> <div class="container-fluid">
<div class="row mt-2 mb-2">
<div class="col-lg-6"> <!-- User Selection -->
<div class="col-6"> <div class="card mb-3">
<div class="mt-0 mb-2 d-sm-flex align-items-center justify-content-between font-edit-13">
<h5 class="text-muted fw-bold mt-3">{{'permissionManagement' | translate}}</h5>
</div>
</div>
<div class="card">
<div class="card-body"> <div class="card-body">
<form [formGroup]="permission"> <form [formGroup]="permission">
<div class="form-group row mb-2"> <div class="row mb-2 align-items-center">
<label for="userCode" class="form-label font-edit-13">{{ 'userCode' | <label class="col-sm-2 col-form-label pe-1">
translate {{ 'userCode' | translate }}
}}</label> </label>
<div class="input-group">
<select class="form-select" id="userCode" formControlName="userCode" (change)="onUserChange()"> <div class="col-sm-4">
<option selected disabled value="">{{ 'choose' | <select
translate }} class="form-select"
formControlName="userCode"
(change)="onUserChange()">
<option value="" disabled selected>
{{ 'choose' | translate }}
</option> </option>
<option *ngFor="let user of users" [value]="user.userId"> <option *ngFor="let user of users" [value]="user.userId">
{{ user.userName }} {{ user.userName }}
</option> </option>
</select> </select>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<!-- Permission Table -->
<div class="card" *ngIf="showPermissions"> <div class="card" *ngIf="showPermissions">
<div class="card-body"> <div class="card-body">
<h4 class="card-title">{{ 'permissions' | translate}}</h4>
<h4 class="card-title">
{{ 'permissions' | translate }}
</h4>
<hr> <hr>
<ul>
<li *ngFor="let node of permissions"> <table class="table table-bordered table-sm align-middle permission-table">
<input <thead class="table-light">
type="checkbox" <tr>
[checked]="node.checked" <th style="width: 40px;"></th>
(change)="toggleNode(node, $event)"> <th>{{ 'Permissions' | translate }}</th>
{{node.name | translate}} <th style="width: 120px;" class="text-center">
<span (click)="toggleExpand(node)"> {{ 'allow' | translate }}
<span *ngIf="node.children && node.children.length"> </th>
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span> </tr>
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span> </thead>
</span>
<tbody>
<ng-container
*ngTemplateOutlet="permissionRow; context: { nodes: permissions, level: 0 }">
</ng-container>
</tbody>
</table>
<div class="text-end mt-3">
<button
class="btn btn-primary btn-sm px-4"
(click)="savePermissions()">
{{ 'save' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Recursive Permission Rows -->
<ng-template #permissionRow let-nodes="nodes" let-level="level">
<ng-container *ngFor="let node of nodes">
<tr>
<!-- Expand / Collapse -->
<td class="text-center">
<span
*ngIf="node.children?.length || node.buttons?.length"
class="expand-icon"
(click)="toggleExpand(node)">
<i
class="bx"
[ngClass]="node.expanded ? 'bx-chevron-down' : 'bx-chevron-right'">
</i>
</span> </span>
<ul *ngIf="node.children && node.children.length > 0 && node.expanded"> </td>
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.children }"></ng-container>
</ul> <!-- Permission Name -->
<ul *ngIf="node.buttons && node.buttons.length > 0 && node.expanded"> <td
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.buttons }"></ng-container> class="permission-cell"
</ul> [style.--level]="level">
</li> {{ node.name | translate }}
</ul> </td>
<ng-template #treeTemplate let-nodes> <!-- Checkbox -->
<ul> <td class="text-center">
<li *ngFor="let node of nodes">
<input <input
type="checkbox" type="checkbox"
[checked]="node.checked" [checked]="node.checked"
(change)="toggleNode(node, $event)"> (change)="toggleNode(node, $event)">
{{node.name | translate}} </td>
<span (click)="toggleExpand(node)"> </tr>
<span *ngIf="node.children && node.children.length">
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span> <!-- Children -->
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span> <ng-container *ngIf="node.expanded">
</span>
<span *ngIf="node.buttons && node.buttons.length"> <ng-container *ngIf="node.children?.length">
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span> <ng-container
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span> *ngTemplateOutlet="permissionRow; context: { nodes: node.children, level: level + 1 }">
</span> </ng-container>
</span> </ng-container>
<ul *ngIf="node.children && node.children.length > 0 && node.expanded">
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.children }"></ng-container> <ng-container *ngIf="node.buttons?.length">
</ul> <ng-container
<ul *ngIf="node.buttons && node.buttons.length > 0 && node.expanded"> *ngTemplateOutlet="permissionRow; context: { nodes: node.buttons, level: level + 1 }">
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.buttons }"></ng-container> </ng-container>
</ul> </ng-container>
</li>
</ul> </ng-container>
</ng-container>
</ng-template> </ng-template>
<div class="row float-end me-4 mb-3 mt-2 font-edit-13">
<button type="button" class="btn btn-primary font-edit-13 px-3 btn-sm" (click)="savePermissions()">{{ 'save' | translate }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -1,19 +1,43 @@
.permission-table td {
vertical-align: middle;
}
.expand-icon {
cursor: pointer;
color: #1e40af;
}
.permission-cell {
position: relative;
padding-left: calc(var(--level) * 22px + 14px);
font-weight: 500;
background-color: rgba(30, 64, 175, calc(var(--level) * 0.08));
color: #1f2937;
}
.permission-cell::before {
content: '';
position: absolute;
left: calc(var(--level) * 22px);
top: 0;
bottom: 0;
width: 2px;
background-color: #9ca3af;
}
li { .permission-cell {
margin: 5px 0; font-size: calc(14px - var(--level) * 0.5px);
} }
ul { .permission-table tbody tr:hover {
list-style-type: none; background-color: #e5e7eb;
margin: 0;
padding-left: 10px; /* Reduce overall left padding */
} }
ul ul { .permission-table thead th {
padding-left: 5px; /* Reduce space for nested items */ background-color: #e5e7eb;
margin-left: 5px; color: #111827;
} }
li { .permission-table td:last-child {
margin-bottom: 3px; /* Add slight vertical spacing between items */ text-align: center;
} }

@ -154,6 +154,7 @@ export class UserPermissionsComponent {
this.permission.reset(); this.permission.reset();
this.permission.get('userCode')?.setValue(""); this.permission.get('userCode')?.setValue("");
this.showPermissions = false; this.showPermissions = false;
this.collapseAll(this.permissions)
} }
}) })
} }
@ -177,4 +178,19 @@ export class UserPermissionsComponent {
} }
} }
} }
collapseAll(nodes: PermissionNode[]): void {
nodes.forEach(node => {
node.expanded = false;
if (node.children) {
this.collapseAll(node.children);
}
if (node.buttons) {
this.collapseAll(node.buttons);
}
});
}
} }

@ -54,7 +54,7 @@
}, },
{ {
"Id" : "ENTITY_CHANGE_PASSWORD_URI", "Id" : "ENTITY_CHANGE_PASSWORD_URI",
"URI": "/user/changePassword", "URI": "/authentication/change-password",
"UUID": "CHANGE_PASSWORD_URI" "UUID": "CHANGE_PASSWORD_URI"
}, },
{ {

@ -200,7 +200,7 @@
] ]
}, },
{ {
"name": "PermissionManager", "name": "permissions",
"route": "/home/permissions", "route": "/home/permissions",
"checked": false, "checked": false,
"expanded": false, "expanded": false,

@ -232,5 +232,6 @@
"deleteUser": "حذف حساب المستخدم", "deleteUser": "حذف حساب المستخدم",
"permissionManagement": "إدارة الأذونات", "permissionManagement": "إدارة الأذونات",
"userCode": "مستخدم", "userCode": "مستخدم",
"choose" : "يختار" "choose" : "يختار",
"allow": "يسمح"
} }

@ -231,5 +231,6 @@
"deleteUser": "Delete User", "deleteUser": "Delete User",
"permissionManagement": "Permission Managment", "permissionManagement": "Permission Managment",
"userCode": "User", "userCode": "User",
"choose" : "Choose" "choose" : "Choose",
"allow": "Allow"
} }
Loading…
Cancel
Save