| import {LoanSingleService} from './service/loan.single.service'; | import {LoanSingleService} from './service/loan.single.service'; | ||||
| import { RatingInputComponent } from './rating-input/rating-input.component'; | import { RatingInputComponent } from './rating-input/rating-input.component'; | ||||
| import { LoanEditPeopleComponent } from './loan-edit-people/loan-edit-people.component'; | import { LoanEditPeopleComponent } from './loan-edit-people/loan-edit-people.component'; | ||||
| import { PeopleCardComponent } from './people-card/people-card.component'; | |||||
| PeopleSelectComponent, | PeopleSelectComponent, | ||||
| LoanDetailComponent, | LoanDetailComponent, | ||||
| RatingInputComponent, | RatingInputComponent, | ||||
| LoanEditPeopleComponent | |||||
| LoanEditPeopleComponent, | |||||
| PeopleCardComponent | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| BrowserModule, | BrowserModule, |
| <p>loan-edit-people works!</p> | |||||
| {{Loan.Id}} | |||||
| <bkp-divider> | |||||
| <kendo-icon [name]="'ascx'" > </kendo-icon>Loan Id: {{Loan.Id}} | |||||
| </bkp-divider> | |||||
| <form #form="ngForm"> | |||||
| <h5 class="title">Select Clients</h5> | |||||
| <kendo-multiselect name="client" [data]="AllPeople" [textField]="'Display'" | |||||
| [valueField]="'Id'" [(ngModel)]="this.Loan.Client" [virtual]="virtual" required="true" | |||||
| (close)="rebuildPeopleMap($event)"> | |||||
| <ng-template kendoMultiSelectItemTemplate let-dataItem> | |||||
| <img class="contact-image" [src]="getContactImageUrl(dataItem.Id)" /> | |||||
| <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | |||||
| </ng-template> | |||||
| </kendo-multiselect> | |||||
| <div class="container"> | |||||
| <div class="row clientCardContainer justify-content-center"> | |||||
| <div *ngFor="let v of this.Loan.Client" class="col-sm-4"> | |||||
| <app-people-card [peopleId]="v.Id"></app-people-card> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <h5 class="title">Assign Brokers</h5> | |||||
| <kendo-multiselect name="brokers" [data]="AllBrokers" [textField]="'Display'" | |||||
| [valueField]="'Id'" [(ngModel)]="this.Loan.Broker" [virtual]="virtual" | |||||
| (close)="rebuildPeopleMap($event)" | |||||
| required="true" | |||||
| > | |||||
| <ng-template kendoMultiSelectItemTemplate let-dataItem> | |||||
| <img class="contact-image" [src]="getContactImageUrl(dataItem.Id)" /> | |||||
| <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | |||||
| </ng-template> | |||||
| </kendo-multiselect> | |||||
| <div class="container"> | |||||
| <div class="row brokerCardContainer justify-content-center"> | |||||
| <div *ngFor="let v of this.Loan.Broker" class="col-sm-4"> | |||||
| <app-people-card [peopleId]="v.Id"></app-people-card> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <h5 class="title">Other Related</h5> | |||||
| <kendo-multiselect name="beneficiaries" [data]="AllPeople" [textField]="'Display'" | |||||
| [valueField]="'Id'" [(ngModel)]="this.Loan.OtherRewarder" [virtual]="virtual" | |||||
| (close)="rebuildPeopleMap($event)"> | |||||
| <ng-template kendoMultiSelectItemTemplate let-dataItem> | |||||
| <img class="contact-image" [src]="getContactImageUrl(dataItem.Id)" /> | |||||
| <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | |||||
| </ng-template> | |||||
| </kendo-multiselect> | |||||
| <div class="container"> | |||||
| <div class="row beneficiaryCardContainer justify-content-center"> | |||||
| <div *ngFor="let v of this.Loan.OtherRewarder, let idx=index" class="col-sm-4"> | |||||
| <app-people-card [peopleId]="v.Id"></app-people-card> | |||||
| <kendo-combobox [name]="'beneficiary_'+idx" [data]="roles" [allowCustom]="true" [(ngModel)]="role[v.Id]" | |||||
| class="beneficiaryRole" required="true"> </kendo-combobox> | |||||
| </div> | |||||
| </div> | |||||
| </div>` | |||||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||||
| </form> | |||||
| <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> | |||||
| .contact-image { | |||||
| width: 20px; | |||||
| height: 20px; | |||||
| margin-right: 8px; | |||||
| border-radius: 50%; | |||||
| } | |||||
| .title { | |||||
| margin-top:50px | |||||
| } | |||||
| .number { | |||||
| display: inline-block; | |||||
| background: #333; | |||||
| color: #fff; | |||||
| border-radius: 50%; | |||||
| width: 18px; | |||||
| height: 18px; | |||||
| text-align: center; | |||||
| } | |||||
| .beneficiaryRole { | |||||
| width: 100%; | |||||
| } | |||||
| .clientCardContainer, .brokerCardContainer, .beneficiaryCardContainer{ | |||||
| padding-top: 20px; | |||||
| padding-bottom: 50px; | |||||
| border-radius: 0px 0px 50px 50px; | |||||
| box-shadow: rgb(204, 219, 232) 3px 3px 6px 0px inset, rgba(255, 255, 255, 0.5) -3px -3px 6px 1px inset; | |||||
| } | |||||
| .clientCardContainer{ | |||||
| background-color: #fbfbec; | |||||
| } | |||||
| .brokerCardContainer{ | |||||
| background-color: #e0ffdf; | |||||
| } | |||||
| .beneficiaryCardContainer{ | |||||
| background-color: #eceeff; | |||||
| } |
| import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; | |||||
| import {Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChild, ViewChildren, AfterViewInit} from '@angular/core'; | |||||
| import {LoanModel} from '../models/loan.model'; | import {LoanModel} from '../models/loan.model'; | ||||
| import {PeopleService} from '../service/people.service'; | |||||
| import {PeopleModel} from '../models/people.model'; | |||||
| import {AuthService} from '../service/auth.service'; | |||||
| import {ClonerService} from '../service/clone.service'; | |||||
| import {BrokerModel} from '../models/broker.model'; | |||||
| import {PeopleMapModel} from '../models/people-map.model'; | |||||
| import {ComboBoxComponent, PreventableEvent} from '@progress/kendo-angular-dropdowns'; | |||||
| import {FormGroup} from '@angular/forms'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-loan-edit-people', | selector: 'app-loan-edit-people', | ||||
| templateUrl: './loan-edit-people.component.html', | templateUrl: './loan-edit-people.component.html', | ||||
| styleUrls: ['./loan-edit-people.component.scss'] | styleUrls: ['./loan-edit-people.component.scss'] | ||||
| }) | }) | ||||
| export class LoanEditPeopleComponent implements OnInit { | |||||
| export class LoanEditPeopleComponent implements OnInit, AfterViewInit { | |||||
| @Input() public Loan: LoanModel; | @Input() public Loan: LoanModel; | ||||
| @Output() public NotifyNext = new EventEmitter<number>(); | |||||
| constructor() { } | |||||
| @Output() public NotifyNext = new EventEmitter<boolean>(); | |||||
| @Output() public NotifyPrev = new EventEmitter<boolean>(); | |||||
| @Output() public errorOccurred = new EventEmitter<string>(); | |||||
| @ViewChildren(ComboBoxComponent) cb!: QueryList<ComboBoxComponent>; | |||||
| @ViewChild('form', {static: true}) form: FormGroup; | |||||
| public AllPeople: PeopleModel[]; | |||||
| public AllBrokers: BrokerModel[]; | |||||
| public role: any = {}; // string to string map | |||||
| public roles = ['Admin', 'Referral', 'Observer', 'Verifier', 'Ex-Broker', 'Ex-Client', 'Banned', 'Unknown']; | |||||
| constructor(private ps: PeopleService, private auth: AuthService, private dcs: ClonerService) { } | |||||
| public virtual: any = { | |||||
| itemHeight: 28 | |||||
| }; | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.ps.getPeopleList('').subscribe( | |||||
| resp => { | |||||
| this.AllPeople = resp.List; | |||||
| } | |||||
| ); | |||||
| this.ps.getBrokerList('').subscribe( | |||||
| resp => { | |||||
| this.AllBrokers = resp.List; | |||||
| } | |||||
| ); | |||||
| // ensure role strings are available for selection | |||||
| this.Loan.PeopleMap.forEach( v => { | |||||
| if ( v.Role.toLowerCase() !== 'client' && v.Role.toLocaleLowerCase() !== 'broker'){ | |||||
| this.roles.push( v.Role); | |||||
| this.roles = [...new Set(this.roles)]; // remove duplicates | |||||
| this.role[v.PeopleId] = v.Role; | |||||
| } | |||||
| }); | |||||
| } | |||||
| ngAfterViewInit(): void { | |||||
| this.cb.changes.subscribe( (data: QueryList<ComboBoxComponent> ) => { | |||||
| data.forEach( v => { | |||||
| if (v.value === undefined && !v.isOpen) { | |||||
| v.toggle(true); | |||||
| } | |||||
| }); | |||||
| }); | |||||
| } | |||||
| public getContactImageUrl(contactId: string): string { | |||||
| return this.auth.getUrl('avatar/' + contactId); | |||||
| } | } | ||||
| public next(): void{ | |||||
| const err = this.rebuildPeopleMap(null); | |||||
| if (err) { | |||||
| this.showError('Please Select The people\'s role'); | |||||
| return; | |||||
| } | |||||
| if ( this.Loan.Client.length === 0) { | |||||
| this.showError('Please Select at least 1 client'); | |||||
| return; | |||||
| } | |||||
| if ( this.Loan.Broker.length === 0) { | |||||
| this.showError('Please Select at least 1 Broker'); | |||||
| return; | |||||
| } | |||||
| console.log(this.Loan.Client, this.Loan.Broker, this.Loan.OtherRewarder, this.role); | |||||
| this.ps.syncPeople(this.Loan).subscribe( resp => {}); | |||||
| this.NotifyNext.emit(true); | |||||
| } | |||||
| public prev(): void { | |||||
| this.NotifyPrev.emit(true); | |||||
| } | |||||
| public rebuildPeopleMap(event: PreventableEvent): boolean{ | |||||
| this.Loan.PeopleMap = []; | |||||
| this.Loan.Client.forEach(v => { | |||||
| this.Loan.PeopleMap.push(new PeopleMapModel( | |||||
| this.Loan.Id, | |||||
| 'Client', | |||||
| v.Id | |||||
| )); | |||||
| }); | |||||
| this.Loan.Broker.forEach(v => { | |||||
| this.Loan.PeopleMap.push(new PeopleMapModel( | |||||
| this.Loan.Id, | |||||
| 'Broker', | |||||
| v.Id | |||||
| )); | |||||
| }); | |||||
| let err = false; | |||||
| this.Loan.OtherRewarder.forEach(v => { | |||||
| if (this.role[v.Id] === undefined){ | |||||
| err = true; | |||||
| } | |||||
| this.Loan.PeopleMap.push(new PeopleMapModel( | |||||
| this.Loan.Id, | |||||
| this.role[v.Id] || 'Unknown', | |||||
| v.Id | |||||
| )); | |||||
| }); | |||||
| return err; | |||||
| } | |||||
| public showError(err: string): void { | |||||
| this.errorOccurred.emit(err); | |||||
| } | |||||
| } | } |
| import {FormControl, FormGroup, Validators} from '@angular/forms'; | import {FormControl, FormGroup, Validators} from '@angular/forms'; | ||||
| import {LoanModel} from '../../models/loan.model'; | import {LoanModel} from '../../models/loan.model'; | ||||
| import {LoanSingleService} from '../../service/loan.single.service'; | import {LoanSingleService} from '../../service/loan.single.service'; | ||||
| import {HttpErrorResponse} from '@angular/common/http'; | |||||
| @Component({ | @Component({ | ||||
| @Input() Loan: LoanModel; | @Input() Loan: LoanModel; | ||||
| @Output() lenderNameChanged = new EventEmitter<string>(); | @Output() lenderNameChanged = new EventEmitter<string>(); | ||||
| @Output() NotifyNext = new EventEmitter<boolean>(); | @Output() NotifyNext = new EventEmitter<boolean>(); | ||||
| @Output() errorOccurred = new EventEmitter<string>(); | |||||
| public minSettlement: Date = new Date(2015, 0, 1); | public minSettlement: Date = new Date(2015, 0, 1); | ||||
| public maxSettlement: Date = new Date(2030, 4, 31); | public maxSettlement: Date = new Date(2030, 4, 31); | ||||
| // console.log(resp); | // console.log(resp); | ||||
| // move to next step | // move to next step | ||||
| this.NotifyNext.emit(true); | this.NotifyNext.emit(true); | ||||
| }, | |||||
| err => { | |||||
| const msg = err instanceof HttpErrorResponse ? err.statusText : ''; | |||||
| this.errorOccurred.emit('Network Error Occurred : ' + err.statusText); | |||||
| this.NotifyNext.emit(false); | |||||
| } | } | ||||
| ); | ); | ||||
| } | } |
| #basicInfo | #basicInfo | ||||
| [Loan]="Loan" | [Loan]="Loan" | ||||
| (lenderNameChanged)="onLenderNameChange($event)" | (lenderNameChanged)="onLenderNameChange($event)" | ||||
| (NotifyNext) ="next(0)" | |||||
| (NotifyNext) ="next(0, $event)" | |||||
| (errorOccurred)="showError($event)" | |||||
| ></app-loan-basic-info> | ></app-loan-basic-info> | ||||
| <app-loan-edit-people *ngIf="currentStep===1" | <app-loan-edit-people *ngIf="currentStep===1" | ||||
| #people | #people | ||||
| [Loan]="Loan" | [Loan]="Loan" | ||||
| (NotifyNext)="next(1)"> | |||||
| (NotifyPrev) ="prev(1, $event)" | |||||
| (NotifyNext)="next(1, $event)" | |||||
| (errorOccurred)="showError($event)" | |||||
| > | |||||
| </app-loan-edit-people> | </app-loan-edit-people> | ||||
| <app-loan-people-reward *ngIf="currentStep === 2" | <app-loan-people-reward *ngIf="currentStep === 2" | ||||
| [Loan]="Loan" | [Loan]="Loan" | ||||
| (NotifyNext) ="next(2)" | |||||
| (NotifyPrev) ="prev(2)" | |||||
| (NotifyNext) ="next(2, $event)" | |||||
| (NotifyPrev) ="prev(2, $event)" | |||||
| (errorOccurred)="showError($event)" | |||||
| > </app-loan-people-reward> | > </app-loan-people-reward> | ||||
| <app-loan-trail-income *ngIf="currentStep === 3" | <app-loan-trail-income *ngIf="currentStep === 3" | ||||
| [Loan]="Loan" | [Loan]="Loan" | ||||
| [LenderName]="LenderName" | [LenderName]="LenderName" | ||||
| (NotifyPrev) ="prev(3)" | |||||
| (NotifyPrev) ="prev(3, $event)" | |||||
| (errorOccurred)="showError($event)" | |||||
| > </app-loan-trail-income> | > </app-loan-trail-income> | ||||
| <div class="row"> | <div class="row"> | ||||
| </div> | </div> | ||||
| <kendo-dialog title="Message" *ngIf="dialogOpened" [minWidth]="250" [width]="450" (close)="action('close')"> | |||||
| <p style="margin: 30px; text-align: center;">{{errorMessage}}</p> | |||||
| <kendo-dialog-actions> | |||||
| <button kendoButton (click)="action('ok')" primary="true">Ok, I got it.</button> | |||||
| </kendo-dialog-actions> | |||||
| </kendo-dialog> | |||||
| public Loan: LoanModel = new LoanModel(''); | public Loan: LoanModel = new LoanModel(''); | ||||
| public LenderName: string; | public LenderName: string; | ||||
| public curStepError = false; | |||||
| public curStepSuccess = false; | |||||
| public showDelete = false; | public showDelete = false; | ||||
| { label: 'Trail Income', isValid: true, errMsg: 'There are some errors' } | { label: 'Trail Income', isValid: true, errMsg: 'There are some errors' } | ||||
| ]; | ]; | ||||
| public dialogOpened = false; | |||||
| public errorMessage = ''; | |||||
| constructor( private lss: LoanSingleService) { } | constructor( private lss: LoanSingleService) { } | ||||
| public get currentStepError(): boolean{ | |||||
| const ret = false; // TODO: set the value | |||||
| this.curStepError = ret; | |||||
| return ret; | |||||
| } | |||||
| // public get currentStepError(): boolean{ | |||||
| // const ret = false; // TODO: set the value | |||||
| // this.curStepError = ret; | |||||
| // return ret; | |||||
| // } | |||||
| public next(step: number): void { | |||||
| if (! this.currentStepError && (this.currentStep !== this.steps.length)) { | |||||
| public next(step: number, success: boolean): void { | |||||
| this.steps[step].isValid = success; | |||||
| if (step === this.currentStep) { | |||||
| this.curStepSuccess = success; | |||||
| } | |||||
| console.log(step, success); | |||||
| if ( this.curStepSuccess && (this.currentStep !== this.steps.length)) { | |||||
| this.currentStep += 1; | this.currentStep += 1; | ||||
| return; | return; | ||||
| this.stepper.validateSteps(); | this.stepper.validateSteps(); | ||||
| } | } | ||||
| public prev(step: number): void { | |||||
| public prev(step: number, event: boolean): void { | |||||
| console.log(step, event); | |||||
| this.currentStep -= 1; | this.currentStep -= 1; | ||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| public action(status): void { | |||||
| this.dialogOpened = false; | |||||
| } | |||||
| public showError(msg: string): void{ | |||||
| this.errorMessage = msg; | |||||
| this.dialogOpened = true; | |||||
| } | |||||
| } | } |
| </ng-template> | </ng-template> | ||||
| <ng-template | <ng-template | ||||
| kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | ||||
| <app-people-select #pps [formControl]="formGroup.get('To')" [translateId]="true" [width]="200" [initSearch]="contacts" ></app-people-select> | |||||
| </ng-template> | |||||
| </kendo-grid-column> | |||||
| <kendo-grid-column field="Role" title="Role" width="100"> | |||||
| <ng-template | |||||
| kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | |||||
| <kendo-combobox | |||||
| name="existingRoles" | |||||
| [data]="existingRoles" | |||||
| <kendo-multicolumncombobox | |||||
| #selectRevelantPeople | |||||
| [data]="relevantPeople" | |||||
| [textField]="'Display'" | |||||
| [valueField]="'Id'" | |||||
| [valuePrimitive]="true" | [valuePrimitive]="true" | ||||
| [listHeight]="300" | |||||
| placeholder="Type here to search" | |||||
| [suggest]="true" | |||||
| [clearButton]="true" | |||||
| [allowCustom]="false" | |||||
| [formControl]="formGroup.get(column.field)" | [formControl]="formGroup.get(column.field)" | ||||
| (valueChange)="peopleChanged($event)" | |||||
| > | > | ||||
| </kendo-combobox> | |||||
| <kendo-combobox-column | |||||
| [field]="'Display'" | |||||
| [title]="'Contact Name'" | |||||
| [width]="200" | |||||
| > | |||||
| <ng-template | |||||
| kendoMultiColumnComboBoxColumnCellTemplate | |||||
| let-dataItem | |||||
| > | |||||
| <img | |||||
| class="contact-image" | |||||
| [src]="getContactImageUrl(dataItem.Id)" | |||||
| /> | |||||
| <span>{{ dataItem.Display }}</span> | |||||
| </ng-template> | |||||
| </kendo-combobox-column> | |||||
| <kendo-combobox-column | |||||
| [field]="'Role'" | |||||
| [title]="'Role of Loan'" | |||||
| [width]="200" | |||||
| > | |||||
| </kendo-combobox-column> | |||||
| <ng-template kendoMultiColumnComboBoxFooterTemplate> | |||||
| <strong> | |||||
| {{ total }} records in total | |||||
| </strong> | |||||
| </ng-template> | |||||
| </kendo-multicolumncombobox> | |||||
| </ng-template> | </ng-template> | ||||
| </kendo-grid-column> | |||||
| <kendo-grid-column field="Role" title="Role" width="100" [editable]="true"> | |||||
| </kendo-grid-column> | </kendo-grid-column> | ||||
| <kendo-grid-column field="Amount" title="Amount" width="100" editor="numeric" format="{0:c}"></kendo-grid-column> | <kendo-grid-column field="Amount" title="Amount" width="100" editor="numeric" format="{0:c}"></kendo-grid-column> |
| line-height: 32px; | line-height: 32px; | ||||
| padding-left: 10px; | padding-left: 10px; | ||||
| } | } | ||||
| .contact-image { | |||||
| width: 20px; | |||||
| height: 20px; | |||||
| margin-right: 8px; | |||||
| border-radius: 50%; | |||||
| } |
| import {AuthService} from '../../service/auth.service'; | import {AuthService} from '../../service/auth.service'; | ||||
| import {LoanModel} from '../../models/loan.model'; | import {LoanModel} from '../../models/loan.model'; | ||||
| import {RewardModel} from '../../models/reward.model'; | import {RewardModel} from '../../models/reward.model'; | ||||
| import {PeopleModel} from '../../models/people.model'; | |||||
| import {PeopleModel, RelevantPeopleModel} from '../../models/people.model'; | |||||
| import {debounce} from 'ts-debounce'; | import {debounce} from 'ts-debounce'; | ||||
| import {LoanSingleService} from '../../service/loan.single.service'; | import {LoanSingleService} from '../../service/loan.single.service'; | ||||
| import {ClonerService} from '../../service/clone.service'; | import {ClonerService} from '../../service/clone.service'; | ||||
| import {PeopleSelectComponent} from '../../people-select/people-select.component'; | |||||
| import {PeopleService} from '../../service/people.service'; | |||||
| import {Observable, of} from 'rxjs'; | |||||
| import {map} from 'rxjs/operators'; | |||||
| Id: new FormControl(dataItem.Id), | Id: new FormControl(dataItem.Id), | ||||
| To: new FormControl(dataItem.To, [Validators.required, Validators.maxLength(128), Validators.minLength(1)]), | To: new FormControl(dataItem.To, [Validators.required, Validators.maxLength(128), Validators.minLength(1)]), | ||||
| From: new FormControl(dataItem.From), | From: new FormControl(dataItem.From), | ||||
| Role: new FormControl(dataItem.Role, [Validators.required, Validators.minLength(2), Validators.maxLength(40)]), | |||||
| Role: new FormControl({value: dataItem.Role, disabled: true} , [Validators.required, Validators.minLength(2), Validators.maxLength(40)]), | |||||
| Amount: new FormControl(dataItem.Amount, Validators.compose([Validators.required, Validators.min(0), Validators.max(1000000)])), | Amount: new FormControl(dataItem.Amount, Validators.compose([Validators.required, Validators.min(0), Validators.max(1000000)])), | ||||
| Description: new FormControl(dataItem.Description, [Validators.required, Validators.maxLength(128)]), | Description: new FormControl(dataItem.Description, [Validators.required, Validators.maxLength(128)]), | ||||
| Ts: new FormControl( dataItem.Ts, Validators.required) | Ts: new FormControl( dataItem.Ts, Validators.required) | ||||
| @Input() public Loan: LoanModel; | @Input() public Loan: LoanModel; | ||||
| @Output() public NotifyNext = new EventEmitter<boolean>(); | @Output() public NotifyNext = new EventEmitter<boolean>(); | ||||
| @Output() public NotifyPrev = new EventEmitter<boolean>(); | @Output() public NotifyPrev = new EventEmitter<boolean>(); | ||||
| @Output() public errorOccurred = new EventEmitter<string>(); | |||||
| public contacts: PeopleModel[] = []; | |||||
| public relevantPeople: RelevantPeopleModel[] = []; | |||||
| public total = 0; | public total = 0; | ||||
| public pendingReward: RewardModel[] = []; | public pendingReward: RewardModel[] = []; | ||||
| private debounceFilter: any ; | private debounceFilter: any ; | ||||
| constructor(private ls: LoanSingleService, private auth: AuthService, private dcs: ClonerService) { | |||||
| constructor(private ls: LoanSingleService, | |||||
| private auth: AuthService, | |||||
| private dcs: ClonerService, | |||||
| private ps: PeopleService) { | |||||
| } | } | ||||
| public ngOnInit(): void { | public ngOnInit(): void { | ||||
| console.log(this.Loan, this.pendingReward); | console.log(this.Loan, this.pendingReward); | ||||
| this.debounceFilter = debounce( (filter: string): void => { | |||||
| this.auth.getPeopleList(filter).subscribe( | |||||
| resp => { | |||||
| this.contacts = resp.List; | |||||
| this.total = resp.Count; | |||||
| console.log(resp); | |||||
| } | |||||
| ); | |||||
| }, 500) ; | |||||
| // this.debounceFilter = debounce( (filter: string): void => { | |||||
| // this.ps.getPeopleList(filter).subscribe( | |||||
| // resp => { | |||||
| // this.contacts = resp.List; | |||||
| // this.total = resp.Count; | |||||
| // console.log(resp); | |||||
| // } | |||||
| // ); | |||||
| // }, 500) ; | |||||
| } | } | ||||
| public initPeople(): void{ | public initPeople(): void{ | ||||
| this.contacts = this.Loan.getRelevantPeople(); | |||||
| this.relevantPeople = this.Loan.getRelevantPeople(); | |||||
| } | } | ||||
| public initReward(): void { | public initReward(): void { | ||||
| Id: 0, | Id: 0, | ||||
| To: '', | To: '', | ||||
| From: '', | From: '', | ||||
| Role: '', | |||||
| Role: 'Unknown', | |||||
| Amount: 0, | Amount: 0, | ||||
| Description: '', | Description: '', | ||||
| PayOutId: 0, | PayOutId: 0, | ||||
| this.formGroup = createFormGroup(dataItem); | this.formGroup = createFormGroup(dataItem); | ||||
| this.editedRowIndex = rowIndex; | this.editedRowIndex = rowIndex; | ||||
| sender.editRow(rowIndex, this.formGroup); | sender.editRow(rowIndex, this.formGroup); | ||||
| console.log('editing' , dataItem, this.contacts, this.formGroup); | |||||
| console.log('editing' , dataItem, this.formGroup); | |||||
| } | } | ||||
| public cancelHandler({ sender, rowIndex }): void{ | public cancelHandler({ sender, rowIndex }): void{ | ||||
| public saveHandler({ sender, rowIndex, formGroup, isNew }): void { | public saveHandler({ sender, rowIndex, formGroup, isNew }): void { | ||||
| const reward = formGroup.value; | const reward = formGroup.value; | ||||
| reward.From = '0'; // Admin | reward.From = '0'; // Admin | ||||
| // check all pending rewards, see if there are conflicting roles | |||||
| const result = this.checkConflictingRoles(reward); | |||||
| if ( result.roles.length > 1 ) { | |||||
| console.log ( result); | |||||
| alert ('conflicting roles' + result.id); | |||||
| return; | |||||
| } | |||||
| // reward.Ts = new Date(); // Now | // reward.Ts = new Date(); // Now | ||||
| reward.LoanId = this.Loan.Id; // Enforce LoanId | reward.LoanId = this.Loan.Id; // Enforce LoanId | ||||
| reward.PayOutId = 0; // make sure it is not paid | reward.PayOutId = 0; // make sure it is not paid | ||||
| this.ls.saveReward(reward, isNew).subscribe( | this.ls.saveReward(reward, isNew).subscribe( | ||||
| resp => { | resp => { | ||||
| if ( reward.Id === 0 ) { | if ( reward.Id === 0 ) { | ||||
| const r = this.Loan.addReward(resp.Amount, resp.Description, resp.Id, resp.LoanId,resp.PayOutId,resp.To,resp.From, resp.Ts); | |||||
| const r = this.Loan.addReward(resp.Amount, resp.Description, resp.Id, resp.LoanId, resp.PayOutId, resp.To, resp.From, resp.Ts); | |||||
| this.pendingReward.unshift(r); | this.pendingReward.unshift(r); | ||||
| }else{ | }else{ | ||||
| const idx = this.pendingReward.findIndex( v => v.Id === reward.Id); | const idx = this.pendingReward.findIndex( v => v.Id === reward.Id); | ||||
| sender.closeRow(rowIndex); | sender.closeRow(rowIndex); | ||||
| } | } | ||||
| private checkConflictingRoles(reward: RewardModel): {id: string, roles: string[]} { | |||||
| console.log(reward, this.pendingReward); | |||||
| const roles = [reward.Role]; | |||||
| this.pendingReward.forEach( v => { | |||||
| if (v.To === reward.To) { | |||||
| const idx = roles.findIndex(r => r === v.Role); | |||||
| if (idx === -1) { roles.push (v.Role); } | |||||
| } | |||||
| }); | |||||
| return {id: reward.To, roles}; | |||||
| peopleChanged(id: string): void { | |||||
| this.formGroup.get('Role').setValue(this.Loan.getRoleById(id)); | |||||
| console.log(this.formGroup); | |||||
| console.log(this.formGroup.getRawValue()); | |||||
| } | } | ||||
| // private checkConflictingRoles(reward: RewardModel): {id: string, roles: string[]} { | |||||
| // console.log(reward, this.pendingReward); | |||||
| // const roles = [reward.Role]; | |||||
| // this.pendingReward.forEach( v => { | |||||
| // if (v.To === reward.To) { | |||||
| // const idx = roles.findIndex(r => r === v.Role); | |||||
| // if (idx === -1) { roles.push (v.Role); } | |||||
| // } | |||||
| // }); | |||||
| // | |||||
| // return {id: reward.To, roles}; | |||||
| // } | |||||
| public removeHandler({ dataItem }): void { | public removeHandler({ dataItem }): void { | ||||
| this.formGroup = undefined; | this.formGroup = undefined; | ||||
| } | } | ||||
| private photoURL(peopleId: string): string { | |||||
| public getContactImageUrl(contactId: string): string { | |||||
| return this.auth.getUrl('avatar/' + contactId); | |||||
| } | |||||
| public photoURL(peopleId: string): string { | |||||
| const url = this.auth.getUrl('avatar/') + peopleId; | const url = this.auth.getUrl('avatar/') + peopleId; | ||||
| return 'url("' + url + '")'; | return 'url("' + url + '")'; | ||||
| } | } | ||||
| private UserName(dataItem: RewardModel): string { | |||||
| public UserName(dataItem: RewardModel): string { | |||||
| return dataItem.UserName; | return dataItem.UserName; | ||||
| } | } | ||||
| @Input() public LenderName: string; | @Input() public LenderName: string; | ||||
| @Output() public NotifyNext = new EventEmitter<boolean>(); | @Output() public NotifyNext = new EventEmitter<boolean>(); | ||||
| @Output() public NotifyPrev = new EventEmitter<boolean>(); | @Output() public NotifyPrev = new EventEmitter<boolean>(); | ||||
| @Output() public errorOccurred = new EventEmitter<string>(); | |||||
| public gridData: any[] = []; | public gridData: any[] = []; | ||||
| import { PeopleModel } from './people.model'; | |||||
| import {PeopleModel, RelevantPeopleModel} from './people.model'; | |||||
| import {PeopleMapModel} from './people-map.model'; | import {PeopleMapModel} from './people-map.model'; | ||||
| import {BrokerModel} from './broker.model'; | import {BrokerModel} from './broker.model'; | ||||
| import {PayInModel} from './pay-in.model'; | import {PayInModel} from './pay-in.model'; | ||||
| public Broker?: BrokerModel[], | public Broker?: BrokerModel[], | ||||
| public OtherRewarder?: PeopleModel[], | public OtherRewarder?: PeopleModel[], | ||||
| public Reward?: RewardModel[], | public Reward?: RewardModel[], | ||||
| public RewardPeople?: PeopleModel[], | |||||
| public PayIn?: PayInModel[], | public PayIn?: PayInModel[], | ||||
| public PeopleMap?: PeopleMapModel[], | public PeopleMap?: PeopleMapModel[], | ||||
| this.setOtherRewarder(resp.OtherRewarder); | this.setOtherRewarder(resp.OtherRewarder); | ||||
| this.setReward(resp.Reward); | this.setReward(resp.Reward); | ||||
| this.setRewardPeople(resp.RewardPeople); | |||||
| this.PeopleMap = resp.PeopleMap; | this.PeopleMap = resp.PeopleMap; | ||||
| this.PayIn = resp.PayIn; | this.PayIn = resp.PayIn; | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| private setBroker(v: any[]): void{ | private setBroker(v: any[]): void{ | ||||
| this.Broker = []; | this.Broker = []; | ||||
| v.forEach((b => { | v.forEach((b => { | ||||
| })); | })); | ||||
| } | } | ||||
| private setRewardPeople(v: any[]): void { | |||||
| this.RewardPeople = []; | |||||
| v.forEach((c) => { | |||||
| this.RewardPeople.push( | |||||
| new PeopleModel(c.Id, c.First, c.Last, c.Middle, c.Title, c.Display, c.Nick) | |||||
| ); | |||||
| }); | |||||
| } | |||||
| public addReward(Amount: number, Description: string, Id: number, | public addReward(Amount: number, Description: string, Id: number, | ||||
| LoanId: string, PayOutId: number, To: string , From: string, Ts: Date): RewardModel { | LoanId: string, PayOutId: number, To: string , From: string, Ts: Date): RewardModel { | ||||
| const r = new RewardModel( | const r = new RewardModel( | ||||
| this.Reward = na; | this.Reward = na; | ||||
| } | } | ||||
| public emptyReward(): RewardModel { | |||||
| return new RewardModel( | |||||
| 0, '', 0, this.Id, | |||||
| 0, '', '', new Date(), this.callBacks() | |||||
| ); | |||||
| } | |||||
| // public emptyReward(): RewardModel { | |||||
| // return new RewardModel( | |||||
| // 0, '', 0, this.Id, | |||||
| // 0, '', '', new Date(), this.callBacks() | |||||
| // ); | |||||
| // } | |||||
| private callBacks(): LoanModelCallBacks { | private callBacks(): LoanModelCallBacks { | ||||
| return { | return { | ||||
| } | } | ||||
| })); | })); | ||||
| this.RewardPeople.forEach( c => { | |||||
| if (c.Id === id) { | |||||
| result = c.First + ' ' + c.Last; | |||||
| } | |||||
| }); | |||||
| return result; | return result; | ||||
| } | } | ||||
| } | } | ||||
| public getUserRole(id: string): string { | public getUserRole(id: string): string { | ||||
| let result = 'R:'; | |||||
| let result = ''; | |||||
| this.PeopleMap.forEach((row) => { | this.PeopleMap.forEach((row) => { | ||||
| if ( row.PeopleId === id ){ | if ( row.PeopleId === id ){ | ||||
| result = row.Role; | result = row.Role; | ||||
| return result; | return result; | ||||
| } | } | ||||
| public getRelevantPeople(): PeopleModel[] { | |||||
| const result: PeopleModel[] = []; | |||||
| public getRelevantPeople(): RelevantPeopleModel[] { | |||||
| const result: RelevantPeopleModel[] = []; | |||||
| this.Client.forEach(( c => { | this.Client.forEach(( c => { | ||||
| result.push(c); | |||||
| result.push({Id: c.Id, Display: c.Display, Role: 'Client'} ) ; | |||||
| })); | })); | ||||
| this.Broker.forEach(( c => { | this.Broker.forEach(( c => { | ||||
| console.log(c); | |||||
| result.push(c.toPeopleModel()); | |||||
| result.push({Id: c.Id, Display: c.Display, Role: 'Broker'}); | |||||
| })); | })); | ||||
| this.OtherRewarder.forEach(( c => { | this.OtherRewarder.forEach(( c => { | ||||
| result.push(c); | |||||
| result.push({Id: c.Id, Display: c.Display, Role: this.getRoleById(c.Id)}); | |||||
| })); | })); | ||||
| console.log(result); | |||||
| return result; | |||||
| this.RewardPeople.forEach( c => { | |||||
| result.push({Id: c.Id, Display: c.Display, Role: 'none'}); | |||||
| }); | |||||
| return [...new Set(result)]; // remove duplicates, if any | |||||
| } | |||||
| public getRoleById(id: string): string { | |||||
| let role = ''; | |||||
| this.PeopleMap.every(v => { | |||||
| if (v.PeopleId === id) { | |||||
| role = v.Role; | |||||
| return false; // stop search | |||||
| }else{ | |||||
| return true; | |||||
| } | |||||
| }); | |||||
| return role; | |||||
| } | } | ||||
| } | } |
| } | } | ||||
| } | } | ||||
| } | } | ||||
| export class RelevantPeopleModel{ | |||||
| Id: string; Display: string; Role: string; | |||||
| } |
| <div class="contact" > | |||||
| <div class="k-hbox"> | |||||
| <kendo-avatar [shape]="'circle'" [imageSrc]="photoURL()"></kendo-avatar> | |||||
| <div> | |||||
| <h5>{{ contact.Nick }} </h5> | |||||
| <p>{{ contact.Title }} {{ contact.First}} {{ contact.Last}}</p> | |||||
| </div> | |||||
| </div> | |||||
| </div> |
| div.contact { | |||||
| padding-top: 20px; | |||||
| padding-left:10px; | |||||
| margin-left: 0px; | |||||
| margin-right: 0px; | |||||
| border-bottom: darkgrey solid 1px; | |||||
| background-color: wheat; | |||||
| } | |||||
| div.contact:hover { | |||||
| box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; | |||||
| } |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { PeopleCardComponent } from './people-card.component'; | |||||
| describe('PeopleCardComponent', () => { | |||||
| let component: PeopleCardComponent; | |||||
| let fixture: ComponentFixture<PeopleCardComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ PeopleCardComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(PeopleCardComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit} from '@angular/core'; | |||||
| import {PeopleModel} from '../models/people.model'; | |||||
| import {PeopleService} from '../service/people.service'; | |||||
| import {AuthService} from '../service/auth.service'; | |||||
| @Component({ | |||||
| selector: 'app-people-card', | |||||
| templateUrl: './people-card.component.html', | |||||
| styleUrls: ['./people-card.component.scss'] | |||||
| }) | |||||
| export class PeopleCardComponent implements OnInit { | |||||
| @Input() peopleId: string; | |||||
| public contact: PeopleModel = new PeopleModel('', '', '', '', '', '', ''); | |||||
| constructor(private ps: PeopleService, private auth: AuthService) { } | |||||
| ngOnInit(): void { | |||||
| this.ps.getPeopleById(this.peopleId).subscribe( | |||||
| resp => { | |||||
| this.contact = new PeopleModel(resp.Id, resp.First, resp.Last, resp.Middle, resp.Title, resp.Display, resp.Nick); | |||||
| // console.log(this.contact); | |||||
| } | |||||
| ); | |||||
| } | |||||
| photoURL(): string { | |||||
| if (this.contact === undefined ) { return ''; } | |||||
| const url = this.auth.getUrl('avatar/') + this.contact.Id; | |||||
| return url; | |||||
| } | |||||
| } |
| <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | <span>{{ dataItem.First +' ' + dataItem.Last}}</span> | ||||
| </ng-template> | </ng-template> | ||||
| </kendo-combobox-column> | </kendo-combobox-column> | ||||
| <ng-template kendoMultiColumnComboBoxFooterTemplate> | <ng-template kendoMultiColumnComboBoxFooterTemplate> | ||||
| <strong> | <strong> | ||||
| {{ total }} records in total | {{ total }} records in total |
| import {map} from 'rxjs/operators'; | import {map} from 'rxjs/operators'; | ||||
| import {PeopleService} from '../service/people.service'; | import {PeopleService} from '../service/people.service'; | ||||
| @Component({ | @Component({ | ||||
| selector: 'app-people-select', | selector: 'app-people-select', | ||||
| templateUrl: './people-select.component.html', | templateUrl: './people-select.component.html', | ||||
| private prepareSearchPeople(): void{ | private prepareSearchPeople(): void{ | ||||
| this.debounceFilter = debounce( (filter: string): void => { | this.debounceFilter = debounce( (filter: string): void => { | ||||
| this.auth.getPeopleList(filter).subscribe( | |||||
| this.ps.getPeopleList(filter).subscribe( | |||||
| resp => { | resp => { | ||||
| this.text.loading = false; | this.text.loading = false; | ||||
| this.searchResult = resp.List; | this.searchResult = resp.List; | ||||
| // Search a user either based on partial name or a complete ID | // Search a user either based on partial name or a complete ID | ||||
| searchUser(nameOrId: string, translateId: boolean): Observable<PeopleModel>{ | searchUser(nameOrId: string, translateId: boolean): Observable<PeopleModel>{ | ||||
| if ( translateId ) { | if ( translateId ) { | ||||
| return this.ps.searchById(nameOrId); | |||||
| return this.ps.getPeopleById(nameOrId); | |||||
| // return this.searchPersonById( nameOrId) ; | // return this.searchPersonById( nameOrId) ; | ||||
| }else{ | }else{ | ||||
| // return this.ps.searchById(nameOrId); | // return this.ps.searchById(nameOrId); | ||||
| setDisabledState(isDisabled: boolean): void { | setDisabledState(isDisabled: boolean): void { | ||||
| this.disabled = isDisabled; | this.disabled = isDisabled; | ||||
| } | } | ||||
| } | } |
| getUrl: this.getUrl.bind(this) | 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}); | |||||
| } | |||||
| } | } |
| import {AuthService} from './auth.service'; | import {AuthService} from './auth.service'; | ||||
| import {Observable} from 'rxjs'; | import {Observable} from 'rxjs'; | ||||
| import {PeopleModel} from '../models/people.model'; | import {PeopleModel} from '../models/people.model'; | ||||
| import {BrokerModel} from '../models/broker.model'; | |||||
| import {LoanModel} from '../models/loan.model'; | |||||
| @Injectable({providedIn: 'root'}) | @Injectable({providedIn: 'root'}) | ||||
| export class PeopleService { | export class PeopleService { | ||||
| constructor(private http: HttpClient, private auth: AuthService ){ } | constructor(private http: HttpClient, private auth: AuthService ){ } | ||||
| searchById(id: string): Observable<PeopleModel> { | |||||
| public getPeopleById(id: string): Observable<PeopleModel> { | |||||
| return this.http.get<PeopleModel>(this.auth.getUrl('people/' + id)); | return this.http.get<PeopleModel>(this.auth.getUrl('people/' + id)); | ||||
| } | } | ||||
| 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.auth.getUrl( 'people-list/'), { params}); | |||||
| } | |||||
| public getBrokerList(filter: string): Observable<{Count: number, List: BrokerModel[]}> { | |||||
| const params = new HttpParams().set('filter', filter); | |||||
| return this.http.get<{Count: number, List: BrokerModel[]}>(this.auth.getUrl( 'broker-list/'), { params}); | |||||
| } | |||||
| public syncPeople(loan: LoanModel): Observable<boolean> { | |||||
| return this.http.post<boolean>(this.auth.getUrl('sync-people/'), loan); | |||||
| } | |||||
| } | } |