Merge pull request 'setup user email' (#34) from mazdak/setup-user-email into dev-pending-01-01-2026

Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/34
mazdak/UX-1985
Naeem Ullah 3 weeks ago
commit bcf4b208cc

@ -5,10 +5,10 @@
<div class="col-xl-11"></div>
<div class="col-xl-1 mt-2">
<div class="page-title-right float-end mx-3">
<select class="form-select" [formControl]="currentLanguage" (change)="onLangChange()" style="width: 100px;">
<option [value]="supportedLanguages.ENGLISH">{{"english" | translate}}</option>
<option [value]="supportedLanguages.ARABIC">{{"arabic" | translate}}</option>
</select>
<ng-select class="form-select" [formControl]="currentLanguage" [items]="languageOptions" bindLabel="label"
bindValue="value" [clearable]="false" [searchable]="false" style="width: 100px;" (change)="onLangChange()">
</ng-select>
</div>
</div>
</div>

@ -13,10 +13,11 @@ import { directions, FormConstants, HiddenValues, supportedLanguages } from '../
import { environment } from '../../../environments/environment';
import { AuthenticationService } from '../../services/authenticate.service';
import { UserCredentials } from '../authenticate';
import { NgSelectModule } from '@ng-select/ng-select';
@Component({
selector: 'app-login',
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent],
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent, NgSelectModule],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
@ -28,7 +29,11 @@ export class LoginComponent {
currentLanguage = new FormControl();
passwordType: string = 'password';
direction: string = '';
supportedLanguages = supportedLanguages;
languageOptions = [
{ label: 'English', value: supportedLanguages.ENGLISH },
{ label: 'Arabic', value: supportedLanguages.ARABIC }
];
ucred: UserCredentials = new UserCredentials();
@ViewChild(PasswordHideShowComponent) passwordHideShow?: PasswordHideShowComponent;
@ -64,7 +69,7 @@ export class LoginComponent {
initializeLanguage(): void {
if (isPlatformBrowser(this.platformId)) {
const savedLanguage = this.storageService.getItem('language') || 'English';
const savedLanguage = this.storageService.getItem('language') || supportedLanguages.ENGLISH;;
this.storageService.setItem('language', savedLanguage);
this.currentLanguage.setValue(savedLanguage)
this.translateService.setDefaultLang(savedLanguage);

@ -12,3 +12,17 @@ export interface SetupUser {
email: string;
role: string;
}
export interface TransactionLog {
logId: number;
porOrgacode: string;
transactionID: string;
drMbmbkmsnumber: string;
crMbmbkmsnumber: string;
ppmPymdcode: string;
sgtGntrdate: string;
channelCode: string;
createdAt: string;
}

@ -0,0 +1,28 @@
import { Pipe, PipeTransform } from '@angular/core';
import { TransactionLog } from '../../models/user';
@Pipe({
name: 'transactionLogFilter',
standalone: true
})
export class TransactionLogFilterPipe implements PipeTransform {
transform(logs: TransactionLog[], searchText: string): TransactionLog[] {
if (!logs || !searchText) {
return logs;
}
const search = searchText.toLowerCase();
return logs.filter(log =>
log.logId?.toString().toLowerCase().includes(search) ||
log.porOrgacode?.toLowerCase().includes(search) ||
log.transactionID?.toLowerCase().includes(search) ||
log.drMbmbkmsnumber?.toLowerCase().includes(search) ||
log.crMbmbkmsnumber?.toLowerCase().includes(search) ||
log.ppmPymdcode?.toLowerCase().includes(search) ||
log.sgtGntrdate?.toLowerCase().includes(search) ||
log.channelCode?.toLowerCase().includes(search) ||
log.createdAt?.toLowerCase().includes(search)
);
}
}

@ -1,24 +1,71 @@
import { Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
import { BehaviorSubject, map, Observable } from 'rxjs';
import { URIKey } from '../../utils/uri-enums';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { HttpURIService } from '../../app.http.uri.service';
import { TransactionLog } from '../../models/user';
@Injectable({
providedIn: 'root'
})
export class TransactionLogService {
constructor(private httpUriService: HttpURIService) { }
private logsSubject = new BehaviorSubject<TransactionLog[]>([]);
private currentPageSubject = new BehaviorSubject<number>(1);
private totalCountSubject = new BehaviorSubject<number>(0);
private itemsPerPageSubject = new BehaviorSubject<number>(5);
getTransactionLogs(): Observable<any[]> {
logs$ = this.logsSubject.asObservable();
currentPage$ = this.currentPageSubject.asObservable();
totalCount$ = this.totalCountSubject.asObservable();
itemsPerPage$ = this.itemsPerPageSubject.asObservable();
constructor(private httpUriService: HttpURIService) {}
loadLogs(): void {
const params = new HttpParams();
return this.httpUriService.requestGET(URIKey.TRANSACTION_LOGS, params).pipe(map((response: any) => {
if (!(response instanceof HttpErrorResponse)) {
return Array.isArray(response) ? response : [];
this.httpUriService
.requestGET<any>(URIKey.TRANSACTION_LOGS, params)
.subscribe({
next: (res) => {
const logs = Array.isArray(res) ? res : res?.data;
this.logsSubject.next(logs ?? []);
this.totalCountSubject.next(logs.length);
},
error: (err) => console.error(err)
});
}
setItemsPerPage(itemsPerPage: number): void {
this.itemsPerPageSubject.next(itemsPerPage);
this.currentPageSubject.next(1);
}
nextPage(): void {
const totalPages = this.getTotalPages();
const currentPage = this.currentPageSubject.value;
if (currentPage < totalPages) {
this.currentPageSubject.next(currentPage + 1);
}
}
return [];
})
);
previousPage(): void {
const currentPage = this.currentPageSubject.value;
if (currentPage > 1) {
this.currentPageSubject.next(currentPage - 1);
}
}
goToPage(page: number): void {
const totalPages = this.getTotalPages();
if (page > 0 && page <= totalPages) {
this.currentPageSubject.next(page);
}
}
getTotalPages(): number {
const totalCount = this.totalCountSubject.value;
const itemsPerPage = this.itemsPerPageSubject.value;
return Math.ceil(totalCount / itemsPerPage);
}
}

@ -139,26 +139,6 @@
<td></td>
<td></td>
<!-- <td>
<div
class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm"
title="View">
<i class="mdi mdi-eye-outline"></i>
</button>
<button *ngIf="buttonPermissions?.edit" class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button *ngIf="buttonPermissions?.delete" class="btn btn-danger btn-sm"
title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td> -->
</tr>
</tbody>
</table>

@ -21,11 +21,28 @@
<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="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 }}">
<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>
<i class="materialdesignicons">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
<i class="dripicons-chevron-down float-end"></i>
</ng-template>
</i>
</div>
</div>
<div class="card-body">
<div *ngIf="isLoading" class="text-center text-muted">
@ -56,7 +73,12 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let log of logs">
<tr *ngFor="
let log of (
(logs$ | async) ?? []
| transactionLogFilter: searchText
).slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
">
<td>{{ log.logId }}</td>
<td>{{ log.porOrgacode }}</td>
<td>{{ log.transactionID }}</td>
@ -69,6 +91,31 @@
</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)="onPageSizeChange(itemsPerPage)" [searchable]="false" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ currentPage }} {{ 'of' | translate }} {{ getTotalPages() }} ({{ 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>
</div>
</div>

@ -4,62 +4,83 @@ import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { ExcelExportService } from '../shared/services/excel-export.service';
import { TRANSACTION_LOGS_FILE_NAME } from '../utils/app.constants';
interface TransactionLog {
logId: number;
porOrgacode: string;
drMbmbkmsnumber: string;
crMbmbkmsnumber: string;
crPcaglacode: string;
drPcaGlacode: string;
ppmPymdcode: string;
sgtGntrdate: string;
sgtGntrcreateat: string;
channelCode: string;
transactionID: string;
createdAt: string;
updatedAt: string;
}
import { pageSizeOptions } from '../utils/app.constants';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TransactionLogFilterPipe } from '../shared/pipes/transactionLogFilter.pipe';
import {TransactionLog} from "../models/user"
@Component({
selector: 'app-transaction-logs',
templateUrl: './transaction-logs.component.html',
providers: [TransactionLogService],
imports:[CommonModule, TranslateModule]
imports: [CommonModule, TranslateModule, NgSelectModule, FormsModule, ReactiveFormsModule, TransactionLogFilterPipe, ]
})
export class TransactionLogsComponent implements OnInit {
currentPage: number = 1;
totalCount: number = 0;
renewalDataExpanded: boolean = true;
pageSizeOptions = pageSizeOptions;
itemsPerPage: number = 5;
logs: TransactionLog[] = [];
isLoading = false;
errorMessage: string = '';
searchText: string = '';
constructor(
private transactionLogService: TransactionLogService,
private excelExportService: ExcelExportService
) {}
ngOnInit(): void {
this.loadLogs();
get logs$(){
return this.transactionLogService.logs$;
}
ngOnInit(): void {
this.transactionLogService.loadLogs();
loadLogs() {
this.isLoading = true;
this.errorMessage = '';
this.transactionLogService.logs$.subscribe((logs: TransactionLog[]) => {
this.logs = logs;
});
this.transactionLogService.getTransactionLogs().subscribe({
next: (res) => {
this.logs = res;
this.isLoading = false;
},
error: (err) => {
console.error('Failed to load transaction logs', err);
this.errorMessage = 'Failed to load transaction logs. Please try again.';
this.isLoading = false;
}
this.transactionLogService.currentPage$.subscribe((page) => {
this.currentPage = page;
});
this.transactionLogService.totalCount$.subscribe((count) => {
this.totalCount = count;
});
this.transactionLogService.itemsPerPage$.subscribe((size) => {
this.itemsPerPage = size;
});
}
onSearch(value: string): void {
this.searchText = value;
}
get paginatedLogs(): TransactionLog[] {
const start = (this.currentPage - 1) * this.itemsPerPage;
const end = start + this.itemsPerPage;
return this.logs.slice(start, end);
}
exportDataInExcel(){
this.excelExportService.exportExcel(this.logs, TRANSACTION_LOGS_FILE_NAME)
}
getTotalPages(): number {
return this.transactionLogService.getTotalPages();
}
onPageSizeChange(pageSize: number): void {
this.transactionLogService.setItemsPerPage(pageSize);
}
nextPage(): void {
this.transactionLogService.nextPage();
}
previousPage(): void {
this.transactionLogService.previousPage();
}
}

@ -126,7 +126,7 @@
<label for="userRole" class="text-nowrap">
{{ 'SelectRole' | translate }}<span class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="position-relative w-100">
<ng-select id="userRole" class="form-select" formControlName="userRole" [items]="roleOptions" bindLabel="label"
bindValue="value" placeholder="{{ 'SelectRole' | translate }}" >
</ng-select>

@ -81,7 +81,7 @@ export class SetupUserComponent implements OnInit {
const newUser : SetupUser = {
userId: this.userForm.value.userId,
userFullname: this.userForm.value.userFullname,
email: `${this.userForm.value.userId}@dummy.com`,
email: this.userForm.value.email,
role: this.userForm.value.userRole,
porOrgacode: this.storageService.getItem('POR_ORGACODE'),
password: this.userForm.value.defaultPassword
@ -138,7 +138,7 @@ ngOnInit(): void {
userFullname: ['', [Validators.required, Validators.maxLength(500)]],
defaultPassword: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
userRole: ['', Validators.required]
userRole: [null, Validators.required]
});
this.userService.loadUsers();

@ -283,8 +283,9 @@
<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" [searchable]="false" [clearable]="false">
[(ngModel)]="itemsPerPage" [searchable]="false" [clearable]="false" appendTo="body" [dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">

@ -12,17 +12,10 @@
</label>
<div class="col-sm-4">
<select
class="form-select"
formControlName="userCode"
(change)="onUserChange()">
<option value="" disabled selected>
{{ 'choose' | translate }}
</option>
<option *ngFor="let user of users" [value]="user.userId">
{{ user.userName }}
</option>
</select>
<ng-select class="form-select" formControlName="userCode" [items]="users" bindLabel="userName" bindValue="userId"
placeholder="{{ 'choose' | translate }}" (change)="onUserChange()">
</ng-select>
</div>
</div>

@ -10,10 +10,11 @@ import { URIKey } from '../utils/uri-enums';
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { FormConstants, HiddenValues, SuccessMessages } from '../utils/enums';
import { CommonModule } from '@angular/common';
import { NgSelectModule } from '@ng-select/ng-select';
@Component({
selector: 'app-user-permissions',
imports: [TranslateModule, ReactiveFormsModule, CommonModule],
imports: [TranslateModule, ReactiveFormsModule, CommonModule, TranslateModule, NgSelectModule],
templateUrl: './user-permissions.component.html',
styleUrl: './user-permissions.component.scss'
})
@ -27,7 +28,7 @@ export class UserPermissionsComponent {
constructor(private credentialService: CredentialService, private fb: FormBuilder, private httpService: HttpURIService, private i18nService: I18NService) {
this.permission = this.fb.group({
allocation: [''],
userCode: [''],
userCode: [null],
userRole: [''],
});

@ -103,6 +103,13 @@
"checked": false,
"expanded": false,
"children": []
},
{
"name": "transactionLogs",
"route": "/home/transactionLogs",
"checked": false,
"expanded": false,
"children": []
}
]
},

@ -166,7 +166,7 @@ ng-select.form-select-sm {
border: none !important;
}
.ng-select .ng-select-container{
width: 98% !important;
width: 100% !important;
}
#downloadReport {

Loading…
Cancel
Save