| @@ -10,7 +10,7 @@ import {LoanEditComponent} from './loan-edit/loan-edit.component'; | |||
| import {LenderUploadsComponent} from './lender-uploads/lender-uploads.component'; | |||
| import {BrokerLoanListComponent} from './broker-loan-list/broker-loan-list.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 {ClientProfileComponent} from './client-profile/client-profile.component'; | |||
| import {E403Component} from './e403/e403.component'; | |||
| @@ -22,6 +22,7 @@ import {RewardUnpaidComponent} from './reward-unpaid/reward-unpaid.component'; | |||
| import {PayInComponent} from './pay-in/pay-in.component'; | |||
| import {PeopleAddComponent} from './people-add/people-add.component'; | |||
| import {SettingsComponent} from './settings/settings.component'; | |||
| import {ProfileComponent} from './profile/profile.component'; | |||
| const routes: Routes = [ | |||
| @@ -50,6 +51,7 @@ const routes: Routes = [ | |||
| {path : 'lender-uploads', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | |||
| {path : 'list-all-people', component: LenderUploadsComponent, canActivate: [AuthGuard] }, | |||
| {path : 'people-add', component: PeopleAddComponent, canActivate: [AuthGuard] }, | |||
| {path : 'profile', component: ProfileComponent, canActivate: [AuthGuard] }, | |||
| {path : 'e403', component: E403Component, }, | |||
| ]; | |||
| @@ -66,7 +66,7 @@ import { PeopleCardComponent } from './people-card/people-card.component'; | |||
| import { LenderUploadsComponent } from './lender-uploads/lender-uploads.component'; | |||
| import { BrokerLoanListComponent } from './broker-loan-list/broker-loan-list.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 { ClientProfileComponent } from './client-profile/client-profile.component'; | |||
| import { E403Component } from './e403/e403.component'; | |||
| @@ -80,6 +80,12 @@ import { PayInComponent } from './pay-in/pay-in.component'; | |||
| import { PeopleAddComponent } from './people-add/people-add.component'; | |||
| import { ListAllPeopleComponent } from './list-all-people/list-all-people.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'; | |||
| @@ -130,7 +136,13 @@ import { SettingsComponent } from './settings/settings.component'; | |||
| PayInComponent, | |||
| PeopleAddComponent, | |||
| ListAllPeopleComponent, | |||
| SettingsComponent | |||
| SettingsComponent, | |||
| ProfileComponent, | |||
| PeopleProfileComponent, | |||
| MessageBoxComponent, | |||
| UserProfileComponent, | |||
| AdminProfileComponent, | |||
| ChangePasswordComponent | |||
| ], | |||
| imports: [ | |||
| BrowserModule, | |||
| @@ -20,7 +20,7 @@ export class BrokerLoanListComponent implements OnInit { | |||
| ngOnInit(): void { | |||
| this.brokerLoans = this.lss; | |||
| this.broker = this.auth.loggedIn.user; | |||
| this.broker = this.auth.loggedIn.User; | |||
| this.loadData(); | |||
| } | |||
| @@ -1,141 +0,0 @@ | |||
| <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> | |||
| @@ -1,109 +0,0 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -15,7 +15,7 @@ export class ClientLoanListComponent implements OnInit { | |||
| constructor(private ls: LoanSingleService, private auth: AuthService) { } | |||
| ngOnInit(): void { | |||
| this.ls.getLoanByClient(this.auth.loggedIn.user.Id).subscribe( | |||
| this.ls.getLoanByClient(this.auth.loggedIn.User.Id).subscribe( | |||
| resp => { | |||
| this.Loans = []; | |||
| resp.forEach( v => { | |||
| @@ -27,7 +27,7 @@ export class ClientProfileComponent implements OnInit { | |||
| ngOnInit(): void { | |||
| this.User = this.auth.loggedIn.user; | |||
| this.User = this.auth.loggedIn.User; | |||
| this.avatarUrl = 'url(' + this.auth.getUrl('avatar/' + this.User.Id) + ')'; | |||
| } | |||
| @@ -42,7 +42,7 @@ export const mainMenuItems: any[] = [ | |||
| text: 'People', | |||
| icon: 'user', | |||
| 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: '--', separator: 'true' }, | |||
| { text: 'Admin', icon: 'email', url: './#send-to-all-people'}, | |||
| @@ -63,12 +63,6 @@ export const brokerMenuItems: any[] = [ | |||
| icon: 'dollar', | |||
| url: './#broker-reward' | |||
| }, | |||
| { | |||
| text: 'Profile', | |||
| fa: faIdCard, | |||
| url: './#broker-profile' | |||
| }, | |||
| ]; | |||
| export const userMenuItems: any[] = [ | |||
| @@ -77,11 +71,6 @@ export const userMenuItems: any[] = [ | |||
| icon: 'categorize', | |||
| url: './#client-loan-list' | |||
| }, | |||
| { | |||
| text: 'Profile', | |||
| fa: faIdCard, | |||
| url: './#client-profile' | |||
| }, | |||
| ]; | |||
| export const peopleMenuItems: any[] = [ | |||
| @@ -0,0 +1,7 @@ | |||
| <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> | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,38 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -12,13 +12,16 @@ export class ApiV1LoginResponse { | |||
| public session: string, | |||
| public sessionExpire: number, // unix timestamp | |||
| 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.machineId = machineId; | |||
| this.session = session; | |||
| this.sessionExpire = sessionExpire; | |||
| this.role = role; | |||
| this.User = User; | |||
| this.UserExtra = UserExtra; | |||
| } | |||
| public static EmptyNew(): ApiV1LoginResponse{ | |||
| @@ -11,5 +11,15 @@ export class UserExtraModel { | |||
| 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; | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| <hr> | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,16 @@ | |||
| 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 { | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| <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> | |||
| @@ -0,0 +1,40 @@ | |||
| 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()); | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| <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> | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,57 @@ | |||
| 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(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| <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> | |||
| @@ -0,0 +1,29 @@ | |||
| .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; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,79 @@ | |||
| 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()); | |||
| }); | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| <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> | |||
| @@ -0,0 +1,25 @@ | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,63 @@ | |||
| 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(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| <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> | |||
| @@ -0,0 +1,25 @@ | |||
| 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(); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,50 @@ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| 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 {Router} from '@angular/router'; | |||
| import {PeopleModel} from '../models/people.model'; | |||
| @@ -10,8 +10,8 @@ export class AuthService { | |||
| public apiUrl = 'https://svr2021.lawipac.com:8080/api/v1/'; | |||
| 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(); | |||
| loginSuccess = new EventEmitter <ApiV1LoginResponse>(); | |||
| @@ -32,11 +32,11 @@ export class AuthService { | |||
| sfm.session, | |||
| sfm.sessionExpire, | |||
| 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); | |||
| @@ -66,30 +66,29 @@ export class AuthService { | |||
| this.loggedIn.machineId = responseData['Biukop-Mid']; | |||
| this.loggedIn.sessionExpire = responseData.sessionExpire; | |||
| 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{ | |||
| this.loggedIn.user = PeopleModel.EmptyNew(); | |||
| this.loggedIn.User = PeopleModel.EmptyNew(); | |||
| } | |||
| this.saveSessionInfo(); | |||
| this.loginSuccess.emit(responseData); | |||
| }, | |||
| error => { | |||
| const fail = ApiV1LoginResponse.EmptyNew(); | |||
| this.loggedIn = fail; | |||
| this.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||
| console.log('login error', error); | |||
| this.loginSuccess.emit(this.loggedIn); | |||
| } | |||
| @@ -131,4 +130,23 @@ export class AuthService { | |||
| // not found if arrive here | |||
| 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; | |||
| } | |||
| } | |||
| @@ -1,9 +1,17 @@ | |||
| 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() | |||
| 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']); | |||
| } | |||
| } | |||
| @@ -1 +1,44 @@ | |||
| <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> | |||
| @@ -0,0 +1,22 @@ | |||
| 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; | |||
| } | |||
| @@ -1,4 +1,7 @@ | |||
| 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({ | |||
| selector: 'app-settings', | |||
| @@ -6,10 +9,56 @@ import { Component, OnInit } from '@angular/core'; | |||
| styleUrls: ['./settings.component.scss'] | |||
| }) | |||
| 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 { | |||
| 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; | |||
| } | |||
| } | |||
| @@ -1,7 +1,10 @@ | |||
| <kendo-appbar id='topBar' *ngIf='login && showMenu' class='appbar' [position]="'top'" [positionMode]="'sticky'"> | |||
| <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> | |||
| <span class="k-appbar-separator"></span> | |||
| @@ -32,7 +35,7 @@ | |||
| <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> | |||
| @@ -29,20 +29,20 @@ export class TopBarComponent implements OnInit , OnDestroy { | |||
| ngOnInit(): void { | |||
| this.initAndSubLogin(); | |||
| console.log(this); | |||
| } | |||
| public initAndSubLogin(): void{ | |||
| 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.loginSub = this.authService.loginSuccess.subscribe( | |||
| (rsp: ApiV1LoginResponse) => { | |||
| this.login = rsp.login; | |||
| this.LoggedInUser = this.authService.loggedIn.user; | |||
| this.LoggedInUser = this.authService.loggedIn.User; | |||
| this.selectMenuShow(rsp.role); | |||
| } | |||
| ); | |||
| } | |||
| @@ -102,7 +102,11 @@ export class TopBarComponent implements OnInit , OnDestroy { | |||
| } | |||
| 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(); | |||
| } | |||
| } | |||