integrated GET call in logger manager to get logging details

aconnect-UX/1576
atif118-mfsys 3 weeks ago
parent 162662f81f
commit e3bd2193b8

@ -22,7 +22,7 @@
{{'loggerManager' | translate}} {{'loggerManager' | translate}}
</div> </div>
<div class="card-body"> <div class="card-body">
<form> <form [formGroup]="logsSearchForm">
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<div class="col-md-6"> <div class="col-md-6">
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
@ -34,14 +34,15 @@
class="password-wrapper position-relative w-100"> class="password-wrapper position-relative w-100">
<div <div
class="d-flex flex-row align-items-stretch"> class="d-flex flex-row align-items-stretch">
<input type="date" id="fromDate" <input formControlName="fromDate"
class="form-control" type="date" id="fromDate"
appNoWhitespaces /> class="form-control" appNoWhitespaces />
</div> </div>
<!-- <div class="text-danger"> <div class="text-danger"
{{ 'requiredField' | translate }} *ngIf="logsSearchForm.get('fromDate')?.touched && logsSearchForm.get('fromDate')?.invalid">
</div> --> {{ 'fieldRequired' | translate }}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -54,9 +55,23 @@
<div <div
class="password-wrapper position-relative w-100"> class="password-wrapper position-relative w-100">
<input id="toDate" type="date" class="form-control" <input formControlName="toDate" id="toDate"
maxlength="500" type="date" class="form-control"
appNoWhitespaces rows="3" /> maxlength="500" appNoWhitespaces rows="3" />
<div class="text-danger" *ngIf="
logsSearchForm.get('toDate')?.touched &&
(logsSearchForm.get('toDate')?.invalid || logsSearchForm.errors?.['toDateInvalid'])
">
<div
*ngIf="logsSearchForm.get('toDate')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div
*ngIf="logsSearchForm.errors?.['toDateInvalid']">
{{ 'toDateInvalidError' | translate }}
</div>
</div>
@ -68,7 +83,8 @@
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end"> <div class="col-md-6 ms-auto text-end">
<button <button [disabled]="logsSearchForm.invalid"
(click)="getlogsData()"
class="btn btn-primary waves-effect waves-light">{{'findLogs' class="btn btn-primary waves-effect waves-light">{{'findLogs'
| translate}}</button> | translate}}</button>
@ -100,14 +116,14 @@
<div <div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center"> class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'loggerManagerDetails' | translate}} {{'loggerManagerDetails' | translate}}
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2" *ngIf="allItems.length">
<div class="search-box"> <div class="search-box">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm" [(ngModel)]="searchText"
placeholder="{{ 'search' | translate }}"> placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i> <i class="fas fa-search search-icon"></i>
</div> </div>
<i class="materialdesignicons"> <i class="materialdesignicons" (click)="toggleTableCard()">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon"> <ng-container *ngIf="logsDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i> <i class="dripicons-chevron-up float-end"></i>
</ng-container> </ng-container>
<ng-template #collapsedIcon> <ng-template #collapsedIcon>
@ -116,7 +132,7 @@
</i> </i>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body" *ngIf="logsDataExpanded && allItems.length; else noRecordsFound">
<div class="table-responsive"> <div class="table-responsive">
<table class="table mb-0 border"> <table class="table mb-0 border">
<thead class="table-light"> <thead class="table-light">
@ -125,43 +141,19 @@
<th>{{'loggingRequestUri' | translate}}</th> <th>{{'loggingRequestUri' | translate}}</th>
<th>{{'loggingResponseCode' | translate}}</th> <th>{{'loggingResponseCode' | translate}}</th>
<th>{{'loggingRemoteIP' | translate}}</th> <th>{{'loggingRemoteIP' | translate}}</th>
<th>{{'loggingTimeTaken' | translate}}</th>
<th>{{'loggingDateTime' | translate}}</th> <th>{{'loggingDateTime' | translate}}</th>
<th>{{'loggingMethod' | translate}}</th> <th>{{'loggingMethod' | translate}}</th>
<th>{{'action' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr
<td></td> *ngFor="let logs of (allItems | tableFilter: searchText: ['id','method','remoteIp','requestBody','requestUri','responseCode','userId','dateTime']).slice((currentPage-1)*itemsPerPage, currentPage*itemsPerPage)">
<td></td> <td>{{logs?.id}}</td>
<td></td> <td>{{logs?.requestUri}}</td>
<td></td> <td>{{logs?.responseCode}}</td>
<td></td> <td>{{logs?.remoteIp}}</td>
<td></td> <td>{{logs?.dateTime | date:'dd-MM-yyyy, hh:mm a'}}</td>
<td></td> <td>{{logs?.method}}</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 class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button class="btn btn-danger btn-sm"
title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -171,28 +163,39 @@
<ng-select class="form-select-sm" <ng-select class="form-select-sm"
[items]="pageSizeOptions" bindLabel="label" [items]="pageSizeOptions" bindLabel="label"
bindValue="value" [(ngModel)]="itemsPerPage" bindValue="value" [(ngModel)]="itemsPerPage"
[searchable]="false" [clearable]="false" [searchable]="false"
(change)="itemsPerPageChanged()" [clearable]="false"
[dropdownPosition]="'top'"> [dropdownPosition]="'top'">
</ng-select> </ng-select>
</div> </div>
<div class="text-muted"> <div class="text-muted" *ngIf="allItems.length > 1">
{{ 'page' | translate }} {{ 'of' | translate }} ({{ {{'page' | translate}} {{currentPage}} {{'of' |
'totalItems' | translate }}) translate}} {{totalPages()}} ({{allItems.length}}
{{'totalItems' | translate}})
</div>
<div class="text-muted" *ngIf="allItems.length === 0">
{{'no_record' | translate}}
</div>
<div class="text-muted" *ngIf="allItems.length === 1">
{{'page' | translate}} {{currentPage}} {{'of' |
translate}} {{totalPages()}} ({{allItems.length}}
{{'record' | translate}})
</div> </div>
<div class="btn-group"> <div class="btn-group">
<button <button class="btn btn-primary waves-effect waves-light"
class="btn btn-primary waves-effect waves-light"> (click)="previousPage()"
[disabled]="currentPage === 1">
{{ 'previous' | translate }} {{ 'previous' | translate }}
</button> </button>
<button <button class="btn btn-primary waves-effect waves-light"
class="btn btn-primary waves-effect waves-light"> (click)="nextPage()"
[disabled]="currentPage >= totalPages()">
{{ 'next' | translate }} {{ 'next' | translate }}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -207,3 +210,8 @@
</div> </div>
</div> </div>
</div> </div>
<ng-template #noRecordsFound>
<div *ngIf="!isLoading && logsList.length === 0" class="text-center text-muted mt-3">
<p>{{'noLoggingDetailsFound' | translate}}</p>
</div>
</ng-template>

@ -1,28 +1,116 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select'; import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../utils/app.constants'; import { pageSizeOptions, toDateAfterFromDateValidator } from '../utils/app.constants';
import { CommonModule } from '@angular/common'; import { CommonModule, DatePipe } from '@angular/common';
import { HttpParams } from '@angular/common/http';
import { URIKey } from '../utils/uri-enums';
import { HttpURIService } from '../app.http.uri.service';
import { LogsManagementResponse } from '../utils/app.interfaces';
import { TableFilterPipe } from '../shared/pipes/table-filter.pipe';
@Component({ @Component({
selector: 'app-logging', selector: 'app-logging',
imports: [TranslateModule, FormsModule, NgSelectModule, CommonModule, ReactiveFormsModule], imports: [ TranslateModule, FormsModule, NgSelectModule, CommonModule, ReactiveFormsModule,
TableFilterPipe
],
providers: [ DatePipe ],
templateUrl: './logging.component.html', templateUrl: './logging.component.html',
styleUrl: './logging.component.scss' styleUrl: './logging.component.scss'
}) })
export class LoggingComponent { export class LoggingComponent implements OnInit {
currentPage: number = 1; currentPage: number = 1;
pageSizeOptions = pageSizeOptions pageSizeOptions = pageSizeOptions
renewalDataExpanded: boolean = true logsDataExpanded: boolean = true
itemsPerPage: number = 5; itemsPerPage: number = 5;
searchText: any; searchText: any = '';
toggleCard(arg0: string) { allItems: any[] = [];
throw new Error('Method not implemented.'); pagedItems: any[] = [];
} logsList: LogsManagementResponse[] = [];
nextPage() { logsSearchForm!: FormGroup;
throw new Error('Method not implemented.'); isLoading: boolean = false;
}
logsForm: any; constructor(
private fb: FormBuilder,
private httpService: HttpURIService,
private datePipe: DatePipe
) { }
ngOnInit() {
this.initializeLogsSearchForm();
}
initializeLogsSearchForm() {
this.logsSearchForm = this.fb.group({
fromDate: ['', Validators.required],
toDate: ['', Validators.required]
},
{
validators: toDateAfterFromDateValidator
})
}
getlogsData(){
if(this.logsSearchForm.invalid)
return;
let formValues = this.logsSearchForm.value;
this.isLoading = true;
let fromDateTransformed = this.datePipe.transform(formValues.fromDate, "dd-MM-yyyy");
let toDateTransformed = this.datePipe.transform(formValues.toDate, "dd-MM-yyyy");
const params = new HttpParams()
.set('fromDate', fromDateTransformed!)
.set('toDate', toDateTransformed!);
this.httpService.requestGET<LogsManagementResponse[]>(URIKey.LOGGER_MANAGER_URI, params).subscribe({
next: (response) => {
this.logsList = response;
this.allItems = [...this.logsList];
this.updatePagedItems();
this.isLoading = false;
},
error: (err) => {
console.error('Error fetching BOD data:', err);
this.logsList = [];
this.isLoading = false;
}
});
}
updatePagedItems(): void {
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
this.pagedItems = this.allItems.slice(startIndex, endIndex);
}
totalPages(): number {
return Math.ceil(this.allItems.length / this.itemsPerPage);
}
previousPage(): void {
if (this.currentPage > 1) {
this.currentPage--;
this.updatePagedItems();
}
}
nextPage(): void {
if (this.currentPage < this.totalPages()) {
this.currentPage++;
this.updatePagedItems();
}
}
itemsPerPageChanged(): void {
this.currentPage = 1;
this.updatePagedItems();
}
toggleTableCard(){
this.logsDataExpanded = !this.logsDataExpanded;
}
} }

@ -1,6 +1,6 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { PermissionNode } from "../utils/app.constants"; import { PermissionNode } from "../utils/app.interfaces";
import { HttpURIService } from "../app.http.uri.service"; import { HttpURIService } from "../app.http.uri.service";
import { StorageService } from "../shared/services/storage.service"; import { StorageService } from "../shared/services/storage.service";

@ -0,0 +1,25 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'tableFilter',
standalone: true
})
export class TableFilterPipe implements PipeTransform {
transform(items: any[], searchText: string, filterProperties: string[]): any[] {
if (!items) return [];
if (!searchText.trim()) return items;
searchText = searchText.toLowerCase().trim();
return items.filter(item => {
return filterProperties.some(prop => {
const value = this.getNestedProperty(item, prop)?.toString().toLowerCase();
return value?.includes(searchText);
});
});
}
private getNestedProperty(obj: any, path: string): any {
return path.split('.').reduce((o, p) => o?.[p], obj);
}
}

@ -1,6 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { PermissionNode } from '../utils/app.constants'; import { PermissionNode } from '../utils/app.interfaces';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { CredentialService } from '../services/credential.service'; import { CredentialService } from '../services/credential.service';
import { I18NService } from '../services/i18n.service'; import { I18NService } from '../services/i18n.service';

@ -1,3 +1,5 @@
import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms";
export const CONSTANTS = { export const CONSTANTS = {
POR_ORGACODE: '0005', POR_ORGACODE: '0005',
}; };
@ -8,10 +10,17 @@ export const pageSizeOptions = [
{ label: '20 items', value: 20 } { label: '20 items', value: 20 }
]; ];
export interface PermissionNode { export const toDateAfterFromDateValidator: ValidatorFn = (
name: string; group: AbstractControl
checked: boolean; ): ValidationErrors | null => {
expanded: boolean; const fromDate = group.get('fromDate')?.value;
children?: PermissionNode[]; const toDate = group.get('toDate')?.value;
buttons?: PermissionNode[];
if (!fromDate || !toDate) {
return null;
} }
return toDate >= fromDate
? null
: { toDateInvalid: true };
};

@ -0,0 +1,18 @@
export interface PermissionNode {
name: string;
checked: boolean;
expanded: boolean;
children?: PermissionNode[];
buttons?: PermissionNode[];
}
export interface LogsManagementResponse {
id: number,
method: string,
remoteIp: string,
requestBody: string,
requestUri: string,
responseCode: number,
userId: string,
dateTime: string
}

@ -12,5 +12,6 @@ export enum URIKey {
RESET_PASSWORD_URI = "RESET_PASSWORD_URI", RESET_PASSWORD_URI = "RESET_PASSWORD_URI",
CHANGE_PASSWORD_URI = "CHANGE_PASSWORD_URI", CHANGE_PASSWORD_URI = "CHANGE_PASSWORD_URI",
THIRD_PARTY_REGISTER_URI = "THIRD_PARTY_REGISTER_URI", THIRD_PARTY_REGISTER_URI = "THIRD_PARTY_REGISTER_URI",
TRANSACTION_LOGS = "TRANSACTION_LOGS" TRANSACTION_LOGS = "TRANSACTION_LOGS",
LOGGER_MANAGER_URI = "LOGGER_MANAGER_URI"
} }

@ -71,6 +71,11 @@
"Id": "ENTITY_TRANSACTION_LOGS", "Id": "ENTITY_TRANSACTION_LOGS",
"URI": "/transaction/logs", "URI": "/transaction/logs",
"UUID": "TRANSACTION_LOGS" "UUID": "TRANSACTION_LOGS"
},
{
"Id": "ENTITY_LOGGER_MANAGER_URI",
"URI": "/logs/getByDate",
"UUID": "LOGGER_MANAGER_URI"
} }
] ]
} }

@ -246,5 +246,7 @@
"permissionManagement": "إدارة الأذونات", "permissionManagement": "إدارة الأذونات",
"userCode": "مستخدم", "userCode": "مستخدم",
"choose" : "يختار", "choose" : "يختار",
"allow": "يسمح" "allow": "يسمح",
"toDateInvalidError": "يجب أن يكون تاريخ اليوم أكبر من أو يساوي تاريخ البداية",
"noLoggingDetailsFound": "لم يتم العثور على تفاصيل التسجيل"
} }

@ -245,5 +245,7 @@
"permissionManagement": "Permission Managment", "permissionManagement": "Permission Managment",
"userCode": "User", "userCode": "User",
"choose" : "Choose", "choose" : "Choose",
"allow": "Allow" "allow": "Allow",
"toDateInvalidError": "To Date must be greater than or equal to From Date",
"noLoggingDetailsFound": "No Logging Details found"
} }
Loading…
Cancel
Save