| @@ -4,76 +4,80 @@ | |||
| "scripts": { | |||
| "ng": "ng", | |||
| "start": "ng serve --proxy-config proxy.conf.json", | |||
| "build": "ng build", | |||
| "build": "ng build ", | |||
| "buildsfm": "ng build --prod --base-href=/broker/ --deploy-url=/broker/ ", | |||
| "test": "ng test", | |||
| "lint": "ng lint", | |||
| "e2e": "ng e2e" | |||
| }, | |||
| "private": true, | |||
| "dependencies": { | |||
| "@angular/animations": "~10.2.0", | |||
| "@angular/common": "~10.2.0", | |||
| "@angular/compiler": "~10.2.0", | |||
| "@angular/core": "~10.2.0", | |||
| "@angular/forms": "~10.2.0", | |||
| "@angular/localize": "~10.2.0", | |||
| "@angular/platform-browser": "~10.2.0", | |||
| "@angular/platform-browser-dynamic": "~10.2.0", | |||
| "@angular/router": "~10.2.0", | |||
| "@fortawesome/angular-fontawesome": "^0.7.0", | |||
| "@fortawesome/fontawesome-svg-core": "^1.2.28", | |||
| "@fortawesome/free-solid-svg-icons": "^5.13.0", | |||
| "@progress/kendo-angular-buttons": "^5.1.1", | |||
| "@progress/kendo-angular-charts": "^4.2.1", | |||
| "@progress/kendo-angular-common": "^1.2.1", | |||
| "@progress/kendo-angular-dateinputs": "^4.0.0", | |||
| "@progress/kendo-angular-dialog": "^4.1.1", | |||
| "@progress/kendo-angular-dropdowns": "^4.2.4", | |||
| "@progress/kendo-angular-editor": "^1.2.5", | |||
| "@progress/kendo-angular-excel-export": "^3.0.0", | |||
| "@progress/kendo-angular-grid": "^4.8.0", | |||
| "@progress/kendo-angular-icons": "^0.2.1", | |||
| "@progress/kendo-angular-indicators": "^0.2.0", | |||
| "@progress/kendo-angular-inputs": "^6.4.0", | |||
| "@progress/kendo-angular-intl": "^2.0.0", | |||
| "@progress/kendo-angular-l10n": "^2.0.0", | |||
| "@progress/kendo-angular-label": "^2.3.3", | |||
| "@progress/kendo-angular-layout": "4.1.5 - 5", | |||
| "@progress/kendo-angular-menu": "^2.0.4", | |||
| "@progress/kendo-angular-navigation": "^0.2.0", | |||
| "@progress/kendo-angular-notification": "^2.1.3", | |||
| "@progress/kendo-angular-pdf-export": "^2.0.0", | |||
| "@progress/kendo-angular-popup": "^3.0.0", | |||
| "@progress/kendo-angular-progressbar": "^0.2.3", | |||
| "@progress/kendo-data-query": "^1.0.0", | |||
| "@progress/kendo-drawing": "^1.5.12", | |||
| "@progress/kendo-svg-icons": "^0.0.3", | |||
| "@angular/animations": "~11.2.5", | |||
| "@angular/common": "~11.2.5", | |||
| "@angular/compiler": "~11.2.5", | |||
| "@angular/core": "~11.2.5", | |||
| "@angular/forms": "~11.2.5", | |||
| "@angular/localize": "~11.2.5", | |||
| "@angular/platform-browser": "~11.2.5", | |||
| "@angular/platform-browser-dynamic": "~11.2.5", | |||
| "@angular/router": "~11.2.5", | |||
| "@fortawesome/angular-fontawesome": "^0.8.2", | |||
| "@fortawesome/fontawesome-svg-core": "^1.2.34", | |||
| "@fortawesome/free-solid-svg-icons": "^5.15.2", | |||
| "@progress/kendo-angular-buttons": "^6.1.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.1.0", | |||
| "@progress/kendo-angular-editor": "^2.0.1", | |||
| "@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.1.2", | |||
| "@progress/kendo-angular-intl": "^3.1.0", | |||
| "@progress/kendo-angular-l10n": "^3.0.0", | |||
| "@progress/kendo-angular-label": "^3.0.1", | |||
| "@progress/kendo-angular-layout": "6", | |||
| "@progress/kendo-angular-menu": "^3.0.0", | |||
| "@progress/kendo-angular-navigation": "^1.0.0", | |||
| "@progress/kendo-angular-notification": "^3.0.0", | |||
| "@progress/kendo-angular-pdf-export": "^3.0.0", | |||
| "@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.0", | |||
| "@progress/kendo-data-query": "^1.5.4", | |||
| "@progress/kendo-drawing": "^1.9.4", | |||
| "@progress/kendo-licensing": "^1.1.3", | |||
| "@progress/kendo-svg-icons": "^0.1.2", | |||
| "@progress/kendo-theme-default": "latest", | |||
| "bootstrap": "^3.4.1", | |||
| "hammerjs": "^2.0.0", | |||
| "bootstrap": "^4.6.0", | |||
| "hammerjs": "^2.0.8", | |||
| "rxjs": "^6.6.6", | |||
| "tslib": "^2.0.0", | |||
| "zone.js": "~0.10.2", | |||
| "@progress/kendo-angular-toolbar": "^3.0.1" | |||
| "ts-debounce": "^3.0.0", | |||
| "tslib": "^2.1.0", | |||
| "zone.js": "~0.11.4" | |||
| }, | |||
| "devDependencies": { | |||
| "@angular-devkit/build-angular": "~0.1002.0", | |||
| "@angular/cli": "~10.2.0", | |||
| "@angular/compiler-cli": "~10.2.0", | |||
| "@types/node": "^12.11.1", | |||
| "@types/jasmine": "~3.5.0", | |||
| "@types/jasminewd2": "~2.0.3", | |||
| "codelyzer": "^6.0.0", | |||
| "@angular-devkit/build-angular": "~0.1102.4", | |||
| "@angular/cli": "~11.2.4", | |||
| "@angular/compiler-cli": "~11.2.5", | |||
| "@types/jasmine": "~3.6.6", | |||
| "@types/jasminewd2": "~2.0.8", | |||
| "@types/node": "^14.14.33", | |||
| "codelyzer": "^6.0.1", | |||
| "jasmine-core": "~3.6.0", | |||
| "jasmine-spec-reporter": "~5.0.0", | |||
| "karma": "~5.0.0", | |||
| "jasmine-spec-reporter": "~6.0.0", | |||
| "karma": "~6.2.0", | |||
| "karma-chrome-launcher": "~3.1.0", | |||
| "karma-coverage-istanbul-reporter": "~3.0.2", | |||
| "karma-jasmine": "~4.0.0", | |||
| "karma-jasmine-html-reporter": "^1.5.0", | |||
| "karma-coverage-istanbul-reporter": "~3.0.3", | |||
| "karma-jasmine": "~4.0.1", | |||
| "karma-jasmine-html-reporter": "^1.5.4", | |||
| "protractor": "~7.0.0", | |||
| "ts-node": "~8.3.0", | |||
| "ts-node": "~9.1.1", | |||
| "tslint": "~6.1.0", | |||
| "typescript": "~4.0.2" | |||
| "typescript": "^4.1.5" | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ import { TransactionListComponent } from './transaction-list/transaction-list.co | |||
| import { TransactionComponent } from './transaction/transaction.component'; | |||
| import {ListAllLoansComponent} from './list-all-loans/list-all-loans.component'; | |||
| import {LoanAddNewComponent} from './loan-add-new/loan-add-new.component'; | |||
| import {LoanEditComponent} from './loan-edit/loan-edit.component'; | |||
| const routes: Routes = [ | |||
| @@ -18,7 +19,7 @@ const routes: Routes = [ | |||
| {path : 'transaction', component: TransactionComponent, canActivate: [AuthGuard]}, | |||
| {path : 'transaction-list', component: TransactionListComponent, canActivate: [AuthGuard]}, | |||
| {path : 'list-all-loans', component: ListAllLoansComponent, }, | |||
| {path : 'start-a-new-loan', component: LoanAddNewComponent, }, | |||
| {path : 'start-a-new-loan', component: LoanEditComponent, }, | |||
| ]; | |||
| @NgModule({ | |||
| @@ -1,6 +1,6 @@ | |||
| <app-top-bar></app-top-bar> | |||
| <app-loan-edit #loanEditComponent></app-loan-edit> | |||
| <app-loan-add-new #loanEditComponent></app-loan-add-new> | |||
| <router-outlet></router-outlet> | |||
| @@ -38,8 +38,8 @@ export class AppComponent implements OnInit , OnDestroy { | |||
| (item:any) =>{ | |||
| // console.log("emit on select : " + item.text); | |||
| if ( item.popup === 'loanEdit'){ | |||
| this.loanEdit.somedata = '' + Math.random() + 'changed'; | |||
| this.loanEdit.open('dialog'); | |||
| // this.loanEdit.somedata = '' + Math.random() + 'changed'; | |||
| // this.loanEdit.open('dialog'); | |||
| } | |||
| } | |||
| ); | |||
| @@ -40,7 +40,7 @@ import { DropDownsModule } from '@progress/kendo-angular-dropdowns'; | |||
| import { TransactionComponent } from './transaction/transaction.component'; | |||
| import { TransactionListComponent } from './transaction-list/transaction-list.component'; | |||
| import { ExcelExportModule } from '@progress/kendo-angular-excel-export'; | |||
| import { RatingComponent } from './transaction-list/rating.component'; | |||
| import { RatingComponent } from './rating-input/rating.component'; | |||
| import { TransDetailsComponent } from './trans-details/trans-details.component'; | |||
| import { TransTailsComponent } from './trans-details/trans-tails/trans-tails.component'; | |||
| import {AuthHttpInterceptor} from './auth/auth-http-interceptor.service'; | |||
| @@ -59,6 +59,10 @@ import { BasicinfoComponent } from './loan-edit/basicinfo/basicinfo.component'; | |||
| import { PeopleRewardComponent } from './loan-edit/people-reward/people-reward.component'; | |||
| import { TrailIncomeComponent } from './loan-edit/trail-income/trail-income.component'; | |||
| import { HintCardComponent } from './loan-edit/hint-card/hint-card.component'; | |||
| import { PeopleSelectComponent } from './loan-edit/people-select/people-select.component'; | |||
| import { LoanDetailComponent } from './loan-detail/loan-detail.component'; | |||
| import {LoanSingleService} from './service/loan.single.service'; | |||
| import { RatingInputComponent } from './rating-input/rating-input.component'; | |||
| @@ -89,10 +93,14 @@ import { HintCardComponent } from './loan-edit/hint-card/hint-card.component'; | |||
| BasicinfoComponent, | |||
| PeopleRewardComponent, | |||
| TrailIncomeComponent, | |||
| HintCardComponent | |||
| HintCardComponent, | |||
| PeopleSelectComponent, | |||
| LoanDetailComponent, | |||
| RatingInputComponent | |||
| ], | |||
| imports: [ | |||
| BrowserModule, | |||
| BrowserAnimationsModule, | |||
| FormsModule, | |||
| CommonModule, | |||
| HttpClientModule, | |||
| @@ -126,6 +134,7 @@ import { HintCardComponent } from './loan-edit/hint-card/hint-card.component'; | |||
| AuthService, | |||
| WebSocketService, | |||
| LoanSummaryService, | |||
| LoanSingleService, | |||
| { | |||
| provide: HTTP_INTERCEPTORS, | |||
| useClass: AuthHttpInterceptor, | |||
| @@ -1,5 +1,5 @@ | |||
| import {Injectable} from '@angular/core'; | |||
| import {HttpEvent, HttpEventType, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; | |||
| import {HttpEvent, HttpEventType, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; | |||
| import {Observable} from 'rxjs'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import {tap} from 'rxjs/operators'; | |||
| @@ -13,6 +13,7 @@ export class AuthHttpInterceptor implements HttpInterceptor { | |||
| intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |||
| let h = req.headers; | |||
| if (this.auth.loggedIn.hasValidSession()) { | |||
| h = h.set('Biukop-Session', this.auth.loggedIn.session); | |||
| } | |||
| @@ -27,11 +28,16 @@ export class AuthHttpInterceptor implements HttpInterceptor { | |||
| }); | |||
| return next.handle(authReq).pipe( | |||
| tap(event => { | |||
| //console.log(event); | |||
| // console.log(event); | |||
| if (event.type === HttpEventType.Response){ | |||
| console.log('Response received'); | |||
| //console.log(event.body); | |||
| //this.auth.logout(); | |||
| const bs = event.headers.get('biukop-session'); | |||
| if (bs !== undefined){ | |||
| if ( this.auth.loggedIn.session !== bs ){ | |||
| this.auth.loggedIn.session = bs; | |||
| this.auth.saveSessionInfo(); | |||
| console.log('switch session:' , bs); | |||
| } | |||
| } | |||
| } | |||
| }) | |||
| ); | |||
| @@ -23,6 +23,7 @@ export class AuthComponent implements OnInit, OnDestroy{ | |||
| constructor(private authService: AuthService, private router: Router, private notificationService: NotificationService) { } | |||
| ngOnInit(): void { | |||
| this.authService.logout(); | |||
| this.loginSub = this.authService.loginSuccess.subscribe( | |||
| responseData => { | |||
| // console.log(responseData); | |||
| @@ -1,3 +1,3 @@ | |||
| <div class="h-divider"> | |||
| <div class="shadow"></div> | |||
| </div> | |||
| <div class="biukop-shadow"></div> | |||
| </div> | |||
| @@ -6,13 +6,13 @@ | |||
| width: 100%; | |||
| position: relative; | |||
| } | |||
| .h-divider .shadow { | |||
| .h-divider .biukop-shadow { | |||
| overflow: hidden; | |||
| height: 20px; | |||
| } | |||
| .h-divider .shadow:after { | |||
| .h-divider .biukop-shadow:after { | |||
| content: ''; | |||
| display: block; | |||
| margin: -25px auto 0; | |||
| @@ -21,7 +21,7 @@ | |||
| border-radius: 125px/12px; | |||
| box-shadow: 0 0 8px black; | |||
| } | |||
| .h-divider .text { | |||
| width: 100px; | |||
| height: 45px; | |||
| @@ -35,7 +35,7 @@ | |||
| box-shadow: 0 2px 4px #999; | |||
| background: white; | |||
| } | |||
| .h-divider .text i { | |||
| position: absolute; | |||
| top: 4px; | |||
| @@ -49,7 +49,7 @@ | |||
| font-style: normal; | |||
| color: #999; | |||
| } | |||
| .h-divider .text2 { | |||
| width: 70px; | |||
| height: 70px; | |||
| @@ -62,7 +62,7 @@ | |||
| box-shadow: 0 2px 4px #999; | |||
| background: white; | |||
| } | |||
| .h-divider img { | |||
| position: absolute; | |||
| margin: 4px; | |||
| @@ -71,4 +71,4 @@ | |||
| border: 1px dashed #aaa; | |||
| } | |||
| /* end of horizontal dividor */ | |||
| /* end of horizontal dividor */ | |||
| @@ -24,6 +24,7 @@ | |||
| <kendo-icon [name]="'photo-camera'" [size]="'medium'"></kendo-icon> | |||
| <app-people-select></app-people-select> | |||
| <button kendoButton (click)="onButtonClick()">Default</button> | |||
| <bkp-divider-text>some text</bkp-divider-text> | |||
| @@ -15,10 +15,10 @@ | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Rating" title="Rating" width="110" [resizable]="false" filter="numeric"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <kendo-rating | |||
| <app-biukop-rating | |||
| [value]="dataItem.Rating" | |||
| [max]="5" | |||
| ></kendo-rating> | |||
| ></app-biukop-rating> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| @@ -3,7 +3,7 @@ | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="First" title="Broker Name" minResizableWidth="250" > | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem)}"></div> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.Id)}"></div> | |||
| <div class="customer-name"> {{ dataItem.First }} , {{ dataItem.Last }} </div> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| @@ -18,7 +18,8 @@ export class ChartTopBrokersComponent implements OnInit { | |||
| this.gridData = this.http.get<TopBrokerModel[]>(this.auth.getUrl('chart/top-broker')); | |||
| } | |||
| private photoURL(dataItem: any): string { | |||
| return 'url("https://via.placeholder.com/48")'; | |||
| private photoURL(peopleId: any): string { | |||
| const url = this.auth.getUrl('avatar/') + peopleId; | |||
| return 'url("' + url + '")'; | |||
| } | |||
| } | |||
| @@ -14,6 +14,11 @@ | |||
| (filterChange)="filterChange($event)" | |||
| 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> | |||
| <kendo-grid-column field="Id" width="50" [class]="'topAlign'" [sortable]="false" [filterable]="false"> | |||
| <ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex" > | |||
| <p title="{{dataItem.Id}}" > {{dataItem.Index}}</p> | |||
| @@ -73,10 +78,10 @@ | |||
| <kendo-grid-column-group title="Loan Details" [columnMenu]="false" [headerClass]="'colGroupLoanDetails'"> | |||
| <kendo-grid-column field="Rating" title="Rating" width="150" [resizable]="false" [filterable]="false" [headerClass]="'colRating'" [class]="'topAlign colRating'"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <kendo-rating | |||
| <app-biukop-rating | |||
| [value]="dataItem.Rating" | |||
| [max]="5" | |||
| ></kendo-rating> | |||
| ></app-biukop-rating> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| @@ -193,10 +198,46 @@ | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <ng-template kendoGridDetailTemplate let-dataItem let-rowIndex="rowIndex" [kendoGridDetailTemplateShowIf]="allowEdit.bind(this)"> | |||
| <!-- <ng-template kendoGridDetailTemplate let-dataItem let-rowIndex="rowIndex" [kendoGridDetailTemplateShowIf]="allowEdit.bind(this)">--> | |||
| <!-- <category-details [category]="dataItem"></category-details>--> | |||
| <div style="background-color:yellow;"> {{dataItem.Id}}</div> | |||
| </ng-template> | |||
| <!-- <app-loan-detail [dataItem]="dataItem"> </app-loan-detail>--> | |||
| <!-- </ng-template>--> | |||
| <kendo-grid-pdf fileName="Transaction.pdf" [allPages]="true" [repeatHeaders]="true"></kendo-grid-pdf> | |||
| <kendo-grid-excel fileName="Transaction.xlsx" [fetchData]="allData"> | |||
| <kendo-excelexport-column field="Index" title="Index"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="ClientNames" title="Client"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="BrokerNames" title="Broker"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="OtherRewarderNames" title="Beneficiary"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Amount" title="Amount"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Status" title="Status"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Rating" title="Rating"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Settlement" title="Settlement"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Lender" title="Lender"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="LenderLoanNumber" title="Lender's Loan ID"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="NumOfTrail" title="Number of Trails"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Trail" title="Total Trails Received"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="PayOut" title="Paid to Beneficiary"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Balance" title="Balance"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="OffsetBalance" title="Offset Balance"> | |||
| </kendo-excelexport-column> | |||
| <kendo-excelexport-column field="Item" title="Loan Target"> | |||
| </kendo-excelexport-column> | |||
| </kendo-grid-excel> | |||
| </kendo-grid> | |||
| @@ -3,6 +3,7 @@ import {DataStateChangeEvent, GridComponent, GridDataResult} from '@progress/ken | |||
| import {CompositeFilterDescriptor, SortDescriptor, toODataString} from '@progress/kendo-data-query'; | |||
| import {LoanSummaryService} from '../service/loan_summary.service'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import {Observable} from 'rxjs'; | |||
| @Component({ | |||
| selector: 'app-list-all-loans', | |||
| @@ -63,4 +64,8 @@ export class ListAllLoansComponent implements OnInit { | |||
| return this.auth.allowEditLoan(); | |||
| } | |||
| public allData = (): Observable<GridDataResult> => { | |||
| return this.service.queryAll({skip: 0, take: 999999, sort: this.sort, filter: this.filter}); | |||
| } | |||
| } | |||
| @@ -1,53 +1,139 @@ | |||
| <div class="container outer"> | |||
| <div class="container"> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div style="margin-top:20px;"></div> | |||
| <div class="row"> | |||
| <div class="col-sm-12"> | |||
| <kendo-stepper | |||
| [steps]="steps" | |||
| [stepType]="'label'" | |||
| [(currentStep)]="currentStep" | |||
| [style.width.px]="570"> | |||
| </kendo-stepper> | |||
| </div> | |||
| </div> | |||
| <div class="row"> | |||
| <div class="col-sm-9"> | |||
| <div class="pane-content"> | |||
| <form class="k-form" [formGroup]="form"> | |||
| <app-loan-basic-info *ngIf="currentStep === 0" [basicInfo]="currentGroup"></app-loan-basic-info> | |||
| <app-loan-people-reward *ngIf="currentStep === 1" [peopleReward]="currentGroup"> </app-loan-people-reward> | |||
| <app-loan-trail-income *ngIf="currentStep === 2" [trailIncome]="currentGroup"> </app-loan-trail-income> | |||
| <span class="k-form-separator"></span> | |||
| <div class="k-form-buttons k-buttons-end"> | |||
| <span class="page">Step {{ currentStep + 1 }} of 3</span> | |||
| <div> | |||
| <button class="k-button prev" *ngIf="currentStep !== 0" (click)="prev()">Previous</button> | |||
| <button class="k-button k-primary" (click)="next()" *ngIf="currentStep !== 2">Next</button> | |||
| <button class="k-button k-primary" (click)="submit()" *ngIf="currentStep === 2">Submit</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| <div class="col-sm-3"> | |||
| <app-loan-edit-hint-card> </app-loan-edit-hint-card> | |||
| </div> | |||
| <div class="example-wrapper" style="opacity:1;"> | |||
| <kendo-dialog title="Transaction" *ngIf="dialogOpened" (close)="close('dialog')" [minWidth]="500" [width]="850" | |||
| [minHeight]="600"> | |||
| <div class="example-wrapper"> | |||
| <div class="row"> | |||
| <div class="col-sm-12"> | |||
| <div class="vertical-spacer"></div> | |||
| <div class="vertical-spacer"></div> | |||
| <hr> | |||
| Proudly presented by Supercredit.com | |||
| <div class="col-xs-12 col-sm-12 "> | |||
| <div class="pane-content"> | |||
| <form class="k-form" [formGroup]="registerForm"> | |||
| <fieldset class="k-form-fieldset"> | |||
| <legend class="k-form-legend">Bank Transaction Details</legend> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="transID" text="Transaction ID"></kendo-label> | |||
| <input formControlName="transID" kendoTextBox #transID required /> | |||
| <kendo-formhint>Unique transaction | |||
| ID from bank </kendo-formhint> | |||
| <kendo-formerror>Error: the ID is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="submissionDate" [optional]="false" | |||
| text="Submission Date"></kendo-label> | |||
| <kendo-datepicker #submissionDate formControlName="submissionDate" | |||
| [min]="min" [max]="max"> | |||
| </kendo-datepicker> | |||
| <kendo-formhint>Date in bank</kendo-formhint> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="clientNames" [optional]="false" | |||
| text="Client Names"></kendo-label> | |||
| <kendo-multiselect | |||
| #clientNames | |||
| formControlName="clientNames" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="brokers" [optional]="false" | |||
| text="Brokers Names"></kendo-label> | |||
| <kendo-multiselect | |||
| #brokers | |||
| formControlName="brokers" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="referals" [optional]="false" | |||
| text="Referals/BDM"></kendo-label> | |||
| <kendo-multiselect | |||
| #referals | |||
| formControlName="referals" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="status" [text]="'Progress Status'"></kendo-label> | |||
| <kendo-dropdownlist | |||
| #status | |||
| formControlName="status" | |||
| [data]="listClientNames" | |||
| [defaultItem]="{ text: 'Select Status', value: null }" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| required | |||
| > | |||
| </kendo-dropdownlist> | |||
| <kendo-formerror>Error: status is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="lender" text="Lender Organization"> | |||
| </kendo-label> | |||
| <input formControlName="lender" kendoTextBox #lender required /> | |||
| <kendo-formhint>E.g. NAB Bank</kendo-formhint> | |||
| <kendo-formerror>Error: Lender is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="limit" text="Loan Limit"> | |||
| </kendo-label> | |||
| <input formControlName="limit" kendoTextBox #limit required /> | |||
| <kendo-formhint>E.g. 8000</kendo-formhint> | |||
| <kendo-formerror>Error: Limit is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="settlementDate" [optional]="false" | |||
| text="Settlement Date"></kendo-label> | |||
| <kendo-datepicker #settlementDate formControlName="settlementDate" | |||
| [min]="min" [max]="max"> | |||
| </kendo-datepicker> | |||
| <kendo-formhint>Date Settled</kendo-formhint> | |||
| </kendo-formfield> | |||
| <!-- <div class="k-form-buttons"> | |||
| <button class="k-button k-primary" | |||
| (click)="submitForm()">Add New </button> | |||
| <button class="k-button" (click)="clearForm()">Clear</button> | |||
| </div> --> | |||
| </fieldset> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <kendo-dialog-actions> | |||
| <button kendoButton (click)="clearForm()">Clear</button> | |||
| <button kendoButton (click)="submitForm()" primary="true">Add New</button> | |||
| </kendo-dialog-actions> | |||
| </kendo-dialog> | |||
| <kendo-window [minWidth]="200" [minHeight]="125" title="About" *ngIf="windowOpened" (close)="close('window')"> | |||
| <p style="text-align: center;">Additional info</p> | |||
| </kendo-window> | |||
| </div> | |||
| @@ -1,5 +1,2 @@ | |||
| .pane-content { padding: 0 10px; } | |||
| div.vertical-spacer { | |||
| margin-bottom: 30px; | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| import {Component, OnInit, ViewChild} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | |||
| import {StepperComponent} from '@progress/kendo-angular-layout'; | |||
| import { Component, OnInit, ViewChild } from '@angular/core'; | |||
| import { FormControl, FormGroup, Validators } from '@angular/forms'; | |||
| @Component({ | |||
| selector: 'app-loan-add-new', | |||
| @@ -8,75 +8,72 @@ import {StepperComponent} from '@progress/kendo-angular-layout'; | |||
| styleUrls: ['./loan-add-new.component.scss'] | |||
| }) | |||
| export class LoanAddNewComponent implements OnInit { | |||
| public submitted: boolean ; | |||
| public currentStep = 0; | |||
| @ViewChild('stepper', { static: true }) | |||
| public stepper: StepperComponent; | |||
| public steps = [ | |||
| { label: 'Basic Info', isValid: true }, | |||
| { label: 'People Reward', isValid: false }, | |||
| { label: 'Trail Income', isValid: true }, | |||
| { label: 'Payable', isValid: true, optional: true } | |||
| ]; | |||
| //popup | |||
| public dialogOpened = false; | |||
| public windowOpened = false; | |||
| public registerForm: FormGroup = new FormGroup({ | |||
| transID: new FormControl(), | |||
| submissionDate: new FormControl(new Date(2020, 0, 10)), | |||
| status: new FormControl(), | |||
| lender: new FormControl('SuperCredit', Validators.required), | |||
| amount: new FormControl(100, [Validators.required , Validators.min(100), Validators.max(1000000000)]), | |||
| settlementDate: new FormControl(new Date(2020, 0, 9)), | |||
| description: new FormControl( '', Validators.maxLength(2000)), | |||
| }); | |||
| //test | |||
| public somedata : string = "opened"; | |||
| //form access | |||
| public form = new FormGroup({ | |||
| basicInfo: this.registerForm, | |||
| peopleReward: this.registerForm, | |||
| trailIncome :this.registerForm, | |||
| payable: this.registerForm | |||
| }); | |||
| //@ViewChild('birthDate', {static: true}) birthDate: LoanEditComponent; | |||
| constructor() { } | |||
| public get currentGroup(): FormGroup { | |||
| return this.getGroupAt(this.currentStep); | |||
| ngOnInit(): void { | |||
| } | |||
| private getGroupAt(index: number): FormGroup { | |||
| const groups = Object.keys(this.form.controls).map((groupName) => this.form.get(groupName)) as FormGroup[]; | |||
| return groups[index]; | |||
| public close(component) { | |||
| this.somedata = "" + Math.random(); | |||
| this[component + 'Opened'] = false; | |||
| } | |||
| public next(): void { | |||
| this.currentStep += 1; | |||
| public open(component) { | |||
| //this.somedata = "" + Math.random(); | |||
| this[component + 'Opened'] = true; | |||
| } | |||
| public prev(): void { | |||
| this.currentStep -= 1; | |||
| public action(status) { | |||
| console.log(`Dialog result: ${status}`); | |||
| this.dialogOpened = false; | |||
| } | |||
| public submit(): void { | |||
| this.submitted = true; | |||
| if (!this.form.valid) { | |||
| this.form.markAllAsTouched(); | |||
| this.stepper.validateSteps(); | |||
| } | |||
| console.log('Submitted data', this.form.value); | |||
| } | |||
| public min: Date = new Date(2015, 0, 1); | |||
| public max: Date = new Date(2030, 4, 31); | |||
| public registerForm: FormGroup = new FormGroup({ | |||
| transID: new FormControl(), | |||
| submissionDate: new FormControl(new Date(2020, 0, 10)), | |||
| clientNames : new FormControl([1]), | |||
| brokers : new FormControl([2]), | |||
| referals : new FormControl([3]), | |||
| status: new FormControl(), | |||
| lender: new FormControl(), | |||
| limit: new FormControl(), | |||
| settlementDate: new FormControl(new Date(2020, 0, 9)), | |||
| }); | |||
| public submitForm(): void { | |||
| this.registerForm.markAllAsTouched(); | |||
| //this.action('yes'); | |||
| } | |||
| ngOnInit(): void { | |||
| public clearForm(): void { | |||
| this.registerForm.reset(); | |||
| //this.action('no'); | |||
| } | |||
| public listClientNames: Array<{ text: string, value: number }> = [ | |||
| { text: 'John', value: 1 }, | |||
| { text: 'Steven', value: 2 }, | |||
| { text: 'William', value: 3 } | |||
| ]; | |||
| public statis: { text: string, value: number } = { text: 'Female', value: 2 }; | |||
| } | |||
| @@ -0,0 +1,6 @@ | |||
| <p>loan-detail works!</p> | |||
| <div style="background-color:yellow;"> {{dataItem.Id}}</div> | |||
| <div style="background-color:yellow;"> {{dataItem.Client}}</div> | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { LoanDetailComponent } from './loan-detail.component'; | |||
| describe('LoanDetailComponent', () => { | |||
| let component: LoanDetailComponent; | |||
| let fixture: ComponentFixture<LoanDetailComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ LoanDetailComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(LoanDetailComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,16 @@ | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| @Component({ | |||
| selector: 'app-loan-detail', | |||
| templateUrl: './loan-detail.component.html', | |||
| styleUrls: ['./loan-detail.component.scss'] | |||
| }) | |||
| export class LoanDetailComponent implements OnInit { | |||
| @Input() public dataItem :any; | |||
| constructor() { } | |||
| ngOnInit(): void { | |||
| } | |||
| } | |||
| @@ -1,79 +1,105 @@ | |||
| <ng-container [formGroup]="basicInfo"> | |||
| <fieldset class="k-form-fieldset"> | |||
| <legend class="k-form-legend"></legend> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="transID" text="Transaction ID"></kendo-label> | |||
| <input formControlName="transID" kendoTextBox #transID disabled/> | |||
| <div class="row"> | |||
| <div class="col-sm-9"> | |||
| <form class="k-form" [formGroup]="basicInfo" (ngSubmit)="next()"> | |||
| <ng-container > | |||
| <fieldset class="k-form-fieldset"> | |||
| <legend class="k-form-legend"></legend> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="LoanId" text="Loan ID"></kendo-label> | |||
| <input formControlName="LoanId" kendoTextBox #LoanId /> | |||
| <kendo-formhint>Unique transaction | |||
| ID from SuperCredit (Auto generated when save, cannot be changed) </kendo-formhint> | |||
| <kendo-formerror>Error: the ID is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formhint>Unique transaction | |||
| ID from SuperCredit (Auto generated when save, cannot be changed) </kendo-formhint> | |||
| <kendo-formerror>Error: the ID is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="status" [text]="'Progress Status'"></kendo-label> | |||
| <kendo-dropdownlist | |||
| #status | |||
| formControlName="status" | |||
| [data]="listLoanStatus" | |||
| [defaultItem]="{ text: 'Select Status', value: null }" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| required | |||
| > | |||
| </kendo-dropdownlist> | |||
| <kendo-formerror>Error: status is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Status" [text]="'Progress Status'" ></kendo-label> | |||
| <kendo-dropdownlist | |||
| #Status | |||
| formControlName="Status" | |||
| [data]="listLoanStatus" | |||
| [defaultItem]="{ text: 'Select Status', value: null }" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| required | |||
| [valuePrimitive] = "true" | |||
| > | |||
| </kendo-dropdownlist> | |||
| <kendo-formhint>cannot be changed after settlement</kendo-formhint> | |||
| <kendo-formerror>Error: status is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="lender" text="Lender Organization"> | |||
| </kendo-label> | |||
| <kendo-textbox formControlName="lender" #lender [clearButton]="true"></kendo-textbox> | |||
| <kendo-formhint>E.g. NAB Bank</kendo-formhint> | |||
| <kendo-formerror>Error: Lender is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Lender" text="Lender Organization"> | |||
| </kendo-label> | |||
| <kendo-textbox (valueChange)="onLenderNameChange($event)" | |||
| formControlName="Lender" #Lender | |||
| [clearButton]="true" | |||
| ></kendo-textbox> | |||
| <kendo-formhint>E.g. NAB Bank, cannot be changed after settlement</kendo-formhint> | |||
| <kendo-formerror>Error: Lender is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Item" text="Loan Target"> | |||
| </kendo-label> | |||
| <kendo-textbox formControlName="Item" #Item [clearButton]="true" ></kendo-textbox> | |||
| <kendo-formhint>E.g. 333 Opera House, Sydney Road, NSW 2000 , cannot be changed after settlement</kendo-formhint> | |||
| <kendo-formerror>Loan Target is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="amount" text="Loan Limit"> </kendo-label> | |||
| <kendo-textbox formControlName="amount" #amount [clearButton]="true"></kendo-textbox> | |||
| <kendo-formhint>E.g. 8000</kendo-formhint> | |||
| <kendo-formerror>Error: Limit should be between 100 ~ 1,000,000,000</kendo-formerror> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="settlementDate" [optional]="false" text="Settlement Date"></kendo-label> | |||
| <kendo-datepicker #settlementDate formControlName="settlementDate" | |||
| [min]="minSettlement" [max]="maxSettlement"> | |||
| </kendo-datepicker> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Amount" text="Loan Limit"> </kendo-label> | |||
| <kendo-numerictextbox formControlName="Amount" #Amount [min]="100" [max]="1000000000" [format]="'c0'"></kendo-numerictextbox> | |||
| <kendo-formhint>E.g. 80000, cannot be changed after settlement</kendo-formhint> | |||
| <kendo-formerror>Error: Limit should be between 100 ~ 1,000,000,000</kendo-formerror> | |||
| </kendo-formfield> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formhint>Date Settled</kendo-formhint> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Settlement" [optional]="false" text="Settlement Date"></kendo-label> | |||
| <kendo-datepicker #Settlement formControlName="Settlement" | |||
| [min]="minSettlement" [max]="maxSettlement"> | |||
| </kendo-datepicker> | |||
| <kendo-formhint>Date Settled</kendo-formhint> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="description" [optional]="false" | |||
| text="Quick notes: ( < 1000 words ) "></kendo-label> | |||
| <kendo-editor #description formControlName="description" style="height: 500px;"></kendo-editor> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Description" [optional]="false" | |||
| text="Quick notes: ( < 1000 words ) " (click)="showDemoDescription($event)"></kendo-label> | |||
| <kendo-editor #Description formControlName="Description" style="height: 500px;" ></kendo-editor> | |||
| </kendo-formfield> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| <!-- <div class="k-form-buttons"> | |||
| <button class="k-button k-primary" | |||
| (click)="submitForm()">Add New </button> | |||
| <button class="k-button" (click)="clearForm()">Clear</button> | |||
| </div> --> | |||
| </fieldset> | |||
| </ng-container> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="Rating" [optional]="false" | |||
| text="Rating: " (click)="showDemoDescription($event)"></kendo-label> | |||
| <app-rating-input #Rating formControlName="Rating" ></app-rating-input> | |||
| </kendo-formfield> | |||
| </fieldset> | |||
| </ng-container> | |||
| <div class="k-form-buttons k-buttons-end"> | |||
| <div> | |||
| <button class="k-button k-primary" type="submit" >Next ▶ </button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| <span class="k-form-separator"></span> | |||
| </div> | |||
| <div class="col-sm-3"> | |||
| <app-loan-edit-hint-card [currentStep]="'basicInfo'"> </app-loan-edit-hint-card> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,5 @@ | |||
| .vertical-spacer{ | |||
| height:1px; | |||
| margin-bottom:20px; | |||
| } | |||
| @@ -1,5 +1,8 @@ | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| import {FormGroup} from '@angular/forms'; | |||
| import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | |||
| import {LoanModel} from '../../models/loan.model'; | |||
| import {LoanSingleService} from '../../service/loan.single.service'; | |||
| @Component({ | |||
| selector: 'app-loan-basic-info', | |||
| @@ -8,10 +11,18 @@ import {FormGroup} from '@angular/forms'; | |||
| }) | |||
| export class BasicinfoComponent implements OnInit { | |||
| constructor( private service: LoanSingleService) { } | |||
| @Input() Loan: LoanModel; | |||
| @Output() lenderNameChanged = new EventEmitter<string>(); | |||
| @Output() NotifyNext = new EventEmitter<boolean>(); | |||
| public minSettlement: Date = new Date(2015, 0, 1); | |||
| public maxSettlement: Date = new Date(2030, 4, 31); | |||
| public value = ` | |||
| public basicInfo: FormGroup ; | |||
| public DemoDescription = ` | |||
| <h1> Rich Text Editing </h1> | |||
| <table> | |||
| <tr> <td> | |||
| @@ -26,19 +37,10 @@ export class BasicinfoComponent implements OnInit { | |||
| <img src="https://edge.alluremedia.com.au/uploads/businessinsider/2015/05/P2P-to-date.jpg" | |||
| width=300"> | |||
| </td> | |||
| </tr> <tr> | |||
| <td colspan="2"> | |||
| <a href="https://google.com.au"> click for more details </a> | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| `; | |||
| constructor() { } | |||
| ngOnInit(): void { | |||
| } | |||
| public listLoanStatus: Array<{text: string, value: string}> = [ | |||
| {text: '1 - Processing: Just received from Client', value: 'Processing'} , | |||
| {text: '2 - Valuation : Valuating From Bank', value: 'Valuation'}, | |||
| @@ -48,5 +50,74 @@ export class BasicinfoComponent implements OnInit { | |||
| {text: '6 - Finished : Fully paid, terminated, or switched)', value: 'Finished'}, | |||
| ]; | |||
| @Input() public basicInfo: FormGroup; | |||
| ngOnInit(): void { | |||
| this.basicInfo = new FormGroup({ | |||
| LoanId: new FormControl({value: this.Loan.Id, disabled: true}), | |||
| Status: new FormControl(), | |||
| Item: new FormControl(this.Loan.Item, Validators.required), | |||
| Lender : new FormControl({value: 'SuperCredit', disabled: false}, Validators.required), | |||
| Amount: new FormControl(100, [Validators.required , Validators.min(100), Validators.max(1000000000)]), | |||
| Settlement: new FormControl(new Date(2020, 0, 9)), | |||
| Description: new FormControl( '', Validators.maxLength(2000)), | |||
| Rating: new FormControl({value: this.Loan.Rating, disabled: false}), | |||
| }); | |||
| this.LoadBasic(); | |||
| } | |||
| onLenderNameChange(value: string): void { | |||
| console.log('emit lender change'); | |||
| this.lenderNameChanged.emit(value); | |||
| } | |||
| public next(): void { | |||
| this.basicInfo.markAllAsTouched(); | |||
| // console.log(this.basicInfo, this.Loan); | |||
| this.Loan.Lender = this.basicInfo.value.Lender; | |||
| this.Loan.Status = this.basicInfo.value.Status; | |||
| this.Loan.Item = this.basicInfo.value.Item; | |||
| this.Loan.Amount = this.basicInfo.value.Amount; | |||
| this.Loan.Settlement = this.basicInfo.value.Settlement; | |||
| this.Loan.Description = this.basicInfo.value.Description; | |||
| this.Loan.Rating = this.basicInfo.value.Rating; | |||
| this.service.updateBasicInfo(this.Loan).subscribe( | |||
| resp => { | |||
| // console.log(resp); | |||
| // move to next step | |||
| this.NotifyNext.emit(true); | |||
| } | |||
| ); | |||
| } | |||
| public loanStatus(value: string): {text: string, value: string} { | |||
| this.listLoanStatus.forEach(( v) => { | |||
| if (v.value = value) { | |||
| return v; | |||
| } | |||
| }); | |||
| return null; | |||
| } | |||
| public showDemoDescription($event): void{ | |||
| this.basicInfo.get('Description').setValue(this.DemoDescription); | |||
| } | |||
| public LoadBasic(): void { | |||
| if (this.Loan === undefined) { | |||
| return ; | |||
| } | |||
| this.basicInfo.get('LoanId').setValue(this.Loan.Id); | |||
| this.basicInfo.get('Lender').setValue(this.Loan.Lender); | |||
| this.lenderNameChanged.emit(this.basicInfo.get('Lender').value); | |||
| this.basicInfo.get('Status').setValue(this.Loan.Status); | |||
| this.basicInfo.get('Item').setValue(this.Loan.Item); | |||
| this.basicInfo.get('Amount').setValue(this.Loan.Amount); | |||
| this.basicInfo.get('Settlement').setValue(this.Loan.Settlement); | |||
| this.basicInfo.get('Description').setValue(this.Loan.Description); | |||
| this.basicInfo.get('Rating').setValue(this.Loan.Rating); | |||
| return ; | |||
| if ( this.Loan.Status === 'Settled') { | |||
| this.basicInfo.get('Status').disable({onlySelf: true, emitEvent: false}); | |||
| this.basicInfo.get('Item').disable({onlySelf: true, emitEvent: false}); | |||
| this.basicInfo.get('Amount').disable({onlySelf: true, emitEvent: false}); | |||
| } | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ | |||
| [shape]="'circle'" | |||
| ></kendo-avatar> | |||
| <div> | |||
| <h1 kendoCardTitle>bg_tourism</h1> | |||
| <h1 kendoCardTitle>bg_tourism - {{currentStep}}</h1> | |||
| <p kendoCardSubtitle>Bulgaria, Europe</p> | |||
| </div> | |||
| </kendo-card-header> | |||
| @@ -1,4 +1,4 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| @Component({ | |||
| selector: 'app-loan-edit-hint-card', | |||
| @@ -7,6 +7,7 @@ import { Component, OnInit } from '@angular/core'; | |||
| }) | |||
| export class HintCardComponent implements OnInit { | |||
| @Input() currentStep: string; | |||
| constructor() { } | |||
| ngOnInit(): void { | |||
| @@ -1,136 +1,59 @@ | |||
| <div class="example-wrapper" style="opacity:1;"> | |||
| <kendo-dialog title="Transaction" *ngIf="dialogOpened" (close)="close('dialog')" [minWidth]="500" [width]="850" | |||
| [minHeight]="600"> | |||
| <div class="example-wrapper"> | |||
| <div class="row"> | |||
| <div class="col-xs-12 col-sm-12 "> | |||
| <div class="pane-content"> | |||
| <form class="k-form" [formGroup]="registerForm"> | |||
| <fieldset class="k-form-fieldset"> | |||
| <legend class="k-form-legend">Bank Transaction Details</legend> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="transID" text="Transaction ID"></kendo-label> | |||
| <input formControlName="transID" kendoTextBox #transID required /> | |||
| <kendo-formhint>Unique transaction | |||
| ID from bank </kendo-formhint> | |||
| <kendo-formerror>Error: the ID is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="submissionDate" [optional]="false" | |||
| text="Submission Date"></kendo-label> | |||
| <kendo-datepicker #submissionDate formControlName="submissionDate" | |||
| [min]="min" [max]="max"> | |||
| </kendo-datepicker> | |||
| <kendo-formhint>Date in bank</kendo-formhint> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="clientNames" [optional]="false" | |||
| text="Client Names"></kendo-label> | |||
| <kendo-multiselect | |||
| #clientNames | |||
| formControlName="clientNames" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="brokers" [optional]="false" | |||
| text="Brokers Names"></kendo-label> | |||
| <kendo-multiselect | |||
| #brokers | |||
| formControlName="brokers" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="referals" [optional]="false" | |||
| text="Referals/BDM"></kendo-label> | |||
| <kendo-multiselect | |||
| #referals | |||
| formControlName="referals" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| [valuePrimitive]="true" | |||
| [data]="listClientNames" | |||
| > | |||
| </kendo-multiselect> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="status" [text]="'Progress Status'"></kendo-label> | |||
| <kendo-dropdownlist | |||
| #status | |||
| formControlName="status" | |||
| [data]="listClientNames" | |||
| [defaultItem]="{ text: 'Select Status', value: null }" | |||
| [textField]="'text'" | |||
| [valueField]="'value'" | |||
| required | |||
| > | |||
| </kendo-dropdownlist> | |||
| <kendo-formerror>Error: status is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="lender" text="Lender Organization"> | |||
| </kendo-label> | |||
| <input formControlName="lender" kendoTextBox #lender required /> | |||
| <kendo-formhint>E.g. NAB Bank</kendo-formhint> | |||
| <kendo-formerror>Error: Lender is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="limit" text="Loan Limit"> | |||
| </kendo-label> | |||
| <input formControlName="limit" kendoTextBox #limit required /> | |||
| <kendo-formhint>E.g. 8000</kendo-formhint> | |||
| <kendo-formerror>Error: Limit is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="settlementDate" [optional]="false" | |||
| text="Settlement Date"></kendo-label> | |||
| <kendo-datepicker #settlementDate formControlName="settlementDate" | |||
| [min]="min" [max]="max"> | |||
| </kendo-datepicker> | |||
| <div class="container outer"> | |||
| <div class="container"> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div style="margin-top:20px;"></div> | |||
| <div class="row"> | |||
| <div class="col-sm-12" class="stepper"> | |||
| <kendo-stepper | |||
| #stepper | |||
| [steps]="steps" | |||
| [stepType]="'label'" | |||
| [(currentStep)]="currentStep" | |||
| [style.width.px]="570"> | |||
| </kendo-stepper> | |||
| </div> | |||
| </div> | |||
| <span class="k-form-separator"></span> | |||
| <app-loan-basic-info *ngIf="currentStep === 0" | |||
| #basicInfo | |||
| [Loan]="Loan" | |||
| (lenderNameChanged)="onLenderNameChange($event)" | |||
| (NotifyNext) ="next(0)" | |||
| ></app-loan-basic-info> | |||
| <app-loan-people-reward *ngIf="currentStep === 1" | |||
| [Loan]="Loan" | |||
| (NotifyNext) ="next(1)" | |||
| (NotifyPrev) ="prev(1)" | |||
| > </app-loan-people-reward> | |||
| <app-loan-trail-income *ngIf="currentStep === 2" | |||
| [Loan]="Loan" | |||
| [LenderName]="LenderName" | |||
| (NotifyPrev) ="prev(2)" | |||
| > </app-loan-trail-income> | |||
| <div class="row"> | |||
| <div class="col-sm-12 delete-button"> | |||
| Delete this Loan <kendo-switch [(ngModel)]="showDelete">Allow Delete</kendo-switch> | |||
| </div> | |||
| <div class="col-sm-12 delete-button" *ngIf="showDelete"> | |||
| <div class="k-form-buttons k-buttons-end delete-button"> | |||
| <button kendoButton icon="delete" class="k-button k-primary" (click)="del()" >Delete </button> | |||
| </div> | |||
| </div> | |||
| <kendo-formhint>Date Settled</kendo-formhint> | |||
| </kendo-formfield> | |||
| </div> | |||
| <div class="row"> | |||
| <div class="col-sm-12"> | |||
| <div class="vertical-spacer"></div> | |||
| <div class="vertical-spacer"></div> | |||
| <hr> | |||
| Proudly presented by SuperCredit.com | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- <div class="k-form-buttons"> | |||
| <button class="k-button k-primary" | |||
| (click)="submitForm()">Add New </button> | |||
| <button class="k-button" (click)="clearForm()">Clear</button> | |||
| </div> --> | |||
| </fieldset> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <kendo-dialog-actions> | |||
| <button kendoButton (click)="clearForm()">Clear</button> | |||
| <button kendoButton (click)="submitForm()" primary="true">Add New</button> | |||
| </kendo-dialog-actions> | |||
| </kendo-dialog> | |||
| <kendo-window [minWidth]="200" [minHeight]="125" title="About" *ngIf="windowOpened" (close)="close('window')"> | |||
| <p style="text-align: center;">Additional info</p> | |||
| </kendo-window> | |||
| </div> | |||
| @@ -1,3 +1,11 @@ | |||
| .pane-content { padding: 0 10px; } | |||
| div.vertical-spacer { | |||
| height: 10px; | |||
| margin-bottom: 30px; | |||
| } | |||
| .delete-button{ | |||
| text-align: right; | |||
| margin-top: 10px; | |||
| } | |||
| @@ -1,5 +1,9 @@ | |||
| import { Component, OnInit, ViewChild } from '@angular/core'; | |||
| import { FormControl, FormGroup, Validators } from '@angular/forms'; | |||
| import {Component, OnInit, ViewChild, EventEmitter, Output} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | |||
| import {StepperComponent} from '@progress/kendo-angular-layout'; | |||
| import {LoanSingleService} from '../service/loan.single.service'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {BasicinfoComponent} from './basicinfo/basicinfo.component'; | |||
| @Component({ | |||
| @@ -8,72 +12,71 @@ import { FormControl, FormGroup, Validators } from '@angular/forms'; | |||
| styleUrls: ['./loan-edit.component.scss'] | |||
| }) | |||
| export class LoanEditComponent implements OnInit { | |||
| //popup | |||
| public dialogOpened = false; | |||
| public windowOpened = false; | |||
| public submitted: boolean ; | |||
| public currentStep = 0; | |||
| @ViewChild('stepper', { static: true }) public stepper: StepperComponent; | |||
| @ViewChild('basicInfo') public basicInfo: BasicinfoComponent; | |||
| @Output() LoanUpdated = new EventEmitter<LoanModel>(); | |||
| //test | |||
| public somedata : string = "opened"; | |||
| public Loan: LoanModel = new LoanModel(''); | |||
| public LenderName: string; | |||
| public curStepError = false; | |||
| //form access | |||
| //@ViewChild('birthDate', {static: true}) birthDate: LoanEditComponent; | |||
| public showDelete = false; | |||
| constructor() { } | |||
| public steps = [ | |||
| { label: 'Basic Info', isValid: true , errMsg: 'There are some errors'}, | |||
| { label: 'People Reward', isValid: true , errMsg: 'There are some errors'}, | |||
| { label: 'Trail Income', isValid: true, errMsg: 'There are some errors' } | |||
| ]; | |||
| ngOnInit(): void { | |||
| } | |||
| constructor( private lss: LoanSingleService) { } | |||
| public close(component) { | |||
| this.somedata = "" + Math.random(); | |||
| this[component + 'Opened'] = false; | |||
| public get currentStepError(): boolean{ | |||
| const ret = false; // TODO: set the value | |||
| this.curStepError = ret; | |||
| return ret; | |||
| } | |||
| public open(component) { | |||
| //this.somedata = "" + Math.random(); | |||
| this[component + 'Opened'] = true; | |||
| } | |||
| public next(step: number): void { | |||
| if (! this.currentStepError && (this.currentStep !== this.steps.length)) { | |||
| this.currentStep += 1; | |||
| public action(status) { | |||
| console.log(`Dialog result: ${status}`); | |||
| this.dialogOpened = false; | |||
| return; | |||
| } | |||
| this.stepper.validateSteps(); | |||
| } | |||
| public prev(step: number): void { | |||
| this.currentStep -= 1; | |||
| } | |||
| public onLenderNameChange(value: string): void{ | |||
| this.LenderName = value; | |||
| } | |||
| public min: Date = new Date(2015, 0, 1); | |||
| public max: Date = new Date(2030, 4, 31); | |||
| public registerForm: FormGroup = new FormGroup({ | |||
| transID: new FormControl(), | |||
| submissionDate: new FormControl(new Date(2020, 0, 10)), | |||
| clientNames : new FormControl([1]), | |||
| brokers : new FormControl([2]), | |||
| referals : new FormControl([3]), | |||
| status: new FormControl(), | |||
| lender: new FormControl(), | |||
| limit: new FormControl(), | |||
| settlementDate: new FormControl(new Date(2020, 0, 9)), | |||
| }); | |||
| public submitForm(): void { | |||
| this.registerForm.markAllAsTouched(); | |||
| //this.action('yes'); | |||
| ngOnInit(): void { | |||
| const demoId = '02ad4e5a-fb5c-4f29-bc89-381a49d77bb7'; | |||
| this.loadLoanById(demoId); | |||
| } | |||
| public clearForm(): void { | |||
| this.registerForm.reset(); | |||
| //this.action('no'); | |||
| public del(): void { | |||
| console.log('delete loan', this.Loan.Id); | |||
| } | |||
| public listClientNames: Array<{ text: string, value: number }> = [ | |||
| { text: 'John', value: 1 }, | |||
| { text: 'Steven', value: 2 }, | |||
| { text: 'William', value: 3 } | |||
| ]; | |||
| public statis: { text: string, value: number } = { text: 'Female', value: 2 }; | |||
| public loadLoanById(id: string): void { | |||
| const self = this; | |||
| console.log('loading loan ', this.Loan.Id); | |||
| this.lss.getLoan(id).subscribe( | |||
| resp => { | |||
| this.Loan.Response = resp; | |||
| this.Loan.apiUrlFunc = self.lss.apiUrlFunc(); | |||
| self.LoanUpdated.emit(this.Loan); | |||
| self.basicInfo.LoadBasic(); | |||
| } | |||
| ); | |||
| } | |||
| } | |||
| @@ -1 +1,59 @@ | |||
| <p>people-reward works!</p> | |||
| <ng-container> | |||
| <fieldset class="k-form-fieldset"> | |||
| <legend class="k-form-legend"></legend> | |||
| <div>SuperCredit Loan Id: {{Loan.Id}}</div> | |||
| <div class="vertical-spacer"></div> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <kendo-grid [data]="gridData" | |||
| [height]="410" | |||
| (add)="addHandler($event)" | |||
| (cancel)="cancelHandler($event)" | |||
| (save)="saveHandler($event)" | |||
| (edit)="editHandler($event)" | |||
| (remove)="removeHandler($event)" | |||
| > | |||
| <ng-template kendoGridToolbarTemplate> | |||
| <button kendoGridAddCommand>Add new</button> | |||
| </ng-template> | |||
| <kendo-grid-command-column title="command" width="220"> | |||
| <ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem> | |||
| <button kendoGridEditCommand [primary]="true" *ngIf="!dataItem.Paid">Edit</button> | |||
| <button kendoGridRemoveCommand *ngIf="!dataItem.Paid" >Remove</button> | |||
| <button kendoGridSaveCommand [disabled]="formGroup?.invalid">{{ isNew ? 'Add' : 'Update' }}</button> | |||
| <button kendoGridCancelCommand>{{ isNew ? 'Discard changes' : 'Cancel' }}</button> | |||
| </ng-template> | |||
| </kendo-grid-command-column> | |||
| <kendo-grid-column field="To" title="Name" width="300"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.To) }"></div> | |||
| <div class="customer-name"> {{ UserName(dataItem) }}</div> | |||
| </ng-template> | |||
| <ng-template | |||
| kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | |||
| <app-people-select [formControl]="formGroup.get('To')" [translateId]="true" [width]="200"></app-people-select> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Description" title="Description" width="200"></kendo-grid-column> | |||
| </kendo-grid> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| </fieldset> | |||
| </ng-container> | |||
| <div class="k-form-buttons k-buttons-end"> | |||
| <div> | |||
| <button class="k-button k-primary" (click)="prev()" > ◀ Prev</button> | |||
| <button kendoButton look="flat" [disabled]="true"> </button> | |||
| <button class="k-button k-primary" (click)="next()"> Next ▶</button> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,20 @@ | |||
| .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; | |||
| } | |||
| .customer-name { | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| line-height: 32px; | |||
| padding-left: 10px; | |||
| } | |||
| @@ -1,5 +1,24 @@ | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| import {FormGroup} from '@angular/forms'; | |||
| import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | |||
| import {LoanSummaryService} from '../../service/loan_summary.service'; | |||
| import {AuthService} from '../../service/auth.service'; | |||
| import {LoanModel} from '../../models/loan.model'; | |||
| import {Observable} from 'rxjs'; | |||
| import {RewardModel} from '../../models/reward.model'; | |||
| import {PeopleModel} from '../../models/people.model'; | |||
| import {debounce} from 'ts-debounce'; | |||
| const createFormGroup = dataItem => new FormGroup({ | |||
| Id: new FormControl(dataItem.Id), | |||
| To: new FormControl(dataItem.To, Validators.required), | |||
| From: new FormControl(dataItem.From), | |||
| Role: new FormControl(dataItem.Role), | |||
| Amount: new FormControl(dataItem.Amount, Validators.compose([Validators.required, Validators.pattern('^[0-9]{1,3}')])), | |||
| Description: new FormControl(dataItem.Description), | |||
| PayOutId: new FormControl({value: dataItem.PaidOutId, disabled: dataItem.PayOutId > 0 }) | |||
| }); | |||
| @Component({ | |||
| selector: 'app-loan-people-reward', | |||
| @@ -8,9 +27,209 @@ import {FormGroup} from '@angular/forms'; | |||
| }) | |||
| export class PeopleRewardComponent implements OnInit { | |||
| constructor() { } | |||
| @Input() public Loan: LoanModel; | |||
| @Output() public NotifyNext = new EventEmitter<boolean>(); | |||
| @Output() public NotifyPrev = new EventEmitter<boolean>(); | |||
| public contacts: PeopleModel[] = []; | |||
| public total = 0; | |||
| public gridData: RewardModel[] = []; | |||
| public data = [ | |||
| { | |||
| RewardId: 14, | |||
| UserName: 'Alina Cox', | |||
| PeopleId: 'df743325-810d-4d25-bb52-91a1a0c3ba22', | |||
| Role: 'broker', | |||
| RewardDescription: 'Trail', | |||
| RewardAmount: 100, | |||
| Paid: false, | |||
| }, | |||
| { | |||
| RewardId: 13, | |||
| UserName: 'Alina Cox', | |||
| PeopleId: 'df743325-810d-4d25-bb52-91a1a0c3ba22', | |||
| Role: 'broker', | |||
| RewardDescription: 'Trail', | |||
| RewardAmount: 100, | |||
| Paid: false, | |||
| }, | |||
| { | |||
| RewardId: 12, | |||
| UserName: 'Alina Cox', | |||
| PeopleId: 'df743325-810d-4d25-bb52-91a1a0c3ba22', | |||
| Role: 'broker', | |||
| RewardDescription: 'broker fee', | |||
| RewardAmount: 100, | |||
| Paid: true, | |||
| }, | |||
| { | |||
| RewardId: 11, | |||
| UserName: 'Helen Morgan', | |||
| PeopleId: '92dd6e14-10da-47ae-89ac-3a52d6567e61', | |||
| Role: 'Referral', | |||
| RewardDescription: 'no fee', | |||
| RewardAmount: 500, | |||
| Paid: true, | |||
| }, | |||
| { | |||
| RewardId: 10, | |||
| UserName: 'Oscar Coudors', | |||
| PeopleId: '09aa4151-7744-422e-9f2c-90b4871018f3', | |||
| Role: 'Upfront', | |||
| RewardDescription: 'no fee', | |||
| RewardAmount: 3000, | |||
| Paid: true, | |||
| } | |||
| ]; | |||
| public formGroup: FormGroup; | |||
| private editedRowIndex: number; | |||
| public existingRoles: string[] = [ | |||
| 'Broker', | |||
| 'Client', | |||
| 'Referral', | |||
| 'Admin' | |||
| ]; | |||
| private debounceFilter: any ; | |||
| constructor(private ls: LoanSummaryService, private auth: AuthService) { | |||
| } | |||
| public ngOnInit(): void { | |||
| // this.gridData = this.service.products(); | |||
| Object.assign(this.gridData, this.Loan.Reward); | |||
| console.log(this.Loan, this.gridData); | |||
| this.debounceFilter = debounce( (filter: string): void => { | |||
| this.auth.getPeopleList(filter).subscribe( | |||
| resp => { | |||
| this.contacts = resp.List; | |||
| this.total = resp.Count; | |||
| console.log(resp); | |||
| } | |||
| ); | |||
| }, 500) ; | |||
| } | |||
| public addHandler({ sender }): void { | |||
| this.closeEditor(sender); | |||
| this.formGroup = createFormGroup({ | |||
| Id: 0, | |||
| To: '', | |||
| From: '', | |||
| Role: '', | |||
| Amount: 0, | |||
| Description: '', | |||
| PayOutId: 0, | |||
| }); | |||
| sender.addRow(this.formGroup); | |||
| } | |||
| public editHandler({ sender, rowIndex, dataItem }): void { | |||
| this.closeEditor(sender); | |||
| if ( dataItem.Paid ) { | |||
| alert( 'cannot edit'); | |||
| }else{ | |||
| this.formGroup = createFormGroup(dataItem); | |||
| this.editedRowIndex = rowIndex; | |||
| sender.editRow(rowIndex, this.formGroup); | |||
| } | |||
| } | |||
| public cancelHandler({ sender, rowIndex }): void{ | |||
| console.log(sender); | |||
| this.closeEditor(sender, rowIndex); | |||
| } | |||
| public saveHandler({ sender, rowIndex, formGroup, isNew }): void { | |||
| const product = formGroup.value; | |||
| console.log('"saving', product, formGroup); | |||
| // this.service.save(product, isNew); | |||
| sender.closeRow(rowIndex); | |||
| } | |||
| public removeHandler({ dataItem }): void { | |||
| console.log(dataItem); | |||
| // this.service.remove(dataItem); | |||
| } | |||
| private closeEditor(grid, rowIndex = this.editedRowIndex): void { | |||
| grid.closeRow(rowIndex); | |||
| this.editedRowIndex = undefined; | |||
| this.formGroup = undefined; | |||
| } | |||
| public onUserNameChanged(event: any, dataItem: any): void { | |||
| console.log(event, dataItem); | |||
| dataItem.UserId = 'fuck this user id'; | |||
| // debounce | |||
| // console.log(event); | |||
| } | |||
| private photoURL(peopleId: string): string { | |||
| const url = this.auth.getUrl('avatar/') + peopleId; | |||
| return 'url("' + url + '")'; | |||
| } | |||
| private UserName(dataItem: RewardModel): string { | |||
| return dataItem.UserName; | |||
| } | |||
| public next(): void{ | |||
| this.NotifyNext.emit(true); | |||
| } | |||
| public prev(): void { | |||
| this.NotifyPrev.emit(true); | |||
| } | |||
| filterChange(filter: string): void { | |||
| if (filter.length > 1 ) { | |||
| this.debounceFilter(filter); | |||
| } | |||
| } | |||
| public getContactImageUrl(contactId: string): string { | |||
| return this.auth.getUrl('avatar/' + contactId); | |||
| } | |||
| public valueChange(value: any): void { | |||
| console.log('valueChange', value); | |||
| } | |||
| public selectionChange(value: any): void { | |||
| console.log('selectionChange', value); | |||
| } | |||
| public open(): void { | |||
| console.log('open'); | |||
| } | |||
| public close(): void { | |||
| console.log('close'); | |||
| } | |||
| public focus(): void { | |||
| console.log('focus'); | |||
| } | |||
| ngOnInit(): void { | |||
| public blur(): void { | |||
| console.log('blur'); | |||
| } | |||
| @Input() public peopleReward: FormGroup; | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| <kendo-multicolumncombobox | |||
| #list | |||
| [data]="searchResult" | |||
| [(ngModel)]="value" | |||
| [textField]="'Display'" | |||
| [valueField]="getEffectiveField()" | |||
| [valuePrimitive]="true" | |||
| [listHeight]="300" | |||
| placeholder="Type here to search" | |||
| [suggest]="true" | |||
| [filterable]="true" | |||
| [clearButton]="true" | |||
| [allowCustom]="false" | |||
| (filterChange)="filterChange($event)" | |||
| (valueChange)="valueChange($event)" | |||
| > | |||
| <kendo-combobox-column | |||
| [field]="'Display'" | |||
| [title]="'Contact Name'" | |||
| [width]="width" | |||
| > | |||
| <ng-template | |||
| kendoMultiColumnComboBoxColumnCellTemplate | |||
| let-dataItem | |||
| > | |||
| <img | |||
| class="contact-image" | |||
| [src]="getContactImageUrl(dataItem.Id)" | |||
| /> | |||
| <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | |||
| </ng-template> | |||
| </kendo-combobox-column> | |||
| <ng-template kendoMultiColumnComboBoxFooterTemplate> | |||
| <strong> | |||
| {{ total }} records in total | |||
| </strong> | |||
| </ng-template> | |||
| </kendo-multicolumncombobox> | |||
| @@ -0,0 +1,9 @@ | |||
| kendo-multicolumncombobox { | |||
| width: 100%; | |||
| } | |||
| .contact-image { | |||
| width: 20px; | |||
| height: 20px; | |||
| margin-right: 8px; | |||
| border-radius: 50%; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { PeopleSelectComponent } from './people-select.component'; | |||
| describe('PeopleSelectComponent', () => { | |||
| let component: PeopleSelectComponent; | |||
| let fixture: ComponentFixture<PeopleSelectComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ PeopleSelectComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(PeopleSelectComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,186 @@ | |||
| import {Component, forwardRef, Input, OnInit, ViewChild} from '@angular/core'; | |||
| import {AuthService} from '../../service/auth.service'; | |||
| import { debounce } from 'ts-debounce'; | |||
| import {PeopleModel} from '../../models/people.model'; | |||
| import {ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR} from '@angular/forms'; | |||
| import {MultiColumnComboBoxComponent} from '@progress/kendo-angular-dropdowns'; | |||
| import {Observable, of} from 'rxjs'; | |||
| import {PercentPipe} from '@angular/common'; | |||
| import {map} from 'rxjs/operators'; | |||
| @Component({ | |||
| selector: 'app-people-select', | |||
| templateUrl: './people-select.component.html', | |||
| styleUrls: ['./people-select.component.scss'], | |||
| providers: [ | |||
| { | |||
| provide: NG_VALUE_ACCESSOR, | |||
| useExisting: forwardRef(() => PeopleSelectComponent), | |||
| multi: true | |||
| } | |||
| ] | |||
| }) | |||
| export class PeopleSelectComponent implements OnInit, ControlValueAccessor { | |||
| @Input() disabled = false; | |||
| @Input() translateId: false; // true to out user Id, false output user name | |||
| @Input() width: number; | |||
| @Input() formControl: FormControl = new FormControl(); // this is a dummy place holder | |||
| @ViewChild('list', {static: true}) public text: MultiColumnComboBoxComponent; | |||
| public searchResult: PeopleModel[] = []; | |||
| public value = ''; // selecting the default and only empty contact element | |||
| public total = 0; | |||
| private debounceFilter: any ; | |||
| // Function to call when the rating changes. | |||
| private onChange = ( nameOrId: string) => {}; | |||
| // Function to call when the input is touched (when a star is clicked). | |||
| private onTouched = () => {}; | |||
| constructor(private auth: AuthService) { } | |||
| ngOnInit(): void { | |||
| this.prepareSearchPeople(); | |||
| } | |||
| private prepareSearchPeople(): void{ | |||
| this.debounceFilter = debounce( (filter: string): void => { | |||
| this.auth.getPeopleList(filter).subscribe( | |||
| resp => { | |||
| this.text.loading = false; | |||
| this.searchResult = resp.List; | |||
| this.total = resp.Count; | |||
| }, | |||
| error => { this.text.loading = false; }, | |||
| () => { this.text.loading = false; } | |||
| ); | |||
| }, 500) ; | |||
| } | |||
| getEffectiveField(): string { | |||
| return this.translateId ? 'Id' : 'Display'; | |||
| } | |||
| filterChange(filter: string): void { | |||
| if ( filter.length > 0 ) { | |||
| this.text.loading = true; | |||
| this.debounceFilter(filter); // conduct search even if filter is empty | |||
| } | |||
| } | |||
| public getContactImageUrl(contactId: string): string { | |||
| return this.auth.getUrl('avatar/' + contactId); | |||
| } | |||
| // ComboBox emit event on value change | |||
| public valueChange(str: string): void { | |||
| console.log('value change', str); | |||
| this.onChange(this.value); | |||
| } | |||
| // public selectionChange(value: PeopleModel): void { | |||
| // this.onTouched(); | |||
| // console.log('selectionChange', value); | |||
| // this.value = this.translateId ? value.Id : value.FullName; | |||
| // this.onChange(this.value); | |||
| // } | |||
| // public open(): void { | |||
| // this.onTouched(); | |||
| // // console.log('open', this.text); | |||
| // } | |||
| // | |||
| // public close(): void { | |||
| // this.onTouched(); | |||
| // // console.log('close', this.text); | |||
| // } | |||
| // | |||
| // public focus(): void { | |||
| // this.onTouched(); | |||
| // // console.log('focus', this.text); | |||
| // } | |||
| // | |||
| // public blur(): void { | |||
| // // console.log('blur', this.text); | |||
| // } | |||
| // Allows Angular to update the model (name or ID). | |||
| // Update the model and changes needed for the view here. | |||
| writeValue(nameOrId: string): void { | |||
| if ( nameOrId === undefined ){ | |||
| console.log('who called me for write', this); | |||
| return; | |||
| } | |||
| const changed = nameOrId !== this.value; | |||
| if (this.needSearch() ){ | |||
| this.text.loading = true; | |||
| console.log('searching ... ', nameOrId, this.translateId? 'by id' : 'by name'); | |||
| this.searchUser(nameOrId, this.translateId).subscribe( | |||
| ppl => { | |||
| const person = new PeopleModel( | |||
| ppl.Id, | |||
| ppl.First, | |||
| ppl.Last, | |||
| ppl.Middle, | |||
| ppl.Title, | |||
| ppl.Display, | |||
| ppl.Nick | |||
| ); | |||
| console.log('got search result ', person); | |||
| this.searchResult.push(person); // make sure it's available for selection, thus proper display | |||
| this.value = this.translateId ? person.Id : person.FullName; | |||
| console.log('before update', this.text.value); | |||
| this.text.value = this.value; | |||
| console.log('after update', this.text.value); | |||
| }, error => { console.error(error); this.text.loading = false; }, | |||
| () => {this.text.loading = false; } | |||
| ); | |||
| if ( changed ) { | |||
| this.onChange(nameOrId); | |||
| } | |||
| } | |||
| } | |||
| needSearch(): boolean { | |||
| this.searchResult.forEach((person) => { | |||
| if ( this.translateId) { | |||
| if ( person.Id === this.value ) { | |||
| return false; // found in cache | |||
| } | |||
| }else{ | |||
| if (person.FullName.includes(this.value)){ | |||
| return false; | |||
| } | |||
| } | |||
| }); | |||
| return true; | |||
| } | |||
| // Search a user either based on partial name or a complete ID | |||
| searchUser(nameOrId: string, translateId: boolean): Observable<PeopleModel>{ | |||
| if ( translateId ) { | |||
| return PeopleModel.searchPersonById( nameOrId) ; | |||
| }else{ | |||
| return PeopleModel.searchPersonByName(nameOrId) ; | |||
| } | |||
| } | |||
| // Allows Angular to register a function to call when the model (rating) changes. | |||
| // Save the function as a property to call later here. | |||
| registerOnChange(fn: (nameOrId: string) => void): void { | |||
| this.onChange = fn; | |||
| } | |||
| // Allows Angular to register a function to call when the input has been touched. | |||
| // Save the function as a property to call later here. | |||
| registerOnTouched(fn: () => void): void { | |||
| this.onTouched = fn; | |||
| } | |||
| // Allows Angular to disable the input. | |||
| setDisabledState(isDisabled: boolean): void { | |||
| this.disabled = isDisabled; | |||
| } | |||
| } | |||
| @@ -1 +1,71 @@ | |||
| <p>trail-income works!</p> | |||
| <div class="row"> | |||
| <div class="col-sm-12"> | |||
| <form class="k-form"> | |||
| <ng-container [formGroup]="trailIncome"> | |||
| <bkp-divider><kendo-icon [name]="'file-pdf'"> </kendo-icon> Record from {{LenderName}} </bkp-divider> | |||
| <kendo-label [for]="LoanIdLabel" text="SuperCredit Loan ID"></kendo-label> | |||
| <p #LoanIdLabel> {{Loan.Id}} </p> | |||
| <div class="vertical-spacer"></div> | |||
| <kendo-formfield> | |||
| <kendo-label [for]="LenderLoanNumber" text="Fund Supplier's Loan Identification"></kendo-label> | |||
| <input #LenderLoanNumber formControlName="LenderLoanNumber" kendoTextBox /> | |||
| <kendo-formhint>Unique transaction ID from Funds supplier </kendo-formhint> | |||
| <kendo-formerror>Error: the ID is required</kendo-formerror> | |||
| </kendo-formfield> | |||
| <div class="vertical-spacer"></div> | |||
| <bkp-divider><kendo-icon [name]="'table'"> </kendo-icon> Monthly Income </bkp-divider> | |||
| <kendo-grid [data]="gridData" | |||
| [height]="410" | |||
| (add)="addHandler($event)" | |||
| (cancel)="cancelHandler($event)" | |||
| (save)="saveHandler($event)" | |||
| (edit)="editHandler($event)" | |||
| (remove)="removeHandler($event)" | |||
| > | |||
| <ng-template kendoGridToolbarTemplate> | |||
| <button kendoGridAddCommand>Add new</button> | |||
| </ng-template> | |||
| <kendo-grid-command-column title="command" width="220"> | |||
| <ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem> | |||
| <button kendoGridEditCommand [primary]="true" *ngIf="!dataItem.Paid">Edit</button> | |||
| <button kendoGridRemoveCommand *ngIf="!dataItem.Paid" >Remove</button> | |||
| <button kendoGridSaveCommand [disabled]="formGroup?.invalid">{{ isNew ? 'Add' : 'Update' }}</button> | |||
| <button kendoGridCancelCommand>{{ isNew ? 'Discard changes' : 'Cancel' }}</button> | |||
| </ng-template> | |||
| </kendo-grid-command-column> | |||
| <kendo-grid-column field="Amount" title="Trail Received" width="200" format="{0:c}" editor="numeric"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Ts" title="Trail Date" width="200" editor="date"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| {{ dataItem.Ts | date: 'yyyy-MM-dd' }} | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Balance" title="Balance" width="200" editor="numeric"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="OffsetBalance" title="Offset Balance" width="200" editor="numeric"> | |||
| </kendo-grid-column> | |||
| </kendo-grid> | |||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||
| <div class="vertical-spacer"></div> | |||
| </ng-container> | |||
| </form> | |||
| <div class="k-form-buttons k-buttons-end"> | |||
| <div> | |||
| <button class="k-button k-primary" (click)="prev()" >◀ Prev</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,4 @@ | |||
| div.vertical-spacer { | |||
| height: 10px; | |||
| margin-bottom: 30px; | |||
| } | |||
| @@ -1,5 +1,14 @@ | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| import {FormGroup} from '@angular/forms'; | |||
| import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core'; | |||
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | |||
| import {LoanModel} from '../../models/loan.model'; | |||
| const createFormGroup = dataItem => new FormGroup({ | |||
| Amount : new FormControl(dataItem.Amount), | |||
| Ts: new FormControl(dataItem.Ts), | |||
| Balance: new FormControl(dataItem.Balance), | |||
| OffsetBalance: new FormControl(dataItem.OffsetBalance), | |||
| }); | |||
| @Component({ | |||
| selector: 'app-loan-trail-income', | |||
| @@ -7,10 +16,81 @@ import {FormGroup} from '@angular/forms'; | |||
| styleUrls: ['./trail-income.component.scss'] | |||
| }) | |||
| export class TrailIncomeComponent implements OnInit { | |||
| @Input() public Loan: LoanModel; | |||
| @Input() public LenderName: string; | |||
| @Output() public NotifyNext = new EventEmitter<boolean>(); | |||
| @Output() public NotifyPrev = new EventEmitter<boolean>(); | |||
| public gridData: any[] = []; | |||
| public formGroup: FormGroup; | |||
| private editedRowIndex: number; | |||
| public trailIncome: FormGroup = new FormGroup({ | |||
| LenderLoanNumber: new FormControl(), | |||
| }); | |||
| constructor() { } | |||
| ngOnInit(): void { } | |||
| public addHandler({ sender }): void { | |||
| this.closeEditor(sender); | |||
| this.formGroup = createFormGroup({ | |||
| Amount: 1000, | |||
| Ts: new Date('2020-05-20'), | |||
| Balance: 800000, | |||
| OffsetBalance: 600000 | |||
| }); | |||
| sender.addRow(this.formGroup); | |||
| } | |||
| public editHandler({ sender, rowIndex, dataItem }): void { | |||
| this.closeEditor(sender); | |||
| if ( dataItem.Paid ) { | |||
| alert( 'cannot edit'); | |||
| }else{ | |||
| this.formGroup = createFormGroup(dataItem); | |||
| this.editedRowIndex = rowIndex; | |||
| sender.editRow(rowIndex, this.formGroup); | |||
| } | |||
| } | |||
| public cancelHandler({ sender, rowIndex }): void { | |||
| console.log(sender); | |||
| this.closeEditor(sender, rowIndex); | |||
| } | |||
| public saveHandler({ sender, rowIndex, formGroup, isNew }): void { | |||
| const product = formGroup.value; | |||
| console.log('saving', product, formGroup); | |||
| // this.service.save(product, isNew); | |||
| sender.closeRow(rowIndex); | |||
| } | |||
| public removeHandler({ dataItem }): void { | |||
| console.log(dataItem); | |||
| // this.service.remove(dataItem); | |||
| } | |||
| private closeEditor(grid, rowIndex = this.editedRowIndex): void{ | |||
| grid.closeRow(rowIndex); | |||
| this.editedRowIndex = undefined; | |||
| this.formGroup = undefined; | |||
| } | |||
| public next(): void{ | |||
| this.NotifyNext.emit(true); | |||
| } | |||
| ngOnInit(): void { | |||
| public prev(): void { | |||
| this.NotifyPrev.emit(true); | |||
| } | |||
| @Input() public trailIncome: FormGroup; | |||
| } | |||
| @@ -18,20 +18,18 @@ export const mainMenuItems: any[] = [ | |||
| text: 'Loans', | |||
| icon: 'dollar', | |||
| items: [ | |||
| { text: 'Add+', icon: 'plus', popup: "loanEdit" }, | |||
| { text: 'Start New Loan', icon: 'plus', url: "./#start-a-new-loan" }, | |||
| { text: 'Edit', icon: 'plus', url: "./#transaction" }, | |||
| { text: 'List All old', icon: 'table' ,url: './#transaction-list' }, | |||
| { text: 'List All New', icon: 'table' ,url: './#list-all-loans' }, | |||
| ] | |||
| }, | |||
| { | |||
| text: 'Trails', | |||
| text: 'Reward', | |||
| icon: 'percent', | |||
| items: [ | |||
| { text: 'List All', icon: 'table', url: '#' }, | |||
| { text: 'List All', icon: 'table', url: '#' }, | |||
| { text: 'By Broker', icon: 'table', url: '#' }, | |||
| { text: '--', separator: "true" }, | |||
| { text: 'Import From ...', icon : 'upload'} | |||
| { text: 'PaidImport From ...', icon : 'upload'} | |||
| ] | |||
| }, | |||
| { | |||
| @@ -0,0 +1,19 @@ | |||
| export class BrokerModel{ | |||
| Id: string; | |||
| First: string; | |||
| Last: string; | |||
| Middle: string; | |||
| Title: string; | |||
| Display: string; | |||
| Nick: string; | |||
| Login: string; | |||
| Enabled: boolean; | |||
| BSB: string; | |||
| ACC: string; | |||
| License: string; | |||
| Organization: string; | |||
| } | |||
| @@ -0,0 +1,121 @@ | |||
| import { PeopleModel } from "./people.model"; | |||
| import {PeopleMapModel} from './people-map.model'; | |||
| import {BrokerModel} from './broker.model'; | |||
| import {PayInModel} from './pay-in.model'; | |||
| import {RewardModel} from './reward.model'; | |||
| import {ApiUrL} from '../service/auth.service'; | |||
| export interface LoanModelCallBacks { | |||
| getUserName(userId: string): string; | |||
| getUserPhotoUrl(userId: string): string; | |||
| getUserRole(userId: string): string; | |||
| } | |||
| export class LoanModel { | |||
| constructor( | |||
| public Id: string, | |||
| public Amount?: number, | |||
| public Status?: string, | |||
| public Item?: string, | |||
| public Rating?: number, | |||
| public Settlement?: Date, | |||
| public Description?: string, | |||
| public Lender?: string, | |||
| public Client?: PeopleModel[], | |||
| public Broker?: BrokerModel[], | |||
| public OtherRewarder?: PeopleModel[], | |||
| public Reward?: RewardModel[], | |||
| public PayIn?: PayInModel[], | |||
| public PeopleMap?: PeopleMapModel[], | |||
| public apiUrlFunc?: ApiUrL | |||
| ){} | |||
| public set Response(resp: LoanModel) { | |||
| this.Id = resp.Id; | |||
| this.Amount = resp.Amount; | |||
| this.Item = resp.Item; | |||
| this.Rating = resp.Rating; | |||
| this.Status = resp.Status; | |||
| this.Settlement = new Date(resp.Settlement); | |||
| this.Description = resp.Description; | |||
| this.Lender = resp.Lender; | |||
| this.Client = resp.Client; | |||
| this.Broker = resp.Broker; | |||
| this.OtherRewarder = resp.OtherRewarder; | |||
| this.setReward(resp.Reward); | |||
| this.PeopleMap = resp.PeopleMap; | |||
| this.PayIn = resp.PayIn; | |||
| } | |||
| private setReward(v: any[]): void{ | |||
| this.Reward = []; | |||
| v.forEach((reward) => { | |||
| const r = new RewardModel( | |||
| reward.Amount, | |||
| reward.Description, | |||
| reward.Id, | |||
| reward.LoanId, | |||
| reward.PayOutId, | |||
| reward.To, | |||
| reward.From, | |||
| reward.Ts, | |||
| this.callBacks() | |||
| ); | |||
| this.Reward.push(r); | |||
| }); | |||
| return; | |||
| } | |||
| public emptyReward(): RewardModel { | |||
| return new RewardModel( | |||
| 0, '', 0, this.Id, | |||
| 0, '', '', new Date(), this.callBacks() | |||
| ); | |||
| } | |||
| private callBacks(): LoanModelCallBacks { | |||
| return { | |||
| getUserName: this.getUserName.bind(this), | |||
| getUserPhotoUrl: this.getUserPhotoUrl.bind(this), | |||
| getUserRole: this.getUserRole.bind(this) | |||
| }; | |||
| } | |||
| public getUserName(id: string): string { | |||
| // search client | |||
| this.Client.forEach(( c => { | |||
| if ( c.Id === id ) { | |||
| return c.First + ' ' + c.Last; | |||
| } | |||
| })); | |||
| this.Broker.forEach(( c => { | |||
| if ( c.Id === id ) { | |||
| return c.First + ' ' + c.Last; | |||
| } | |||
| })); | |||
| this.OtherRewarder.forEach(( c => { | |||
| if ( c.Id === id ) { | |||
| return c.First + ' ' + c.Last; | |||
| } | |||
| })); | |||
| return 'P:' + id; | |||
| } | |||
| public getUserPhotoUrl(id: string): string { | |||
| const url = this.apiUrlFunc.getUrl('avatar/') + id; | |||
| return 'url("' + url + '")'; | |||
| } | |||
| public getUserRole(id: string): string { | |||
| return 'role' + id; | |||
| } | |||
| } | |||
| @@ -0,0 +1,4 @@ | |||
| export class PayInModel { | |||
| Id: number; | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| export class PayoutAudit{ | |||
| id: string; | |||
| //parent | |||
| payOut: string; | |||
| //date | |||
| when : Date; | |||
| //by who, at least 2 | |||
| who : string ; | |||
| //for what? edit? paid? | |||
| what: string; | |||
| //ip | |||
| ip: string; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| import { PayoutAudit } from "./payout-audit.model"; | |||
| export class PayOutModel{ | |||
| id: string; | |||
| //parent | |||
| loan: string | |||
| //parties | |||
| id_user: string | |||
| role: string; | |||
| // | |||
| amount: number; | |||
| //paid | |||
| paid: boolean; | |||
| paid_date: Date; | |||
| paid_to: string; //bank details; | |||
| //confirmed, by receiptient | |||
| confirmed: boolean; | |||
| //source | |||
| source?: string; | |||
| //audit | |||
| audits: PayoutAudit[]; | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| export class PeopleMapModel { | |||
| } | |||
| @@ -0,0 +1,57 @@ | |||
| import {Observable} from 'rxjs'; | |||
| export class PeopleModel{ | |||
| constructor( | |||
| public Id: string, | |||
| public First: string, | |||
| public Last: string, | |||
| public Middle: string, | |||
| public Title: string, | |||
| public Display: string, | |||
| public Nick: string, | |||
| ){} | |||
| get FullName(): string { | |||
| if (this.Middle === '') { | |||
| return this.First + ' ' + this.Last; | |||
| }else{ | |||
| return this.First + ' ' + this.Middle + ' ' + this.Last; | |||
| } | |||
| } | |||
| static searchPersonByName(name: string): Observable<PeopleModel>{ | |||
| return new Observable ( observer => { | |||
| const dummy: PeopleModel = new PeopleModel( | |||
| 'dummy-id', | |||
| 'FSearch', | |||
| 'LResult', | |||
| '', | |||
| 'Mr.', | |||
| 'Display Name', | |||
| 'Nick Name' | |||
| ); | |||
| setTimeout(() => { | |||
| observer.next(dummy); | |||
| observer.complete(); | |||
| }, 1000); | |||
| }); | |||
| } | |||
| static searchPersonById( id: string): Observable<PeopleModel> { | |||
| return new Observable ( observer => { | |||
| const dummy: PeopleModel = new PeopleModel( | |||
| id, | |||
| 'FSearch', | |||
| 'LResult', | |||
| '', | |||
| 'Mr.', | |||
| 'P:' + id, | |||
| 'Nick Name' | |||
| ); | |||
| setTimeout(() => { | |||
| observer.next(dummy); | |||
| observer.complete(); | |||
| }, 1000); | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,39 @@ | |||
| import {LoanModelCallBacks} from './loan.model'; | |||
| export class RewardModel { | |||
| constructor( | |||
| public Amount: number, | |||
| public Description: string, | |||
| public Id: number, | |||
| public LoanId: string, | |||
| public PayOutId: number, | |||
| public To: string, | |||
| public From: string, | |||
| public Ts: Date, | |||
| private lc: LoanModelCallBacks | |||
| ){} | |||
| public get Role(): string { | |||
| return this.lc.getUserRole(this.To); | |||
| } | |||
| public get UserId(): string { | |||
| return this.To; | |||
| } | |||
| public set UserId(v) { | |||
| this.To = v; | |||
| } | |||
| public get UserName(): string { | |||
| return this.lc.getUserName(this.To); | |||
| } | |||
| public get photoUrlTo(): string{ | |||
| return this.lc.getUserPhotoUrl(this.To); | |||
| } | |||
| public get photoUrlFrom(): string{ | |||
| return this.lc.getUserPhotoUrl(this.From); | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| <span | |||
| *ngFor="let starred of stars; let i = index" | |||
| (click)="onTouched(); rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))"> | |||
| <ng-container *ngIf="starred; else noStar"> | |||
| <span ngClass='k-icon k-i-star yellow'></span> | |||
| </ng-container> | |||
| <ng-template #noStar> | |||
| <span ngClass="k-icon k-i-star-outline "></span> | |||
| </ng-template> | |||
| </span> | |||
| @@ -0,0 +1,11 @@ | |||
| span { | |||
| display: inline-block; | |||
| width: 25px; | |||
| line-height: 25px; | |||
| text-align: center; | |||
| cursor: pointer; | |||
| } | |||
| .yellow { | |||
| color: #ffa600; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
| import { RatingInputComponent } from './rating-input.component'; | |||
| describe('RatingInputComponent', () => { | |||
| let component: RatingInputComponent; | |||
| let fixture: ComponentFixture<RatingInputComponent>; | |||
| beforeEach(async () => { | |||
| await TestBed.configureTestingModule({ | |||
| declarations: [ RatingInputComponent ] | |||
| }) | |||
| .compileComponents(); | |||
| }); | |||
| beforeEach(() => { | |||
| fixture = TestBed.createComponent(RatingInputComponent); | |||
| component = fixture.componentInstance; | |||
| fixture.detectChanges(); | |||
| }); | |||
| it('should create', () => { | |||
| expect(component).toBeTruthy(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,65 @@ | |||
| import {Component, forwardRef, HostBinding, Input, OnInit} from '@angular/core'; | |||
| import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; | |||
| @Component({ | |||
| selector: 'app-rating-input', | |||
| templateUrl: './rating-input.component.html', | |||
| styleUrls: ['./rating-input.component.scss'], | |||
| providers: [ | |||
| { | |||
| provide: NG_VALUE_ACCESSOR, | |||
| useExisting: forwardRef(() => RatingInputComponent), | |||
| multi: true | |||
| } | |||
| ], | |||
| }) | |||
| export class RatingInputComponent implements OnInit, ControlValueAccessor { | |||
| stars: boolean[] = Array(5).fill(false); | |||
| // Allow the input to be disabled, and when it is make it somewhat transparent. | |||
| @Input() disabled = false; | |||
| @HostBinding('style.opacity') | |||
| get opacity(): number { | |||
| return this.disabled ? 0.25 : 1; | |||
| } | |||
| constructor() { } | |||
| ngOnInit(): void { | |||
| } | |||
| // Function to call when the rating changes. | |||
| onChange = (rating: number) => {}; | |||
| // Function to call when the input is touched (when a star is clicked). | |||
| onTouched = () => {}; | |||
| get value(): number { | |||
| return this.stars.reduce((total, starred) => { | |||
| return total + (starred ? 1 : 0); | |||
| }, 0); | |||
| } | |||
| rate(rating: number) { | |||
| if (!this.disabled) { | |||
| this.writeValue(rating); | |||
| } | |||
| } | |||
| // Allows Angular to update the model (rating). | |||
| // Update the model and changes needed for the view here. | |||
| writeValue(rating: number): void { | |||
| this.stars = this.stars.map((_, i) => rating > i); | |||
| this.onChange(this.value) | |||
| } | |||
| // Allows Angular to register a function to call when the model (rating) changes. | |||
| // Save the function as a property to call later here. | |||
| registerOnChange(fn: (rating: number) => void): void { | |||
| this.onChange = fn; | |||
| } | |||
| // Allows Angular to register a function to call when the input has been touched. | |||
| // Save the function as a property to call later here. | |||
| registerOnTouched(fn: () => void): void { | |||
| this.onTouched = fn; | |||
| } | |||
| // Allows Angular to disable the input. | |||
| setDisabledState(isDisabled: boolean): void { | |||
| this.disabled = isDisabled; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| import { Component, OnInit, Input } from '@angular/core'; | |||
| @Component({ | |||
| selector: 'kendo-rating', | |||
| selector: 'app-biukop-rating', | |||
| template: ` | |||
| <span *ngFor="let item of stars" [ngClass]="ratingIcon(item)"></span> | |||
| `, | |||
| @@ -24,4 +24,4 @@ export class RatingComponent implements OnInit { | |||
| public ratingIcon(item: number): string { | |||
| return (item <= this.value) ? 'k-icon k-i-star yellow' : 'k-icon k-i-star-outline'; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,17 +1,24 @@ | |||
| import {EventEmitter, Injectable, OnDestroy, OnInit} from '@angular/core'; | |||
| import { NotificationService } from '@progress/kendo-angular-notification'; | |||
| import {HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; | |||
| import {HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpParams, HttpRequest} from '@angular/common/http'; | |||
| import {apiV1LoginResponse} from '../models/api-v1-login-response'; | |||
| import {Observable} from 'rxjs'; | |||
| import {AppComponent} from '../app.component'; | |||
| import {Router} from '@angular/router'; | |||
| import {PeopleModel} from '../models/people.model'; | |||
| export interface ApiUrL { | |||
| getUrl(key: string): string; | |||
| }; | |||
| @Injectable() | |||
| export class AuthService { | |||
| public apiUrl = 'https://svr2021.lawipac.com:8080/api/v1/'; | |||
| public apiWsUrl = 'wss://svr2021.lawipac.com:8080/api/v1/ws'; | |||
| public loggedIn = new apiV1LoginResponse(false, '', '',0 ); | |||
| // public apiUrl = 'https://c5016.biukop.com.au:8080/api/v1/'; | |||
| // public apiWsUrl = 'wss://c5016.biukop.com.au:8080/api/v1/ws'; | |||
| public loggedIn = new apiV1LoginResponse(false, '', '', 0 ); | |||
| loginSuccess = new EventEmitter <apiV1LoginResponse>(); | |||
| constructor( private http: HttpClient , | |||
| @@ -19,8 +26,7 @@ export class AuthService { | |||
| } | |||
| // tslint:disable-next-line:typedef | |||
| public AutoLogin() { | |||
| public AutoLogin(): void { | |||
| const sfm: apiV1LoginResponse = JSON.parse(localStorage.getItem('sfm')); | |||
| if (!sfm) { | |||
| console.log('no auto login'); | |||
| @@ -33,11 +39,11 @@ export class AuthService { | |||
| sfm.sessionExpire | |||
| ); | |||
| this.loginSuccess.emit(this.loggedIn); | |||
| //console.log ( 'auto login emit events', this.loggedIn); | |||
| // console.log ( 'auto login emit events', this.loggedIn); | |||
| } | |||
| // tslint:disable-next-line:typedef | |||
| isAuthenticated() { | |||
| isAuthenticated(): boolean { | |||
| return this.loggedIn.login; | |||
| } | |||
| @@ -45,8 +51,12 @@ export class AuthService { | |||
| return true; | |||
| } | |||
| // tslint:disable-next-line:typedef | |||
| login(email: string, password: string) { | |||
| saveSessionInfo(): void { | |||
| localStorage.setItem('sfm', JSON.stringify(this.loggedIn)); | |||
| localStorage.setItem('mid', this.loggedIn.machineId); | |||
| } | |||
| login(email: string, password: string): void { | |||
| this.http.post<apiV1LoginResponse>(this.apiUrl + 'login', {u: email, p: password}).subscribe( | |||
| responseData => { | |||
| @@ -54,12 +64,11 @@ export class AuthService { | |||
| this.loggedIn.login = responseData.login; | |||
| this.loggedIn.machineId = responseData['Biukop-Mid']; | |||
| this.loggedIn.sessionExpire = responseData.sessionExpire; | |||
| localStorage.setItem('sfm', JSON.stringify(this.loggedIn)); | |||
| localStorage.setItem('mid', this.loggedIn.machineId); | |||
| this.saveSessionInfo(); | |||
| this.loginSuccess.emit(responseData); | |||
| }, | |||
| error => { | |||
| const fail = new apiV1LoginResponse(false,'','',0); | |||
| const fail = new apiV1LoginResponse(false, '', '', 0); | |||
| this.loggedIn = fail; | |||
| console.log('login error', error); | |||
| this.loginSuccess.emit(this.loggedIn); | |||
| @@ -68,12 +77,11 @@ export class AuthService { | |||
| ); | |||
| } | |||
| // tslint:disable-next-line:typedef | |||
| logout() { | |||
| this.loggedIn = new apiV1LoginResponse(false,'','',0); | |||
| logout(): void { | |||
| this.loggedIn = new apiV1LoginResponse(false, '', '', 0); | |||
| localStorage.removeItem('sfm'); | |||
| this.loginSuccess.emit(this.loggedIn); | |||
| this.router.navigate(['/login']).then(r =>{ | |||
| this.router.navigate(['/login']).then(r => { | |||
| console.log('prepare to log back in'); | |||
| } ); | |||
| this.http.post(`${this.apiUrl}logout`, '').subscribe( | |||
| @@ -87,7 +95,6 @@ export class AuthService { | |||
| } | |||
| // tslint:disable-next-line:variable-name | |||
| public getUrl(key: string): string{ | |||
| const s = this.apiUrl + key; | |||
| const kvPair: {key: string, value: string}[] = [ | |||
| @@ -103,4 +110,15 @@ export class AuthService { | |||
| // not found if arrive here | |||
| return s; | |||
| }} | |||
| } | |||
| public apiUrlFunc(): ApiUrL { | |||
| return { | |||
| getUrl: this.getUrl.bind(this) | |||
| }; | |||
| } | |||
| public getPeopleList(filter: string): Observable<{Count: number, List: PeopleModel[]}> { | |||
| const params = new HttpParams().set('filter', filter); | |||
| return this.http.get<{Count:number, List: PeopleModel[]}>(this.apiUrl + 'people-list', { params}); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| import {HttpClient} from '@angular/common/http'; | |||
| import {ApiUrL, AuthService} from './auth.service'; | |||
| import {Observable} from 'rxjs'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {Injectable} from '@angular/core'; | |||
| @Injectable() | |||
| export class LoanSingleService { | |||
| constructor(private http: HttpClient, private auth: AuthService ){ } | |||
| getLoan(loanId: string): Observable<LoanModel> { | |||
| return this.http.get<LoanModel>(this.auth.getUrl('loan/' + loanId)); | |||
| } | |||
| public updateBasicInfo(loan: LoanModel): Observable<any>{ | |||
| return this.http.post(this.auth.getUrl('loan/basic/' + loan.Id), loan); | |||
| } | |||
| public photoUrl(peopleId: string): string{ | |||
| const url = this.auth.getUrl('avatar/') + peopleId; | |||
| return 'url("' + url + '")'; | |||
| } | |||
| public apiUrlFunc(): ApiUrL { | |||
| return this.auth.apiUrlFunc(); | |||
| } | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| import { Injectable } from '@angular/core'; | |||
| import { HttpClient } from '@angular/common/http'; | |||
| import { GridDataResult } from '@progress/kendo-angular-grid'; | |||
| import { toODataString } from '@progress/kendo-data-query'; | |||
| import { Observable, BehaviorSubject } from 'rxjs'; | |||
| import { map, tap } from 'rxjs/operators'; | |||
| import {AuthService} from './auth.service'; | |||
| @@ -11,7 +10,7 @@ export abstract class LoanQueryService extends BehaviorSubject<GridDataResult> { | |||
| private BASE_URL = ''; | |||
| constructor( | |||
| protected constructor( | |||
| private http: HttpClient, | |||
| private auth: AuthService, | |||
| protected tableName: string | |||
| @@ -39,7 +38,7 @@ export abstract class LoanQueryService extends BehaviorSubject<GridDataResult> { | |||
| // console.log(index, response.data[index].Settlement , typeof response.data[index].Settlement ); | |||
| // console.log(index, response.data[index].LastPayInDate , typeof response.data[index].LastPayInDate ); | |||
| }); | |||
| console.log(response); | |||
| // console.log(response); | |||
| return response; | |||
| }), | |||
| tap(() => this.loading = false) | |||
| @@ -48,7 +47,7 @@ export abstract class LoanQueryService extends BehaviorSubject<GridDataResult> { | |||
| public convertToDateTimeObject(str: string): Date{ | |||
| if (str === '0001-01-01T00:00:00Z' || str === ''){ | |||
| return null; //new Date('1900-12-31T13:00:00.000Z'); | |||
| return null; // new Date('1900-12-31T13:00:00.000Z'); | |||
| }else{ | |||
| return new Date(str); | |||
| } | |||
| @@ -10,7 +10,7 @@ kendoGridSelectBy="id" | |||
| [resizable]="true" | |||
| [height] = "700" | |||
| class="fullheight_grid" | |||
| [columnMenu]="{ filter: true }" | |||
| [columnMenu]="{ filter: true }" | |||
| > | |||
| <ng-template kendoGridToolbarTemplate> | |||
| <input placeholder="Search in all columns..." kendoTextBox (input)="onFilter($event.target.value)"/> | |||
| @@ -35,7 +35,7 @@ class="fullheight_grid" | |||
| <kendo-grid-column field="brokers" title="Brokers" [width]="220"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="bdm" title="BDM/Referals" [width]="220"> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column | |||
| field="country" | |||
| title="Country" | |||
| @@ -50,7 +50,7 @@ class="fullheight_grid" | |||
| </kendo-grid-column-group> | |||
| <kendo-grid-column-group title="Loan Information" [columnMenu]="false"> | |||
| <kendo-grid-column field="status" title="Status" [width]="220"> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column | |||
| field="status" | |||
| @@ -74,10 +74,10 @@ class="fullheight_grid" | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <span [ngClass]="{'red text-bold': dataItem.loan_limit < 1000000}">{{ dataItem.loan_limit | currency }}</span> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="lender" title="Lender" [width]="100"> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| <!-- <kendo-grid-column | |||
| field="is_online" | |||
| @@ -101,10 +101,10 @@ class="fullheight_grid" | |||
| filter="numeric" | |||
| > | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <kendo-rating | |||
| <app-biukop-rating | |||
| [value]="dataItem.rating" | |||
| [max]="5" | |||
| ></kendo-rating> | |||
| ></app-biukop-rating> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column | |||
| @@ -147,13 +147,13 @@ class="fullheight_grid" | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| {{dataItem.submittsion_date | date: 'yyyy-MM-dd'}} | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="settlement_date" width="200" title="Settlement Date"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| {{dataItem.settlement_date | date: 'yyyy-MM-dd'}} | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column> | |||
| </kendo-grid-column-group> | |||
| <kendo-grid-pdf fileName="Transaction.pdf" [repeatHeaders]="true"></kendo-grid-pdf> | |||
| @@ -163,4 +163,4 @@ class="fullheight_grid" | |||
| some details {{dataItem.id}} | |||
| <trans-details [item]="dataItem"></trans-details> | |||
| </div> | |||
| </kendo-grid> | |||
| </kendo-grid> | |||