| import {LenderUploadsComponent} from './lender-uploads/lender-uploads.component'; | import {LenderUploadsComponent} from './lender-uploads/lender-uploads.component'; | ||||
| import {BrokerLoanListComponent} from './broker-loan-list/broker-loan-list.component'; | import {BrokerLoanListComponent} from './broker-loan-list/broker-loan-list.component'; | ||||
| import {BrokerRewardComponent} from './broker-reward/broker-reward.component'; | import {BrokerRewardComponent} from './broker-reward/broker-reward.component'; | ||||
| import {BrokerProfileComponent} from './broker-profile/broker-profile.component'; | |||||
| import {BrokerProfileComponent} from './profile/broker-profile/broker-profile.component'; | |||||
| import {ClientLoanListComponent} from './client-loan-list/client-loan-list.component'; | import {ClientLoanListComponent} from './client-loan-list/client-loan-list.component'; | ||||
| import {ClientProfileComponent} from './client-profile/client-profile.component'; | import {ClientProfileComponent} from './client-profile/client-profile.component'; | ||||
| import {E403Component} from './e403/e403.component'; | import {E403Component} from './e403/e403.component'; | ||||
| import {PayInComponent} from './pay-in/pay-in.component'; | import {PayInComponent} from './pay-in/pay-in.component'; | ||||
| import {PeopleAddComponent} from './people-add/people-add.component'; | import {PeopleAddComponent} from './people-add/people-add.component'; | ||||
| import {SettingsComponent} from './settings/settings.component'; | import {SettingsComponent} from './settings/settings.component'; | ||||
| import {ProfileComponent} from './profile/profile.component'; | |||||
| const routes: Routes = [ | const routes: Routes = [ | ||||
| {path : 'lender-uploads', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | {path : 'lender-uploads', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | ||||
| {path : 'list-all-people', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | {path : 'list-all-people', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | ||||
| {path : 'people-add', component: PeopleAddComponent, canActivate: [AuthGuard] }, | {path : 'people-add', component: PeopleAddComponent, canActivate: [AuthGuard] }, | ||||
| {path : 'profile', component: ProfileComponent, canActivate: [AuthGuard] }, | |||||
| {path : 'e403', component: E403Component, }, | {path : 'e403', component: E403Component, }, | ||||
| ]; | ]; | ||||
| import { LenderUploadsComponent } from './lender-uploads/lender-uploads.component'; | import { LenderUploadsComponent } from './lender-uploads/lender-uploads.component'; | ||||
| import { BrokerLoanListComponent } from './broker-loan-list/broker-loan-list.component'; | import { BrokerLoanListComponent } from './broker-loan-list/broker-loan-list.component'; | ||||
| import { BrokerRewardComponent } from './broker-reward/broker-reward.component'; | import { BrokerRewardComponent } from './broker-reward/broker-reward.component'; | ||||
| import { BrokerProfileComponent } from './broker-profile/broker-profile.component'; | |||||
| import { BrokerProfileComponent } from './profile/broker-profile/broker-profile.component'; | |||||
| import { ClientLoanListComponent } from './client-loan-list/client-loan-list.component'; | import { ClientLoanListComponent } from './client-loan-list/client-loan-list.component'; | ||||
| import { ClientProfileComponent } from './client-profile/client-profile.component'; | import { ClientProfileComponent } from './client-profile/client-profile.component'; | ||||
| import { E403Component } from './e403/e403.component'; | import { E403Component } from './e403/e403.component'; | ||||
| import { PeopleAddComponent } from './people-add/people-add.component'; | import { PeopleAddComponent } from './people-add/people-add.component'; | ||||
| import { ListAllPeopleComponent } from './list-all-people/list-all-people.component'; | import { ListAllPeopleComponent } from './list-all-people/list-all-people.component'; | ||||
| import { SettingsComponent } from './settings/settings.component'; | import { SettingsComponent } from './settings/settings.component'; | ||||
| import { ProfileComponent } from './profile/profile.component'; | |||||
| import { PeopleProfileComponent } from './profile/people-profile/people-profile.component'; | |||||
| import { MessageBoxComponent } from './message-box/message-box.component'; | |||||
| import { UserProfileComponent } from './profile/user-profile/user-profile.component'; | |||||
| import { AdminProfileComponent } from './profile/admin-profile/admin-profile.component'; | |||||
| import { ChangePasswordComponent } from './profile/change-password/change-password.component'; | |||||
| PayInComponent, | PayInComponent, | ||||
| PeopleAddComponent, | PeopleAddComponent, | ||||
| ListAllPeopleComponent, | ListAllPeopleComponent, | ||||
| SettingsComponent | |||||
| SettingsComponent, | |||||
| ProfileComponent, | |||||
| PeopleProfileComponent, | |||||
| MessageBoxComponent, | |||||
| UserProfileComponent, | |||||
| AdminProfileComponent, | |||||
| ChangePasswordComponent | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| BrowserModule, | BrowserModule, |
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.brokerLoans = this.lss; | this.brokerLoans = this.lss; | ||||
| this.broker = this.auth.loggedIn.user; | |||||
| this.broker = this.auth.loggedIn.User; | |||||
| this.loadData(); | this.loadData(); | ||||
| } | } | ||||
| <div class="container outer"> | |||||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | |||||
| <div class="vertical-spacer"></div> | |||||
| <div class="container inner"> | |||||
| <div class="row justify-content-center"> | |||||
| <div class="col-sm-12"> | |||||
| <h5> Edit : {{broker.First + ' ' + broker.Last}} </h5> | |||||
| <div class="dropzone-wrapper"> | |||||
| <div class="fileselect-wrapper"> | |||||
| <div class="row justify-content-center"> | |||||
| <div #brokerPhoto class="broker-photo" [ngStyle]="{'background-image' : avatarUrl }" ></div> | |||||
| </div> | |||||
| <kendo-fileselect | |||||
| #fileSelect | |||||
| zoneId="myZone" | |||||
| [restrictions]="myRestrictions" | |||||
| [showFileList]="false" | |||||
| (select)="onSelect($event)" | |||||
| > | |||||
| </kendo-fileselect> | |||||
| only jpg and png are allowed | |||||
| </div> | |||||
| </div> | |||||
| <div class="vertical-spacer"></div> | |||||
| <form class="k-form" #brokerForm="ngForm" (submit)="save(brokerForm)"> | |||||
| <ng-container > | |||||
| <fieldset class="k-form-fieldset"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="First" text="First Name (only Admin can change)"></kendo-label> | |||||
| <kendo-textbox kendoTextBox #First name="First" [(ngModel)]="broker.First" | |||||
| [showSuccessIcon]="broker.Display.length >= 3 && broker.Display.length <=44 "> </kendo-textbox> | |||||
| <kendo-formerror>First name is required</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Last" text="Last Name (only Admin can change)"></kendo-label> | |||||
| <kendo-textbox kendoTextBox #Last name="Last" [(ngModel)]="broker.Last" | |||||
| [showSuccessIcon]="broker.Display.length >= 3 && broker.Display.length <=44 "></kendo-textbox> | |||||
| <kendo-formerror>Last Name is required</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Display" text="Display As"></kendo-label> | |||||
| <kendo-textbox #Display name="Display" [(ngModel)]="broker.Display" required [minlength]="3" [maxlength]="120" | |||||
| [showSuccessIcon]="broker.Display.length >= 3 && broker.Display.length <=100 "> </kendo-textbox> | |||||
| <kendo-formerror>Display name cannot empty</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="License" text="License"></kendo-label> | |||||
| <kendo-textbox #License name="License" [(ngModel)]="broker.License" required [minlength]="3" [maxlength]="120" | |||||
| [showSuccessIcon]="broker.License.length >= 0 && broker.License.length <=20 "> </kendo-textbox> | |||||
| <kendo-formerror>license is required, key in unknown if have one</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="BSB" text="BSB"></kendo-label> | |||||
| <kendo-textbox #BSB name="BSB" [(ngModel)]="broker.BSB" required [minlength]="3" [maxlength]="7" | |||||
| [showSuccessIcon]="broker.BSB.length >= 0 && broker.BSB.length <=7"> </kendo-textbox> | |||||
| <kendo-formerror>BSB required for accepting payment</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="ACC" text="ACC"></kendo-label> | |||||
| <kendo-textbox #ACC name="ACC" [(ngModel)]="broker.ACC" required [minlength]="3" [maxlength]="11" | |||||
| [showSuccessIcon]="broker.ACC.length >= 0 && broker.ACC.length <=11 "> </kendo-textbox> | |||||
| <kendo-formerror>ACC is required for accepting payment</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Organization" text="Organization"></kendo-label> | |||||
| <kendo-textbox #Organization name="Organization" [(ngModel)]="broker.Organization" required | |||||
| [disabled]="true" [minlength]="3" [maxlength]="25" | |||||
| [showSuccessIcon]="broker.Organization.length >= 1 && broker.Organization.length <=25 "> | |||||
| </kendo-textbox> | |||||
| <kendo-formerror>Organization is required, (only changed by admin)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| </fieldset> | |||||
| </ng-container> | |||||
| <div class="k-form-buttons k-buttons-end"> | |||||
| <div> | |||||
| <button kendoButton class="k-button k-primary" type="submit" icon="save" | |||||
| [disabled]="brokerForm.form.status !=='VALID'"> Save </button> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| <kendo-switch [(ngModel)]="changePassword" ngModelOptions="{standalone: true}"> </kendo-switch> Change Password | |||||
| <div class="vertical-spacer"></div> | |||||
| <form *ngIf="changePassword" [formGroup]="ChangePassForm" class="k-form" (submit)="savePassword()"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="OldPassword" text="Current Password"></kendo-label> | |||||
| <input kendoTextBox #OldPassword name="OldPassword" formControlName="OldPassword" | |||||
| type="password" [minlength]="3" [maxlength]="25" /> | |||||
| <kendo-formerror>Current password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass" text="New Password"></kendo-label> | |||||
| <input kendoTextBox #NewPass name="NewPass" formControlName="NewPass" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass1" text="New Password (repeat) "></kendo-label> | |||||
| <input kendoTextBox #NewPass1 name="NewPass1" formControlName="NewPass1" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password repeat is needed (3-20 chars) </kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <button *ngIf="passEqual() && ! ChangePassForm.invalid" kendoButton class="k-button k-primary" type="submit" icon="save"> Change Password </button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-dialog title="Message " *ngIf="opened" (close)="close('cancel')" [minWidth]="250" [width]="450"> | |||||
| <p style="margin: 30px; text-align: center;">{{ Message }}</p> | |||||
| <kendo-dialog-actions> | |||||
| <button kendoButton (click)="close('Ok, I got it')" primary="true">Yes</button> | |||||
| </kendo-dialog-actions> | |||||
| </kendo-dialog> | |||||
| </div> |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {PeopleModel} from '../models/people.model'; | |||||
| import {LoanSummaryService} from '../service/loan_summary.service'; | |||||
| import {AuthService} from '../service/auth.service'; | |||||
| import {FormControl, FormGroup, NgForm} from '@angular/forms'; | |||||
| import {FileInfo, FileRestrictions, FileSelectComponent, SelectEvent} from '@progress/kendo-angular-upload'; | |||||
| import {PeopleService} from '../service/people.service'; | |||||
| import {BrokerModel} from '../models/broker.model'; | |||||
| import {ConfirmedValidator} from '../validator/confirmed.validator'; | |||||
| @Component({ | |||||
| selector: 'app-broker-profile', | |||||
| templateUrl: './broker-profile.component.html', | |||||
| styleUrls: ['./broker-profile.component.scss'] | |||||
| }) | |||||
| export class BrokerProfileComponent implements OnInit { | |||||
| @Input() public broker: BrokerModel = BrokerModel.EmptyNew(); | |||||
| @ViewChild('fileSelect', {static: true}) fs: FileSelectComponent; | |||||
| public avatarUrl = 'url(https://svr2021.lawipac.com:8080/api/v1/avatar/1000)' ; | |||||
| public myRestrictions: FileRestrictions = { | |||||
| allowedExtensions: ['.jpg', '.png', '.jpeg'], | |||||
| maxFileSize: 2194304 | |||||
| }; | |||||
| public opened = false; // dialog box | |||||
| public Message = ''; // dialog message | |||||
| public changePassword = false; | |||||
| public ChangePassForm: FormGroup = new FormGroup({ | |||||
| OldPassword: new FormControl(), | |||||
| NewPass: new FormControl(), | |||||
| NewPass1: new FormControl(), | |||||
| }); | |||||
| constructor( private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | |||||
| this.broker = BrokerModel.getFromUserAndExtra(this.auth.loggedIn.user, this.auth.loggedIn.userExtra); | |||||
| this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.broker.Id) + ')'; | |||||
| } | |||||
| public save(brokerForm: NgForm): void{ | |||||
| if (! brokerForm.touched || brokerForm.form.pristine) { | |||||
| return; | |||||
| } | |||||
| this.ps.saveBroker(this.broker).subscribe( () => { | |||||
| this.showDialog('updated successfully '); | |||||
| brokerForm.form.markAsPristine(); | |||||
| }, err => { | |||||
| this.showDialog('Failed to Update: ' + err.toString()); | |||||
| }); | |||||
| } | |||||
| public savePassword(): void{ | |||||
| this.ps.savePassword(this.broker.Id, this.ChangePassForm.value).subscribe( | |||||
| () => { | |||||
| this.showDialog('Password Changed'); | |||||
| }, err => { | |||||
| this.showDialog('Failed to Change Password :' + err.toString()); | |||||
| } | |||||
| ); | |||||
| } | |||||
| public passEqual(): boolean{ | |||||
| const v = this.ChangePassForm.value; | |||||
| if ( this.ChangePassForm.valid && v.NewPass === v.NewPass1 && this.ChangePassForm.touched) { | |||||
| return true; | |||||
| }else{ | |||||
| this.ChangePassForm.controls['NewPass1'].setErrors({'incorrect': true}); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| public onSelect(ev: SelectEvent): void { | |||||
| if (ev.files) { | |||||
| ev.files.every((file: FileInfo) => { | |||||
| if (file.rawFile && !file.validationErrors) { | |||||
| const reader = new FileReader(); | |||||
| reader.onloadend = () => { | |||||
| const str = reader.result as string; | |||||
| this.ps.updateAvatar(str, this.broker.Id).subscribe( resp => { | |||||
| this.avatarUrl = 'url(' + str + ' )'; | |||||
| }, err => { | |||||
| this.showDialog('Failed to Update Avatar: ' + err.toString()); | |||||
| }); | |||||
| this.fs.clearFiles(); | |||||
| }; | |||||
| reader.readAsDataURL(file.rawFile); | |||||
| }else{ | |||||
| this.showDialog('Only jpg, and png are supported (max 2MB)'); | |||||
| setTimeout(() => { this.fs.clearFiles(); }, 10); | |||||
| } | |||||
| return false; // we only take first file | |||||
| }); | |||||
| } | |||||
| } | |||||
| public showDialog(msg: string): void { | |||||
| this.Message = msg; | |||||
| this.opened = true; // open dialog | |||||
| } | |||||
| public close(status): void { | |||||
| this.opened = false; | |||||
| } | |||||
| } |
| constructor(private ls: LoanSingleService, private auth: AuthService) { } | constructor(private ls: LoanSingleService, private auth: AuthService) { } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.ls.getLoanByClient(this.auth.loggedIn.user.Id).subscribe( | |||||
| this.ls.getLoanByClient(this.auth.loggedIn.User.Id).subscribe( | |||||
| resp => { | resp => { | ||||
| this.Loans = []; | this.Loans = []; | ||||
| resp.forEach( v => { | resp.forEach( v => { |
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.User = this.auth.loggedIn.user; | |||||
| this.User = this.auth.loggedIn.User; | |||||
| this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.User.Id) + ')'; | this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.User.Id) + ')'; | ||||
| } | } | ||||
| text: 'People', | text: 'People', | ||||
| icon: 'user', | icon: 'user', | ||||
| items: [ | items: [ | ||||
| { text: 'Add ', icon: 'plus', url: './#add-people' }, | |||||
| { text: 'Add ', icon: 'plus', url: './#people-add' }, | |||||
| { text: 'List All', fa: faIdCard , url: './#list-all-people'}, | { text: 'List All', fa: faIdCard , url: './#list-all-people'}, | ||||
| { text: '--', separator: 'true' }, | { text: '--', separator: 'true' }, | ||||
| { text: 'Admin', icon: 'email', url: './#send-to-all-people'}, | { text: 'Admin', icon: 'email', url: './#send-to-all-people'}, | ||||
| icon: 'dollar', | icon: 'dollar', | ||||
| url: './#broker-reward' | url: './#broker-reward' | ||||
| }, | }, | ||||
| { | |||||
| text: 'Profile', | |||||
| fa: faIdCard, | |||||
| url: './#broker-profile' | |||||
| }, | |||||
| ]; | ]; | ||||
| export const userMenuItems: any[] = [ | export const userMenuItems: any[] = [ | ||||
| icon: 'categorize', | icon: 'categorize', | ||||
| url: './#client-loan-list' | url: './#client-loan-list' | ||||
| }, | }, | ||||
| { | |||||
| text: 'Profile', | |||||
| fa: faIdCard, | |||||
| url: './#client-profile' | |||||
| }, | |||||
| ]; | ]; | ||||
| export const peopleMenuItems: any[] = [ | export const peopleMenuItems: any[] = [ |
| <kendo-dialog title="{{Title}} " *ngIf="opened" (close)="close('cancel')" [minWidth]="250" [width]="450"> | |||||
| <p style="margin: 30px; text-align: center;">{{ Message }}</p> | |||||
| <kendo-dialog-actions> | |||||
| <button kendoButton *ngIf="Mode!='YesOnly'" (click)="close('no')" primary="true">{{NoButtonText}}</button> | |||||
| <button kendoButton (click)="close('yes')" primary="true">{{YesButtonText}}</button> | |||||
| </kendo-dialog-actions> | |||||
| </kendo-dialog> |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { MessageBoxComponent } from './message-box.component'; | |||||
| describe('MessageBoxComponent', () => { | |||||
| let component: MessageBoxComponent; | |||||
| let fixture: ComponentFixture<MessageBoxComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ MessageBoxComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(MessageBoxComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit, EventEmitter, Output} from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-message-box', | |||||
| templateUrl: './message-box.component.html', | |||||
| styleUrls: ['./message-box.component.scss'] | |||||
| }) | |||||
| export class MessageBoxComponent implements OnInit { | |||||
| @Input() YesButtonText = 'Ok I got it'; | |||||
| @Input() NoButtonText = 'No'; | |||||
| @Input() Title = 'Please Notice'; | |||||
| @Input() Mode = 'YesOnly'; | |||||
| @Output() onClose = new EventEmitter<string>(); | |||||
| public opened = false; // dialog box | |||||
| public Message = ''; // dialog message | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| public close(status: string): void { | |||||
| this.onClose.emit(status); | |||||
| this.opened = false; | |||||
| } | |||||
| public Show(Msg: string, mode?: string, title?: string, yesText?: string, noText?: string): void{ | |||||
| this.Message = Msg; | |||||
| this.Mode = mode || 'YesOnly'; | |||||
| this.Title = title || 'Please notice'; | |||||
| this.YesButtonText = yesText || 'Ok I got it'; | |||||
| this.NoButtonText = noText || 'No'; | |||||
| this.opened = true; | |||||
| } | |||||
| } |
| public session: string, | public session: string, | ||||
| public sessionExpire: number, // unix timestamp | public sessionExpire: number, // unix timestamp | ||||
| public role: string, | public role: string, | ||||
| public user: PeopleModel, | |||||
| public userExtra?: UserExtraModel // extra user informaiton | |||||
| public User: PeopleModel, | |||||
| public UserExtra?: UserExtraModel // extra user informaiton | |||||
| ) { | ) { | ||||
| this.login = login; | this.login = login; | ||||
| this.machineId = machineId; | this.machineId = machineId; | ||||
| this.session = session; | this.session = session; | ||||
| this.sessionExpire = sessionExpire; | this.sessionExpire = sessionExpire; | ||||
| this.role = role; | |||||
| this.User = User; | |||||
| this.UserExtra = UserExtra; | |||||
| } | } | ||||
| public static EmptyNew(): ApiV1LoginResponse{ | public static EmptyNew(): ApiV1LoginResponse{ |
| Object.assign(this, payload); | Object.assign(this, payload); | ||||
| } | } | ||||
| public static EmptyNew(): UserExtraModel { | |||||
| const rt = new UserExtraModel({}); | |||||
| rt.BSB = ''; | |||||
| rt.ACC = ''; | |||||
| rt.License = ''; | |||||
| rt.Organization = ''; | |||||
| rt.Enabled = false; | |||||
| rt.Login = ''; | |||||
| return rt; | |||||
| } | |||||
| } | } | ||||
| <hr> |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { AdminProfileComponent } from './admin-profile.component'; | |||||
| describe('AdminProfileComponent', () => { | |||||
| let component: AdminProfileComponent; | |||||
| let fixture: ComponentFixture<AdminProfileComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ AdminProfileComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(AdminProfileComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit} from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-admin-profile', | |||||
| templateUrl: './admin-profile.component.html', | |||||
| styleUrls: ['./admin-profile.component.scss'] | |||||
| }) | |||||
| export class AdminProfileComponent implements OnInit { | |||||
| @Input() public PeopleId = '0'; | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| } |
| <h5> Broker Related : {{broker.Display }} </h5> | |||||
| <div class="vertical-spacer"></div> | |||||
| <form class="k-form" #brokerForm="ngForm" (submit)="save(brokerForm)"> | |||||
| <ng-container > | |||||
| <fieldset class="k-form-fieldset"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="License" text="License"></kendo-label> | |||||
| <kendo-textbox #License name="License" [(ngModel)]="broker.License" required [minlength]="3" [maxlength]="120" | |||||
| > </kendo-textbox> | |||||
| <kendo-formerror>license is required, key in unknown if have one</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="BSB" text="BSB"></kendo-label> | |||||
| <kendo-textbox #BSB name="BSB" [(ngModel)]="broker.BSB" required [minlength]="3" [maxlength]="7" | |||||
| > </kendo-textbox> | |||||
| <kendo-formerror>BSB required for accepting payment</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="ACC" text="ACC"></kendo-label> | |||||
| <kendo-textbox #ACC name="ACC" [(ngModel)]="broker.ACC" required [minlength]="3" [maxlength]="11" | |||||
| > </kendo-textbox> | |||||
| <kendo-formerror>ACC is required for accepting payment</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Organization" text="Organization"></kendo-label> | |||||
| <kendo-textbox #Organization name="Organization" [(ngModel)]="broker.Organization" required | |||||
| [disabled]="!showAdmin" [minlength]="3" [maxlength]="25" | |||||
| > | |||||
| </kendo-textbox> | |||||
| <kendo-formerror>Organization is required, (only changed by admin)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| </fieldset> | |||||
| </ng-container> | |||||
| <div class="k-form-buttons k-buttons-end"> | |||||
| <div> | |||||
| <button kendoButton class="k-button k-primary" type="submit" icon="save" | |||||
| [disabled]="brokerForm.form.status !=='VALID'"> Save Broker</button> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| <app-message-box #messageBox></app-message-box> |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {AuthService} from '../../service/auth.service'; | |||||
| import {FormControl, FormGroup, NgForm} from '@angular/forms'; | |||||
| import {FileInfo, FileRestrictions, FileSelectComponent, SelectEvent} from '@progress/kendo-angular-upload'; | |||||
| import {PeopleService} from '../../service/people.service'; | |||||
| import {BrokerModel} from '../../models/broker.model'; | |||||
| import {MessageBoxComponent} from '../../message-box/message-box.component'; | |||||
| @Component({ | |||||
| selector: 'app-broker-profile', | |||||
| templateUrl: './broker-profile.component.html', | |||||
| styleUrls: ['./broker-profile.component.scss'] | |||||
| }) | |||||
| export class BrokerProfileComponent implements OnInit { | |||||
| @Input() public PeopleId = ''; | |||||
| @Input() public showAdmin: false; | |||||
| @Input() public broker: BrokerModel = BrokerModel.EmptyNew(); | |||||
| @ViewChild('messageBox', {static: true})msgBox: MessageBoxComponent; | |||||
| constructor( private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | |||||
| this.broker = BrokerModel.getFromUserAndExtra(this.auth.loggedIn.User, this.auth.loggedIn.UserExtra); | |||||
| } | |||||
| public save(brokerForm: NgForm): void{ | |||||
| if (! brokerForm.touched || brokerForm.form.pristine) { | |||||
| return; | |||||
| } | |||||
| this.ps.saveBroker(this.broker).subscribe( () => { | |||||
| this.msgBox.Show('updated successfully '); | |||||
| brokerForm.form.markAsPristine(); | |||||
| }, err => { | |||||
| this.msgBox.Show('Failed to Update: ' + err.toString()); | |||||
| }); | |||||
| } | |||||
| } |
| <div *ngIf="canChangePassword()"> | |||||
| <kendo-switch [(ngModel)]="changePassword" ngModelOptions="{standalone: true}" (valueChange)="hidePass()"> | |||||
| </kendo-switch> Change Password | |||||
| </div> | |||||
| <div class="vertical-spacer"></div> | |||||
| <form *ngIf="changePassword" [formGroup]="ChangePassForm" class="k-form" (submit)="savePassword()"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="OldPassword" text="Current Password"></kendo-label> | |||||
| <input kendoTextBox #OldPassword name="OldPassword" formControlName="OldPassword" | |||||
| type="password" [minlength]="3" [maxlength]="25" /> | |||||
| <kendo-formerror>Current password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass" text="New Password"></kendo-label> | |||||
| <input kendoTextBox #NewPass name="NewPass" formControlName="NewPass" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass1" text="New Password (repeat) "></kendo-label> | |||||
| <input kendoTextBox #NewPass1 name="NewPass1" formControlName="NewPass1" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password repeat is needed (3-20 chars) </kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <button *ngIf="passwordEqual() && ! ChangePassForm.invalid" kendoButton class="k-button k-primary" type="submit" icon="save"> Change Password </button> | |||||
| </form> | |||||
| <app-message-box #messageBox ></app-message-box> |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { ChangePasswordComponent } from './change-password.component'; | |||||
| describe('ChangePasswordComponent', () => { | |||||
| let component: ChangePasswordComponent; | |||||
| let fixture: ComponentFixture<ChangePasswordComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ ChangePasswordComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(ChangePasswordComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {FormControl, FormGroup} from '@angular/forms'; | |||||
| import {AuthService} from '../../service/auth.service'; | |||||
| import {PeopleService} from '../../service/people.service'; | |||||
| import {MessageBoxComponent} from '../../message-box/message-box.component'; | |||||
| @Component({ | |||||
| selector: 'app-change-password', | |||||
| templateUrl: './change-password.component.html', | |||||
| styleUrls: ['./change-password.component.scss'] | |||||
| }) | |||||
| export class ChangePasswordComponent implements OnInit { | |||||
| @Input() PeopleId = ''; | |||||
| @ViewChild('messageBox', {static: true}) msgBox: MessageBoxComponent; | |||||
| public changePassword = false; | |||||
| public ChangePassForm: FormGroup = new FormGroup({ | |||||
| OldPassword: new FormControl(), | |||||
| NewPass: new FormControl(), | |||||
| NewPass1: new FormControl(), | |||||
| }); | |||||
| constructor(private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | |||||
| } | |||||
| public hidePass(): void{ | |||||
| if ( this.changePassword ) { | |||||
| this.ChangePassForm.reset(); | |||||
| } | |||||
| } | |||||
| public savePassword(): void{ | |||||
| this.ps.savePassword('0', this.ChangePassForm.value).subscribe( | |||||
| () => { | |||||
| this.changePassword = false; | |||||
| this.msgBox.Show('Password Changed'); | |||||
| }, err => { | |||||
| this.msgBox.Show('Failed to Change Password :' + err.toString()); | |||||
| } | |||||
| ); | |||||
| } | |||||
| public passwordEqual(): boolean{ | |||||
| const v = this.ChangePassForm.value; | |||||
| if ( this.ChangePassForm.valid && v.NewPass === v.NewPass1 && this.ChangePassForm.touched && v.NewPass1 !== '') { | |||||
| return true; | |||||
| }else{ | |||||
| this.ChangePassForm.get('NewPass1').setErrors({incorrect: true}); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| public canChangePassword(): boolean { | |||||
| return this.auth.isBroker() || this.auth.isAdmin(); | |||||
| } | |||||
| } |
| <app-message-box #messagebox (onClose)="onDialogClose($event)"></app-message-box> | |||||
| <h5> <kendo-icon [name]="'ascx'" > </kendo-icon> Personal Information </h5> | |||||
| <div class="dropzone-wrapper"> | |||||
| <div class="fileselect-wrapper"> | |||||
| <div class="row justify-content-center"> | |||||
| <div #peoplePhoto class="people-photo" [ngStyle]="{'background-image' : avatarUrl }" ></div> | |||||
| </div> | |||||
| <kendo-fileselect | |||||
| #fileSelect | |||||
| zoneId="myZone" | |||||
| [restrictions]="myRestrictions" | |||||
| [showFileList]="false" | |||||
| (select)="onSelectAvatar($event)" | |||||
| > | |||||
| </kendo-fileselect> | |||||
| </div> | |||||
| </div> | |||||
| <div class="vertical-spacer"></div> | |||||
| <form class="k-form" #peopleForm="ngForm" (submit)="save(peopleForm)"> | |||||
| <ng-container > | |||||
| <fieldset class="k-form-fieldset"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="First" text="First Name (only Admin can change)"></kendo-label> | |||||
| <kendo-textbox kendoTextBox #First name="First" [(ngModel)]="People.First" | |||||
| [showSuccessIcon]="People.Display.length >= 3 && People.Display.length <=44 "> </kendo-textbox> | |||||
| <kendo-formerror>First name is required</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Last" text="Last Name (only Admin can change)"></kendo-label> | |||||
| <kendo-textbox kendoTextBox #Last name="Last" [(ngModel)]="People.Last" | |||||
| [showSuccessIcon]="People.Display.length >= 3 && People.Display.length <=44 "></kendo-textbox> | |||||
| <kendo-formerror>Last Name is required</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Display" text="Display As"></kendo-label> | |||||
| <kendo-textbox #Display name="Display" [(ngModel)]="People.Display" required [minlength]="3" [maxlength]="120" | |||||
| [showSuccessIcon]="People.Display.length >= 3 && People.Display.length <=100 "> </kendo-textbox> | |||||
| <kendo-formerror>Display name cannot empty</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| </fieldset> | |||||
| </ng-container> | |||||
| <div class="k-form-buttons k-buttons-end"> | |||||
| <div> | |||||
| <button kendoButton class="k-button k-primary" type="submit" icon="save" | |||||
| [disabled]="peopleForm.form.status !=='VALID'"> Save Personal Information</button> | |||||
| </div> | |||||
| </div> | |||||
| </form> |
| .dropzoneInvisible{ | |||||
| opacity:0.1; | |||||
| } | |||||
| .avatar { | |||||
| width: 100%; | |||||
| } | |||||
| .people-photo { | |||||
| display: inline-block; | |||||
| width: 256px; | |||||
| height: 256px; | |||||
| border-radius: 50%; | |||||
| background-size: 256px 256px; | |||||
| background-position: center center; | |||||
| vertical-align: middle; | |||||
| line-height: 132px; | |||||
| margin-left: 5px; | |||||
| margin-bottom: 10px; | |||||
| background-repeat: no-repeat; | |||||
| box-shadow: 1px 1px 10px #000000; | |||||
| } | |||||
| div.vertical-spacer { | |||||
| height:1px; | |||||
| margin-bottom: 30px; | |||||
| } |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { PeopleProfileComponent } from './people-profile.component'; | |||||
| describe('PeopleProfileComponent', () => { | |||||
| let component: PeopleProfileComponent; | |||||
| let fixture: ComponentFixture<PeopleProfileComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ PeopleProfileComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(PeopleProfileComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {MessageBoxComponent} from '../../message-box/message-box.component'; | |||||
| import {FileInfo, FileRestrictions, FileSelectComponent, SelectEvent} from '@progress/kendo-angular-upload'; | |||||
| import {AuthService} from '../../service/auth.service'; | |||||
| import {PeopleService} from '../../service/people.service'; | |||||
| import {NgForm} from '@angular/forms'; | |||||
| import {PeopleModel} from '../../models/people.model'; | |||||
| @Component({ | |||||
| selector: 'app-people-profile', | |||||
| templateUrl: './people-profile.component.html', | |||||
| styleUrls: ['./people-profile.component.scss'] | |||||
| }) | |||||
| export class PeopleProfileComponent implements OnInit { | |||||
| @Input() PeopleId = ''; | |||||
| public People: PeopleModel = PeopleModel.EmptyNew(); | |||||
| public avatarUrl = 'url(https://svr2021.lawipac.com:8080/api/v1/avatar/1000)' ; | |||||
| public myRestrictions: FileRestrictions = { | |||||
| allowedExtensions: ['.jpg', '.png', '.jpeg'], | |||||
| maxFileSize: 2194304 | |||||
| }; | |||||
| @ViewChild('messagebox', {static: true}) msgBox: MessageBoxComponent; | |||||
| @ViewChild('fileSelect', {static: true}) fs: FileSelectComponent; | |||||
| constructor(private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | |||||
| this.People = this.auth.loggedIn.User; | |||||
| this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.PeopleId) + ')'; | |||||
| this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.PeopleId) + ')'; | |||||
| } | |||||
| onDialogClose(status: string): void { | |||||
| console.log(status); | |||||
| } | |||||
| public onSelectAvatar(ev: SelectEvent): void { | |||||
| if (ev.files) { | |||||
| ev.files.every((file: FileInfo) => { | |||||
| if (file.rawFile && !file.validationErrors) { | |||||
| const reader = new FileReader(); | |||||
| reader.onloadend = () => { | |||||
| const str = reader.result as string; | |||||
| this.ps.updateAvatar(str, this.PeopleId).subscribe( resp => { | |||||
| this.avatarUrl = 'url(' + str + ' )'; | |||||
| }, err => { | |||||
| this.msgBox.Show('Failed to Update Avatar: ' + err.toString()); | |||||
| }); | |||||
| this.fs.clearFiles(); | |||||
| }; | |||||
| reader.readAsDataURL(file.rawFile); | |||||
| }else{ | |||||
| this.msgBox.Show('Only jpg, and png are supported (max 2MB)'); | |||||
| setTimeout(() => { this.fs.clearFiles(); }, 10); | |||||
| } | |||||
| return false; // we only take first file | |||||
| }); | |||||
| } | |||||
| } | |||||
| public save(peopleForm: NgForm): void{ | |||||
| if (! peopleForm.touched || peopleForm.form.pristine) { | |||||
| this.msgBox.Show('Nothing has been changed'); | |||||
| return; | |||||
| } | |||||
| this.ps.saveUser(this.People).subscribe( () => { | |||||
| this.msgBox.Show('Updated successfully '); | |||||
| if ( this.auth.loggedIn.User.Id === this.People.Id ) { | |||||
| this.auth.UpdatePeopleInfo(this.People); | |||||
| } | |||||
| peopleForm.form.markAsPristine(); | |||||
| }, err => { | |||||
| this.msgBox.Show('Failed to Update: ' + err.toString()); | |||||
| }); | |||||
| } | |||||
| } |
| <div class="parent"> | |||||
| <div class="container settings"> | |||||
| <div class="row justify-content-center"> | |||||
| <div class="col-sm-12"> | |||||
| <app-people-profile #peopleProfile *ngIf="showPeople()" [PeopleId]="PeopleId" ></app-people-profile> | |||||
| <div *ngIf="showUser()" class="vertical-spacer"> <hr></div> | |||||
| <app-user-profile #userProfile *ngIf="showUser()" [PeopleId]="PeopleId" [showAdmin]="showAdmin()"></app-user-profile> | |||||
| <div *ngIf="showBroker()" class="vertical-spacer"><hr></div> | |||||
| <app-broker-profile #brokerProfile *ngIf="showBroker()" [PeopleId]="PeopleId" [showAdmin]="showAdmin()"></app-broker-profile> | |||||
| <div *ngIf="showAdmin()" class="vertical-spacer"><hr></div> | |||||
| <app-admin-profile #adminProfile *ngIf="showAdmin()" [PeopleId]="PeopleId" ></app-admin-profile> | |||||
| <div *ngIf="showPassword()" class="vertical-spacer" ><hr></div> | |||||
| <app-change-password #changePassword *ngIf="showPassword()" [PeopleId]="PeopleId" ></app-change-password> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| div.parent { | |||||
| display: table; | |||||
| width: 100%; | |||||
| height: 100vh; | |||||
| opacity: 0.95; | |||||
| background: url('/assets/img/bg-settings.jpg') no-repeat center center fixed; | |||||
| background-size: cover; | |||||
| } | |||||
| .settings { | |||||
| max-width: 800px; | |||||
| text-align: center; | |||||
| vertical-align: middle; | |||||
| padding: 20px; | |||||
| box-shadow: 0 0 6px black; | |||||
| border-radius: 5px; | |||||
| background-color: white; | |||||
| margin: 10% auto 100px; | |||||
| } | |||||
| div.vertical-spacer { | |||||
| height:1px; | |||||
| margin-bottom: 30px; | |||||
| } |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { ProfileComponent } from './profile.component'; | |||||
| describe('ProfileComponent', () => { | |||||
| let component: ProfileComponent; | |||||
| let fixture: ComponentFixture<ProfileComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ ProfileComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(ProfileComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {FormControl, FormGroup} from '@angular/forms'; | |||||
| import {AuthService} from '../service/auth.service'; | |||||
| import {PeopleService} from '../service/people.service'; | |||||
| import {PeopleProfileComponent} from './people-profile/people-profile.component'; | |||||
| import {BrokerProfileComponent} from './broker-profile/broker-profile.component'; | |||||
| import {UserProfileComponent} from './user-profile/user-profile.component'; | |||||
| @Component({ | |||||
| selector: 'app-profile', | |||||
| templateUrl: './profile.component.html', | |||||
| styleUrls: ['./profile.component.scss'] | |||||
| }) | |||||
| export class ProfileComponent implements OnInit { | |||||
| @Input() PeopleId = ''; | |||||
| @ViewChild('peopleProfile', {static: true}) pp: PeopleProfileComponent; | |||||
| @ViewChild('userProfile', {static: true}) up: UserProfileComponent; | |||||
| @ViewChild('brokerProfile', {static: true}) bp: BrokerProfileComponent; | |||||
| public role = 'client'; | |||||
| constructor(private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | |||||
| this.role = this.auth.loggedIn.role; | |||||
| this.PeopleId = this.auth.loggedIn.User.Id; | |||||
| console.log('profile people id', this.PeopleId); | |||||
| } | |||||
| public showPeople(): boolean { | |||||
| return true; // always true | |||||
| } | |||||
| public showUser(): boolean { | |||||
| return this.isUser() || this.isBroker() || this.isAdmin(); | |||||
| } | |||||
| public showBroker(): boolean { | |||||
| return this.isBroker() && ! this.isAdmin(); | |||||
| } | |||||
| public showAdmin(): boolean { | |||||
| return this.isAdmin(); | |||||
| } | |||||
| public showPassword(): boolean { | |||||
| return this.isUser() || this.isBroker() || this.isAdmin(); | |||||
| } | |||||
| public isAdmin(): boolean { | |||||
| return this.auth.isAdmin(); | |||||
| } | |||||
| public isBroker(): boolean { | |||||
| return this.auth.isBroker(); | |||||
| } | |||||
| public isUser(): boolean { | |||||
| return this.auth.isUser(); | |||||
| } | |||||
| } |
| <h5 *ngIf="canEditUser()" > <kendo-icon [name]="'ascx'" > </kendo-icon> Login Related </h5> | |||||
| <form *ngIf="canEditUser()" class="k-form" #userForm="ngForm" (submit)="save(userForm)"> | |||||
| <ng-container > | |||||
| <fieldset class="k-form-fieldset"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="Login" text="Unique Login id:"></kendo-label> | |||||
| <kendo-textbox kendoTextBox #Login name="Login" [(ngModel)]="UserExtra.Login" | |||||
| [disabled] = "!showAdmin" | |||||
| [showSuccessIcon]="UserExtra.Login.length >= 3 && UserExtra.Login.length <=44 "> </kendo-textbox> | |||||
| <kendo-formerror>First name is required</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <kendo-formfield *ngIf="showAdmin "> | |||||
| <label class="k-label">Allow Login | |||||
| <input type="checkbox" name="enabled" kendoCheckBox | |||||
| [(ngModel)]="UserExtra.Enabled" [disabled]="currentUserIsAdmin()"/> | |||||
| </label> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| </fieldset> | |||||
| </ng-container> | |||||
| <div class="k-form-buttons k-buttons-end"> | |||||
| <div> | |||||
| <button kendoButton class="k-button k-primary" type="submit" icon="save" | |||||
| [disabled]="userForm.form.status !=='VALID'"> Modify Login</button> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| <app-message-box #messageBox ></app-message-box> |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { UserProfileComponent } from './user-profile.component'; | |||||
| describe('UserProfileComponent', () => { | |||||
| let component: UserProfileComponent; | |||||
| let fixture: ComponentFixture<UserProfileComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ UserProfileComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(UserProfileComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| import {UserExtraModel} from '../../models/user-extra.model'; | |||||
| import {NgForm} from '@angular/forms'; | |||||
| import {AuthService} from '../../service/auth.service'; | |||||
| import {HttpClient} from '@angular/common/http'; | |||||
| import {MessageBoxComponent} from '../../message-box/message-box.component'; | |||||
| @Component({ | |||||
| selector: 'app-user-profile', | |||||
| templateUrl: './user-profile.component.html', | |||||
| styleUrls: ['./user-profile.component.scss'] | |||||
| }) | |||||
| export class UserProfileComponent implements OnInit { | |||||
| @Input() public PeopleId = ''; | |||||
| @Input() public showAdmin: false; | |||||
| public UserExtra: UserExtraModel = UserExtraModel.EmptyNew(); | |||||
| @ViewChild('messageBox', {static: true}) msgBox: MessageBoxComponent; | |||||
| constructor(private auth: AuthService, private http: HttpClient) { } | |||||
| ngOnInit(): void { | |||||
| this.PeopleId = this.auth.loggedIn.User.Id; | |||||
| this.UserExtra = this.auth.loggedIn.UserExtra; | |||||
| } | |||||
| public save(userForm: NgForm): void { | |||||
| const expected = userForm.form.get('Login').value; | |||||
| this.http.post<string>(this.auth.getUrl('user/' + this.PeopleId), this.UserExtra).subscribe( | |||||
| resp => { | |||||
| if ( resp === expected ) { | |||||
| this.msgBox.Show('Successfully Changed'); | |||||
| }else{ | |||||
| this.msgBox.Show('Login not Changed: ' + resp ); | |||||
| } | |||||
| }, err => { | |||||
| this.msgBox.Show('Error Occurred' + err.toString()); | |||||
| } | |||||
| ); | |||||
| } | |||||
| public currentUserIsAdmin(): boolean { | |||||
| return this.auth.loggedIn.role === 'admin'; | |||||
| } | |||||
| public canEditUser(): boolean { | |||||
| return this.UserExtra !== undefined && this.UserExtra.Login !== undefined; | |||||
| } | |||||
| } |
| import {EventEmitter, Injectable, OnDestroy, OnInit} from '@angular/core'; | |||||
| import {HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpParams, HttpRequest} from '@angular/common/http'; | |||||
| import {EventEmitter, Injectable} from '@angular/core'; | |||||
| import {HttpClient} from '@angular/common/http'; | |||||
| import {ApiV1LoginResponse} from '../models/api-v1-login-response'; | import {ApiV1LoginResponse} from '../models/api-v1-login-response'; | ||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| import {PeopleModel} from '../models/people.model'; | import {PeopleModel} from '../models/people.model'; | ||||
| public apiUrl = 'https://svr2021.lawipac.com:8080/api/v1/'; | public apiUrl = 'https://svr2021.lawipac.com:8080/api/v1/'; | ||||
| public apiWsUrl = 'wss://svr2021.lawipac.com:8080/api/v1/ws'; | public apiWsUrl = 'wss://svr2021.lawipac.com:8080/api/v1/ws'; | ||||
| //public apiUrl = 'https://c5016.biukop.com.au:8080/api/v1/'; | |||||
| //public apiWsUrl = 'wss://c5016.biukop.com.au:8080/api/v1/ws'; | |||||
| // public apiUrl = 'https://c5016.biukop.com.au:8080/api/v1/'; | |||||
| // public apiWsUrl = 'wss://c5016.biukop.com.au:8080/api/v1/ws'; | |||||
| public loggedIn = ApiV1LoginResponse.EmptyNew(); | public loggedIn = ApiV1LoginResponse.EmptyNew(); | ||||
| loginSuccess = new EventEmitter <ApiV1LoginResponse>(); | loginSuccess = new EventEmitter <ApiV1LoginResponse>(); | ||||
| sfm.session, | sfm.session, | ||||
| sfm.sessionExpire, | sfm.sessionExpire, | ||||
| sfm.role, | sfm.role, | ||||
| new PeopleModel(sfm.user.Id, sfm.user.First, sfm.user.Last, sfm.user.Middle, sfm.user.Title, sfm.user.Display, sfm.user.Nick) | |||||
| new PeopleModel(sfm.User.Id, sfm.User.First, sfm.User.Last, sfm.User.Middle, sfm.User.Title, sfm.User.Display, sfm.User.Nick) | |||||
| ); | ); | ||||
| if ( sfm.userExtra !== undefined ) { | |||||
| this.loggedIn.userExtra = new UserExtraModel(sfm.userExtra); | |||||
| if ( sfm.UserExtra !== undefined ) { | |||||
| this.loggedIn.UserExtra = new UserExtraModel(sfm.UserExtra); | |||||
| } | } | ||||
| this.loginSuccess.emit(this.loggedIn); | this.loginSuccess.emit(this.loggedIn); | ||||
| this.loggedIn.machineId = responseData['Biukop-Mid']; | this.loggedIn.machineId = responseData['Biukop-Mid']; | ||||
| this.loggedIn.sessionExpire = responseData.sessionExpire; | this.loggedIn.sessionExpire = responseData.sessionExpire; | ||||
| this.loggedIn.role = responseData.role; | this.loggedIn.role = responseData.role; | ||||
| if ( responseData.user !== undefined) { | |||||
| this.loggedIn.user = new PeopleModel( | |||||
| responseData.user.Id, | |||||
| responseData.user.First, | |||||
| responseData.user.Last, | |||||
| responseData.user.Middle, | |||||
| responseData.user.Title, | |||||
| responseData.user.Display, | |||||
| responseData.user.Nick | |||||
| if ( responseData.User !== undefined) { | |||||
| this.loggedIn.User = new PeopleModel( | |||||
| responseData.User.Id, | |||||
| responseData.User.First, | |||||
| responseData.User.Last, | |||||
| responseData.User.Middle, | |||||
| responseData.User.Title, | |||||
| responseData.User.Display, | |||||
| responseData.User.Nick | |||||
| ); | ); | ||||
| if (responseData.userExtra !== undefined ) { | |||||
| this.loggedIn.userExtra = new UserExtraModel(responseData.userExtra); | |||||
| if (responseData.UserExtra !== undefined ) { | |||||
| this.loggedIn.UserExtra = new UserExtraModel(responseData.UserExtra); | |||||
| } | } | ||||
| }else{ | }else{ | ||||
| this.loggedIn.user = PeopleModel.EmptyNew(); | |||||
| this.loggedIn.User = PeopleModel.EmptyNew(); | |||||
| } | } | ||||
| this.saveSessionInfo(); | this.saveSessionInfo(); | ||||
| this.loginSuccess.emit(responseData); | this.loginSuccess.emit(responseData); | ||||
| }, | }, | ||||
| error => { | error => { | ||||
| const fail = ApiV1LoginResponse.EmptyNew(); | |||||
| this.loggedIn = fail; | |||||
| this.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| console.log('login error', error); | console.log('login error', error); | ||||
| this.loginSuccess.emit(this.loggedIn); | this.loginSuccess.emit(this.loggedIn); | ||||
| } | } | ||||
| // not found if arrive here | // not found if arrive here | ||||
| return s; | return s; | ||||
| } | } | ||||
| public UpdatePeopleInfo(people: PeopleModel): void{ | |||||
| this.loggedIn.User.Display = people.Display; | |||||
| this.loggedIn.User.First = people.First; | |||||
| this.loggedIn.User.Last = people.Last; | |||||
| this.saveSessionInfo(); | |||||
| } | |||||
| public isAdmin(): boolean { | |||||
| return this.loggedIn.role === 'admin'; | |||||
| } | |||||
| public isBroker(): boolean { | |||||
| return this.loggedIn.role === 'broker'; | |||||
| } | |||||
| public isUser(): boolean { | |||||
| return this.loggedIn.login === true; | |||||
| } | |||||
| } | } |
| import { EventEmitter, Injectable } from '@angular/core'; | import { EventEmitter, Injectable } from '@angular/core'; | ||||
| import {HttpClient} from '@angular/common/http'; | |||||
| import {AuthService} from './auth.service'; | |||||
| import {ClonerService} from './clone.service'; | |||||
| import {Router} from '@angular/router'; | |||||
| @Injectable() | @Injectable() | ||||
| export class MenuService { | export class MenuService { | ||||
| itemClicked = new EventEmitter <any>(); | |||||
| itemClicked = new EventEmitter <any>(); | |||||
| constructor(auth: AuthService, private router: Router ){ } | |||||
| // display profile editing based on current user | |||||
| public showProfile(): void { | |||||
| this.router.navigate(['/profile']); | |||||
| } | |||||
| } | } |
| <p>settings works!</p> | |||||
| <div class="parent"> | |||||
| <div class="container settings"> | |||||
| <div class="row justify-content-center"> | |||||
| <div class="col-6"> | |||||
| <kendo-switch [(ngModel)]="changeAdminPassword" ngModelOptions="{standalone: true}" (valueChange)="hideAdminPass()"> </kendo-switch> Change Admin Password | |||||
| <div class="vertical-spacer"></div> | |||||
| <form *ngIf="changeAdminPassword" [formGroup]="ChangeAdminPassForm" class="k-form" (submit)="saveAdminPassword()"> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="OldPassword" text="Current Password"></kendo-label> | |||||
| <input kendoTextBox #OldPassword name="OldPassword" formControlName="OldPassword" | |||||
| type="password" [minlength]="3" [maxlength]="25" /> | |||||
| <kendo-formerror>Current password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass" text="New Password"></kendo-label> | |||||
| <input kendoTextBox #NewPass name="NewPass" formControlName="NewPass" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password is needed (3-20 chars)</kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <kendo-formfield> | |||||
| <kendo-label [for]="NewPass1" text="New Password (repeat) "></kendo-label> | |||||
| <input kendoTextBox #NewPass1 name="NewPass1" formControlName="NewPass1" | |||||
| [minlength]="3" [maxlength]="25" type="password" /> | |||||
| <kendo-formerror>New password repeat is needed (3-20 chars) </kendo-formerror> | |||||
| </kendo-formfield> | |||||
| <div class="vertical-spacer"></div> | |||||
| <button *ngIf="adminPasswordEqual() && ! ChangeAdminPassForm.invalid" kendoButton class="k-button k-primary" type="submit" icon="save"> Change Password </button> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <kendo-dialog title="Message " *ngIf="opened" (close)="close('cancel')" [minWidth]="250" [width]="450"> | |||||
| <p style="margin: 30px; text-align: center;">{{ Message }}</p> | |||||
| <kendo-dialog-actions> | |||||
| <button kendoButton (click)="close('Ok, I got it')" primary="true">Yes</button> | |||||
| </kendo-dialog-actions> | |||||
| </kendo-dialog> | |||||
| div.parent { | |||||
| display: table; | |||||
| width: 100%; | |||||
| height: 100vh; | |||||
| opacity: 0.95; | |||||
| background: url('/assets/img/bg-settings.jpg') no-repeat center center fixed; | |||||
| background-size: cover; | |||||
| } | |||||
| .settings { | |||||
| margin-left: auto; | |||||
| margin-right: auto; | |||||
| margin-top: 10%; | |||||
| max-width: 600px; | |||||
| text-align: center; | |||||
| vertical-align: middle; | |||||
| padding: 20px; | |||||
| box-shadow: 0 0 6px black; | |||||
| border-radius: 5px; | |||||
| background-color: white; | |||||
| } |
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import {AuthService} from '../service/auth.service'; | |||||
| import {PeopleService} from '../service/people.service'; | |||||
| import {FormControl, FormGroup} from '@angular/forms'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-settings', | selector: 'app-settings', | ||||
| styleUrls: ['./settings.component.scss'] | styleUrls: ['./settings.component.scss'] | ||||
| }) | }) | ||||
| export class SettingsComponent implements OnInit { | export class SettingsComponent implements OnInit { | ||||
| public changeAdminPassword = false; | |||||
| public opened = false; // dialog box | |||||
| public Message = ''; // dialog message | |||||
| constructor() { } | |||||
| public ChangeAdminPassForm: FormGroup = new FormGroup({ | |||||
| OldPassword: new FormControl(), | |||||
| NewPass: new FormControl(), | |||||
| NewPass1: new FormControl(), | |||||
| }); | |||||
| constructor(private auth: AuthService, private ps: PeopleService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| setTimeout(() => { | |||||
| this.changeAdminPassword = true; | |||||
| }, 1000); | |||||
| } | } | ||||
| public hideAdminPass(): void{ | |||||
| if ( this.changeAdminPassword ) { | |||||
| this.ChangeAdminPassForm.reset(); | |||||
| } | |||||
| } | |||||
| public saveAdminPassword(): void{ | |||||
| this.ps.savePassword('0', this.ChangeAdminPassForm.value).subscribe( | |||||
| () => { | |||||
| this.showDialog('Password Changed'); | |||||
| }, err => { | |||||
| this.showDialog('Failed to Change Password :' + err.toString()); | |||||
| } | |||||
| ); | |||||
| } | |||||
| public adminPasswordEqual(): boolean{ | |||||
| const v = this.ChangeAdminPassForm.value; | |||||
| if ( this.ChangeAdminPassForm.valid && v.NewPass === v.NewPass1 && this.ChangeAdminPassForm.touched && v.NewPass1 !== '') { | |||||
| return true; | |||||
| }else{ | |||||
| this.ChangeAdminPassForm.controls['NewPass1'].setErrors({'incorrect': true}); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| public showDialog(msg: string): void { | |||||
| this.Message = msg; | |||||
| this.opened = true; // open dialog | |||||
| } | |||||
| public close(status): void { | |||||
| this.opened = false; | |||||
| } | |||||
| } | } |
| <kendo-appbar id='topBar' *ngIf='login && showMenu' class='appbar' [position]="'top'" [positionMode]="'sticky'"> | <kendo-appbar id='topBar' *ngIf='login && showMenu' class='appbar' [position]="'top'" [positionMode]="'sticky'"> | ||||
| <kendo-appbar-section *ngIf="LoggedInUser && LoggedInUser.Id !==''"> | <kendo-appbar-section *ngIf="LoggedInUser && LoggedInUser.Id !==''"> | ||||
| <kendo-avatar [imageSrc]="loggedInUserAvatar()" [shape]="'circle'" [width]="'26px'" [height]="'26px'" (click)="logout()"></kendo-avatar> | |||||
| <span>{{LoggedInUser.Display}}</span> | |||||
| <kendo-avatar [imageSrc]="loggedInUserAvatar()" class="k-cursor-pointer" | |||||
| [shape]="'circle'" [width]="'26px'" [height]="'26px'" (click)="profile()"> | |||||
| </kendo-avatar> | |||||
| <span class="k-cursor-pointer" (click)="profile()">{{LoggedInUser.Display}}</span> | |||||
| </kendo-appbar-section> | </kendo-appbar-section> | ||||
| <span class="k-appbar-separator"></span> | <span class="k-appbar-separator"></span> | ||||
| <kendo-appbar-section> | <kendo-appbar-section> | ||||
| <kendo-avatar [imageSrc]="Avatar" [shape]="'circle'" [width]="'26px'" [height]="'26px'" (click)="logout()"></kendo-avatar> | |||||
| <kendo-avatar class="k-cursor-pointer" [imageSrc]="Avatar" [shape]="'circle'" [width]="'26px'" [height]="'26px'" (click)="logout()"></kendo-avatar> | |||||
| </kendo-appbar-section> | </kendo-appbar-section> | ||||
| </kendo-appbar> | </kendo-appbar> | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.initAndSubLogin(); | this.initAndSubLogin(); | ||||
| console.log(this); | |||||
| } | } | ||||
| public initAndSubLogin(): void{ | public initAndSubLogin(): void{ | ||||
| this.login = this.authService.loggedIn.login; | this.login = this.authService.loggedIn.login; | ||||
| this.LoggedInUser = this.authService.loggedIn.user; | |||||
| this.LoggedInUser = this.authService.loggedIn.User; | |||||
| this.selectMenuShow(this.authService.loggedIn.role); | this.selectMenuShow(this.authService.loggedIn.role); | ||||
| this.loginSub = this.authService.loginSuccess.subscribe( | this.loginSub = this.authService.loginSuccess.subscribe( | ||||
| (rsp: ApiV1LoginResponse) => { | (rsp: ApiV1LoginResponse) => { | ||||
| this.login = rsp.login; | this.login = rsp.login; | ||||
| this.LoggedInUser = this.authService.loggedIn.user; | |||||
| this.LoggedInUser = this.authService.loggedIn.User; | |||||
| this.selectMenuShow(rsp.role); | this.selectMenuShow(rsp.role); | ||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| public loggedInUserAvatar(): string { | public loggedInUserAvatar(): string { | ||||
| return this.authService.getUrl('avatar/') + this.authService.loggedIn.user.Id; | |||||
| return this.authService.getUrl('avatar/') + this.authService.loggedIn.User.Id; | |||||
| } | } | ||||
| public profile(): void { | |||||
| this.menuService.showProfile(); | |||||
| } | |||||
| } | } |