| import { LoanCardComponent } from './loan-card/loan-card.component'; | import { LoanCardComponent } from './loan-card/loan-card.component'; | ||||
| import {AppConfig} from './app.config'; | import {AppConfig} from './app.config'; | ||||
| import { UploadingProgressCardComponent } from './uploading-progress-card/uploading-progress-card.component'; | import { UploadingProgressCardComponent } from './uploading-progress-card/uploading-progress-card.component'; | ||||
| import { LoanSelectComponent } from './loan-select/loan-select.component'; | |||||
| import {PopupModule} from '@progress/kendo-angular-popup'; | |||||
| ImagePopupDialogComponent, | ImagePopupDialogComponent, | ||||
| PopupIncomeFilterComponent, | PopupIncomeFilterComponent, | ||||
| LoanCardComponent, | LoanCardComponent, | ||||
| UploadingProgressCardComponent | |||||
| ], | |||||
| imports: [ | |||||
| BrowserModule, | |||||
| BrowserAnimationsModule, | |||||
| FormsModule, | |||||
| CommonModule, | |||||
| HttpClientModule, | |||||
| ReactiveFormsModule, | |||||
| AppRoutingModule, | |||||
| MenuModule, | |||||
| ContextMenuModule, | |||||
| BrowserAnimationsModule, | |||||
| DialogsModule, | |||||
| ButtonsModule, | |||||
| FloatingActionButtonModule, | |||||
| GridModule, | |||||
| PDFModule, | |||||
| ExcelModule, | |||||
| InputsModule, | |||||
| IconsModule, | |||||
| FontAwesomeModule, | |||||
| NavigationModule, | |||||
| LayoutModule, | |||||
| IndicatorsModule, | |||||
| LabelModule, | |||||
| NotificationModule, | |||||
| ChartsModule, | |||||
| DateInputsModule, | |||||
| DropDownsModule, | |||||
| ExcelExportModule, | |||||
| EditorModule, | |||||
| UploadModule, | |||||
| FileSelectModule, | |||||
| ProgressBarModule, | |||||
| PagerModule | |||||
| UploadingProgressCardComponent, | |||||
| LoanSelectComponent | |||||
| ], | ], | ||||
| imports: [ | |||||
| BrowserModule, | |||||
| BrowserAnimationsModule, | |||||
| FormsModule, | |||||
| CommonModule, | |||||
| HttpClientModule, | |||||
| ReactiveFormsModule, | |||||
| AppRoutingModule, | |||||
| MenuModule, | |||||
| ContextMenuModule, | |||||
| BrowserAnimationsModule, | |||||
| DialogsModule, | |||||
| ButtonsModule, | |||||
| FloatingActionButtonModule, | |||||
| GridModule, | |||||
| PDFModule, | |||||
| ExcelModule, | |||||
| InputsModule, | |||||
| IconsModule, | |||||
| FontAwesomeModule, | |||||
| NavigationModule, | |||||
| LayoutModule, | |||||
| IndicatorsModule, | |||||
| LabelModule, | |||||
| NotificationModule, | |||||
| ChartsModule, | |||||
| DateInputsModule, | |||||
| DropDownsModule, | |||||
| ExcelExportModule, | |||||
| EditorModule, | |||||
| UploadModule, | |||||
| FileSelectModule, | |||||
| ProgressBarModule, | |||||
| PagerModule, | |||||
| PopupModule | |||||
| ], | |||||
| providers: [ | providers: [ | ||||
| MenuService, | MenuService, | ||||
| AuthGuard, | AuthGuard, |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
| import { ChartPastYearMontnlyPerformanceComponent } from './chart-past-year-monthly-performance.component'; | |||||
| import { ChartPastYearMonthlyPerformanceComponent } from './chart-past-year-monthly-performance.component'; | |||||
| describe('ChartPastYearMontnlyPerformanceComponent', () => { | |||||
| let component: ChartPastYearMontnlyPerformanceComponent; | |||||
| let fixture: ComponentFixture<ChartPastYearMontnlyPerformanceComponent>; | |||||
| describe('ChartPastYearMonthlyPerformanceComponent', () => { | |||||
| let component: ChartPastYearMonthlyPerformanceComponent; | |||||
| let fixture: ComponentFixture<ChartPastYearMonthlyPerformanceComponent>; | |||||
| beforeEach(async () => { | beforeEach(async () => { | ||||
| await TestBed.configureTestingModule({ | await TestBed.configureTestingModule({ | ||||
| declarations: [ ChartPastYearMontnlyPerformanceComponent ] | |||||
| declarations: [ ChartPastYearMonthlyPerformanceComponent ] | |||||
| }) | }) | ||||
| .compileComponents(); | .compileComponents(); | ||||
| }); | }); | ||||
| beforeEach(() => { | beforeEach(() => { | ||||
| fixture = TestBed.createComponent(ChartPastYearMontnlyPerformanceComponent); | |||||
| fixture = TestBed.createComponent(ChartPastYearMonthlyPerformanceComponent); | |||||
| component = fixture.componentInstance; | component = fixture.componentInstance; | ||||
| fixture.detectChanges(); | fixture.detectChanges(); | ||||
| }); | }); |
| [height]="1000" | [height]="1000" | ||||
| [navigable]="true" | [navigable]="true" | ||||
| [filterable]="true" | [filterable]="true" | ||||
| [selectable]="selectableSettings" | |||||
| kendoGridSelectBy="Id" | |||||
| [selectedKeys]="gridSelection" | |||||
| (selectionChange)="onSelectionChange($event)" | |||||
| (dataStateChange)="dataStateChange($event)" | (dataStateChange)="dataStateChange($event)" | ||||
| (filterChange)="filterChange($event)" | (filterChange)="filterChange($event)" | ||||
| (cellClick)="onCellClick($event)" | (cellClick)="onCellClick($event)" | ||||
| class="fullheight_grid" | class="fullheight_grid" | ||||
| > | > | ||||
| <ng-template kendoGridToolbarTemplate> | |||||
| <button kendoGridExcelCommand type="button" icon="file-excel" style="float:right;">Export to Excel</button> | |||||
| <button kendoGridPDFCommand icon="file-pdf" style="float:right;">Export to PDF</button> | |||||
| <ng-template *ngIf="EnableExportExcel || EnableExportPdf || EnableSelectButton" kendoGridToolbarTemplate> | |||||
| <button *ngIf="EnableExportExcel" kendoGridExcelCommand type="button" icon="file-excel" style="float:right;">Export to Excel</button> | |||||
| <button *ngIf="EnableExportPdf" kendoGridPDFCommand icon="file-pdf" style="float:right;">Export to PDF</button> | |||||
| <div class="selected-loans" #notification> | |||||
| <app-loan-card *ngFor="let l of SelectedLoans" [Loan]="l" [ShowCloseButton]="true" | |||||
| (Closed)="RemoveFromSelection(l)" | |||||
| @fadeIn @slideOutUp | |||||
| ></app-loan-card> | |||||
| </div> | |||||
| <div class="selection-percentage" *ngIf="SelectedLoans.length >0"> | |||||
| <span class="badge badge-info">{{SelectedLoans.length}}</span>/ | |||||
| <span class="badge badge-info">{{MaxSelect}}</span> | |||||
| </div> | |||||
| <button *ngIf="EnableSelectButton" kendoButton [primary]=true | |||||
| class="select-loan" icon="close-circle" style="float:right;" | |||||
| (click)="onFinishSelection()" | |||||
| > | |||||
| Finish Selection</button> | |||||
| </ng-template> | </ng-template> | ||||
| <kendo-grid-column field="Id" width="50" [class]="'topAlign'" [sortable]="false" [filterable]="false"> | |||||
| <kendo-grid-checkbox-column *ngIf="EnableSelectButton" width="50" ></kendo-grid-checkbox-column> | |||||
| <kendo-grid-column field="Id" title="#" width="50" [class]="'topAlign'" [sortable]="false" [filterable]="false"> | |||||
| <ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex" > | <ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex" > | ||||
| <p title="{{dataItem.Id}}" > {{dataItem.Index}}</p> | <p title="{{dataItem.Id}}" > {{dataItem.Index}}</p> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column-group title="People" [columnMenu]="false" [headerClass]="'colGroupPeople'"> | <kendo-grid-column-group title="People" [columnMenu]="false" [headerClass]="'colGroupPeople'"> | ||||
| <kendo-grid-column field="Client" width="220" title="Client(s)" [class]="'topAlign'" [headerClass]="'colClient'"> | <kendo-grid-column field="Client" width="220" title="Client(s)" [class]="'topAlign'" [headerClass]="'colClient'"> | ||||
| <ng-template kendoGridCellTemplate let-dataItem> | <ng-template kendoGridCellTemplate let-dataItem> |
| .fullheight_grid { | .fullheight_grid { | ||||
| height: calc(100vh - 48px) !important; | |||||
| height: 100% !important; | |||||
| } | } | ||||
| .k-grid td:first-child{ | .k-grid td:first-child{ | ||||
| text-align: left; | text-align: left; | ||||
| } | } | ||||
| 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{ | .customer-photo{ | ||||
| display: inline-block; | display: inline-block; | ||||
| width: 32px; | width: 32px; |
| import {Component, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; | |||||
| import {CellCloseEvent, DataStateChangeEvent, GridComponent, GridDataResult} from '@progress/kendo-angular-grid'; | |||||
| import {Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core'; | |||||
| import { | |||||
| CellCloseEvent, | |||||
| DataStateChangeEvent, | |||||
| GridComponent, | |||||
| GridDataResult, | |||||
| SelectableSettings, | |||||
| SelectionEvent | |||||
| } from '@progress/kendo-angular-grid'; | |||||
| import {CompositeFilterDescriptor, SortDescriptor, toODataString} from '@progress/kendo-data-query'; | import {CompositeFilterDescriptor, SortDescriptor, toODataString} from '@progress/kendo-data-query'; | ||||
| import {LoanSummaryService} from '../service/loan_summary.service'; | import {LoanSummaryService} from '../service/loan_summary.service'; | ||||
| import {AuthService} from '../service/auth.service'; | import {AuthService} from '../service/auth.service'; | ||||
| import {Observable} from 'rxjs'; | import {Observable} from 'rxjs'; | ||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| import {LoanModel} from '../models/loan.model'; | |||||
| import {LoanSelectComponent} from '../loan-select/loan-select.component'; | |||||
| import {NotificationService, Type} from '@progress/kendo-angular-notification'; | |||||
| import {animate, style, transition, trigger} from '@angular/animations'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-list-all-loans', | selector: 'app-list-all-loans', | ||||
| templateUrl: './list-all-loans.component.html', | templateUrl: './list-all-loans.component.html', | ||||
| styleUrls: ['./list-all-loans.component.scss'], | styleUrls: ['./list-all-loans.component.scss'], | ||||
| encapsulation: ViewEncapsulation.None, | encapsulation: ViewEncapsulation.None, | ||||
| animations: [ | |||||
| trigger('fadeIn', [ | |||||
| transition(':enter', [ | |||||
| style({ opacity: '0' }), | |||||
| animate('.5s ease-out', style({ opacity: '1'})), | |||||
| ]), | |||||
| ]), | |||||
| trigger('slideOutUp', [ | |||||
| transition(':leave', [ | |||||
| style({ opacity: '1' }), | |||||
| animate('.5s ease-out', style({ opacity: '0' })), | |||||
| ]), | |||||
| ]), | |||||
| ], | |||||
| }) | }) | ||||
| export class ListAllLoansComponent implements OnInit { | export class ListAllLoansComponent implements OnInit { | ||||
| public view: LoanSummaryService; | public view: LoanSummaryService; | ||||
| public filter: CompositeFilterDescriptor; | public filter: CompositeFilterDescriptor; | ||||
| public pageSize = 10; | public pageSize = 10; | ||||
| public skip = 0; | public skip = 0; | ||||
| @ViewChild(GridComponent, { static: true }) public grid: GridComponent; | |||||
| constructor(private service: LoanSummaryService, private auth: AuthService, private router: Router) { } | |||||
| @Output() LoanSelected: EventEmitter<LoanModel[]|LoanModel> = new EventEmitter<LoanModel[]|LoanModel>(); | |||||
| @Input() EnableExportExcel = false; | |||||
| @Input() EnableExportPdf = false; | |||||
| @Input() EnableSelectButton = false; | |||||
| @Input() MaxSelect = 1; | |||||
| @Input() Preselect: LoanModel[] = []; | |||||
| public SelectedLoans: LoanModel[] = []; | |||||
| public selectableSettings: SelectableSettings|boolean; | |||||
| private privateSingleSelection = true; | |||||
| public gridSelection: string[] = []; | |||||
| @ViewChild(GridComponent, { static: true }) public grid: GridComponent; | |||||
| @ViewChild(GridComponent, { read: ViewContainerRef }) public gridVR: ViewContainerRef; | |||||
| constructor(private service: LoanSummaryService, | |||||
| private auth: AuthService, | |||||
| private router: Router, | |||||
| private notificationService: NotificationService) { } | |||||
| public ngOnInit(): void { | public ngOnInit(): void { | ||||
| // Bind directly to the service as it is a Subject | // Bind directly to the service as it is a Subject | ||||
| // Fetch the data with the initial state | // Fetch the data with the initial state | ||||
| this.loadData(); | this.loadData(); | ||||
| // selectable | |||||
| this.setSelectableSettings(); | |||||
| // Preselect on Init only | |||||
| this.Preselect.forEach( v => { | |||||
| if ( v !== undefined && v !== null && v.Id !== '' ){ | |||||
| this.AddLoanToSelection(v); | |||||
| this.gridSelection.unshift(v.Id); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Input() set SingleSelection(single: boolean) { | |||||
| this.privateSingleSelection = single; | |||||
| this.setSelectableSettings(); | |||||
| } | |||||
| get SingleSelection(): boolean{ | |||||
| return this.privateSingleSelection; | |||||
| } | |||||
| public setSelectableSettings(): void { | |||||
| this.privateSingleSelection = this.MaxSelect === 1; | |||||
| if ( this.EnableSelectButton ){ | |||||
| this.selectableSettings = { | |||||
| checkboxOnly: true, | |||||
| mode: this.privateSingleSelection ? 'single' : 'multiple', | |||||
| drag: false, | |||||
| }; | |||||
| }else{ | |||||
| this.selectableSettings = false; | |||||
| } | |||||
| } | } | ||||
| public dataStateChange({ skip, take, sort, filter }: DataStateChangeEvent): void { | public dataStateChange({ skip, take, sort, filter }: DataStateChangeEvent): void { | ||||
| } | } | ||||
| public onCellClick(event: CellCloseEvent): void { | public onCellClick(event: CellCloseEvent): void { | ||||
| this.router.navigate(['/edit-loan/' + event.dataItem.Id]); | |||||
| // this.LoanSelected.emit(event.dataItem); | |||||
| } | |||||
| public AddLoanToSelection(loan: LoanModel): void{ | |||||
| const idx = this.SelectedLoans.findIndex( v => { | |||||
| return v.Id === loan.Id; | |||||
| }); | |||||
| if ( idx === -1 ) { // not found | |||||
| this.SelectedLoans.unshift(loan); | |||||
| } | |||||
| } | } | ||||
| public onFinishSelection(): void{ | |||||
| if ( this.MaxSelect === 1 ){ | |||||
| this.LoanSelected.emit(this.SelectedLoans[0]); | |||||
| }else{ | |||||
| this.LoanSelected.emit(this.SelectedLoans); | |||||
| } | |||||
| console.log('loan selected', this); | |||||
| this.notifyUser('ended'); | |||||
| } | |||||
| public RemoveFromSelection(l: LoanModel): void{ | |||||
| this.SelectedLoans = this.SelectedLoans.filter( v => l.Id !== v.Id ); | |||||
| this.gridSelection = this.gridSelection.filter( v => v !== l.Id ); | |||||
| } | |||||
| public onSelectionChange(sel: SelectionEvent): void { | |||||
| // to avoid race condition, we do action after 200ms | |||||
| setTimeout( () => { | |||||
| sel.deselectedRows.forEach(v => { | |||||
| this.RemoveFromSelection(v.dataItem); | |||||
| }); | |||||
| sel.selectedRows.forEach(v => { | |||||
| if ( this.SelectedLoans.length < this.MaxSelect) { | |||||
| this.AddLoanToSelection(v.dataItem); | |||||
| }else{ | |||||
| // remove those that was added by grid selection | |||||
| this.gridSelection = this.gridSelection.filter( existing => existing !== v.dataItem.Id ); | |||||
| this.notifyUser('Maximum selection reached', { style: 'warning', icon: true }); | |||||
| } | |||||
| }); | |||||
| }, 200); | |||||
| } | |||||
| public notifyUser( msg: string, lookAndFeel?: Type): void { | |||||
| if (lookAndFeel === undefined ){ | |||||
| lookAndFeel = { style: 'success', icon: true }; | |||||
| } | |||||
| this.notificationService.show({ | |||||
| appendTo: this.gridVR, | |||||
| content: msg, | |||||
| cssClass: 'button-notification', | |||||
| animation: { type: 'slide', duration: 400 }, | |||||
| position: { horizontal: 'right', vertical: 'top' }, | |||||
| type: lookAndFeel, | |||||
| closable: false, | |||||
| hideAfter: 5000, | |||||
| }); | |||||
| } | |||||
| } | } |
| <div class="income-container"> | <div class="income-container"> | ||||
| <kendo-splitter orientation="vertical" style="height: 100%;"> | |||||
| <kendo-splitter-pane [collapsible]="false"> | |||||
| <div class="pane-content"> | |||||
| <h3>Outer splitter / Middle pane</h3> | |||||
| <p>Resizable only.</p> | |||||
| </div> | |||||
| </kendo-splitter-pane> | |||||
| <kendo-splitter-pane [collapsible]="true" size="50%"> | |||||
| <kendo-splitter class="full-height"> | |||||
| <kendo-splitter-pane size="300px"> | |||||
| <div class="pane-content"> | |||||
| <app-lender-uploads (uploadSuccessful)="onSuccess($event)"></app-lender-uploads> | |||||
| </div> | |||||
| </kendo-splitter-pane> | |||||
| <kendo-splitter-pane > | |||||
| <div class="pane-content"> | |||||
| <div *ngFor="let u of uploads" class="aaaa"> | |||||
| Type: {{u.files[0].extension}} | |||||
| Name: {{u.files[0].name}} | |||||
| Funder: Name: {{u.response.body.Funder}} | |||||
| </div> | |||||
| </div> | |||||
| </kendo-splitter-pane> | |||||
| </kendo-splitter> | |||||
| </kendo-splitter-pane> | |||||
| </kendo-splitter> | |||||
| <app-pay-in [uploadMeta]="uploadMeta" | |||||
| [Loan]="Loan" | |||||
| [showLoanColumn]="true" | |||||
| [showUploadColumn]="true" | |||||
| [LoadDataNow]="startLoad" | |||||
| > | |||||
| </app-pay-in> | |||||
| </div> | </div> |
| div.income-container { | div.income-container { | ||||
| height: calc(100vh - 48px); | height: calc(100vh - 48px); | ||||
| } | |||||
| div.pane-content{ | |||||
| height:100%; | |||||
| overflow:hidden; | |||||
| } | } |
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import {SuccessEvent} from '@progress/kendo-angular-upload'; | |||||
| import {UploadMetaModel} from '../models/uploadMetaModel'; | |||||
| import {UploadAttachService} from '../service/upload.attach.service'; | |||||
| import {LoanModel} from '../models/loan.model'; | |||||
| import {LoanSingleService} from '../service/loan.single.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-list-income', | selector: 'app-list-income', | ||||
| styleUrls: ['./list-income.component.scss'] | styleUrls: ['./list-income.component.scss'] | ||||
| }) | }) | ||||
| export class ListIncomeComponent implements OnInit { | export class ListIncomeComponent implements OnInit { | ||||
| public uploads: SuccessEvent[] = []; | |||||
| public uploadMeta: UploadMetaModel = new UploadMetaModel({}); | |||||
| public Loan: LoanModel = new LoanModel({}); | |||||
| public startLoad = false | |||||
| constructor() { } | |||||
| private UploadMetaFilterIsReady = false; | |||||
| private LoanFilterIsReady = false; | |||||
| constructor(private uas: UploadAttachService, private lss: LoanSingleService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.UploadMetaFilterIsReady = true; | |||||
| // this.UploadMetaFilterIsReady = false; | |||||
| // this.uas.getUploadMeta(1).subscribe( | |||||
| // resp => { | |||||
| // this.UploadMetaFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // // this.uploadMeta = new UploadMetaModel(resp); | |||||
| // }, error => { | |||||
| // this.UploadMetaFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // }, () => { | |||||
| // this.UploadMetaFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // } | |||||
| // ); | |||||
| // this.lss.getLoan('0482b524-d396-4b03-8abb-f8325c87e2ed').subscribe( | |||||
| // resp => { | |||||
| // this.Loan = new LoanModel(resp); | |||||
| // this.LoanFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // }, err => { | |||||
| // this.LoanFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // }, () => { | |||||
| // this.LoanFilterIsReady = true; | |||||
| // this.checkStartUpload(); | |||||
| // } | |||||
| // ); | |||||
| this.startLoad = true; | |||||
| } | } | ||||
| public onSuccess(e: SuccessEvent): void { | |||||
| this.uploads.push (e); | |||||
| console.log(e); | |||||
| private checkStartUpload(): void { | |||||
| this.startLoad = this.UploadMetaFilterIsReady | |||||
| } | } | ||||
| } | } |
| <div class="card loan" (click)="gotoLoan(LoanId)"> | |||||
| <div class="card loan" (click)="onClick()"> | |||||
| <span class="badge badge-success">{{Loan.Item}}</span> | <span class="badge badge-success">{{Loan.Item}}</span> | ||||
| <span> Amount: {{Loan.Amount | currency }}</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> | </div> | ||||
| -o-transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1); | -o-transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1); | ||||
| transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1); | transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1); | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| overflow: hidden; | |||||
| overflow: unset; | |||||
| -webkit-box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff; | -webkit-box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff; | ||||
| box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff; | box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff; | ||||
| margin-top:10px; | |||||
| margin-right: 10px; | |||||
| span.k-icon.close{ | |||||
| position:absolute; | |||||
| top: -10px; | |||||
| right: -10px; | |||||
| border-radius: 100%; | |||||
| } | |||||
| } | } |
| import {Component, Input, OnInit} from '@angular/core'; | |||||
| import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; | |||||
| import {LoanModel} from '../models/loan.model'; | import {LoanModel} from '../models/loan.model'; | ||||
| import {LoanSingleService} from '../service/loan.single.service'; | import {LoanSingleService} from '../service/loan.single.service'; | ||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| }) | }) | ||||
| export class LoanCardComponent implements OnInit { | export class LoanCardComponent implements OnInit { | ||||
| @Input() LoanId = ''; | @Input() LoanId = ''; | ||||
| public Loan: LoanModel = new LoanModel({}); | |||||
| @Input() ShowCloseButton = false; | |||||
| @Input() public Loan: LoanModel = new LoanModel({}); | |||||
| @Output() Selected: EventEmitter<LoanModel> = new EventEmitter<LoanModel>(); | |||||
| @Output() Closed: EventEmitter<LoanModel> = new EventEmitter<LoanModel>(); | |||||
| constructor(private lss: LoanSingleService, private router: Router) { } | constructor(private lss: LoanSingleService, private router: Router) { } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.lss.getLoan(this.LoanId).subscribe( resp => { | |||||
| this.Loan.Response = resp; | |||||
| }); | |||||
| if ( this.LoanId !== undefined && this.LoanId !== '' && this.Loan.Id === ''){ | |||||
| this.lss.getLoan(this.LoanId).subscribe( resp => { | |||||
| this.Loan.Response = resp; | |||||
| }); | |||||
| } | |||||
| } | |||||
| public onClick(): void { | |||||
| this.Selected.emit(this.Loan); | |||||
| } | |||||
| public onClose(): void{ | |||||
| this.Closed.emit(this.Loan); | |||||
| } | } | ||||
| public gotoLoan(id: string): void { | public gotoLoan(id: string): void { |
| pi.LoanNumber = this.Loan.LenderLoanNumber; | pi.LoanNumber = this.Loan.LenderLoanNumber; | ||||
| pi.OffsetBalance = v.OffsetBalance; | pi.OffsetBalance = v.OffsetBalance; | ||||
| pi.Settlement = this.Loan.Settlement; | pi.Settlement = this.Loan.Settlement; | ||||
| pi.Trail = v.Trail; | |||||
| pi.IncomeAmount = v.Trail; //TODO: rename Trail to incomeAmount and add IncomeType | |||||
| pi.Ts = new Date(v.Ts); | pi.Ts = new Date(v.Ts); | ||||
| pi.UploadId = v.UploadId; | pi.UploadId = v.UploadId; | ||||
| <div #anchor class="anchor wrapper"> | |||||
| <button kendoButton *ngIf="!readOnly" (click)="onToggle()" [icon]="'edit'" ></button> | |||||
| <app-loan-card *ngIf="Loan!==undefined && Loan !== null && Loan.Id !=='' " | |||||
| [Loan]="Loan" [ShowCloseButton]="false"></app-loan-card> | |||||
| </div> | |||||
| <kendo-popup [anchor]="anchor" [offset]="Offset" (anchorViewportLeave)="showPopup = false" *ngIf="showPopup"> | |||||
| <div class='popup-content'> | |||||
| <app-list-all-loans #list | |||||
| (LoanSelected)="onLoanSelected($event)" | |||||
| [EnableSelectButton]="true" | |||||
| [SingleSelection]="true" | |||||
| [MaxSelect]="Max" | |||||
| [Preselect]="[Loan]" | |||||
| > | |||||
| </app-list-all-loans> | |||||
| </div> | |||||
| </kendo-popup> | |||||
| .popup-content { | |||||
| box-shadow: 1px 1px 10px black; | |||||
| padding: 10px; | |||||
| width: 90vw; | |||||
| height: 50vw; | |||||
| color: #787878; | |||||
| background-color: #fcf7f8; | |||||
| border: 1px solid rgba(0,0,0,.05); | |||||
| border-radius:20px; | |||||
| } | |||||
| div.anchor.wrapper { | |||||
| width:100%; | |||||
| position: relative; | |||||
| button{ | |||||
| position: absolute; | |||||
| /* left: 0px; */ | |||||
| right: 0px; | |||||
| top: -10px; | |||||
| color: red; | |||||
| border-radius: 100%; | |||||
| z-index: 1; | |||||
| } | |||||
| } |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { LoanSelectComponent } from './loan-select.component'; | |||||
| describe('LoanSelectComponent', () => { | |||||
| let component: LoanSelectComponent; | |||||
| let fixture: ComponentFixture<LoanSelectComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ LoanSelectComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(LoanSelectComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import { Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {LoanModel} from '../models/loan.model'; | |||||
| import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; | |||||
| import {ListAllLoansComponent} from '../list-all-loans/list-all-loans.component'; | |||||
| @Component({ | |||||
| selector: 'app-loan-select', | |||||
| templateUrl: './loan-select.component.html', | |||||
| styleUrls: ['./loan-select.component.scss'], | |||||
| providers: [ | |||||
| { | |||||
| provide: NG_VALUE_ACCESSOR, | |||||
| multi: true, | |||||
| useExisting: LoanSelectComponent | |||||
| } | |||||
| ] | |||||
| }) | |||||
| export class LoanSelectComponent implements OnInit, ControlValueAccessor { | |||||
| @ViewChild('list', {static: false} ) list: ListAllLoansComponent; | |||||
| @Input() Max = 1; // we do not support multiple loan selection at the moment | |||||
| @Input() readOnly = false; | |||||
| public Loan: LoanModel; // for multiple selection ,it has to be array; we don't support it now. | |||||
| // popup | |||||
| public showPopup = false; | |||||
| public Offset = { left: 0, top: 0 }; | |||||
| // formControl state | |||||
| private disabled = false; | |||||
| private touched = false; | |||||
| // Control Value Accessor | |||||
| onChange = (Loan) => {}; // empty | |||||
| onTouched = () => {}; | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| console.log('init loan-select', this); | |||||
| } | |||||
| writeValue(loan: LoanModel): void { | |||||
| this.Loan = loan; | |||||
| } | |||||
| registerOnChange(onChange: any): void { | |||||
| this.onChange = onChange; | |||||
| } | |||||
| registerOnTouched(onTouched: any): void { | |||||
| this.onTouched = onTouched; | |||||
| } | |||||
| private markAsTouched(): void { | |||||
| if (!this.touched) { | |||||
| this.onTouched(); | |||||
| this.touched = true; | |||||
| } | |||||
| } | |||||
| onLoanSelected(loan: LoanModel): void { | |||||
| this.Loan = loan; | |||||
| this.markAsTouched(); | |||||
| this.onTouched(); | |||||
| this.onChange(loan); | |||||
| this.showPopup = false; | |||||
| } | |||||
| public onToggle(): void { | |||||
| this.showPopup = !this.showPopup; | |||||
| } | |||||
| } |
| { text: 'Start New Loan', icon: 'plus', url: './#edit-loan/' }, | { text: 'Start New Loan', icon: 'plus', url: './#edit-loan/' }, | ||||
| { text: 'List All', icon: 'table' , url: './#list-all-loans' }, | { text: 'List All', icon: 'table' , url: './#list-all-loans' }, | ||||
| { text: '--', separator: 'true' }, | { text: '--', separator: 'true' }, | ||||
| { text: 'Income', icon: 'dollar', url: './#pay-in' }, | |||||
| { text: 'list income', icon: 'dollar', url: './#list-income' }, | |||||
| { text: '--', separator: 'true' }, | { text: '--', separator: 'true' }, | ||||
| { text: 'Uploads', icon: 'dollar', url: './#lender-uploads' }, | { text: 'Uploads', icon: 'dollar', url: './#lender-uploads' }, | ||||
| { text: 'Uploads by Id', icon: 'dollar', url: './#upload-details/30' }, | |||||
| { text: 'list income', icon: 'dollar', url: './#list-income' }, | |||||
| { text: '--', separator: 'true' }, | |||||
| { text: 'Test Uploads by Id', icon: 'dollar', url: './#upload-details/1' }, | |||||
| { text: 'Test Pay-in', icon: 'dollar', url: './#pay-in' }, | |||||
| ] | ] | ||||
| }, | }, | ||||
| { | { |
| import {LoanModel} from './loan.model'; | |||||
| import {UploadMetaModel} from './uploadMetaModel'; | |||||
| import {PayInModel} from './pay-in.model'; | |||||
| export class PayInModelEx extends PayInModel{ | |||||
| public Loan: LoanModel; | |||||
| public UploadMeta: UploadMetaModel; | |||||
| constructor(payload: Partial<PayInModelEx>){ | |||||
| super(payload); | |||||
| this.Loan = new LoanModel( payload.Loan || {}); | |||||
| this.UploadMeta = new UploadMetaModel(payload.UploadMeta || {}); | |||||
| } | |||||
| } |
| import {LoanModel} from './loan.model'; | |||||
| import {UploadMetaModel} from './uploadMetaModel'; | |||||
| export class PayInModel { | export class PayInModel { | ||||
| public Id: number; | public Id: number; | ||||
| public LoanNumber: string; | public LoanNumber: string; | ||||
| public OffsetBalance: number; | public OffsetBalance: number; | ||||
| public Settlement: Date; | public Settlement: Date; | ||||
| public Trail: number; | |||||
| public IncomeAmount: number; | |||||
| public IncomeType: string; | |||||
| public Ts: Date; | public Ts: Date; | ||||
| public UploadId: number; | public UploadId: number; | ||||
| constructor(payload: Partial<PayInModel>){ | constructor(payload: Partial<PayInModel>){ | ||||
| this.LoanId = payload.LoanId || '' ; | this.LoanId = payload.LoanId || '' ; | ||||
| this.LoanNumber = payload.LoanNumber || ''; | this.LoanNumber = payload.LoanNumber || ''; | ||||
| this.OffsetBalance = payload.OffsetBalance || 0; | this.OffsetBalance = payload.OffsetBalance || 0; | ||||
| if (payload.Settlement === undefined || payload.Settlement === null) { | |||||
| this.Settlement = new Date(); | |||||
| }else{ | |||||
| this.Settlement = new Date(payload.Settlement); | |||||
| } | |||||
| this.Settlement = new Date(payload.Settlement); | this.Settlement = new Date(payload.Settlement); | ||||
| this.Trail = payload.Trail || 0 ; | |||||
| this.Ts = new Date(payload.Ts); | |||||
| this.IncomeAmount = payload.IncomeAmount || 0 ; | |||||
| this.IncomeType = payload.IncomeType || ''; | |||||
| if (payload.Ts === undefined || payload.Ts === null) { | |||||
| this.Ts = new Date(); | |||||
| }else{ | |||||
| this.Ts = new Date(payload.Ts); | |||||
| } | |||||
| this.UploadId = payload.UploadId; | this.UploadId = payload.UploadId; | ||||
| } | } | ||||
| } | } | ||||
| [pageSize]="filter.Take" | [pageSize]="filter.Take" | ||||
| [skip]="filter.Skip" | [skip]="filter.Skip" | ||||
| [sortable]="sortable" | [sortable]="sortable" | ||||
| [filterable]="filterable" | |||||
| [filterable]="false" | |||||
| [loading]="loading" | [loading]="loading" | ||||
| [sort]="filter.Sort" | [sort]="filter.Sort" | ||||
| <div style="width:100%; margin:0px; display:block"> | <div style="width:100%; margin:0px; display:block"> | ||||
| <div *ngIf="allowAddNew" class="add-new-tool-bar"> | <div *ngIf="allowAddNew" class="add-new-tool-bar"> | ||||
| <button kendoGridAddCommand icon="plus" >Add new Income</button> | <button kendoGridAddCommand icon="plus" >Add new Income</button> | ||||
| </div> | </div> | ||||
| <div [ngStyle]="{'width': allowAddNew? '70%': '100%'}" class="filter-panel-wrapper" > | <div [ngStyle]="{'width': allowAddNew? '70%': '100%'}" class="filter-panel-wrapper" > | ||||
| <button kendoButton icon="filter" (click)="showFilter()" >Filter</button> | |||||
| <!-- <button kendoButton icon="filter" >Upload</button>--> | |||||
| <span *ngIf="uploadMeta.Id > 0 " class="badge badge-pill badge-primary specific-upload"> {{uploadMeta.Id}} </span> | |||||
| <span *ngIf="uploadMeta.Id > 0 " class="badge badge-secondary specific-upload"> {{uploadMeta.FileName}} </span> | |||||
| <button kendoButton icon="filter" (click)="showFilter()" >Filter</button> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </ng-template> | </ng-template> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="Trail" title="Trail Received" width="150" format="{0:c}" editor="numeric"> | |||||
| <kendo-grid-column field="Lender" title="Lender" width="150" editor="string"> | |||||
| </kendo-grid-column> | |||||
| <kendo-grid-column field="IncomeAmount" title="Income" width="150" format="{0:c}" editor="numeric"> | |||||
| </kendo-grid-column> | |||||
| <kendo-grid-column field="IncomeType" title="Type" width="150" editor="string"> | |||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="Ts" title="Trail Date" editor="date" width="100"> | <kendo-grid-column field="Ts" title="Trail Date" editor="date" width="100"> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="Amount" title="Loan Amount" width="150" format="{0:c}" | |||||
| [editable]="false" [sortable]="false"> | |||||
| </kendo-grid-column> | |||||
| <kendo-grid-column field="Balance" title="Balance" width="150"> | <kendo-grid-column field="Balance" title="Balance" width="150"> | ||||
| <ng-template kendoGridCellTemplate let-dataItem> | <ng-template kendoGridCellTemplate let-dataItem> | ||||
| <div *ngIf="dataItem.Balance >=0 "> {{ dataItem.Balance | currency}} </div> | <div *ngIf="dataItem.Balance >=0 "> {{ dataItem.Balance | currency}} </div> | ||||
| > | > | ||||
| </kendo-switch> | </kendo-switch> | ||||
| <kendo-numerictextbox *ngIf="showBalance" name="balance" | |||||
| <kendo-numerictextbox *ngIf="showBalance" | |||||
| [formControl]="incomeFormGroup.get('Balance')" | [formControl]="incomeFormGroup.get('Balance')" | ||||
| [min]="-1" [max]="999999999" [autoCorrect]="true" class="balance"> | [min]="-1" [max]="999999999" [autoCorrect]="true" class="balance"> | ||||
| </kendo-numerictextbox> | </kendo-numerictextbox> | ||||
| > | > | ||||
| </kendo-switch> | </kendo-switch> | ||||
| <kendo-numerictextbox *ngIf="showOffsetBalance" name="offsetBalance" | |||||
| <kendo-numerictextbox *ngIf="showOffsetBalance" | |||||
| [formControl]="incomeFormGroup.get('OffsetBalance')" | [formControl]="incomeFormGroup.get('OffsetBalance')" | ||||
| [min]="-1" [max]="999999999" [autoCorrect]="true" class="balance"> | [min]="-1" [max]="999999999" [autoCorrect]="true" class="balance"> | ||||
| </kendo-numerictextbox> | </kendo-numerictextbox> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="UploadId" title="Uploads" width="100" format="{0:c}" [editable]="false" [sortable]="false"> | |||||
| <kendo-grid-column *ngIf="showUploadColumn" | |||||
| field="UploadId" title="Uploads" width="100" format="{0:c}" [editable]="false" [sortable]="false"> | |||||
| <ng-template kendoGridCellTemplate let-dataItem > | <ng-template kendoGridCellTemplate let-dataItem > | ||||
| <button kendoButton *ngIf="dataItem.UploadId > 0" | <button kendoButton *ngIf="dataItem.UploadId > 0" | ||||
| (click)="showUpload(dataItem.UploadId)" icon="attachment"> {{ dataItem.UploadId }} | |||||
| (click)="showUpload(dataItem.UploadId)" icon="attachment"> {{ dataItem.UploadId }} | |||||
| </button> | </button> | ||||
| <p *ngIf="dataItem.UploadId <=0" > - </p> | <p *ngIf="dataItem.UploadId <=0" > - </p> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="LoanId" title="Loan" width="210" [editable]="false" [sortable]="false"> | |||||
| <kendo-grid-column *ngIf="showLoanColumn" | |||||
| field="LoanId" title="Loan" width="210" | |||||
| [editable]="true " | |||||
| [sortable]="false"> | |||||
| <ng-template kendoGridCellTemplate let-dataItem> | <ng-template kendoGridCellTemplate let-dataItem> | ||||
| <app-loan-card [LoanId]="dataItem.LoanId"></app-loan-card> | |||||
| <app-loan-card *ngIf="dataItem.LoanId !== '' " [LoanId]="dataItem.LoanId"></app-loan-card> | |||||
| </ng-template> | |||||
| <ng-template kendoGridEditTemplate let-dataItem> | |||||
| <app-loan-select [formControl]="incomeFormGroup.get('Loan')" | |||||
| [readOnly]="this.filterLoan !== undefined && this.filterLoan.Id !==''" | |||||
| ></app-loan-select> | |||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| kendo-grid { | kendo-grid { | ||||
| height: calc(100vh - 48px); | |||||
| height: 100%; // calc(100vh - 48px); | |||||
| .add-new-tool-bar{ | |||||
| width:30%; | |||||
| display: inline-block; | |||||
| } | |||||
| .add-new-tool-bar{ | |||||
| width:30%; | |||||
| display: inline-block; | |||||
| } | |||||
| .filter-panel-wrapper{ | |||||
| display: inline-block; | |||||
| text-align:right; | |||||
| } | |||||
| .filter-panel-wrapper{ | |||||
| display: inline-block; | |||||
| text-align:right; | |||||
| span.specific-upload{ | |||||
| margin-right: 10px; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| .balance { | .balance { | ||||
| display: inline-block; | |||||
| margin-left: 0px !important; | |||||
| width: calc(100% - 60px) !important; | |||||
| display: inline-block; | |||||
| margin-left: 0px !important; | |||||
| width: calc(100% - 60px) !important; | |||||
| } | } | ||||
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; | |||||
| import {PayInModel} from '../models/pay-in.model'; | import {PayInModel} from '../models/pay-in.model'; | ||||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | import {FormControl, FormGroup, Validators} from '@angular/forms'; | ||||
| import {PayInListFilterModel} from '../models/pay-in-list.filter.model'; | import {PayInListFilterModel} from '../models/pay-in-list.filter.model'; | ||||
| import {PopupIncomeFilterComponent} from '../popup-income-filter/popup-income-filter.component'; | import {PopupIncomeFilterComponent} from '../popup-income-filter/popup-income-filter.component'; | ||||
| import {PageChangeEvent, SortSettings} from '@progress/kendo-angular-grid'; | import {PageChangeEvent, SortSettings} from '@progress/kendo-angular-grid'; | ||||
| import {SortDescriptor} from '@progress/kendo-data-query'; | import {SortDescriptor} from '@progress/kendo-data-query'; | ||||
| import {UploadMetaModel} from '../models/uploadMetaModel'; | |||||
| 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'; | |||||
| const createFormGroup = dataItem => new FormGroup({ | |||||
| Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required), | |||||
| Trail : new FormControl(dataItem.Trail, Validators.required), | |||||
| Ts: new FormControl(dataItem.Ts, Validators.required), | |||||
| Balance: new FormControl(dataItem.Balance, Validators.required), | |||||
| OffsetBalance: new FormControl(dataItem.OffsetBalance, Validators.required), | |||||
| }); | |||||
| @Component({ | @Component({ | ||||
| export class PayInComponent implements OnInit { | export class PayInComponent implements OnInit { | ||||
| @Input() allowAddNew = true; | @Input() allowAddNew = true; | ||||
| @Input() allowEdit = true; | @Input() allowEdit = true; | ||||
| @Input() showLoanColumn = true; | |||||
| @Input() showUploadColumn = true; | |||||
| private filterUploadMeta: UploadMetaModel = new UploadMetaModel({}); | |||||
| public filterLoan = new LoanModel({}); | |||||
| @Input() filter: PayInListFilterModel = new PayInListFilterModel({}); | @Input() filter: PayInListFilterModel = new PayInListFilterModel({}); | ||||
| @Output() errorOccurred = new EventEmitter<string>(); | |||||
| @ViewChild('filterDialog', {static: true}) filterDialog: PopupIncomeFilterComponent; | @ViewChild('filterDialog', {static: true}) filterDialog: PopupIncomeFilterComponent; | ||||
| private privateLoadDataNow = false; | |||||
| public gridData: PayInListResult = { data: [], total: 0}; | public gridData: PayInListResult = { data: [], total: 0}; | ||||
| public incomeFormGroup: FormGroup; | public incomeFormGroup: FormGroup; | ||||
| public sortable: SortSettings = { | public sortable: SortSettings = { | ||||
| mode: 'single' | mode: 'single' | ||||
| }; | }; | ||||
| public filterable = false; | |||||
| public loading = false; | public loading = false; | ||||
| private debouncedLoadFilteredData = () => {}; | |||||
| constructor(private pis: PayInService, private router: Router) { } | |||||
| constructor(private pis: PayInService, private lss: LoanSingleService, private router: Router) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.loadFilteredData(); | |||||
| this.debouncedLoadFilteredData = debounce(this.loadFilteredData, 100); | |||||
| } | } | ||||
| public loadFilteredData(): void{ | public loadFilteredData(): void{ | ||||
| this.gridData.total = resp.total; | this.gridData.total = resp.total; | ||||
| this.gridData.data = []; | this.gridData.data = []; | ||||
| resp.data.forEach(v => { | resp.data.forEach(v => { | ||||
| this.gridData.data.push(new PayInModel(v)); | |||||
| this.loading = false; | |||||
| this.gridData.data.push(new PayInModelEx(v)); | |||||
| }); | }); | ||||
| this.loading = false; | |||||
| }, err => { | }, err => { | ||||
| this.loading = false; | this.loading = false; | ||||
| }, () => { | }, () => { | ||||
| ); | ); | ||||
| } | } | ||||
| // Upload Filter | |||||
| @Input() set uploadMeta(value: UploadMetaModel) { | |||||
| this.filterUploadMeta = value; | |||||
| if ( value.Id <= 0 ) { | |||||
| this.filter.UploadIds = []; | |||||
| } else{ | |||||
| this.filter.UploadIds = [ value.Id.toString() ]; | |||||
| } | |||||
| this.debouncedLoadFilteredData(); | |||||
| console.log('upload filter changed', value, this.filter); | |||||
| } | |||||
| get uploadMeta(): UploadMetaModel { | |||||
| return this.filterUploadMeta; | |||||
| } | |||||
| // LoanId filter | |||||
| @Input() set Loan(value: LoanModel){ | |||||
| this.filterLoan = value; | |||||
| if ( value.Id === '' ) { | |||||
| this.filter.LoanIds = []; | |||||
| }else{ | |||||
| this.filter.LoanIds = [ value.Id ]; | |||||
| } | |||||
| this.debouncedLoadFilteredData(); | |||||
| console.log('filter loanId changed', value, this.filter); | |||||
| } | |||||
| get Loan(): LoanModel { | |||||
| return this.filterLoan; | |||||
| } | |||||
| @Input() set LoadDataNow( value: boolean) { | |||||
| this.privateLoadDataNow = value; | |||||
| if ( value === true ) { | |||||
| this.debouncedLoadFilteredData(); | |||||
| } | |||||
| } | |||||
| get LoadDataNow(): boolean { | |||||
| return this.privateLoadDataNow; | |||||
| } | |||||
| public addHandler({ sender }): void { | public addHandler({ sender }): void { | ||||
| this.closeEditor(sender); | this.closeEditor(sender); | ||||
| const offsetBalance = -1; | const offsetBalance = -1; | ||||
| this.showBalance = balance >= 0 ; | this.showBalance = balance >= 0 ; | ||||
| this.showOffsetBalance = offsetBalance >= 0 ; | this.showOffsetBalance = offsetBalance >= 0 ; | ||||
| this.incomeFormGroup = createFormGroup({ | |||||
| Id: 0, | |||||
| Trail: 168, | |||||
| Ts: new Date(), | |||||
| Balance: balance, | |||||
| OffsetBalance: offsetBalance | |||||
| }); | |||||
| this.incomeFormGroup = this.createFormGroup(new PayInModelEx({})); | |||||
| console.log(this.incomeFormGroup); | |||||
| sender.addRow(this.incomeFormGroup); | sender.addRow(this.incomeFormGroup); | ||||
| } | } | ||||
| private createFormGroup(dataItem: PayInModelEx): FormGroup { | |||||
| const Loan = this.filterLoan.Id !== '' ? this.filterLoan : dataItem.Loan; | |||||
| const UploadMeta = this.filterUploadMeta.Id !== 0 ? this.filterUploadMeta : dataItem.UploadMeta ; | |||||
| return new FormGroup({ | |||||
| Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required), | |||||
| Lender: new FormControl(dataItem.Lender), | |||||
| Amount: new FormControl(dataItem.Amount), | |||||
| LoanNumber: new FormControl(dataItem.LoanNumber), | |||||
| IncomeAmount: new FormControl(dataItem.IncomeAmount, Validators.required), | |||||
| IncomeType: new FormControl(dataItem.IncomeType, Validators.required), | |||||
| Ts: new FormControl(dataItem.Ts, Validators.required), | |||||
| Balance: new FormControl(dataItem.Balance, Validators.required), | |||||
| OffsetBalance: new FormControl(dataItem.OffsetBalance, Validators.required), | |||||
| Loan: new FormControl(Loan), | |||||
| UploadMeta: new FormControl(UploadMeta) | |||||
| }); | |||||
| } | |||||
| public editHandler({ sender, rowIndex, dataItem }): void { | public editHandler({ sender, rowIndex, dataItem }): void { | ||||
| this.closeEditor(sender); | this.closeEditor(sender); | ||||
| this.showBalance = dataItem.Balance >= 0 ; | this.showBalance = dataItem.Balance >= 0 ; | ||||
| this.showOffsetBalance = dataItem.OffsetBalance >= 0 ; | this.showOffsetBalance = dataItem.OffsetBalance >= 0 ; | ||||
| this.incomeFormGroup = createFormGroup(dataItem); | |||||
| this.incomeFormGroup = this.createFormGroup(dataItem); | |||||
| this.editedRowIndex = rowIndex; | this.editedRowIndex = rowIndex; | ||||
| sender.editRow(rowIndex, this.incomeFormGroup); | sender.editRow(rowIndex, this.incomeFormGroup); | ||||
| if ( !this.showBalance) { v.Balance = -1; } | if ( !this.showBalance) { v.Balance = -1; } | ||||
| if ( !this.showOffsetBalance) { v.OffsetBalance = -1; } | if ( !this.showOffsetBalance) { v.OffsetBalance = -1; } | ||||
| // const pi = new PayInModel( | |||||
| // v.Id, | |||||
| // this.Loan.Amount, | |||||
| // v.Balance, | |||||
| // this.Loan.Lender, | |||||
| // this.Loan.Id, | |||||
| // this.Loan.LenderLoanNumber, | |||||
| // v.OffsetBalance, | |||||
| // this.Loan.Settlement, | |||||
| // v.Trail, | |||||
| // v.Ts, | |||||
| // 0, | |||||
| // ); | |||||
| // console.log('saving PayIn', pi); | |||||
| // this.ls.savePayIn(pi, isNew).subscribe( | |||||
| // (resp: PayInModel) => { | |||||
| // this.Loan.cuPayIn(resp); | |||||
| // }, | |||||
| // err => { | |||||
| // this.errorOccurred.emit('Error saving Income'); | |||||
| // } | |||||
| // ); | |||||
| const pi = this.buildPayInForSave(v, isNew); | |||||
| this.lss.savePayIn(pi, isNew).subscribe( | |||||
| (resp: PayInModel) => { | |||||
| this.Loan.cuPayIn(resp); | |||||
| this.updatePiInGrid(new PayInModel(resp)); | |||||
| }, | |||||
| err => { | |||||
| // TODO: this.errorOccurred.emit('Error saving Income'); | |||||
| } | |||||
| ); | |||||
| sender.closeRow(rowIndex); | sender.closeRow(rowIndex); | ||||
| } | } | ||||
| private buildPayInForSave(v: any, isNew: boolean): PayInModel { | |||||
| const pi = new PayInModel({}); | |||||
| if ( isNew ) { pi.Id = 0; } | |||||
| // are we using filtered loan? | |||||
| let loan = v.Loan; | |||||
| if (loan === null || this.filterLoan !== undefined && this.filterLoan.Id !== '') { | |||||
| loan = this.filterLoan; | |||||
| } | |||||
| pi.Id = v.Id; | |||||
| pi.Amount = loan.Amount; | |||||
| pi.Balance = v.Balance; | |||||
| pi.Lender = loan.Id === '' ? v.Lender : loan.Lender; | |||||
| pi.LoanId = loan.Id; | |||||
| pi.LoanNumber = loan.LenderLoanNumber; | |||||
| pi.OffsetBalance = v.OffsetBalance; | |||||
| pi.Settlement = loan.Settlement; | |||||
| pi.IncomeAmount = v.IncomeAmount; | |||||
| pi.IncomeType = v.IncomeType; | |||||
| pi.UploadId = this.filterUploadMeta.Id; | |||||
| pi.Ts = v.Ts; | |||||
| return pi; | |||||
| } | |||||
| private updatePiInGrid(pi: PayInModel): void { | |||||
| const notFound = this.gridData.data.every( v => { | |||||
| if ( v.Id === pi.Id ){ | |||||
| Object.assign(v, pi); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| }); | |||||
| if ( notFound ) { | |||||
| this.gridData.data.unshift(pi); | |||||
| } | |||||
| } | |||||
| public removeHandler({ dataItem }): void { | public removeHandler({ dataItem }): void { | ||||
| console.log(dataItem); | |||||
| // const na = this.Loan.PayIn.filter(v => { | |||||
| // return v.Id !== dataItem.Id; | |||||
| // }); | |||||
| // | |||||
| // this.Loan.PayIn = na; | |||||
| // this.ls.removePayIn(dataItem.Id); | |||||
| console.log('delete', dataItem); | |||||
| this.Loan.PayIn = this.Loan.PayIn.filter(v => v.Id !== dataItem.Id ); | |||||
| this.gridData.data = this.gridData.data.filter(v => v.Id !== dataItem.Id) ; | |||||
| this.lss.removePayIn(dataItem.Id); | |||||
| } | } | ||||
| private closeEditor(grid, rowIndex = this.editedRowIndex): void{ | private closeEditor(grid, rowIndex = this.editedRowIndex): void{ |
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div *ngIf="scanDone" class="analysis-result"> | <div *ngIf="scanDone" class="analysis-result"> | ||||
| <kendo-grid *ngIf="ua.AAA !==undefined && ua.AAA !== null" [data]="analysisAAA"> | |||||
| <ng-template kendoGridToolbarTemplate> | |||||
| {{ ua.Funders[0] }} | |||||
| </ng-template> | |||||
| <kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </kendo-grid-column> | |||||
| <kendo-grid-column field="LoanNumber"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Settlement" format='{0:MM/dd/yyyy h:mm a}'> </kendo-grid-column> | |||||
| <kendo-grid-column field="LoanAmount" format='{0:c}' > </kendo-grid-column> | |||||
| <kendo-grid-column field="Balance" format='{0:c}' > </kendo-grid-column> | |||||
| <kendo-grid-column field="InTrail" format='{0:c}'> </kendo-grid-column> | |||||
| </kendo-grid> | |||||
| <div *ngIf="ua.Funders.length === 0"> | |||||
| <kendo-grid> </kendo-grid> | |||||
| </div> | |||||
| <div *ngIf="ua.Funders === undefined || ua.Funders.length == 0"> No result analyzed </div> | |||||
| <kendo-tabstrip *ngIf="ua.Funders !== undefined && ua.Funders.length > 0"> | |||||
| <kendo-tabstrip-tab *ngIf="ua.AAA !==undefined && ua.AAA !== null" [title]="'AAA'" [selected]="true"> | |||||
| <ng-template kendoTabContent> | |||||
| <kendo-grid [data]="analysisAAA"> | |||||
| <ng-template kendoGridToolbarTemplate> | |||||
| {{ ua.Funders[0] }} | |||||
| </ng-template> | |||||
| <kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </kendo-grid-column> | |||||
| <kendo-grid-column field="LoanNumber"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Settlement" format='{0:MM/dd/yyyy h:mm a}'> </kendo-grid-column> | |||||
| <kendo-grid-column field="LoanAmount" format='{0:c}' > </kendo-grid-column> | |||||
| <kendo-grid-column field="Balance" format='{0:c}' > </kendo-grid-column> | |||||
| <kendo-grid-column field="InTrail" format='{0:c}'> </kendo-grid-column> | |||||
| </kendo-grid> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab [title]="'Paris'" > | |||||
| <ng-template kendoTabContent> | |||||
| <p> | |||||
| Paris is the capital and most populous city of France. It has an area of 105 square kilometres (41 square miles) and a population in 2013 of 2,229,621 within its administrative limits. The city is both a commune and department, and forms the centre and headquarters of the Île-de-France, or Paris Region, which has an area of 12,012 square kilometres (4,638 square miles) and a population in 2014 of 12,005,077, comprising 18.2 percent of the population of France. | |||||
| </p> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab [title]="'Tallinn'"> | |||||
| <ng-template kendoTabContent> | |||||
| <p> | |||||
| Tallinn is the capital and largest city of Estonia. It is situated on the northern coast of the country, on the shore of the Gulf of Finland, 80 km (50 mi) south of Helsinki, east of Stockholm and west of Saint Petersburg. From the 13th century until 1918 (and briefly during the Nazi occupation of Estonia from 1941 to 1944), the city was known as Reval. Tallinn occupies an area of 159.2 km2 (61.5 sq mi) and has a population of 443,894. Approximately 32% of Estonia's total population lives in Tallinn. | |||||
| </p> | |||||
| <p> | |||||
| Tallinn was founded in 1248, but the earliest human settlements are over 5,000 years old, making it one of the oldest capital cities of Northern Europe. Due to its strategic location, the city became a major trade hub, especially from the 14th to the 16th century, when it grew in importance as part of the Hanseatic League. | |||||
| </p> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab [title]="'Sydney'" [disabled]="true"></kendo-tabstrip-tab> | |||||
| </kendo-tabstrip> | |||||
| </div> | </div> | ||||
| </ng-template> | </ng-template> | ||||
| (sizeChange)="onPayInSizeChange($event)" | (sizeChange)="onPayInSizeChange($event)" | ||||
| [(size)]="PayInSize"> | [(size)]="PayInSize"> | ||||
| <div class="pane-content"> | <div class="pane-content"> | ||||
| <h3>Outer splitter / Bottom pane</h3> | |||||
| <p>Non-resizable and non-collapsible.</p> | |||||
| <app-pay-in *ngIf="ua.Input !== undefined && ua.Input !== null" | |||||
| [uploadMeta]="ua.Input" | |||||
| [showUploadColumn]="false" | |||||
| [showLoanColumn]="true" | |||||
| [LoadDataNow] = "true" | |||||
| > | |||||
| </app-pay-in> | |||||
| </div> | </div> | ||||
| </kendo-splitter-pane> | </kendo-splitter-pane> | ||||
| </kendo-splitter> | </kendo-splitter> |
| height: 100%; | height: 100%; | ||||
| width: 100%; | width: 100%; | ||||
| background-color: lightgoldenrodyellow; | background-color: lightgoldenrodyellow; | ||||
| padding: 10px; | |||||
| box-shadow: inset 1px 1px 10px #9c9c8a; | |||||
| overflow: scroll; | |||||
| kendo-tabstrip{ | |||||
| box-shadow: 1px 1px 20px black; | |||||
| } | |||||
| kendo-grid{ | kendo-grid{ | ||||
| height: 100%; | height: 100%; | ||||
| } | } |