FINERACT-1724: Server-side pagination support for clients screen

This commit is contained in:
Arnold Galovics
2023-05-24 10:37:38 +02:00
committed by Arnold Gálovics
parent 517584280f
commit aa438ed4b2
7 changed files with 107 additions and 56 deletions
+1 -1
View File
@@ -102,7 +102,7 @@
"vendorChunk": false
},
"development": {
"optimization": true,
"optimization": false,
"sourceMap": true,
"namedChunks": true,
"outputHashing": "all",
+1
View File
@@ -15,6 +15,7 @@
"build": "npm run env -s && ng build --prod --output-hashing=none",
"build:prod": "node --max-old-space-size=16384 ./node_modules/@angular/cli/bin/ng build --prod --output-hashing=none --base-href=/web-app/",
"start": "npm run env -s && ng serve --aot --proxy-config proxy.conf.js",
"serve:dev": "npm run env -s && ng serve",
"serve:sw": "npm run build -s && npx http-server ./dist -p 4200",
"lint": "ng lint && stylelint \"src/**/*.scss\" --syntax scss && htmlhint \"src\" --config .htmlhintrc",
"test": "npm run env -s && ng test",
+16 -24
View File
@@ -6,9 +6,6 @@
</mat-form-field>
</div>
<div class="filter-box" *ngIf="existsClientsToFilter">
<mat-form-field class="filter-box m-r-30">
<input matInput placeholder="Filter by client name or external Id" class="filter-box" (keyup)="applyFilter($event.target.value)">
</mat-form-field>
<mat-checkbox #showClosedAccounts labelPosition="after" class="m-r-10">Show Closed Accounts
</mat-checkbox>
</div>
@@ -23,25 +20,26 @@
</div>
<div [hidden]="!existsClientsToFilter">
<table mat-table [dataSource]="dataSource" matSort>
<mat-progress-bar mode="indeterminate" *ngIf="isLoading"></mat-progress-bar>
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="sortChanged($event)">
<!-- Name Column -->
<ng-container matColumnDef="entityName">
<ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let row"> {{row.entityName}} </td>
<td mat-cell *matCellDef="let row"> {{row.displayName}} </td>
</ng-container>
<!-- Account no Column -->
<ng-container matColumnDef="entityAccountNo">
<ng-container matColumnDef="accountNumber">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Account No. </th>
<td mat-cell *matCellDef="let row"> {{row.entityAccountNo}} </td>
<td mat-cell *matCellDef="let row"> {{row.accountNumber}} </td>
</ng-container>
<!-- External ID Column -->
<ng-container matColumnDef="entityExternalId">
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef mat-sort-header> External Id </th>
<td mat-cell *matCellDef="let row">
<mifosx-external-identifier externalId="{{row.entityExternalId}}"></mifosx-external-identifier>
<mifosx-external-identifier externalId="{{row.externalId}}"></mifosx-external-identifier>
</td>
</ng-container>
@@ -49,33 +47,27 @@
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef> Status </th>
<td mat-cell *matCellDef="let row">
<div [ngClass]="row.entityStatus.code | statusLookup">
<div [ngClass]="row.status.code | statusLookup">
<fa-icon icon="stop"></fa-icon>
<span class="m-l-10 status">
{{ row.entityStatus.value | translate }}
{{ row.status.value | translate }}
</span>
</div>
</td>
</ng-container>
<!-- Color Column -->
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Office Name </th>
<td mat-cell *matCellDef="let row"> {{row.parentName}} </td>
</ng-container>
<!-- ID Column -->
<ng-container matColumnDef="staffName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Staff Name </th>
<td mat-cell *matCellDef="let row"> {{row.staffName}} </td>
<ng-container matColumnDef="officeName">
<th mat-header-cell *matHeaderCellDef> Office Name </th>
<td mat-cell *matCellDef="let row"> {{row.officeName}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="[row.entityId,'general']" class="select-row">
</tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="[row.id,'general']" class="select-row">
</tr>
</table>
<mat-paginator [hidden]="!moreClientsToFilter" [pageSize]="50" [pageSizeOptions]="[50, 100]"
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="[5, 50, 100]" [length]="totalRows" (page)="pageChanged($event)"
showFirstLastButtons></mat-paginator>
</div>
+53 -29
View File
@@ -1,13 +1,13 @@
/** Angular Imports. */
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSort, Sort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
/** Custom Services */
import { SearchService } from 'app/search/search.service';
import { environment } from 'environments/environment';
import { ClientsService } from './clients.service';
@Component({
selector: 'mifosx-clients',
@@ -15,51 +15,75 @@ import { environment } from 'environments/environment';
styleUrls: ['./clients.component.scss'],
})
export class ClientsComponent implements OnInit {
@ViewChild('showClosedAccounts', { static: true }) showClosedAccounts: MatCheckbox;
@ViewChild('showClosedAccounts') showClosedAccounts: MatCheckbox;
displayedColumns = ['entityName', 'entityAccountNo', 'entityExternalId', 'status', 'parentName', 'staffName'];
dataSource: MatTableDataSource<any>;
displayedColumns = ['displayName', 'accountNumber', 'externalId', 'status', 'officeName'];
dataSource: MatTableDataSource<any> = new MatTableDataSource();
existsClientsToFilter = false;
notExistsClientsToFilter = false;
moreClientsToFilter = false;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
totalRows: number;
isLoading = false;
pageSize = 50;
currentPage = 0;
filterText = '';
constructor(private searchService: SearchService) { }
sortAttribute = '';
sortDirection = '';
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(private clientService: ClientsService) { }
ngOnInit() {
if (environment.preloadClients) {
this.getClients('');
this.getClients();
}
}
/**
* Filter Client Data
* @param {string} filterValue Value to filter data.
*/
applyFilter(filterValue: string = '') {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
/**
* Searches server for query and resource.
*/
search(value: string) {
if (value !== '') {
this.getClients(value);
}
this.filterText = value;
this.getClients();
}
private getClients(value: string) {
this.searchService.getSearchResults(value, 'clients').subscribe((data: any) => {
this.dataSource = new MatTableDataSource(data);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.existsClientsToFilter = (data.length > 0);
private getClients() {
this.isLoading = true;
this.clientService.searchByText(this.filterText, this.currentPage, this.pageSize, this.sortAttribute, this.sortDirection)
.subscribe((data: any) => {
this.dataSource.data = data.content;
this.totalRows = data.totalElements;
this.existsClientsToFilter = (data.numberOfElements > 0);
this.notExistsClientsToFilter = !this.existsClientsToFilter;
this.moreClientsToFilter = (data.length > 50);
this.isLoading = false;
}, (error: any) => {
this.isLoading = false;
});
}
pageChanged(event: PageEvent) {
this.pageSize = event.pageSize;
this.currentPage = event.pageIndex;
this.getClients();
}
sortChanged(event: Sort) {
if (event.direction === '') {
this.sortDirection = '';
this.sortAttribute = '';
} else {
this.sortAttribute = event.active;
this.sortDirection = event.direction;
}
this.currentPage = 0;
this.paginator.firstPage();
this.getClients();
}
}
+22
View File
@@ -381,4 +381,26 @@ export class ClientsService {
getCollateralTemplate(clientId: any) {
return this.http.get(`/clients/${clientId}/collaterals/template`);
}
searchByText(text: string, page: number, pageSize: number, sortAttribute: string = '', sortDirection: string = '') {
let request: any = {
request: {
text
},
page,
size: pageSize
}
if (sortAttribute !== '' && sortDirection !== '') {
request = {
...request,
sorts: [
{
direction: sortDirection,
property: sortAttribute
}
]
}
}
return this.http.post(`/v2/clients/search`, request);
}
}
+7 -2
View File
@@ -22,13 +22,18 @@ export class ApiPrefixInterceptor implements HttpInterceptor {
* Intercepts a Http request and prefixes it with `serverUrl`.
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let baseUrl = this.settingsService.serverUrl;
const versionRegex = /^\/(v[1-9][0-9]*\/).*$/;
if (versionRegex.test(request.url)) {
baseUrl = this.settingsService.baseServerUrl;
}
/**
* Ignore URLs that are complete for i18n
*/
if (!request.url.includes('http:') && !request.url.includes('https:')) {
request = request.clone({ url: this.settingsService.serverUrl + request.url });
request = request.clone({ url: baseUrl + request.url });
}
return next.handle(request);
}
+7
View File
@@ -148,6 +148,13 @@ export class SettingsService {
return environment.baseApiUrl;
}
/**
* Returns server url with api path without version
*/
get baseServerUrl() {
return this.server + environment.apiProvider;
}
/**
* Returns server url with api path and version
*/