| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "broker", | |||
| "version": "2.0.25", | |||
| "version": "2.0.37", | |||
| "scripts": { | |||
| "ng": "ng", | |||
| "versionIncrease": "npm --no-git-tag-version version patch", | |||
| @@ -26,22 +26,23 @@ | |||
| "@fortawesome/angular-fontawesome": "^0.8.2", | |||
| "@fortawesome/fontawesome-svg-core": "^1.2.35", | |||
| "@fortawesome/free-solid-svg-icons": "^5.15.3", | |||
| "@progress/kendo-angular-buttons": "^6.1.0", | |||
| "@progress/kendo-angular-buttons": "^6.0.0", | |||
| "@progress/kendo-angular-charts": "^5.1.0", | |||
| "@progress/kendo-angular-common": "^2.0.0", | |||
| "@progress/kendo-angular-dateinputs": "^5.1.0", | |||
| "@progress/kendo-angular-dialog": "^5.0.0", | |||
| "@progress/kendo-angular-dropdowns": "^5.2.0", | |||
| "@progress/kendo-angular-dropdowns": "^5.0.0", | |||
| "@progress/kendo-angular-editor": "^2.0.2", | |||
| "@progress/kendo-angular-excel-export": "^4.0.0", | |||
| "@progress/kendo-angular-grid": "^5.0.3", | |||
| "@progress/kendo-angular-icons": "^0.4.2", | |||
| "@progress/kendo-angular-indicators": "^1.0.0", | |||
| "@progress/kendo-angular-inputs": "^7.2.0", | |||
| "@progress/kendo-angular-inputs": "^7.0.0", | |||
| "@progress/kendo-angular-intl": "^3.0.0", | |||
| "@progress/kendo-angular-l10n": "^3.0.0", | |||
| "@progress/kendo-angular-label": "^3.0.2", | |||
| "@progress/kendo-angular-layout": "^6.1.4", | |||
| "@progress/kendo-angular-listview": "^2.0.1", | |||
| "@progress/kendo-angular-menu": "^3.0.1", | |||
| "@progress/kendo-angular-navigation": "^1.0.0", | |||
| "@progress/kendo-angular-notification": "^3.0.0", | |||
| @@ -50,10 +51,10 @@ | |||
| "@progress/kendo-angular-popup": "^4.0.0", | |||
| "@progress/kendo-angular-progressbar": "^2.0.0", | |||
| "@progress/kendo-angular-toolbar": "^4.0.0", | |||
| "@progress/kendo-angular-treeview": "^5.1.1", | |||
| "@progress/kendo-angular-treeview": "^5.0.0", | |||
| "@progress/kendo-angular-upload": "^7.1.0", | |||
| "@progress/kendo-data-query": "^1.5.4", | |||
| "@progress/kendo-drawing": "^1.2.0", | |||
| "@progress/kendo-drawing": "^1.5.12", | |||
| "@progress/kendo-licensing": "^1.0.2", | |||
| "@progress/kendo-svg-icons": "^0.1.2", | |||
| "@progress/kendo-theme-default": "^4.36.0", | |||
| @@ -32,6 +32,8 @@ import {PayOutDetailsComponent} from './pay-out-details/pay-out-details.componen | |||
| import {ContactEditComponent} from './contact-edit/contact-edit.component'; | |||
| import {ContactModel} from './models/contact.model'; | |||
| import {BrokerDashboardComponent} from './broker-dashboard/broker-dashboard.component'; | |||
| import {LoanStepsComponent} from './loan-steps/loan-steps.component'; | |||
| import {LoanRowListComponent} from './loan-row-list/loan-row-list.component'; | |||
| const routes: Routes = [ | |||
| @@ -69,6 +71,8 @@ const routes: Routes = [ | |||
| {path : 'reward-paid/:id', component: RewardPaidComponent, canActivate: [AuthGuard] }, | |||
| {path: 'contact', component: ContactEditComponent, canActivate: [AuthGuard] }, | |||
| {path : 'contact/:id', component: ContactEditComponent, canActivate: [AuthGuard] }, | |||
| {path : 'loan-steps/:id', component: LoanStepsComponent, canActivate: [AuthGuard] }, | |||
| {path : 'loan-row-list', component: LoanRowListComponent, canActivate: [AuthGuard] }, | |||
| {path : 'e403', component: E403Component }, | |||
| ]; | |||
| @@ -117,6 +117,11 @@ import { StringFilterComponent } from './grid-filter/string-filter/string-filter | |||
| import { DateFilterComponent } from './grid-filter/date-filter/date-filter.component'; | |||
| import { ContactEditComponent } from './contact-edit/contact-edit.component'; | |||
| import { BrokerDashboardComponent } from './broker-dashboard/broker-dashboard.component'; | |||
| import { LoanStepsComponent } from './loan-steps/loan-steps.component'; | |||
| import { ListViewModule } from '@progress/kendo-angular-listview'; | |||
| import { LoanRowListComponent } from './loan-row-list/loan-row-list.component'; | |||
| import { LoanSingleRowComponent } from './loan-single-row/loan-single-row.component'; | |||
| @@ -202,6 +207,9 @@ export function initializeApp(appConfig: AppConfig): () => Promise<void> { | |||
| DateFilterComponent, | |||
| ContactEditComponent, | |||
| BrokerDashboardComponent, | |||
| LoanStepsComponent, | |||
| LoanRowListComponent, | |||
| LoanSingleRowComponent, | |||
| ], | |||
| imports: [ | |||
| BrowserModule, | |||
| @@ -238,7 +246,8 @@ export function initializeApp(appConfig: AppConfig): () => Promise<void> { | |||
| ProgressBarModule, | |||
| PagerModule, | |||
| PopupModule, | |||
| PDFExportModule | |||
| PDFExportModule, | |||
| ListViewModule | |||
| ], | |||
| providers: [ | |||
| MenuService, | |||
| @@ -0,0 +1,25 @@ | |||
| <kendo-listview | |||
| [data]="allLoans | async" | |||
| [pageable]="true" | |||
| [skip]="skip" | |||
| [pageSize]="take" | |||
| (pageChange)="handlePageChange($event)" | |||
| containerClass="k-d-flex k-flex-col k-flex-nowrap" | |||
| > | |||
| <ng-template kendoListViewHeaderTemplate> | |||
| <div class="header"> | |||
| <div class="title">All Loans</div> | |||
| <kendo-textbox | |||
| placeholder="Filter items..." | |||
| (valueChange)="handleFilterChange($event)" | |||
| > | |||
| <ng-template kendoTextBoxSuffixTemplate> | |||
| <span class="k-icon k-i-search"></span> | |||
| </ng-template> | |||
| </kendo-textbox> | |||
| </div> | |||
| </ng-template> | |||
| <ng-template kendoListViewItemTemplate let-dataItem="dataItem" let-index="index" let-isFirst="isFirst" let-isLast="isLast"> | |||
| <app-loan-single-row [Loan]="dataItem" [Loading]="allLoans.loading"></app-loan-single-row> | |||
| </ng-template> | |||
| </kendo-listview> | |||
| @@ -0,0 +1,20 @@ | |||
| kendo-listview{ | |||
| height: 100vh; | |||
| } | |||
| .k-listview { | |||
| font-family: sans-serif; | |||
| height: calc(100vh - 48px); | |||
| } | |||
| .product { | |||
| width: 100%; | |||
| } | |||
| .header { | |||
| display: flex; | |||
| padding: 12px; | |||
| } | |||
| .title { | |||
| flex: 1; | |||
| font-size: 22px; | |||
| font-weight: bold; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { LoanRowListComponent } from './loan-row-list.component'; | |||
| describe('LoanRowListComponent', () => { | |||
| let component: LoanRowListComponent; | |||
| let fixture: ComponentFixture<LoanRowListComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ LoanRowListComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(LoanRowListComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,53 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {LoanSummaryService} from '../service/loan_summary.service'; | |||
| import {SessionService} from '../service/session.service'; | |||
| import {AppConfig} from '../app.config'; | |||
| import {CompositeFilterDescriptor, SortDescriptor} from '@progress/kendo-data-query'; | |||
| import {PageChangeEvent} from '@progress/kendo-angular-listview'; | |||
| @Component({ | |||
| selector: 'app-loan-row-list', | |||
| templateUrl: './loan-row-list.component.html', | |||
| styleUrls: ['./loan-row-list.component.scss'] | |||
| }) | |||
| export class LoanRowListComponent implements OnInit { | |||
| public allLoans: LoanSummaryService; | |||
| public skip: number = 0 | |||
| public take: number = 10; | |||
| constructor(private lss: LoanSummaryService, private ss: SessionService, private config: AppConfig) { } | |||
| ngOnInit(): void { | |||
| this.allLoans = this.lss; | |||
| this.LoadLoansForCurrentUser() | |||
| } | |||
| public LoadLoansForCurrentUser() { | |||
| let people = this.ss.loggedIn.User; | |||
| const sort: Array <SortDescriptor> = [{dir: 'desc', field: 'Settlement'}]; | |||
| if ( this.ss.loggedIn.role.toLowerCase() == 'admin' || this.ss.loggedIn.role.toLowerCase() == 'manager' ){ | |||
| const filter: CompositeFilterDescriptor = {logic: 'and', filters: [] }; | |||
| this.lss.query({ skip: this.skip, take: this.take, sort, filter}); | |||
| }else{ // load broker's loans | |||
| const filter: CompositeFilterDescriptor = {logic: 'or', filters: [] }; | |||
| filter.filters.push({field:'client_ids', operator: 'contains', value: people.Id, ignoreCase: true}) | |||
| filter.filters.push({field:'broker_ids', operator: 'contains', value: people.Id, ignoreCase: true}) | |||
| filter.filters.push({field:'other_rewarder_ids', operator: 'contains', value: people.Id, ignoreCase: true}) | |||
| this.lss.query({ skip: this.skip, take: this.take, sort, filter}); | |||
| } | |||
| } | |||
| public handleFilterChange(query: string): void { | |||
| const normalizedQuery = query.toLowerCase(); | |||
| const filterExpession = (item) => | |||
| item.productName.toLowerCase().indexOf(normalizedQuery) !== -1 || | |||
| item.categoryName.toLowerCase().indexOf(normalizedQuery) !== -1; | |||
| console.log(normalizedQuery, filterExpession); | |||
| } | |||
| public handlePageChange( event: PageChangeEvent) { | |||
| this.skip = event.skip; | |||
| this.take = event.take; | |||
| this.LoadLoansForCurrentUser(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| <div class="container" style="border-top-style: none; border-top-color: #ff0000; border-left: 10px groove #969696; padding: 10px; margin: 5px; background-color: #e9e9e9; box-shadow: inset 1 1 10 10;"> | |||
| <div class="row" style="padding-bottom: 10px;"> | |||
| <div class="col-md-4"> | |||
| <h5>Overview<div *ngIf="Loading" class="spinner-border text-success" role="status"> | |||
| <span class="sr-only">Loading...</span> | |||
| </div></h5> | |||
| <ul class="list-group"> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| {{Loan.Id}} | |||
| <span class="badge badge-primary badge-pill">ID</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| {{Loan.Amount | currency}} | |||
| <span class="badge badge-primary badge-pill">Amount</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| {{Loan.Settlement | date:"yyyy-MMM-dd"}} | |||
| <span class="badge badge-primary badge-pill">Settlement</span> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| <div class="col-md-4"> | |||
| <h5>People <div *ngIf="Loading" class="spinner-border text-success" role="status"> | |||
| <span class="sr-only">P)_PLoading... Peopl</span> | |||
| </div></h5> | |||
| <ul class="list-group"> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| <div *ngFor="let p of Loan.Client, let idx=index "> | |||
| <div *ngIf="Loan.ClientIds.length >0" class="customer-photo" [ngStyle]="{'background-image' : photoURL(Loan.ClientIds[idx])}"></div> | |||
| <div *ngIf="Loan.ClientIds.length >0" class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| <span class="badge badge-primary badge-pill">Client</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| <div *ngFor="let p of Loan.Broker, let idx=index "> | |||
| <div *ngIf="Loan.BrokerIds.length >0" class="customer-photo" [ngStyle]="{'background-image' : photoURL(Loan.BrokerIds[idx])}"></div> | |||
| <div *ngIf="Loan.BrokerIds.length >0" class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| <span class="badge badge-primary badge-pill">BDM</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| <div *ngFor="let p of Loan.OtherRewarder, let idx=index "> | |||
| <div *ngIf="Loan.OtherRewarderIds.length > 0 " class="customer-photo" [ngStyle]="{'background-image' : photoURL(Loan.OtherRewarderIds[idx])}"></div> | |||
| <div *ngIf="Loan.OtherRewarderIds.length > 0 " class="customer-name"> {{ p }}</div> | |||
| </div> | |||
| <span class="badge badge-primary badge-pill">Other</span> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| <div class="col-md-4"> | |||
| <h5 class="text-left">Remarks <div *ngIf="Loading" class="spinner-border text-success" role="status"> | |||
| <span class="sr-only">Loading...</span> | |||
| </div></h5> | |||
| <ul class="list-group"> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| {{Loan.Lender}} | |||
| <span class="badge badge-primary badge-pill">Lender</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| {{Loan.LenderLoanNumber}} | |||
| <span class="badge badge-primary badge-pill">#</span> | |||
| </li> | |||
| <li class="list-group-item d-flex justify-content-between align-items-center"> | |||
| <button kendoButton (click)="gotoSteps()">Actions List</button> | |||
| <button kendoButton (click)="editLoan()">Edit</button> | |||
| </li> | |||
| </ul> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,20 @@ | |||
| .customer-photo{ | |||
| display: inline-block; | |||
| width: 16px; | |||
| height: 16px; | |||
| border-radius: 50%; | |||
| background-size: 16px 35px; | |||
| background-position: center center; | |||
| vertical-align: middle; | |||
| line-height: 16px; | |||
| box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2); | |||
| margin-left: 1px; | |||
| margin-bottom: 2px; | |||
| } | |||
| .customer-name { | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| line-height: 16px; | |||
| padding-left: 10px; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { LoanSingleRowComponent } from './loan-single-row.component'; | |||
| describe('LoanSingleRowComponent', () => { | |||
| let component: LoanSingleRowComponent; | |||
| let fixture: ComponentFixture<LoanSingleRowComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ LoanSingleRowComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(LoanSingleRowComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,36 @@ | |||
| import { Component, Input, OnInit } from '@angular/core'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {LoanSummaryService} from '../service/loan_summary.service'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import {Router} from '@angular/router'; | |||
| import {NotificationService} from '@progress/kendo-angular-notification'; | |||
| @Component({ | |||
| selector: 'app-loan-single-row', | |||
| templateUrl: './loan-single-row.component.html', | |||
| styleUrls: ['./loan-single-row.component.scss'] | |||
| }) | |||
| export class LoanSingleRowComponent implements OnInit { | |||
| @Input() public Loan: LoanModel = new LoanModel({}); | |||
| @Input() public Loading: boolean = false; | |||
| constructor(private service: LoanSummaryService, | |||
| private auth: AuthService, | |||
| private router: Router, | |||
| private notificationService: NotificationService) { } | |||
| ngOnInit(): void { | |||
| } | |||
| private photoURL(peopleId: any): string { | |||
| const url = this.auth.getUrl('avatar/') + peopleId; | |||
| return 'url("' + url + '")'; | |||
| } | |||
| public gotoSteps() { | |||
| this.router.navigate(['loan-steps/', this.Loan.Id]).then(); | |||
| } | |||
| public editLoan() { | |||
| this.router.navigate(['edit-loan/', this.Loan.Id]).then(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| {{LoanId}} | |||
| <kendo-listview | |||
| [data]="steps" | |||
| containerClass="k-d-flex k-flex-col k-flex-nowrap" | |||
| > | |||
| <ng-template | |||
| kendoListViewItemTemplate | |||
| let-dataItem="dataItem" | |||
| let-index="index" | |||
| let-isFirst="isFirst" | |||
| let-isLast="isLast" | |||
| > | |||
| <div class="product" [class.border-bottom]="!isLast"> | |||
| <strong>[{{ index }}]:</strong> | |||
| <span class="product-name">{{ dataItem.label }}</span> | |||
| <span class="item-position" | |||
| >({{ isFirst ? "first" : isLast ? "last" : "mid" }})</span | |||
| > | |||
| </div> | |||
| </ng-template> | |||
| </kendo-listview> | |||
| <kendo-tilelayout [columns]="1" [gap]="gap" [reorderable]="true"> | |||
| <kendo-tilelayout-item [col]="1" title="Tile 1"> | |||
| <kendo-tilelayout-item-body> | |||
| {{ firstTileContent }} | |||
| </kendo-tilelayout-item-body> | |||
| </kendo-tilelayout-item> | |||
| <kendo-tilelayout-item [col]="1" title="Tile 2"> | |||
| <kendo-tilelayout-item-body> | |||
| {{ secondTileContent }} | |||
| </kendo-tilelayout-item-body> | |||
| </kendo-tilelayout-item> | |||
| <kendo-tilelayout-item title="Tile 3" [col]="1"> | |||
| <kendo-tilelayout-item-body> | |||
| {{ thirdTileContent }} | |||
| </kendo-tilelayout-item-body> | |||
| </kendo-tilelayout-item> | |||
| <kendo-tilelayout-item title="Tile 4" [col]="1"> | |||
| <kendo-tilelayout-item-body> | |||
| {{ fourthTileContent }} | |||
| </kendo-tilelayout-item-body> | |||
| </kendo-tilelayout-item> | |||
| </kendo-tilelayout> | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { LoanStepsComponent } from './loan-steps.component'; | |||
| describe('LoanStepsComponent', () => { | |||
| let component: LoanStepsComponent; | |||
| let fixture: ComponentFixture<LoanStepsComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ LoanStepsComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(LoanStepsComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,51 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {LoanSingleService} from '../service/loan.single.service'; | |||
| import {ActivatedRoute, Router} from '@angular/router'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import { TileLayoutGap } from '@progress/kendo-angular-layout'; | |||
| @Component({ | |||
| selector: 'app-loan-steps', | |||
| templateUrl: './loan-steps.component.html', | |||
| styleUrls: ['./loan-steps.component.scss'] | |||
| }) | |||
| export class LoanStepsComponent implements OnInit { | |||
| public LoanId: string; | |||
| public steps = [ | |||
| { label: "Step One" }, | |||
| { label: "Step Two" }, | |||
| { label: "Step Three", optional: true }, | |||
| { label: "Step Four" }, | |||
| { label: "Step Five" }, | |||
| ]; | |||
| public gap: TileLayoutGap = { | |||
| rows: 25, | |||
| columns: 40 | |||
| }; | |||
| public firstTileContent = `Reactive Netscape cherry pick domain contribution lazy Edge program.`; | |||
| public secondTileContent = `Lazy reflog freelancer Dijkstra directed acyclic graph concurrent uglify concurrency Safari.`; | |||
| public thirdTileContent = `Senior-engineer Edge backend UI subclass tech debt duck typing merge sort lazy.`; | |||
| public fourthTileContent = `Infrastructure tl;dr spy data store remote procedure call bootcamp pairing child keycaps.`; | |||
| constructor(private lss: LoanSingleService, private actRoute: ActivatedRoute, private auth: AuthService, private router: Router) { } | |||
| ngOnInit(): void { | |||
| const LoanId = this.actRoute.snapshot.params.id; | |||
| setTimeout(() => { | |||
| this.steps = this.stepsKeys(50); | |||
| }, 2000); | |||
| } | |||
| private stepsKeys(num: number): {label: string}[] { | |||
| let ret = []; | |||
| for (let i=1; i<=num; i++){ | |||
| ret.push( {label: i, optional: false}); | |||
| } | |||
| return ret; | |||
| } | |||
| } | |||
| @@ -18,7 +18,9 @@ export const mainMenuItems: any[] = [ | |||
| icon: 'dollar', | |||
| items: [ | |||
| { text: 'Start New Loan', icon: 'plus', url: './#edit-loan/' }, | |||
| { text: 'List All', icon: 'table' , url: './#list-all-loans' }, | |||
| { text: 'Export Loans', icon: 'table' , url: './#list-all-loans' }, | |||
| { text: 'List all', icon: 'table' , url: './#loan-row-list' }, | |||
| { text: 'Steps (demo)', icon: 'table' , url: './#loan-steps/6f024e92-6600-4a5a-8961-95fdba4ed31d' }, | |||
| { text: '--', separator: 'true' }, | |||
| { text: 'list income', icon: 'dollar', url: './#list-income' }, | |||
| { text: '--', separator: 'true' }, | |||
| @@ -53,7 +55,7 @@ export const brokerMenuItems: any[] = [ | |||
| { | |||
| text: 'My Loans', | |||
| icon: 'categorize', | |||
| url: './#broker-loan-list' | |||
| url: './#broker-dashboard' | |||
| }, | |||
| { | |||
| text: 'My Earnings', | |||