Merge branch 'dev-pending-20-01-2026-naeem' into dev-pending-20-01-2026

mazdak/UX-2123
Naeem Ullah 2 weeks ago
commit 772653b51c

@ -15,7 +15,7 @@
"watch:dev": "ng build --watch --configuration=development",
"watch:test": "ng build --watch --configuration=test",
"test": "ng test",
"serve:ssr": "node dist/aconnect-ux/server/main.js"
"serve:ssr": "node dist/aconnect-ux/server/main.js"
},
"private": true,
"dependencies": {

@ -36,7 +36,6 @@
</div>
<div *ngIf="loginForm.get('USER_ID')?.invalid && loginForm.get('USER_ID')?.touched" class="text-danger">
<small *ngIf="loginForm.get('USER_ID')?.errors?.['required']">{{"userNameRequired" | translate}}</small>
<small *ngIf="loginForm.get('USER_ID')?.errors?.['pattern']">{{"userNamePattterenError" | translate}}</small>
</div>
</div>
<div class="mb-3">

@ -19,6 +19,10 @@ export interface TransactionLog {
transactionID: string;
drMbmbkmsnumber: string;
crMbmbkmsnumber: string;
crPcaglacode: string;
drPcaGlacode: string;
amount: number;
paymentMode: string;
ppmPymdcode: string;
sgtGntrdate: string;
channelCode: string;

@ -3,9 +3,9 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
<div
class="d-sm-flex align-items-center justify-content-between navbar-header p-0"
></div>
</div>
</div>
</div>
@ -19,21 +19,40 @@
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'transactionLogs' | translate}}
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center"
>
{{ "transactionLogs" | translate }}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm" [(ngModel)]="searchText"
(ngModelChange)="onSearch($event)" placeholder="{{ 'search' | translate }}">
<input
type="text"
class="form-control form-control-sm"
[(ngModel)]="searchText"
(ngModelChange)="onSearch($event)"
placeholder="{{ 'search' | translate }}"
/>
<i class="fas fa-search search-icon"></i>
</div>
<div class="d-flex align-items-center gap-2">
<i (click)="exportDataInExcel()" id="downloadReport" class="fa fa-download"></i>
</div>
<div class="d-flex align-items-center gap-2">
<i
(click)="exportDataInExcel()"
id="downloadReport"
class="fa fa-download"
></i>
</div>
<i class="materialdesignicons" (click)="toggleTableCard()">
<ng-container *ngIf="transactionDataExpanded; else collapsedIcon">
<i
class="materialdesignicons"
(click)="toggleTableCard()"
>
<ng-container
*ngIf="
transactionDataExpanded;
else collapsedIcon
"
>
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
@ -43,76 +62,136 @@
</div>
</div>
<div class="card-body" *ngIf="transactionDataExpanded && allItems.length; else noRecordsFound">
<div
class="card-body"
*ngIf="
transactionDataExpanded && allItems.length;
else noRecordsFound
"
>
<div *ngIf="isLoading" class="text-center text-muted">
<p>{{'loadingTransactionLogs' | translate}}</p>
<p>{{ "loadingTransactionLogs" | translate }}</p>
</div>
<div *ngIf="!isLoading && transactionLog.length === 0" class="text-center text-muted">
<p>{{'noTransactionLogsFound' | translate}}</p>
<div
*ngIf="!isLoading && transactionLog.length === 0"
class="text-center text-muted"
>
<p>{{ "noTransactionLogsFound" | translate }}</p>
</div>
<div *ngIf="errorMessage" class="alert alert-danger">
{{ errorMessage }}
</div>
<div *ngIf="!isLoading && transactionLog.length > 0" class="table-responsive">
<div
*ngIf="!isLoading && transactionLog.length > 0"
class="table-responsive"
>
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th>{{'logID' | translate}}</th>
<th>{{'organization' | translate}}</th>
<th>{{'transactionID' | translate}}</th>
<th>{{'drAccount' | translate}}</th>
<th>{{'crAccount' | translate}}</th>
<th>{{'paymentMode' | translate}}</th>
<th>{{'date' | translate}}</th>
<th>{{'channel' | translate}}</th>
<th>{{'createdAt' | translate}}</th>
<th>{{ "logID" | translate }}</th>
<th>{{ "organization" | translate }}</th>
<th>{{ "transactionID" | translate }}</th>
<th>{{ "drAccount" | translate }}</th>
<th>{{ "crAccount" | translate }}</th>
<th>{{ "drPcaGlacode" | translate }}</th>
<th>{{ "crPcaglacode" | translate }}</th>
<th>{{ "paymentMode" | translate }}</th>
<th>{{ "date" | translate }}</th>
<th>{{ "channel" | translate }}</th>
<th>{{ "createdAt" | translate }}</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let log of (allItems | tableFilter: searchText: ['logID','organization','transactionID','drAccount','crAccount','paymentMode','date','channel','createdAt']).slice((currentPage-1)*itemsPerPage, currentPage*itemsPerPage)">
*ngFor="
let log of (
allItems
| tableFilter
: searchText
: [
'logID',
'organization',
'transactionID',
'drAccount',
'drPcaGlacode',
'crPcaglacode',
'crAccount',
'paymentMode',
'date',
'channel',
'createdAt',
]
).slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
)
"
>
<td>{{ log.logId }}</td>
<td>{{ log.porOrgacode }}</td>
<td>{{ log.transactionID }}</td>
<td>{{ log.drMbmbkmsnumber || '-' }}</td>
<td>{{ log.crMbmbkmsnumber || '-' }}</td>
<td>{{ log.drMbmbkmsnumber || "-" }}</td>
<td>{{ log.crMbmbkmsnumber || "-" }}</td>
<td>{{ log.drPcaGlacode || "-" }}</td>
<td>{{ log.crPcaglacode || "-" }}</td>
<td>{{ log.ppmPymdcode }}</td>
<td>{{ log.sgtGntrdate | date: 'MMM dd, yyyy' }}</td>
<td>
{{ log.sgtGntrdate | date: "MMM dd, yyyy" }}
</td>
<td>{{ log.channelCode }}</td>
<td>{{ log.createdAt | date: 'MMM dd, yyyy HH:mm' }}</td>
<td>
{{
log.createdAt | date: "MMM dd, yyyy HH:mm"
}}
</td>
</tr>
</tbody>
</table>
<div
class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm" [items]="pageSizeOptions" bindLabel="label" bindValue="value"
[(ngModel)]="itemsPerPage" (change)="itemsPerPageChanged()" [searchable]="false" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
class="d-flex justify-content-between align-items-center mt-3"
>
<div class="form-group mb-0">
<ng-select
class="form-select-sm"
[items]="pageSizeOptions"
bindLabel="label"
bindValue="value"
[(ngModel)]="itemsPerPage"
(change)="itemsPerPageChanged()"
[searchable]="false"
[clearable]="false"
[dropdownPosition]="'top'"
>
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ currentPage }} {{ 'of' | translate }} {{ totalPages() }} ({{ totalCount }} {{ 'totalItems' | translate }})
</div>
<div class="text-muted">
{{ "page" | translate }} {{ currentPage }}
{{ "of" | translate }} {{ totalPages() }} ({{
totalCount
}}
{{ "totalItems" | translate }})
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light" (click)="previousPage()">
{{ 'previous' | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light" (click)="nextPage()">
{{ 'next' | translate }}
</button>
</div>
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light"
(click)="previousPage()"
>
{{ "previous" | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light"
(click)="nextPage()"
>
{{ "next" | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
@ -127,7 +206,10 @@
</div>
</div>
<ng-template #noRecordsFound>
<div *ngIf="!isLoading && allItems.length === 0" class="text-center text-muted mt-3">
<p>{{'noTransactionLogsFound' | translate}}</p>
</div>
<div
*ngIf="!isLoading && allItems.length === 0"
class="text-center text-muted mt-3"
>
<p>{{ "noTransactionLogsFound" | translate }}</p>
</div>
</ng-template>

@ -80,7 +80,7 @@
formControlName="userFullname"
name="userFullname"
maxlength="500"
placeholder="{{ 'userName' | translate }}" appNoWhitespaces
placeholder="{{ 'Full Name' | translate }}"
rows="3" />
<div class="text-danger" *ngIf="userForm.get('userFullname')?.touched && userForm.get('userFullname')?.invalid">
@ -98,12 +98,6 @@
">
{{'nameMinLength' | translate }}
</div>
<div class="text-danger" *ngIf="
userForm.get('userFullname')?.errors?.['pattern'] &&
userForm.get('userFullname')?.value
">
{{'emptySpaceRestriction' | translate}}
</div >
</div>
</div>
@ -259,11 +253,10 @@
<button class="btn btn-info btn-sm" title="View" (click)="onView(item.userId)">
<i class="mdi mdi-eye-outline"></i>
</button>
<button *ngIf="buttonPermissions?.delete" class="btn btn-danger btn-sm" title="Delete"
(click)="onDelete(item.userId)">
<i class="fas fa-trash-alt"></i>
</button>
<button *ngIf="buttonPermissions?.delete" class="btn btn-danger btn-sm" title="Delete"
(click)="confirmDelete(item.userId)">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>

@ -14,7 +14,7 @@ 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';
import { ToastrService, ActiveToast, ToastRef } from 'ngx-toastr';
@Component({
@ -125,6 +125,13 @@ export class SetupUserComponent implements OnInit {
this.userSetupDataExpanded = !this.userSetupDataExpanded;
}
confirmDelete(userId: string) {
const confirmed = window.confirm('Are you sure you want to delete this user?');
if (confirmed) {
this.onDelete(userId);
}
}
ngOnInit(): void {
this.getButtonPermissions();
@ -138,8 +145,7 @@ ngOnInit(): void {
],
userFullname: ['', [
Validators.required,
Validators.minLength(5),
Validators.pattern(/^\S+$/)
Validators.minLength(5)
]
],
defaultPassword: ['', [

@ -1,59 +1,60 @@
<div class="page-content">
<div class="container-fluid">
<!-- User Selection -->
<div class="card mb-3">
<!-- User Selection Card -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<form [formGroup]="permission">
<div class="row mb-2 align-items-center">
<label class="col-sm-2 col-form-label pe-1">
{{ 'userCode' | translate }}
</label>
<div class="col-sm-4">
<ng-select class="form-select" formControlName="userCode" [items]="users" bindLabel="userName" bindValue="userId"
placeholder="{{ 'choose' | translate }}" (change)="onUserChange()">
</ng-select>
</div>
<form [formGroup]="permission" class="row align-items-center">
<label class="col-md-2 col-form-label fw-semibold">
{{ 'userCode' | translate }}
</label>
<div class="col-md-6">
<ng-select
class="form-select"
formControlName="userCode"
[items]="users"
bindLabel="userName"
bindValue="userId"
placeholder="{{ 'choose' | translate }}"
(change)="onUserChange()"
[searchable]="true"
[clearable]="true"
[dropdownPosition]="'auto'"
[virtualScroll]="true"
[bufferAmount]="10">
</ng-select>
</div>
</form>
</div>
</div>
<!-- Permission Table -->
<div class="card" *ngIf="showPermissions">
<!-- Permissions Table Card -->
<div class="card shadow-sm" *ngIf="showPermissions">
<div class="card-body">
<h4 class="card-title">
{{ 'permissions' | translate }}
</h4>
<hr>
<table class="table table-bordered table-sm align-middle permission-table">
<thead class="table-light">
<tr>
<th style="width: 40px;"></th>
<th>{{ 'Permissions' | translate }}</th>
<th style="width: 120px;" class="text-center">
{{ 'allow' | translate }}
</th>
</tr>
</thead>
<tbody>
<ng-container
*ngTemplateOutlet="permissionRow; context: { nodes: permissions, level: 0 }">
</ng-container>
</tbody>
</table>
<h4 class="card-title mb-3">{{ 'permissions' | translate }}</h4>
<div class="table-responsive scrollable-table">
<table class="table table-hover table-bordered table-sm permission-table">
<thead class="table-light">
<tr>
<th style="width: 40px;"></th>
<th>{{ 'Permissions' | translate }}</th>
<th style="width: 120px;" class="text-center">{{ 'allow' | translate }}</th>
</tr>
</thead>
<tbody>
<ng-container *ngTemplateOutlet="permissionRow; context: { nodes: permissions, level: 0 }"></ng-container>
</tbody>
</table>
</div>
<div class="text-end mt-3">
<button
class="btn btn-primary btn-sm px-4"
(click)="savePermissions()">
<button class="btn btn-primary btn-sm px-4" (click)="savePermissions()">
{{ 'save' | translate }}
</button>
</div>
@ -69,50 +70,30 @@
<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 *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>
</td>
<!-- Permission Name -->
<td
class="permission-cell"
[style.--level]="level">
<td class="permission-cell" [style.--level]="level">
{{ node.name | translate }}
</td>
<!-- Checkbox -->
<td class="text-center">
<input
type="checkbox"
[checked]="node.checked"
(change)="toggleNode(node, $event)">
<input type="checkbox" [checked]="node.checked" (change)="toggleNode(node, $event)">
</td>
</tr>
<!-- Children -->
<ng-container *ngIf="node.expanded">
<ng-container *ngIf="node.children?.length">
<ng-container
*ngTemplateOutlet="permissionRow; context: { nodes: node.children, level: level + 1 }">
</ng-container>
<ng-container *ngTemplateOutlet="permissionRow; context: { nodes: node.children, level: level + 1 }"></ng-container>
</ng-container>
<ng-container *ngIf="node.buttons?.length">
<ng-container
*ngTemplateOutlet="permissionRow; context: { nodes: node.buttons, level: level + 1 }">
</ng-container>
<ng-container *ngTemplateOutlet="permissionRow; context: { nodes: node.buttons, level: level + 1 }"></ng-container>
</ng-container>
</ng-container>
</ng-container>

@ -1,18 +1,58 @@
.permission-table td {
vertical-align: middle;
/* Card styling */
.card {
border-radius: 0.5rem;
border: none;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
/* Scrollable table wrapper */
.scrollable-table {
max-height: 450px; /* adjustable */
overflow-y: auto;
border-radius: 0.5rem;
}
/* Table styles */
.permission-table {
min-width: 100%;
border-radius: 0.5rem;
}
.permission-table thead th {
background-color: #f3f4f6;
color: #1f2937;
font-weight: 600;
border-bottom: 2px solid #e5e7eb;
}
.permission-table tbody tr:hover {
background-color: #eef2ff;
}
.permission-table td:last-child {
text-align: center;
}
/* Expand / collapse icon */
.expand-icon {
cursor: pointer;
color: #1e40af;
color: #3b82f6;
transition: transform 0.2s;
}
.expand-icon:hover {
transform: scale(1.2);
}
/* Recursive permission cells */
.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;
background-color: rgba(59, 130, 246, calc(var(--level) * 0.08));
color: #111827;
font-size: calc(14px - var(--level) * 0.5px);
transition: background-color 0.2s;
}
.permission-cell::before {
@ -25,19 +65,34 @@
background-color: #9ca3af;
}
.permission-cell {
font-size: calc(14px - var(--level) * 0.5px);
/* Scrollable ng-select dropdown */
::ng-deep ng-dropdown-panel {
max-height: 250px;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-radius: 0.5rem;
}
.permission-table tbody tr:hover {
background-color: #e5e7eb;
/* Button styling */
.btn-primary {
background-color: #3b82f6;
border-color: #3b82f6;
font-weight: 500;
transition: background-color 0.2s;
}
.permission-table thead th {
background-color: #e5e7eb;
color: #111827;
.btn-primary:hover {
background-color: #2563eb;
border-color: #2563eb;
}
.permission-table td:last-child {
text-align: center;
/* Responsive adjustments */
@media (max-width: 768px) {
.col-md-6 {
width: 100%;
}
.permission-table td,
.permission-table th {
font-size: 13px;
}
}

@ -197,6 +197,8 @@
"transactionID": "Transaction ID",
"drAccount": "DR Account",
"crAccount": "CR Account",
"crPcaglacode": "CR GL Account",
"drPcaGlacode": "DR GL Account",
"paymentMode": "Payment Mode",
"channel": "Channel",
"createdAt": "Created At",

Loading…
Cancel
Save