| @@ -52,6 +52,7 @@ import { ChartRecentTenLoansComponent } from './chart-recent-ten-loans/chart-rec | |||
| import { ChartTopBrokersComponent } from './chart-top-brokers/chart-top-brokers.component'; | |||
| import { ListAllLoansComponent } from './list-all-loans/list-all-loans.component'; | |||
| import { TopBarComponent } from './top-bar/top-bar.component'; | |||
| import {LoanSummaryService} from './service/loan_summary.service'; | |||
| @@ -111,6 +112,7 @@ import { TopBarComponent } from './top-bar/top-bar.component'; | |||
| AuthGuard, | |||
| AuthService, | |||
| WebSocketService, | |||
| LoanSummaryService, | |||
| { | |||
| provide: HTTP_INTERCEPTORS, | |||
| useClass: AuthHttpInterceptor, | |||
| @@ -1 +1,113 @@ | |||
| <p>list-all-loans works!</p> | |||
| <kendo-grid | |||
| [data]="view | async" | |||
| [loading]="view.loading" | |||
| [pageSize]="pageSize" | |||
| [skip]="skip" | |||
| [sortable]="true" | |||
| [sort]="sort" | |||
| [pageable]="true" | |||
| [height]="1000" | |||
| [navigable]="true" | |||
| (dataStateChange)="dataStateChange($event)" | |||
| class="fullheight_grid" | |||
| > | |||
| <kendo-grid-column field="Id" width="50" [class]="'topAlign'" [sortable]="false"> | |||
| <ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex+1" > | |||
| <p title="{{dataItem.Id}}" > {{dataItem.Index}}</p> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column-group title="People" [columnMenu]="false" [headerClass]="'colGroupPeople'"> | |||
| <kendo-grid-column field="Client" width="220" title="Client(s)" [class]="'topAlign'" [headerClass]="'colClient'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div *ngFor="let p of dataItem.Client, let idx=index "> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.ClientIds[idx])}"></div> | |||
| <div class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Broker" width="220" title="Broker (s)" [class]="'topAlign'" [headerClass]="'colBroker'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div *ngFor="let p of dataItem.Broker, let idx=index "> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.BrokerIds[idx])}"></div> | |||
| <div class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="OtherRewarder" width="220" title="Beneficiary(ies)" [class]="'topAlign'" [headerClass]="'colOtherRewarder'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div *ngFor="let p of dataItem.OtherRewarder, let idx=index "> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.OtherRewarderIds[idx])}"></div> | |||
| <div class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <kendo-grid-column-group title="Loan Details" [columnMenu]="false" [headerClass]="'colGroupLoanDetails'"> | |||
| <kendo-grid-column field="Rating" title="Rating" width="110" [resizable]="false" filter="numeric" [headerClass]="'colRating'" [class]="'topAlign colRating'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <kendo-rating | |||
| [value]="dataItem.Rating" | |||
| [max]="5" | |||
| ></kendo-rating> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Item" [sortable]="true" [class]="'topAlign'" width="300"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="status" title="Status" [width]="180" [headerClass]="'colStatus'" [class]="'alignStatus colStatus'" [resizable]="false" filter="boolean" > | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span *ngIf="dataItem.Status != 'none'" class="badge badge-success">{{dataItem.Status}}</span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Settlement" title="Settlement" [class]="'topAlign'" width="120"> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <kendo-grid-column-group title="Lender Details" [columnMenu]="false" [headerClass]="'colGroupLenderDetails'"> | |||
| <kendo-grid-column field="Amount" title="Amount($) " [sortable]="true" width="130" [headerClass]="'colAmount'" [class]="'topAlign colAmount'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span [ngClass]="{'green text-bold': dataItem.Amount < 500000}">{{ dataItem.Amount | currency }}</span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Balance" title="Balance($)" width="130" [class]="'topAlign'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span *ngIf="dataItem.Balance > 0" [ngClass]="{'green text-bold': dataItem.Balance < 500000}">{{ dataItem.Balance | currency }}</span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="OffsetBalance" title="Offset Balance($)" width="140" [class]="'topAlign'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span *ngIf="dataItem.OffsetBalance > 0" [ngClass]="{'red text-bold': dataItem.Percentage > 60}"> | |||
| {{ dataItem.OffsetBalance | currency }} | |||
| </span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Trail" title="Income Trail($) " [sortable]="true" width="130" [headerClass]="'colTrail'" [class]="'topAlign colTrail'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span *ngIf="dataItem.Trail > 0" [ngClass]="{'green text-bold': dataItem.Trail < 500000}">{{ dataItem.Trail | currency }}</span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="LastPayInDate" title="Recent Trail" width="120" [sortable]="true" [class]="'topAlign'"> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <kendo-grid-column-group title="Income Distribution" [columnMenu]="false" [headerClass]="'colIncomeDistribution colPayOut'"> | |||
| <kendo-grid-column field="PayOut" title="Paid ($)" width="140" [class]="'topAlign colPayOut'" [headerClass]="'colPayOut'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span [ngClass]="{'red text-bold': dataItem.PayOut > dataItem.Trail}"> | |||
| {{ dataItem.PayOut | currency }} | |||
| </span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <div *kendoGridDetailTemplate="let dataItem"> | |||
| <div style="height:100px; background-color: grey;"> {{ dataItem.Item }} details should be here.</div> | |||
| <!-- category-details [category]="dataItem"></category-details --> | |||
| </div> | |||
| </kendo-grid> | |||
| @@ -0,0 +1,73 @@ | |||
| .fullheight_grid { | |||
| height: calc(100vh - 48px) !important; | |||
| } | |||
| .k-grid td:first-child{ | |||
| vertical-align: top; | |||
| } | |||
| .k-grid td.topAlign{ | |||
| vertical-align: top; | |||
| } | |||
| .k-grid td.alignStatus{ | |||
| vertical-align: top; | |||
| text-align: left; | |||
| } | |||
| .customer-photo{ | |||
| display: inline-block; | |||
| width: 32px; | |||
| height: 32px; | |||
| border-radius: 50%; | |||
| background-size: 32px 35px; | |||
| background-position: center center; | |||
| vertical-align: middle; | |||
| line-height: 32px; | |||
| box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2); | |||
| margin-left: 5px; | |||
| } | |||
| .customer-name { | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| line-height: 32px; | |||
| padding-left: 10px; | |||
| } | |||
| .k-grid-header .k-header.colGroupPeople, | |||
| .k-grid-header .k-header.colClient, | |||
| .k-grid-header .k-header.colBroker, | |||
| .k-grid-header .k-header.colOtherRewarder | |||
| { | |||
| text-align:center; | |||
| } | |||
| .k-grid-header .k-header.colGroupLoanDetails, | |||
| .k-grid-header .k-header.colGroupLenderDetails, | |||
| .k-grid-header .k-header.colGroupIncomeDistribution{ | |||
| border-left: 5px solid #ffeabe !important; | |||
| } | |||
| .colRating, .colAmount, .colPayOut{ | |||
| border-left: 5px solid #ffeabe !important; | |||
| } | |||
| .colTrail{ | |||
| font-width: 300; | |||
| color: green ; | |||
| //border-left: 2px solid green !important; | |||
| //border-right: 2px solid green !important; | |||
| border-bottom: 2px solid green !important; | |||
| } | |||
| .k-grid-header .k-header.colIncomeDistribution, | |||
| .k-grid-header .k-header.colGroupLoanDetails{ | |||
| background-color: #ffeabe; | |||
| } | |||
| .red { | |||
| color: #d9534f; | |||
| } | |||
| .text-bold { | |||
| font-weight: 600; | |||
| } | |||
| @@ -22,4 +22,8 @@ describe('ListAllLoansComponent', () => { | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -1,15 +1,59 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {Component, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; | |||
| import {DataStateChangeEvent, GridComponent, GridDataResult} from '@progress/kendo-angular-grid'; | |||
| import {Observable} from 'rxjs'; | |||
| import {SortDescriptor} from '@progress/kendo-data-query'; | |||
| import {LoanSummaryService} from '../service/loan_summary.service'; | |||
| import {images} from '../transaction-list/images'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| @Component({ | |||
| selector: 'app-list-all-loans', | |||
| templateUrl: './list-all-loans.component.html', | |||
| styleUrls: ['./list-all-loans.component.scss'] | |||
| styleUrls: ['./list-all-loans.component.scss'], | |||
| encapsulation: ViewEncapsulation.None, | |||
| }) | |||
| export class ListAllLoansComponent implements OnInit { | |||
| public view: LoanSummaryService; | |||
| public sort: Array<SortDescriptor> = []; | |||
| public pageSize = 10; | |||
| public skip = 0; | |||
| constructor() { } | |||
| @ViewChild(GridComponent, { static: true }) public grid: GridComponent; | |||
| ngOnInit(): void { | |||
| constructor(private service: LoanSummaryService, private auth: AuthService) { } | |||
| public ngOnInit(): void { | |||
| // Bind directly to the service as it is a Subject | |||
| this.view = this.service; | |||
| // Fetch the data with the initial state | |||
| this.loadData(); | |||
| } | |||
| public dataStateChange({ skip, take, sort }: DataStateChangeEvent): void { | |||
| // Save the current state of the Grid component | |||
| this.skip = skip; | |||
| this.pageSize = take; | |||
| this.sort = sort; | |||
| // Reload the data with the new state | |||
| this.loadData(); | |||
| // console.log(this.skip, this.pageSize, this.sort); | |||
| // Expand the first row initially | |||
| this.grid.expandRow(0); | |||
| } | |||
| private loadData(): void { | |||
| this.service.query({ skip: this.skip, take: this.pageSize, sort: this.sort }); | |||
| } | |||
| private photoURL(peopleId: any): string { | |||
| let url = this.auth.getUrl('avatar/') + peopleId; | |||
| console.log(url, peopleId); | |||
| return 'url("' + url + '")'; | |||
| } | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| import { Injectable } from '@angular/core'; | |||
| import { HttpClient } from '@angular/common/http'; | |||
| import { GridDataResult } from '@progress/kendo-angular-grid'; | |||
| import { toODataString } from '@progress/kendo-data-query'; | |||
| import { Observable, BehaviorSubject } from 'rxjs'; | |||
| import { map, tap } from 'rxjs/operators'; | |||
| import {AuthService} from './auth.service'; | |||
| export abstract class LoanQueryService extends BehaviorSubject<GridDataResult> { | |||
| public loading: boolean; | |||
| private BASE_URL = ''; | |||
| constructor( | |||
| private http: HttpClient, | |||
| private auth: AuthService, | |||
| protected tableName: string | |||
| ) { | |||
| super(null); | |||
| this.BASE_URL = this.auth.getUrl('grid/loan/'); | |||
| } | |||
| public query(state: any): void { | |||
| this.fetch(this.tableName, state) | |||
| .subscribe(x => super.next(x)); | |||
| } | |||
| protected fetch(tableName: string, state: any): Observable<GridDataResult> { | |||
| const queryStr = `${toODataString(state)}&$count=true`; | |||
| this.loading = true; | |||
| return this.http | |||
| .get<GridDataResult>(`${this.BASE_URL}${tableName}?${queryStr}`) | |||
| .pipe( | |||
| map(response => { | |||
| console.log( response.data[0]['Settlement']); | |||
| console.log( response.data[0]['LastPayInDate']); | |||
| return response; | |||
| }), | |||
| tap(() => this.loading = false) | |||
| ); | |||
| } | |||
| } | |||
| @Injectable() | |||
| export class ProductsService extends LoanQueryService { | |||
| constructor(http: HttpClient, auth: AuthService) { super(http, auth, 'Products'); } | |||
| public queryForCategory({ CategoryID }: { CategoryID: number }, state?: any): void { | |||
| this.query(Object.assign({}, state, { | |||
| filter: { | |||
| filters: [{ | |||
| field: 'CategoryID', operator: 'eq', value: CategoryID | |||
| }], | |||
| logic: 'and' | |||
| } | |||
| })); | |||
| } | |||
| public queryForProductName(ProductName: string, state?: any): void { | |||
| this.query(Object.assign({}, state, { | |||
| filter: { | |||
| filters: [{ | |||
| field: 'ProductName', operator: 'contains', value: ProductName | |||
| }], | |||
| logic: 'and' | |||
| } | |||
| })); | |||
| } | |||
| } | |||
| @Injectable() | |||
| export class LoanSummaryService extends LoanQueryService { | |||
| constructor(http: HttpClient, auth: AuthService) { super(http, auth, 'full-loan-overview'); } | |||
| queryAll(st?: any): Observable<GridDataResult> { | |||
| const state = Object.assign({}, st); | |||
| delete state.skip; | |||
| delete state.take; | |||
| return this.fetch(this.tableName, state); | |||
| } | |||
| } | |||