From 504a14b968cfb70c36c82ae663c9825ce8227d6e Mon Sep 17 00:00:00 2001 From: Jivjyot Singh Date: Sat, 20 Jul 2019 11:42:44 +0530 Subject: [PATCH] refactor(clients): clients list page - use statuslookup.pipe to set styles to represent status of the client - add button to import client and create client - add server side pagination - toggle show closed clients in table --- src/app/centers/centers.component.ts | 2 +- src/app/clients/clients-routing.module.ts | 5 -- src/app/clients/clients.component.html | 50 +++++++------ src/app/clients/clients.component.scss | 33 +++------ src/app/clients/clients.component.ts | 72 +++++++++++-------- src/app/clients/clients.datasource.ts | 60 ++++++++++++++++ src/app/clients/clients.module.ts | 2 +- src/app/clients/clients.service.ts | 9 ++- .../common-resolvers/clients.resolver.ts | 30 -------- src/app/pipes/status-lookup.pipe.ts | 11 ++- src/app/shared/icons.module.ts | 8 ++- src/assets/styles/_colours.scss | 5 +- src/assets/styles/_helper.scss | 16 +++++ 13 files changed, 186 insertions(+), 117 deletions(-) create mode 100644 src/app/clients/clients.datasource.ts delete mode 100644 src/app/clients/common-resolvers/clients.resolver.ts diff --git a/src/app/centers/centers.component.ts b/src/app/centers/centers.component.ts index 8cd00b953..68cc6720a 100644 --- a/src/app/centers/centers.component.ts +++ b/src/app/centers/centers.component.ts @@ -8,7 +8,7 @@ import { Observable } from 'rxjs'; selector: 'mifosx-app-centers', templateUrl: './centers.component.html', styleUrls: ['./centers.component.scss'], - encapsulation: ViewEncapsulation.None + }) export class CentersComponent implements OnInit { private ELEMENT_DATA: any = undefined; diff --git a/src/app/clients/clients-routing.module.ts b/src/app/clients/clients-routing.module.ts index eb8c5a89f..652b490a1 100644 --- a/src/app/clients/clients-routing.module.ts +++ b/src/app/clients/clients-routing.module.ts @@ -21,7 +21,6 @@ import { DatatableTabComponent } from './clients-view/datatable-tab/datatable-ta import { AddressTabComponent } from './clients-view/address-tab/address-tab.component'; /** Custom Resolvers */ -import { ClientsResolver } from './common-resolvers/clients.resolver'; import { ClientViewResolver } from './common-resolvers/client-view.resolver'; import { ClientAccountsResolver } from './common-resolvers/client-accounts.resolver'; import { ClientAddressResolver } from './common-resolvers/client-address.resolver'; @@ -47,9 +46,6 @@ const routes: Routes = [ { path: '', component: ClientsComponent, - resolve: { - clientsData: ClientsResolver - } }, { path: ':clientId', @@ -152,7 +148,6 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - ClientsResolver, ClientViewResolver, ClientAccountsResolver, ClientAddressResolver, diff --git a/src/app/clients/clients.component.html b/src/app/clients/clients.component.html index b128bcc58..bb9e86621 100644 --- a/src/app/clients/clients.component.html +++ b/src/app/clients/clients.component.html @@ -1,10 +1,22 @@ -
- - - - - -
+ +
+ +
+ + +
+
+
@@ -25,16 +37,14 @@ - - - - + + @@ -42,11 +52,11 @@ - + - - + + @@ -64,11 +74,11 @@ -
{{row.externalId}} Status -
- +
Status +
+
-
Mobile Number {{ client.mobileNo }} Gender {{ client.gender.name }} Gender {{ client.gender.name }}
- +
-
+ \ No newline at end of file diff --git a/src/app/clients/clients.component.scss b/src/app/clients/clients.component.scss index 18b291765..5c31bdbf3 100644 --- a/src/app/clients/clients.component.scss +++ b/src/app/clients/clients.component.scss @@ -1,26 +1,11 @@ - table { - width: 100%; +.container { + .action-button{ + margin-left: auto; + } + .search-button{ + min-width: 30px; + margin: 4px; + + } } - -.mat-form-field { - font-size: 14px; - width: 100%; -} - -td, th { - width: 15%; -} - -.filter{ - width: 50%; -} - -.inactive { - color: red; -} - -.active { - color: green; -} - diff --git a/src/app/clients/clients.component.ts b/src/app/clients/clients.component.ts index a4e087f97..fa45c061e 100644 --- a/src/app/clients/clients.component.ts +++ b/src/app/clients/clients.component.ts @@ -1,48 +1,60 @@ -import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; -import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; +import { MatPaginator, MatSort, MatTableDataSource, MatCheckbox } from '@angular/material'; +import { ClientsDataSource } from './clients.datasource'; + +/** rxjs Imports */ +import { merge } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +/** Custom Services */ +import { ClientsService } from './clients.service'; + @Component({ selector: 'mifosx-clients', templateUrl: './clients.component.html', styleUrls: ['./clients.component.scss'], - encapsulation: ViewEncapsulation.None - }) -export class ClientsComponent implements OnInit { - post: any = []; +export class ClientsComponent implements OnInit, AfterViewInit { + @ViewChild('showClosedAccounts') showClosedAccounts: MatCheckbox; + displayedColumns = ['name', 'clientno', 'externalid', 'status', 'mobileNo', 'gender', 'office', 'staff']; - dataSource = new MatTableDataSource(); + dataSource: ClientsDataSource; @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - constructor(private route: ActivatedRoute) { - this.route.data.subscribe((clientsData: { clientsData: any }) => { - const res = clientsData.clientsData.pageItems; - res.active = !!res.active; - this.dataSource = new MatTableDataSource(res); - this.dataSource.paginator = this.paginator; - this.dataSource.sortingDataAccessor = (data: any, property: any) => { - switch (property) { - case 'gender': return data.gender.name; - default: return data[property]; - } - }; - this.dataSource.sort = this.sort; - }); + constructor(private clientsService: ClientsService) { + } + ngOnInit() { + this.getClients(); + } + ngAfterViewInit() { + this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); + merge(this.sort.sortChange, this.paginator.page, this.showClosedAccounts.change) + .pipe( + tap(() => this.loadClientsPage()) + ) + .subscribe(); + } - ngOnInit() { } - - applyFilter(filterValue: string) { - filterValue = filterValue.trim(); // Remove whitespace - filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches - this.dataSource.filter = filterValue; - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); + /** + * Loads a page of journal entries. + */ + loadClientsPage() { + if (!this.sort.direction) { + delete this.sort.active; } + this.dataSource.getClients(this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize, !this.showClosedAccounts.checked); } + /** + * Initializes the data source for clients table and loads the first page. + */ + getClients() { + this.dataSource = new ClientsDataSource(this.clientsService); + this.dataSource.getClients(this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize, !this.showClosedAccounts.checked); + } } diff --git a/src/app/clients/clients.datasource.ts b/src/app/clients/clients.datasource.ts new file mode 100644 index 000000000..fe8ecaff8 --- /dev/null +++ b/src/app/clients/clients.datasource.ts @@ -0,0 +1,60 @@ +/** Angular Imports */ +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; + +/** rxjs Imports */ +import { Observable, BehaviorSubject } from 'rxjs'; + +/** Custom Services */ +import { ClientsService } from './clients.service'; + +/** + * Clients custom data source to implement server side filtering, pagination and sorting. + */ +export class ClientsDataSource implements DataSource { + + /** clients behavior subject to represent loaded journal clients page. */ + private clientsSubject = new BehaviorSubject([]); + /** Records subject to represent total number of filtered clients records. */ + private recordsSubject = new BehaviorSubject(0); + /** Records observable which can be subscribed to get the value of total number of filtered clients records. */ + public records$ = this.recordsSubject.asObservable(); + + /** + * @param {ClientsService} clientsService Clients Service + */ + constructor(private clientsService: ClientsService) { } + + /** + * Gets clients on the basis of provided parameters and emits the value. + * @param {any} filterBy Properties by which clients should be filtered. + * @param {string} orderBy Property by which clients should be sorted. + * @param {string} sortOrder Sort order: ascending or descending. + * @param {number} pageIndex Page number. + * @param {number} limit Number of clients within the page. + */ + getClients(orderBy: string = '', sortOrder: string = '', pageIndex: number = 0, limit: number = 10, clientActive: boolean = true) { + this.clientsSubject.next([]); + this.clientsService.getClients(orderBy, sortOrder, pageIndex * limit, limit) + .subscribe((clients: any) => { + clients.pageItems = (clientActive) ? (clients.pageItems.filter((client: any) => client.active)) : clients.pageItems; + this.recordsSubject.next(clients.totalFilteredRecords); + this.clientsSubject.next(clients.pageItems); + }); + } + + /** + * @param {CollectionViewer} collectionViewer + */ + connect(collectionViewer: CollectionViewer): Observable { + return this.clientsSubject.asObservable(); + } + + /** + * @param {CollectionViewer} collectionViewer + */ + disconnect(collectionViewer: CollectionViewer): void { + this.clientsSubject.complete(); + this.recordsSubject.complete(); + } + +} diff --git a/src/app/clients/clients.module.ts b/src/app/clients/clients.module.ts index dd54f570a..8ca8095fd 100644 --- a/src/app/clients/clients.module.ts +++ b/src/app/clients/clients.module.ts @@ -5,7 +5,7 @@ import { DatePipe } from '@angular/common'; /** Custom Modules */ import { ClientsRoutingModule } from './clients-routing.module'; import { SharedModule } from 'app/shared/shared.module'; -import {PipesModule} from '../pipes/pipes.module'; +import { PipesModule } from '../pipes/pipes.module'; /** Custom Components */ import { ClientsComponent } from './clients.component'; diff --git a/src/app/clients/clients.service.ts b/src/app/clients/clients.service.ts index a2baa259a..f7c0b7303 100644 --- a/src/app/clients/clients.service.ts +++ b/src/app/clients/clients.service.ts @@ -16,8 +16,13 @@ export class ClientsService { */ constructor(private http: HttpClient) { } - getClients(): Observable { - const httpParams = new HttpParams().set('orderBy', 'id'); + getClients(orderBy: string, sortOrder: string, offset: number, limit: number): Observable { + const httpParams = new HttpParams() + .set('offset', offset.toString()) + .set('limit', limit.toString()) + .set('sortOrder', sortOrder) + .set('orderBy', orderBy); + return this.http.get('/clients', { params: httpParams }); } diff --git a/src/app/clients/common-resolvers/clients.resolver.ts b/src/app/clients/common-resolvers/clients.resolver.ts deleted file mode 100644 index a672e861f..000000000 --- a/src/app/clients/common-resolvers/clients.resolver.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** Angular Imports */ -import { Injectable } from '@angular/core'; -import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; - -/** rxjs Imports */ -import { Observable } from 'rxjs'; - -/** Custom Services */ -import { ClientsService } from '../clients.service'; - -/** - * Clients data resolver. - */ -@Injectable() -export class ClientsResolver implements Resolve { - - /** - * @param {ClientsService} ClientsService Clients service. - */ - constructor(private clientsService: ClientsService) { } - - /** - * Returns the Clients data. - * @returns {Observable} - */ - resolve(): Observable { - return this.clientsService.getClients(); - } - -} diff --git a/src/app/pipes/status-lookup.pipe.ts b/src/app/pipes/status-lookup.pipe.ts index f58871b43..0920b194f 100644 --- a/src/app/pipes/status-lookup.pipe.ts +++ b/src/app/pipes/status-lookup.pipe.ts @@ -26,7 +26,16 @@ export class StatusLookupPipe implements PipeTransform { 'shareAccountStatusType.active': 'status-active', 'shareAccountStatusType.rejected': 'status-rejected', 'loanProduct.active': 'status-active', - 'loanProduct.inActive': 'status-inactive' + 'loanProduct.inActive': 'status-inactive', + 'clientStatusType.pending': 'status-pending', + 'clientStatusType.closed': 'status-closed', + 'clientStatusType.rejected': 'status-rejected', // write + 'clientStatusType.withdraw': 'status-withdraw', // write + 'clientStatusType.active': 'status-active', + 'clientStatusType.submitted.and.pending.approval': 'status-pending', + 'clientStatusTYpe.approved': 'status-approved', + 'clientStatusType.transfer.in.progress': 'status-transfer-progress', // write + 'clientStatusType.transfer.on.hold': 'status-transfer-hold' // write }; return cssClassNameLookup[status]; } diff --git a/src/app/shared/icons.module.ts b/src/app/shared/icons.module.ts index 5bfe5e6a9..84db67a3d 100644 --- a/src/app/shared/icons.module.ts +++ b/src/app/shared/icons.module.ts @@ -72,10 +72,12 @@ import { faUsers, faUserShield, faDownload, + faUpload, faCloudDownloadAlt, faCloudUploadAlt, faTimes, - faStickyNote + faStickyNote, + faStop } from '@fortawesome/free-solid-svg-icons'; /** Add icons to the library for convenient access in other components. */ @@ -147,10 +149,12 @@ library.add( faUsers, faUserShield, faDownload, + faUpload, faCloudDownloadAlt, faCloudUploadAlt, faTimes, - faStickyNote + faStickyNote, + faStop ); /** diff --git a/src/assets/styles/_colours.scss b/src/assets/styles/_colours.scss index a72466c07..4e52cf1d7 100644 --- a/src/assets/styles/_colours.scss +++ b/src/assets/styles/_colours.scss @@ -71,5 +71,8 @@ $status-approved: #0f36e4; $status-overpaid: #800080; $status-active-overdue: #ff0000; $status-matured: #ff4000; - $state-done: #00ce00; +$status-transfer-progress:#FF4000; +$status-transfer-hold:#DF0101; + + diff --git a/src/assets/styles/_helper.scss b/src/assets/styles/_helper.scss index e3bf0328f..7b3195de4 100644 --- a/src/assets/styles/_helper.scss +++ b/src/assets/styles/_helper.scss @@ -26,6 +26,14 @@ body { color: rgba(0, 0, 0, 0.87); line-height: 1.5; font-family: Roboto, Helvetica Neue, sans-serif; + + table { + border: 1px solid rgba($color: #000000, $alpha: 0.12); + width: 100%; + tr:nth-child(even) { + background-color: #f2f2f2; + } + } } div { @@ -1707,4 +1715,12 @@ textarea.noresize { .mat-step-header .mat-step-icon-state-done { background-color: $state-done; + + .status-transfer-progress { + color: $status-transfer-progress; + } + + .status-transfer-hold { + color: $status-transfer-hold; + } }