| @@ -100,6 +100,7 @@ import { UploadingProgressCardComponent } from './uploading-progress-card/upload | |||
| import { LoanSelectComponent } from './loan-select/loan-select.component'; | |||
| import {PopupModule} from '@progress/kendo-angular-popup'; | |||
| import { LoansAllComponent } from './loans-all/loans-all.component'; | |||
| import {FunderNameService} from './service/funder.name.service'; | |||
| @@ -214,6 +215,7 @@ export function initializeApp(appConfig: AppConfig): () => Promise<void> { | |||
| WebSocketService, | |||
| LoanSummaryService, | |||
| LoanSingleService, | |||
| FunderNameService, | |||
| { | |||
| provide: HTTP_INTERCEPTORS, | |||
| useClass: AuthHttpInterceptor, | |||
| @@ -33,10 +33,9 @@ | |||
| <span class="badge badge-info">{{MaxSelect}}</span> | |||
| </div> | |||
| <button *ngIf="EnableSelectButton" kendoButton [primary]=true | |||
| class="select-loan" icon="close-circle" style="float:right;" | |||
| class="select-loan" icon="save" style="float:right;" | |||
| (click)="onFinishSelection()" | |||
| > | |||
| Finish Selection</button> | |||
| >{{FinishButtonText}}</button> | |||
| </ng-template> | |||
| @@ -1,100 +1,102 @@ | |||
| .fullheight_grid { | |||
| kendo-grid.fullheight_grid { | |||
| height: 100% !important; | |||
| } | |||
| .k-grid td:first-child{ | |||
| vertical-align: top; | |||
| } | |||
| .k-grid td.topAlign{ | |||
| vertical-align: top; | |||
| } | |||
| .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; | |||
| } | |||
| .k-grid td.alignStatus{ | |||
| vertical-align: top; | |||
| text-align: left; | |||
| } | |||
| .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; | |||
| } | |||
| div.selected-loans{ | |||
| position: relative; | |||
| padding-right:200px; | |||
| width: calc(100% - 150px); | |||
| min-height: 60px; | |||
| overflow: auto hidden; | |||
| app-loan-card{ | |||
| display: block; | |||
| .k-grid-header .k-header.colGroupLoanDetails, | |||
| .k-grid-header .k-header.colGroupLenderDetails, | |||
| .k-grid-header .k-header.colGroupIncomeDistribution{ | |||
| border-left: 5px solid #ffeabe !important; | |||
| } | |||
| } | |||
| div.selection-percentage{ | |||
| float:right; | |||
| position:absolute; | |||
| right: 10px; | |||
| bottom: 1px; | |||
| } | |||
| div.selected-loans{ | |||
| position: relative; | |||
| padding-right:200px; | |||
| width: calc(100% - 150px); | |||
| min-height: 60px; | |||
| overflow: auto hidden; | |||
| app-loan-card{ | |||
| display: block; | |||
| } | |||
| } | |||
| div.selection-percentage{ | |||
| float:right; | |||
| position:absolute; | |||
| right: 10px; | |||
| bottom: 1px; | |||
| } | |||
| button.select-loan{ | |||
| position: absolute; | |||
| right: 10px; | |||
| top: 10px; | |||
| } | |||
| .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; | |||
| margin-bottom: 10px; | |||
| } | |||
| button.select-loan{ | |||
| position: absolute; | |||
| right: 10px; | |||
| top: 10px; | |||
| } | |||
| .customer-name { | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| line-height: 32px; | |||
| padding-left: 10px; | |||
| } | |||
| .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; | |||
| margin-bottom: 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; | |||
| } | |||
| .customer-name { | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| line-height: 32px; | |||
| padding-left: 10px; | |||
| } | |||
| .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; | |||
| } | |||
| .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; | |||
| } | |||
| .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; | |||
| } | |||
| .k-grid-header .k-header.colIncomeDistribution, | |||
| .k-grid-header .k-header.colGroupLoanDetails{ | |||
| background-color: #ffeabe; | |||
| } | |||
| .red { | |||
| color: #d9534f; | |||
| } | |||
| .red { | |||
| color: #d9534f; | |||
| } | |||
| .text-bold { | |||
| font-weight: 600; | |||
| .text-bold { | |||
| font-weight: 600; | |||
| } | |||
| } | |||
| @@ -21,7 +21,7 @@ import {animate, style, transition, trigger} from '@angular/animations'; | |||
| selector: 'app-list-all-loans', | |||
| templateUrl: './list-all-loans.component.html', | |||
| styleUrls: ['./list-all-loans.component.scss'], | |||
| encapsulation: ViewEncapsulation.None, | |||
| encapsulation: ViewEncapsulation.Emulated, | |||
| animations: [ | |||
| trigger('fadeIn', [ | |||
| transition(':enter', [ | |||
| @@ -44,11 +44,13 @@ export class ListAllLoansComponent implements OnInit { | |||
| public pageSize = 10; | |||
| public skip = 0; | |||
| @Output() LoanSelected: EventEmitter<LoanModel[]|LoanModel> = new EventEmitter<LoanModel[]|LoanModel>(); | |||
| @Output() Cancelled: EventEmitter<LoanModel[]|LoanModel> = new EventEmitter<LoanModel[]|LoanModel>(); | |||
| @Output() LoanClicked: EventEmitter<LoanModel> = new EventEmitter<LoanModel>(); | |||
| @Input() EnableExportExcel = false; | |||
| @Input() EnableExportPdf = false; | |||
| @Input() EnableSelectButton = false; | |||
| @Input() FinishButtonText = 'Finish Select'; | |||
| @Input() MaxSelect = 1; | |||
| @Input() Preselect: LoanModel[] = []; | |||
| public SelectedLoans: LoanModel[] = []; | |||
| @@ -160,8 +162,16 @@ export class ListAllLoansComponent implements OnInit { | |||
| this.LoanSelected.emit(this.SelectedLoans); | |||
| } | |||
| console.log('loan selected', this); | |||
| this.notifyUser('ended'); | |||
| this.notifyUser('try to save'); | |||
| } | |||
| public onCancelSelection(): void{ | |||
| if ( this.MaxSelect === 1 ){ | |||
| this.Cancelled.emit(this.SelectedLoans[0]); | |||
| }else{ | |||
| this.Cancelled.emit(this.SelectedLoans); | |||
| } | |||
| this.notifyUser('cancel saving'); | |||
| } | |||
| public RemoveFromSelection(l: LoanModel): void{ | |||
| @@ -1,7 +1,7 @@ | |||
| <div class="card loan" (click)="onClick()" *ngIf="Loan!==undefined"> | |||
| <span class="badge badge-success">{{Loan.Item}}</span> | |||
| <span> Amount: {{Loan.Amount | currency }}</span> | |||
| <span> Settlement: {{Loan.Settlement | date:'yyyy-mm-dd' }}</span> | |||
| <span> Settlement: {{Loan.Settlement | date:'yyyy-MM-dd' }}</span> | |||
| <span *ngIf="ShowCloseButton" class="k-icon k-i-close-circle close" (click)="onClose()" size="large"></span> | |||
| </div> | |||
| @@ -5,13 +5,20 @@ | |||
| </div> | |||
| <kendo-popup [offset]="Offset" (anchorViewportLeave)="showPopup = false" *ngIf="showPopup"> | |||
| <button kendoButton look="flat" | |||
| class="close-popup" icon="close-circle" style="float:right;" | |||
| (click)="onToggle()" | |||
| ></button> | |||
| <div class='popup-content'> | |||
| <app-list-all-loans #list | |||
| (LoanSelected)="onLoanSelected($event)" | |||
| [EnableSelectButton]="true" | |||
| [SingleSelection]="true" | |||
| [MaxSelect]="Max" | |||
| [Preselect]="[Loan]" | |||
| [FinishButtonText]="FinishButtonText" | |||
| (LoanClicked)="onClickLoan($event)" | |||
| (LoanSelected)="onLoanSelected($event)" | |||
| > | |||
| </app-list-all-loans> | |||
| </div> | |||
| @@ -23,3 +23,11 @@ div.anchor.wrapper { | |||
| z-index: 1; | |||
| } | |||
| } | |||
| button.close-popup{ | |||
| float: right; | |||
| position: absolute; | |||
| right: 0px; | |||
| border-radius: 100%; | |||
| z-index: 1; | |||
| } | |||
| @@ -22,6 +22,7 @@ export class LoanSelectComponent implements OnInit, ControlValueAccessor { | |||
| @Output() valueChange: EventEmitter<LoanModel> = new EventEmitter<LoanModel>(); | |||
| public Loan: LoanModel; // for multiple selection ,it has to be array; we don't support it now. | |||
| public FinishButtonText = 'Save'; | |||
| // popup | |||
| public showPopup = false; | |||
| public Offset = { left: 10, top: 48 }; | |||
| @@ -56,22 +57,31 @@ export class LoanSelectComponent implements OnInit, ControlValueAccessor { | |||
| if (!this.touched) { | |||
| this.onTouched(); | |||
| this.touched = true; | |||
| this.FinishButtonText = 'Save & Close'; | |||
| } | |||
| } | |||
| onLoanSelected(loan: LoanModel): void { | |||
| if (this.Loan.Id !== loan.Id){ | |||
| if (loan !== undefined && this.Loan !== undefined && this.Loan.Id !== loan.Id){ | |||
| this.valueChange.emit(loan); | |||
| this.onChange(loan); | |||
| this.Loan = loan; | |||
| } | |||
| this.Loan = loan; | |||
| if (loan === undefined || loan == null ){ | |||
| this.Loan = new LoanModel({}); | |||
| }else{ | |||
| this.Loan = loan; | |||
| } | |||
| this.markAsTouched(); | |||
| this.onTouched(); | |||
| this.showPopup = false; | |||
| } | |||
| onClickLoan(loan: LoanModel): void { | |||
| this.markAsTouched(); | |||
| } | |||
| public onToggle(): void { | |||
| this.showPopup = !this.showPopup; | |||
| @@ -48,7 +48,16 @@ | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Lender" title="Lender" width="150" editor="string"> | |||
| <kendo-grid-column field="Lender" title="Lender" width="150" > | |||
| <ng-template kendoGridEditTemplate let-dataItem> | |||
| <kendo-combobox | |||
| [formControl]="incomeFormGroup.get('Lender')" | |||
| [data]="funderListView | async" | |||
| [loading]="funderListService.loading"> | |||
| </kendo-combobox> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="LoanNumber" title="Lender Loan Id" width="150" editor="string"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="IncomeAmount" title="Income" width="150" format="{0:c}" editor="numeric"> | |||
| @@ -133,7 +142,7 @@ | |||
| </ng-template> | |||
| <ng-template kendoGridEditTemplate let-dataItem> | |||
| <app-loan-select [formControl]="incomeFormGroup.get('Loan')" (valueChange)="onLoanChange($event)" | |||
| [readOnly]="this.filterLoan !== undefined && this.filterLoan.Id !==''" | |||
| [Max]="1" [readOnly]="this.filterLoan !== undefined && this.filterLoan.Id !==''" | |||
| ></app-loan-select> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| @@ -13,6 +13,8 @@ import {debounce} from 'ts-debounce'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {LoanSingleService} from '../service/loan.single.service'; | |||
| import {PayInModelEx} from '../models/pay-in-ex.model'; | |||
| import {Observable} from 'rxjs'; | |||
| import {FunderNameService} from '../service/funder.name.service'; | |||
| @@ -48,9 +50,17 @@ export class PayInComponent implements OnInit { | |||
| }; | |||
| public loading = false; | |||
| public funderListView: Observable<string[]>; | |||
| private debouncedLoadFilteredData = () => {}; | |||
| constructor(private pis: PayInService, private lss: LoanSingleService, private router: Router) { } | |||
| constructor(private pis: PayInService, | |||
| private lss: LoanSingleService, | |||
| private router: Router, | |||
| private funderListService: FunderNameService) { | |||
| this.funderListView = this.funderListService; | |||
| this.funderListService.query(); | |||
| } | |||
| ngOnInit(): void { | |||
| this.debouncedLoadFilteredData = debounce(this.loadFilteredData, 500); | |||
| @@ -141,7 +151,7 @@ export class PayInComponent implements OnInit { | |||
| Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required), | |||
| Lender: new FormControl({value: dataItem.Lender, disabled: Loan.Id !== '' }, Validators.required), | |||
| Amount: new FormControl(dataItem.Amount), | |||
| LoanNumber: new FormControl(dataItem.LoanNumber, Validators.required), | |||
| LoanNumber: new FormControl({value: dataItem.LoanNumber, disabled: Loan.Id !== ''}, Validators.required), | |||
| IncomeAmount: new FormControl(dataItem.IncomeAmount, Validators.required), | |||
| IncomeType: new FormControl(dataItem.IncomeType, Validators.required), | |||
| Ts: new FormControl(dataItem.Ts, Validators.required), | |||
| @@ -203,7 +213,7 @@ export class PayInComponent implements OnInit { | |||
| pi.Balance = v.Balance; | |||
| pi.Lender = loan.Id === '' ? v.Lender : loan.Lender; | |||
| pi.LoanId = loan.Id; | |||
| pi.LoanNumber = loan.LenderLoanNumber; | |||
| pi.LoanNumber = loan.Id === '' ? v.LoanNumber : loan.LenderLoanNumber; | |||
| pi.OffsetBalance = v.OffsetBalance; | |||
| pi.Settlement = loan.Settlement; | |||
| pi.IncomeAmount = v.IncomeAmount; | |||
| @@ -297,6 +307,9 @@ export class PayInComponent implements OnInit { | |||
| if ( loan.Id !== '' ) { | |||
| this.incomeFormGroup.get('Lender').setValue(loan.Lender); | |||
| this.incomeFormGroup.get('Lender').disable({onlySelf: true, emitEvent: false}); | |||
| this.incomeFormGroup.get('LoanNumber').setValue(loan.LenderLoanNumber); | |||
| this.incomeFormGroup.get('LoanNumber').disable({onlySelf: true, emitEvent: false}); | |||
| } | |||
| } | |||
| } | |||
| @@ -122,20 +122,21 @@ export class AuthService { | |||
| public getUrl(key: string): string{ | |||
| const s = this.apiUrl + key; | |||
| const kvPair: {key: string, value: string}[] = [ | |||
| {key: 'login', value: this.apiUrl + 'login'}, | |||
| {key: 'logout', value: this.apiUrl + 'logout'} | |||
| ]; | |||
| kvPair.forEach( item => { | |||
| if ( item.key === key) { | |||
| return item.value; | |||
| } | |||
| }); | |||
| // not found if arrive here | |||
| return s; | |||
| return this.config.getUrl(key); | |||
| // const s = this.apiUrl + key; | |||
| // const kvPair: {key: string, value: string}[] = [ | |||
| // {key: 'login', value: this.apiUrl + 'login'}, | |||
| // {key: 'logout', value: this.apiUrl + 'logout'} | |||
| // ]; | |||
| // | |||
| // kvPair.forEach( item => { | |||
| // if ( item.key === key) { | |||
| // return item.value; | |||
| // } | |||
| // }); | |||
| // | |||
| // // not found if arrive here | |||
| // return s; | |||
| } | |||
| public UpdatePeopleInfo(people: PeopleModel): void{ | |||
| @@ -0,0 +1,24 @@ | |||
| import {Injectable} from '@angular/core'; | |||
| import {HttpClient} from '@angular/common/http'; | |||
| import {AppConfig} from '../app.config'; | |||
| import {BehaviorSubject, Observable} from 'rxjs'; | |||
| import {tap} from 'rxjs/operators'; | |||
| @Injectable() | |||
| export class FunderNameService extends BehaviorSubject<string[]> { | |||
| public loading = false; | |||
| constructor( private http: HttpClient, private config: AppConfig) { | |||
| super(null); | |||
| } | |||
| public query(): void { | |||
| this.fetch().subscribe( x => super.next(x)); | |||
| } | |||
| private fetch(): Observable<string[]>{ | |||
| this.loading = true; | |||
| return this.http.get<string[]>(this.config.getUrl('funder-list/')).pipe( | |||
| tap(() => this.loading = false) | |||
| ); | |||
| } | |||
| } | |||