| { | { | ||||
| "/api/v1": { | "/api/v1": { | ||||
| "target": "http://svr2021:8080", | |||||
| "target": "https://svr2021.lawipac.com:8080", | |||||
| "secure": false, | "secure": false, | ||||
| "pathRewrite": {"^/api/v1" : "/api/v1"} | "pathRewrite": {"^/api/v1" : "/api/v1"} | ||||
| } | } |
| .appbar, | .appbar, | ||||
| .k-appbar-sticky{ | .k-appbar-sticky{ | ||||
| z-index: 1000; | |||||
| /* z-index: 1000; */ | |||||
| } | |||||
| .k-notification-group{ | |||||
| z-index:1000; | |||||
| } | } | ||||
| #topBar .main-menu-item , #topBar .k-icon, #topBar .k-menu-item { | #topBar .main-menu-item , #topBar .k-icon, #topBar .k-menu-item { |
| import {Title} from '@angular/platform-browser'; | import {Title} from '@angular/platform-browser'; | ||||
| import {WsLoginEventModel} from './models/websocket/ws.login.event.model'; | import {WsLoginEventModel} from './models/websocket/ws.login.event.model'; | ||||
| import {SessionService} from './service/session.service'; | import {SessionService} from './service/session.service'; | ||||
| import {AppConfig} from './app.config'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-root', | selector: 'app-root', | ||||
| // tslint:disable-next-line:typedef | // tslint:disable-next-line:typedef | ||||
| ngOnInit() { | ngOnInit() { | ||||
| this.listenToMenuEvent(); | this.listenToMenuEvent(); | ||||
| // must be the last, as it may emit events | |||||
| this.authService.AutoLogin(); | |||||
| } | } | ||||
| listenToMenuEvent(): void { | listenToMenuEvent(): void { | ||||
| onWSLogin(e: WsLoginEventModel): void { | onWSLogin(e: WsLoginEventModel): void { | ||||
| if ( e.T === 'logout' ) { // regardless where are they, logout means logout | if ( e.T === 'logout' ) { // regardless where are they, logout means logout | ||||
| if ( this.ss.isCurrentUser(e.Uid) ) { | |||||
| if ( this.ss.isLoggedIn() && this.ss.isCurrentUser(e.Uid) ) { | |||||
| if ( e.Sid === this.ss.SessionId ) { | |||||
| this.ss.logoutAndClearLocalStorage(); | this.ss.logoutAndClearLocalStorage(); | ||||
| }else{ | |||||
| this.ss.logout(); | |||||
| } | |||||
| } | } | ||||
| }else{ // some one logged in from another browser tab | }else{ // some one logged in from another browser tab | ||||
| if ( e.Mid === this.ss.loggedIn.machineId) { // same browser | if ( e.Mid === this.ss.loggedIn.machineId) { // same browser |
| try { | try { | ||||
| if ( location.href.includes('//localhost:4200/') ) { | if ( location.href.includes('//localhost:4200/') ) { | ||||
| AppConfig.config = AppConfig.debugConfig; | AppConfig.config = AppConfig.debugConfig; | ||||
| // AppConfig.config.Server = '/api/v1/'; | |||||
| console.log('Using Debug Config:', AppConfig.config); | console.log('Using Debug Config:', AppConfig.config); | ||||
| }else{ | }else{ | ||||
| const json = this.decode(el.innerText); | const json = this.decode(el.innerText); | ||||
| AppConfig.config = JSON.parse(json); | AppConfig.config = JSON.parse(json); | ||||
| // AppConfig.config.Server = '/api/v1/'; | |||||
| console.log('Using Production Config:', AppConfig.config); | console.log('Using Production Config:', AppConfig.config); | ||||
| } | } | ||||
| resolve(); | resolve(); |
| intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | ||||
| let h = req.headers; | let h = req.headers; | ||||
| if (this.ss.loggedIn.hasValidSession()) { | |||||
| if (this.ss.SessionId !== '') { | |||||
| h = h.set('Biukop-Session', this.ss.loggedIn.session); | h = h.set('Biukop-Session', this.ss.loggedIn.session); | ||||
| } | } | ||||
| if (this.ss.loggedIn.hasValidMachineId()) { | |||||
| h = h.set('Biukop-Mid', this.ss.loggedIn.machineId); | |||||
| if (this.ss.MachineId !== '') { | |||||
| h = h.set('Biukop-Mid', this.ss.MachineId); | |||||
| } | } | ||||
| const authReq = req.clone({ | const authReq = req.clone({ | ||||
| headers: h, | headers: h, | ||||
| withCredentials: true | withCredentials: true | ||||
| }); | }); | ||||
| const self = this; | |||||
| return next.handle(authReq).pipe( | return next.handle(authReq).pipe( | ||||
| tap(event => { | tap(event => { | ||||
| // console.log(event); | // console.log(event); | ||||
| if (event.type === HttpEventType.Response){ | if (event.type === HttpEventType.Response){ | ||||
| const bs = event.headers.get('biukop-session'); | const bs = event.headers.get('biukop-session'); | ||||
| if (bs !== undefined){ | |||||
| if ( this.ss.loggedIn.session !== bs ){ | |||||
| this.ss.loggedIn.session = bs; | |||||
| this.ss.saveSessionInfo(); | |||||
| console.log('switch session:' , bs); | |||||
| } | |||||
| } | |||||
| self.setSession(bs); | |||||
| const mid = event.headers.get('biukop-mid'); | |||||
| self.setMachineId(mid); | |||||
| } | } | ||||
| }) | }) | ||||
| ); | ); | ||||
| } | } | ||||
| public setSession(bs: string): void { | |||||
| // console.log('receive session:' , bs); | |||||
| if (bs){ | |||||
| if ( this.ss.loggedIn.session !== bs ){ | |||||
| this.ss.loggedIn.session = bs; | |||||
| this.ss.saveSessionInfo(); | |||||
| console.log('switch session:' , bs); | |||||
| } | |||||
| } | |||||
| } | |||||
| public setMachineId(mid: string): void{ | |||||
| // console.log('receive mid:' , mid); | |||||
| if ( mid && mid !== '' ){ | |||||
| if ( this.ss.MachineId !== mid ){ | |||||
| this.ss.MachineId = mid; | |||||
| console.log('switch machine id:' , mid); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } |
| private router: Router, private notificationService: NotificationService) { } | private router: Router, private notificationService: NotificationService) { } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| if ( this.ss.loggedIn.login ){ | |||||
| this.ss.logout(); | |||||
| } | |||||
| this.ss.logout(); | |||||
| // this.ss.logoutAndClearLocalStorage(); | // this.ss.logoutAndClearLocalStorage(); | ||||
| this.loginSub = this.ss.loginSuccess.subscribe( | |||||
| this.loginSub = this.ss.loginResult.subscribe( | |||||
| responseData => { | responseData => { | ||||
| // console.log(responseData); | // console.log(responseData); | ||||
| this.onLogin(responseData); | this.onLogin(responseData); |
| .k-form.none { | |||||
| width: 400px; | |||||
| } | |||||
| div.parent { | |||||
| display: table; | |||||
| width: 100%; | |||||
| height: 100vh; | |||||
| opacity: 0.95; | |||||
| background: url("../../assets/img/body_bg.jpg") no-repeat center center fixed; | |||||
| background-size: cover; | |||||
| } | |||||
| .form_login { | |||||
| margin-left: auto; | |||||
| margin-right: auto; | |||||
| margin-top: 10%; | |||||
| max-width: 400px; | |||||
| padding-top: 20px; | |||||
| text-align: center; | |||||
| vertical-align: middle; | |||||
| padding-left: 20px; | |||||
| padding-right: 20px; | |||||
| padding-bottom: 20px; | |||||
| box-shadow: 0 0 6px black; | |||||
| border-radius: 5px; | |||||
| } |
| "use strict"; | |||||
| var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | |||||
| var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | |||||
| if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | |||||
| else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | |||||
| return c > 3 && r && Object.defineProperty(target, key, r), r; | |||||
| }; | |||||
| exports.__esModule = true; | |||||
| exports.AuthComponent = void 0; | |||||
| var core_1 = require("@angular/core"); | |||||
| var forms_1 = require("@angular/forms"); | |||||
| var AuthComponent = /** @class */ (function () { | |||||
| function AuthComponent(authService, rounter, notificationService) { | |||||
| this.authService = authService; | |||||
| this.rounter = rounter; | |||||
| this.notificationService = notificationService; | |||||
| this.userForm = new forms_1.FormGroup({ | |||||
| password: new forms_1.FormControl('password', [forms_1.Validators.minLength(3), forms_1.Validators.maxLength(20)]), | |||||
| email: new forms_1.FormControl('email@email.com', forms_1.Validators.email) | |||||
| }); | |||||
| } | |||||
| AuthComponent.prototype.ngOnInit = function () { | |||||
| var _this = this; | |||||
| this.loginSub = this.authService.loginSuccess.subscribe(function (ok) { _this.onLogin(ok); }); | |||||
| }; | |||||
| AuthComponent.prototype.ngOnDestroy = function () { | |||||
| this.loginSub.unsubscribe(); | |||||
| }; | |||||
| AuthComponent.prototype.submitForm = function () { | |||||
| console.log(this.userForm); | |||||
| this.loading = true; | |||||
| this.authService.login(this.userForm.value.email, this.userForm.value.password); | |||||
| }; | |||||
| AuthComponent.prototype.onLogin = function (ok) { | |||||
| this.loading = false; | |||||
| console.log(" found login success " + ok); | |||||
| if (ok) | |||||
| this.rounter.navigate(["/dashboard"]); | |||||
| else | |||||
| this.show(); | |||||
| }; | |||||
| AuthComponent.prototype.show = function () { | |||||
| this.notificationService.show({ | |||||
| content: 'Your loggin is failed, please try again.', | |||||
| cssClass: 'button-notification', | |||||
| animation: { type: 'slide', duration: 400 }, | |||||
| position: { horizontal: 'center', vertical: 'top' }, | |||||
| type: { style: 'error', icon: true }, | |||||
| hideAfter: 2000 | |||||
| }); | |||||
| }; | |||||
| AuthComponent = __decorate([ | |||||
| core_1.Component({ | |||||
| selector: 'app-auth', | |||||
| templateUrl: './auth.component.html', | |||||
| styleUrls: ['./auth.component.scss'] | |||||
| }) | |||||
| ], AuthComponent); | |||||
| return AuthComponent; | |||||
| }()); | |||||
| exports.AuthComponent = AuthComponent; |
| <kendo-chart [transitions]="true"> | <kendo-chart [transitions]="true"> | ||||
| <ng-template kendoChartDonutCenterTemplate> | |||||
| <h3>{{totalNumberOfLoans}}</h3> | |||||
| </ng-template> | |||||
| <kendo-chart-title text="Stages of Loans"></kendo-chart-title> | <kendo-chart-title text="Stages of Loans"></kendo-chart-title> | ||||
| <kendo-chart-series> | <kendo-chart-series> | ||||
| <kendo-chart-series-item | <kendo-chart-series-item |
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import {Observable} from 'rxjs'; | |||||
| import {Observable, Subscriber} from 'rxjs'; | |||||
| import {TypeOfLoansModel} from '../models/type-of-loans.model'; | import {TypeOfLoansModel} from '../models/type-of-loans.model'; | ||||
| import {HttpClient} from '@angular/common/http'; | import {HttpClient} from '@angular/common/http'; | ||||
| import {AuthService} from '../service/auth.service'; | import {AuthService} from '../service/auth.service'; | ||||
| import {map} from 'rxjs/operators'; | import {map} from 'rxjs/operators'; | ||||
| import {equalsArray, randomNumber} from '../helper.function'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-chart-type-of-loans', | selector: 'app-chart-type-of-loans', | ||||
| }) | }) | ||||
| export class ChartTypeOfLoansComponent implements OnInit { | export class ChartTypeOfLoansComponent implements OnInit { | ||||
| public autoFit = true; | public autoFit = true; | ||||
| // public data: Observable<TypeOfLoansModel[]>; | |||||
| public data: Observable<TypeOfLoansModel[]>; | public data: Observable<TypeOfLoansModel[]>; | ||||
| public totalNumberOfLoans = 0; | |||||
| constructor(private auth: AuthService, private http: HttpClient) { } | constructor(private auth: AuthService, private http: HttpClient) { } | ||||
| private previous: TypeOfLoansModel[] = []; | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.data = this.getTypeOfLoans(); | |||||
| this.data = new Observable<TypeOfLoansModel[]>( (observer) => { | |||||
| this.updateTypeOfLoans(observer); | |||||
| }); | |||||
| } | } | ||||
| public labelContent(e: any): string { | public labelContent(e: any): string { | ||||
| return e.category; | |||||
| return e.dataItem.Count + ' ' + e.category + ' (' + + (e.dataItem.Share * 100).toFixed(2) + '%' + ')' ; | |||||
| } | |||||
| public updateTypeOfLoans(observer: Subscriber<TypeOfLoansModel[]>): void { | |||||
| this.getTypeOfLoans().subscribe( | |||||
| resp => { | |||||
| if ( ! equalsArray(this.previous, resp ) ){ | |||||
| this.previous = resp; | |||||
| this.totalNumberOfLoans = 0; | |||||
| resp.reduce( (acc, curr) => { | |||||
| this.totalNumberOfLoans += curr.Count; | |||||
| return acc; | |||||
| }); | |||||
| observer.next(resp); | |||||
| } | |||||
| }); | |||||
| const refresh = randomNumber(3000, 6000); | |||||
| setTimeout( () => { | |||||
| // this.updateTypeOfLoans(observer); | |||||
| }, refresh); | |||||
| } | } | ||||
| public getTypeOfLoans(): Observable<TypeOfLoansModel[]> { | public getTypeOfLoans(): Observable<TypeOfLoansModel[]> { | ||||
| if (Array.isArray(input)) { | if (Array.isArray(input)) { | ||||
| input.forEach( (value, index) =>{ | input.forEach( (value, index) =>{ | ||||
| ret.push({ | ret.push({ | ||||
| Kind: value.Kind + ' ' + (value.Share * 100).toFixed(2) + '%', | |||||
| Kind: value.Kind, | |||||
| Count: value.Count, | Count: value.Count, | ||||
| Share: value.Share, | Share: value.Share, | ||||
| Amount: value.Amount | Amount: value.Amount |
| export const equalsArray = (a, b) => { | |||||
| if (a === b) { return true; } | |||||
| if (a instanceof Date && b instanceof Date) { | |||||
| return a.getTime() === b.getTime(); | |||||
| } | |||||
| if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) { | |||||
| return a === b; | |||||
| } | |||||
| if (a.prototype !== b.prototype) { return false; } | |||||
| const keys = Object.keys(a); | |||||
| if (keys.length !== Object.keys(b).length) { return false; } | |||||
| return keys.every(k => equalsArray(a[k], b[k])); | |||||
| }; | |||||
| export function randomNumber(min: number, max: number): number { // min and max included | |||||
| return Math.floor(Math.random() * (max - min + 1) + min); | |||||
| } |
| this.dialogWidth = this.scrWidth; | this.dialogWidth = this.scrWidth; | ||||
| } | } | ||||
| console.log(this.img.nativeElement.naturalWidth, this.img.nativeElement.naturalHeight, | |||||
| this.img.nativeElement.width, this.img.nativeElement.height, this.iWidth, this.imgWidthPercent); | |||||
| // console.log(this.img.nativeElement.naturalWidth, this.img.nativeElement.naturalHeight, | |||||
| // this.img.nativeElement.width, this.img.nativeElement.height, this.iWidth, this.imgWidthPercent); | |||||
| } | } | ||||
| return 'url("' + url + '")'; | return 'url("' + url + '")'; | ||||
| } | } | ||||
| public allowEdit(): boolean { | |||||
| return this.auth.allowEditLoan(); | |||||
| } | |||||
| public allData = (): Observable<GridDataResult> => { | public allData = (): Observable<GridDataResult> => { | ||||
| return this.service.queryAll({skip: 0, take: 999999, sort: this.sort, filter: this.filter}); | return this.service.queryAll({skip: 0, take: 999999, sort: this.sort, filter: this.filter}); | ||||
| } | } |
| // tslint:disable-next-line:class-name | |||||
| import {PeopleModel} from './people.model'; | import {PeopleModel} from './people.model'; | ||||
| import {UserExtraModel} from './user-extra.model'; | import {UserExtraModel} from './user-extra.model'; | ||||
| export class ApiV1LoginResponse { | export class ApiV1LoginResponse { | ||||
| constructor( | |||||
| public login: boolean, | |||||
| public machineId: string, | |||||
| public session: string, | |||||
| public sessionExpire: number, // unix timestamp | |||||
| public role: string, | |||||
| public User: PeopleModel, | |||||
| public UserExtra?: UserExtraModel // extra user information | |||||
| ) { | |||||
| this.login = login; | |||||
| this.machineId = machineId; | |||||
| this.session = session; | |||||
| this.sessionExpire = sessionExpire; | |||||
| this.role = role; | |||||
| this.User = User; | |||||
| this.UserExtra = UserExtra; | |||||
| } | |||||
| public login: boolean; | |||||
| public machineId: string; | |||||
| public session: string; | |||||
| public sessionExpire: number; // unix timestamp | |||||
| public role: string; | |||||
| public User: PeopleModel; | |||||
| public UserExtra?: UserExtraModel; // extra user information | |||||
| public static EmptyNew(): ApiV1LoginResponse{ | |||||
| return new ApiV1LoginResponse( | |||||
| false, '', '', 0, '', PeopleModel.EmptyNew() ); | |||||
| constructor(payload: Partial<ApiV1LoginResponse>) { | |||||
| this.login = payload.login || false; | |||||
| this.machineId = payload.machineId || ''; | |||||
| this.session = payload.session || '' ; | |||||
| this.sessionExpire = payload.sessionExpire || new Date().getTime(); | |||||
| this.role = payload.role || ''; | |||||
| this.User = new PeopleModel(payload.User || {}); | |||||
| this.UserExtra = new UserExtraModel(payload.UserExtra || {}); | |||||
| } | } | ||||
| public hasValidSession(): boolean { | public hasValidSession(): boolean { | ||||
| } | } | ||||
| } | } | ||||
| public hasValidMachineId(): boolean { | |||||
| if (this.machineId === undefined || this.machineId === '') { | |||||
| return false; | |||||
| }else{ | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | } |
| import {UserExModel} from './userExModel'; | |||||
| export class PayOutExModel{ | |||||
| Id: number; | |||||
| Ts: Date; | |||||
| To: UserExModel; | |||||
| Prepared: UserExModel; | |||||
| Approved: UserExModel; | |||||
| Paid: UserExModel; | |||||
| PayDate: Date; | |||||
| PayAmount: number; | |||||
| Status: string; | |||||
| constructor( payload: Partial<PayOutExModel>) { | |||||
| this.Id = payload.Id || 0; | |||||
| if (payload.Ts){ | |||||
| this.Ts = new Date( payload.Ts); | |||||
| }else{ | |||||
| this.Ts = new Date('1800-01-01'); | |||||
| } | |||||
| this.To = new UserExModel(payload.To); | |||||
| this.Prepared = new UserExModel(payload.Prepared); | |||||
| this.Approved = new UserExModel(payload.Approved); | |||||
| this.Paid = new UserExModel(payload.Paid); | |||||
| if (payload.PayDate) { | |||||
| this.PayDate = new Date(payload.PayDate); | |||||
| }else{ | |||||
| this.PayDate = new Date('1800-01-01'); | |||||
| } | |||||
| this.PayAmount = payload.PayAmount || 0; | |||||
| this.Status = payload.Status || ''; | |||||
| } | |||||
| } |
| import { PayoutAudit } from "./payout-audit.model"; | |||||
| export class PayOutModel{ | |||||
| id: string; | |||||
| //parent | |||||
| loan: string | |||||
| //parties | |||||
| id_user: string | |||||
| role: string; | |||||
| // | |||||
| amount: number; | |||||
| //paid | |||||
| paid: boolean; | |||||
| paid_date: Date; | |||||
| paid_to: string; //bank details; | |||||
| //confirmed, by receiptient | |||||
| confirmed: boolean; | |||||
| //source | |||||
| source?: string; | |||||
| //audit | |||||
| audits: PayoutAudit[]; | |||||
| } |
| import {LoanModel} from './loan.model'; | import {LoanModel} from './loan.model'; | ||||
| import {PayOutModel} from './payout.model'; | |||||
| import {PayOutModel} from './payout.ex.model'; | |||||
| import {PeopleModel} from './people.model'; | import {PeopleModel} from './people.model'; | ||||
| import {UserExModel} from './userExModel'; | |||||
| export class RewardExModel { | export class RewardExModel { | ||||
| public Id: number; | public Id: number; | ||||
| public FromId: string; | public FromId: string; | ||||
| public PayOutId: number; | public PayOutId: number; | ||||
| public To?: PeopleModel; | |||||
| public To?: UserExModel; | |||||
| public Loan?: LoanModel; | public Loan?: LoanModel; | ||||
| public From?: PeopleModel; | |||||
| public From?: UserExModel; | |||||
| public PayOut?: PayOutModel; | public PayOut?: PayOutModel; | ||||
| constructor(payload: Partial<RewardExModel>) { | constructor(payload: Partial<RewardExModel>) { | ||||
| this.FromId = payload.FromId || ''; | this.FromId = payload.FromId || ''; | ||||
| this.PayOutId = payload.PayOutId || 0; | this.PayOutId = payload.PayOutId || 0; | ||||
| // TODO: must make reward Ex workable | |||||
| // if (payload.To) { | |||||
| // this.To = new PeopleModel(payload.To); | |||||
| // } | |||||
| // | |||||
| // if (payload.Loan) { | |||||
| // this.Loan = new LoanModel(payload.Loan); | |||||
| // } | |||||
| // | |||||
| // if ( payload.From) { | |||||
| // this.From = new PeopleModel(payload.From); | |||||
| // } | |||||
| // | |||||
| // if ( payload. PayOut ) { | |||||
| // this.PayOut = new PayOutModel(payload.PayOut); | |||||
| // } | |||||
| if (payload.To) { | |||||
| this.To = new UserExModel(payload.To); | |||||
| } | |||||
| if (payload.Loan) { | |||||
| this.Loan = new LoanModel(payload.Loan); | |||||
| } | |||||
| if ( payload.From) { | |||||
| this.From = new UserExModel(payload.From); | |||||
| } | |||||
| if ( payload. PayOut ) { | |||||
| this.PayOut = new PayOutModel(payload.PayOut); | |||||
| } | |||||
| } | } | ||||
| } | } |
| Super = 'super', | Super = 'super', | ||||
| } | } | ||||
| export class UserModel extends PeopleModel{ | |||||
| export class UserExModel extends PeopleModel{ | |||||
| Role: UserRoles; | Role: UserRoles; | ||||
| Broker?: BrokerModel; | Broker?: BrokerModel; | ||||
| Login: string; | Login: string; | ||||
| constructor(payload: Partial<UserModel>) { | |||||
| constructor(payload: Partial<UserExModel>) { | |||||
| super(payload); | super(payload); | ||||
| this.Role = payload.Role || 'People' as UserRoles; | this.Role = payload.Role || 'People' as UserRoles; | ||||
| this.Login = payload.Login || ''; | this.Login = payload.Login || ''; |
| export class PayOutDetailsComponent implements OnInit { | export class PayOutDetailsComponent implements OnInit { | ||||
| sales = []; | sales = []; | ||||
| background = "url('https://svr2021.lawipac.com:8080/api/v1/avatar/0')"; | |||||
| background = 'url(\'https://svr2021.lawipac.com:8080/api/v1/avatar/0\')'; | |||||
| constructor() { } | constructor() { } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.sales = this.range(1, 20); | |||||
| console.log(this.sales); | |||||
| } | } | ||||
| range(start, end) { | |||||
| if(start === end) return [start]; | |||||
| range(start: number, end: number): number[] { | |||||
| if (start === end) { return [start]; } | |||||
| return [start, ...this.range(start + 1, end)]; | return [start, ...this.range(start + 1, end)]; | ||||
| } | } | ||||
| private ss: SessionService, | private ss: SessionService, | ||||
| private config: AppConfig) { } | private config: AppConfig) { } | ||||
| public AutoLogin(): void { | |||||
| const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); | |||||
| if (!sfm) { | |||||
| console.log('no auto login'); | |||||
| return; | |||||
| } | |||||
| this.ss.loggedIn = new ApiV1LoginResponse( | |||||
| sfm.login, | |||||
| sfm.machineId, | |||||
| sfm.session, | |||||
| sfm.sessionExpire, | |||||
| sfm.role, | |||||
| new PeopleModel(sfm.User) | |||||
| ); | |||||
| if ( sfm.UserExtra !== undefined ) { | |||||
| this.ss.loggedIn.UserExtra = new UserExtraModel(sfm.UserExtra); | |||||
| } | |||||
| this.ss.loginSuccess.emit(this.ss.loggedIn); | |||||
| } | |||||
| isAuthenticated(): boolean { | isAuthenticated(): boolean { | ||||
| return this.ss.loggedIn.login; | |||||
| } | |||||
| allowEditLoan(): boolean{ | |||||
| return true; | |||||
| return this.ss.isLoggedIn(); | |||||
| } | } | ||||
| login(email: string, password: string): void { | login(email: string, password: string): void { | ||||
| this.http.post<ApiV1LoginResponse>(this.config.apiUrl + 'login', {u: email, p: password}).subscribe( | this.http.post<ApiV1LoginResponse>(this.config.apiUrl + 'login', {u: email, p: password}).subscribe( | ||||
| responseData => { | responseData => { | ||||
| this.ss.loggedIn.session = responseData['Biukop-Session']; | |||||
| this.ss.loggedIn.login = responseData.login; | |||||
| this.ss.loggedIn.machineId = responseData['Biukop-Mid']; | |||||
| this.ss.loggedIn.sessionExpire = responseData.sessionExpire; | |||||
| this.ss.loggedIn.role = responseData.role; | |||||
| if ( responseData.User !== undefined) { | |||||
| this.ss.loggedIn.User = new PeopleModel(responseData.User); | |||||
| if (responseData.UserExtra !== undefined ) { | |||||
| this.ss.loggedIn.UserExtra = new UserExtraModel(responseData.UserExtra); | |||||
| }else{ | |||||
| this.ss.loggedIn.UserExtra = UserExtraModel.EmptyNew(); | |||||
| } | |||||
| }else{ | |||||
| this.ss.loggedIn.User = PeopleModel.EmptyNew(); | |||||
| } | |||||
| this.ss.saveSessionInfo(); | |||||
| this.ss.loginSuccess.emit(responseData); | |||||
| const sfm = new ApiV1LoginResponse({ | |||||
| session: responseData['Biukop-Session'], | |||||
| login: responseData.login, | |||||
| machineId: responseData['Biukop-Mid'], | |||||
| sessionExpire: responseData.sessionExpire, | |||||
| role: responseData.role, | |||||
| User: responseData.User || new PeopleModel({}), | |||||
| UserExtra: responseData.UserExtra || new UserExtraModel({}), | |||||
| }); | |||||
| this.ss.login(sfm); | |||||
| this.ss.saveSessionInfo(); | |||||
| }, | }, | ||||
| error => { | error => { | ||||
| this.ss.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| console.log('login error', error); | |||||
| this.ss.loginSuccess.emit(this.ss.loggedIn); | |||||
| this.ss.login( new ApiV1LoginResponse({}) ) ; | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| this.ss.logout(); | this.ss.logout(); | ||||
| } | } | ||||
| public getUrl(key: string): string{ | public getUrl(key: string): string{ | ||||
| return this.config.getUrl(key); | return this.config.getUrl(key); | ||||
| } | } | ||||
| // whether a specific login id is available | |||||
| public LoginAvailable(v: string): Observable<boolean> { | public LoginAvailable(v: string): Observable<boolean> { | ||||
| return this.http.get<boolean>(this.getUrl('login-available/' + v)); | return this.http.get<boolean>(this.getUrl('login-available/' + v)); | ||||
| } | } |
| import {Injectable} from '@angular/core'; | |||||
| import {HttpClient} from '@angular/common/http'; | |||||
| import {AuthService} from './auth.service'; | |||||
| import {Observable} from 'rxjs'; | |||||
| import {PayOutExModel} from '../models/payout.ex.model'; | |||||
| import {CompositeFilterDescriptor} from '@progress/kendo-data-query'; | |||||
| @Injectable({providedIn: 'root'}) | |||||
| export class PayOutService { | |||||
| constructor(private http: HttpClient, private auth: AuthService ){ } | |||||
| public getPayOutEx(id: string): Observable<PayOutExModel> { | |||||
| return this.http.get<PayOutExModel>(this.auth.getUrl('payout-ex/' + id)); | |||||
| } | |||||
| public getPayOutList(filter: CompositeFilterDescriptor): Observable<PayOutExModel[]> { | |||||
| return this.http.post<PayOutExModel[]>(this.auth.getUrl('payout-ex-list/'), filter); | |||||
| } | |||||
| } |
| import {EventEmitter, Injectable} from '@angular/core'; | |||||
| import {EventEmitter, Injectable, OnInit} from '@angular/core'; | |||||
| import {AppConfig} from '../app.config'; | import {AppConfig} from '../app.config'; | ||||
| import {ApiV1LoginResponse} from '../models/api-v1-login-response'; | import {ApiV1LoginResponse} from '../models/api-v1-login-response'; | ||||
| import {PeopleModel} from '../models/people.model'; | import {PeopleModel} from '../models/people.model'; | ||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| import {NotificationService} from '@progress/kendo-angular-notification'; | import {NotificationService} from '@progress/kendo-angular-notification'; | ||||
| import {debounce} from 'ts-debounce'; | import {debounce} from 'ts-debounce'; | ||||
| import {UserExtraModel} from '../models/user-extra.model'; | |||||
| import {WebSocketService} from '../websocket'; | |||||
| @Injectable() | @Injectable() | ||||
| export class SessionService { | export class SessionService { | ||||
| public loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| loginSuccess = new EventEmitter <ApiV1LoginResponse>(); | |||||
| public loggedIn = new ApiV1LoginResponse({}); | |||||
| loginResult = new EventEmitter <ApiV1LoginResponse>(); | |||||
| logoutEvent = new EventEmitter <ApiV1LoginResponse>(); | |||||
| private machineId = localStorage.getItem('mid') || ''; | |||||
| debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes | debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes | ||||
| constructor(private config: AppConfig, private http: HttpClient, private router: Router, private ns: NotificationService){ | |||||
| const self = this; | |||||
| window.addEventListener('storage', event => { | |||||
| if (event.storageArea === localStorage && event.key === this.config.storageKey) { | |||||
| // It's local storage | |||||
| self.debouncedLocalStorageMonitor(event).then( r => {}); | |||||
| } | |||||
| }, false); | |||||
| constructor(private config: AppConfig, private http: HttpClient, | |||||
| private router: Router, private ns: NotificationService, | |||||
| private ws: WebSocketService){ | |||||
| // | |||||
| const self = this; | |||||
| this.AutoLogin(); | |||||
| window.addEventListener('storage', event => { | |||||
| if (event.storageArea === localStorage && event.key === this.config.storageKey) { | |||||
| // It's local storage | |||||
| self.debouncedLocalStorageMonitor(event).then( r => {}); | |||||
| } | |||||
| }, false); | |||||
| } | |||||
| private AutoLogin(): void { | |||||
| const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); | |||||
| if (!sfm) { | |||||
| return; | |||||
| } | |||||
| this.login(sfm); | |||||
| } | |||||
| public login( resp: ApiV1LoginResponse): void{ | |||||
| this.loggedIn = new ApiV1LoginResponse(resp); | |||||
| if ( this.MachineId !== '' && resp.machineId !== '' && this.MachineId !== resp.machineId ) { | |||||
| this.MachineId = resp.machineId; // update machine id | |||||
| } | |||||
| if (this.loggedIn.session !== ''){ | |||||
| this.ws.SessionId = this.loggedIn.session; | |||||
| } | |||||
| if ( resp.UserExtra !== undefined ) { | |||||
| this.loggedIn.UserExtra = new UserExtraModel(resp.UserExtra); | |||||
| } | |||||
| this.loginResult.emit(this.loggedIn); | |||||
| } | } | ||||
| private localStorageChange(event: StorageEvent): void { | private localStorageChange(event: StorageEvent): void { | ||||
| console.log(event); | |||||
| console.log('local storage change', event); | |||||
| const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); | const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); | ||||
| if ( sfm && sfm.session && sfm.User && sfm.User.Id && this.loggedIn.User.Id) { | if ( sfm && sfm.session && sfm.User && sfm.User.Id && this.loggedIn.User.Id) { | ||||
| if ( sfm.session === this.loggedIn.session && sfm.User.Id !== this.loggedIn.User.Id){ | if ( sfm.session === this.loggedIn.session && sfm.User.Id !== this.loggedIn.User.Id){ | ||||
| public saveSessionInfo(): void { | public saveSessionInfo(): void { | ||||
| localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn)); | localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn)); | ||||
| localStorage.setItem('mid', this.loggedIn.machineId); | |||||
| } | } | ||||
| public isAdmin(): boolean { | public isAdmin(): boolean { | ||||
| return this.loggedIn.role === 'admin'; | return this.loggedIn.role === 'admin'; | ||||
| } | } | ||||
| return this.loggedIn.login === true; | return this.loggedIn.login === true; | ||||
| } | } | ||||
| public get CurrentUser(): PeopleModel { | |||||
| if (this.isLoggedIn() ) { | |||||
| return this.loggedIn.User; | |||||
| }else{ | |||||
| return new PeopleModel({}); | |||||
| } | |||||
| } | |||||
| public isCurrentUser(id: string): boolean { | public isCurrentUser(id: string): boolean { | ||||
| return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id; | return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id; | ||||
| } | } | ||||
| } | } | ||||
| logout(): void { | logout(): void { | ||||
| if ( !this.loggedIn.login ){ | |||||
| if ( ! this.isLoggedIn() ){ | |||||
| return; | return; | ||||
| } | } | ||||
| this.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| this.loginSuccess.emit(this.loggedIn); | |||||
| this.router.navigate(['/login']); | |||||
| localStorage.removeItem(this.config.storageKey); | |||||
| this.http.post(`${this.config.apiUrl}logout`, '').subscribe( | this.http.post(`${this.config.apiUrl}logout`, '').subscribe( | ||||
| resp => { | resp => { | ||||
| this.show(); | |||||
| this.ToastMessage(); | |||||
| }, | }, | ||||
| event => { | event => { | ||||
| this.show('logout from server (with warnings)'); | |||||
| this.ToastMessage('logout from server (with warnings)'); | |||||
| this.reLogin(); | |||||
| }, () => { | |||||
| this.reLogin(); | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| public isLoggedIn(): boolean{ | |||||
| if ( this.loggedIn.login !== undefined && | |||||
| this.loggedIn.login && | |||||
| this.machineId !== '' && | |||||
| this.loggedIn.session !== '' ) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| private reLogin(): void { | |||||
| this.loggedIn = new ApiV1LoginResponse({}); | |||||
| localStorage.removeItem(this.config.storageKey); | |||||
| this.router.navigate(['/login']).then( () => { | |||||
| this.logoutEvent.emit(this.loggedIn); | |||||
| }); | |||||
| } | |||||
| logoutWithoutPersistingStorage(): void { | logoutWithoutPersistingStorage(): void { | ||||
| if ( !this.loggedIn.login ){ | |||||
| if ( !this.isLoggedIn() ){ | |||||
| return; | return; | ||||
| } | } | ||||
| this.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| this.loginSuccess.emit(this.loggedIn); | |||||
| console.log('logout without storate'); | |||||
| this.loggedIn = new ApiV1LoginResponse({}); | |||||
| this.logoutEvent.emit(this.loggedIn); | |||||
| this.router.navigate(['/login']).then(r => { | this.router.navigate(['/login']).then(r => { | ||||
| this.show(); | |||||
| this.ToastMessage(); | |||||
| } ); | } ); | ||||
| } | } | ||||
| logoutAndClearLocalStorage(): void { | logoutAndClearLocalStorage(): void { | ||||
| if ( !this.loggedIn.login ){ | |||||
| if ( !this.isLoggedIn() ){ | |||||
| return; | return; | ||||
| } | } | ||||
| console.log('logout ++++++++++++++ storate'); | |||||
| localStorage.removeItem(this.config.storageKey); | localStorage.removeItem(this.config.storageKey); | ||||
| this.loggedIn = ApiV1LoginResponse.EmptyNew(); | |||||
| this.loginSuccess.emit(this.loggedIn); | |||||
| this.loggedIn = new ApiV1LoginResponse({}); | |||||
| this.logoutEvent.emit(this.loggedIn); | |||||
| this.router.navigate(['/login']).then(r => { | this.router.navigate(['/login']).then(r => { | ||||
| this.show(); | |||||
| this.ToastMessage(); | |||||
| } ); | } ); | ||||
| } | } | ||||
| public show( msg: string = 'Successfully logged out'): void { | |||||
| public ToastMessage(msg: string = 'Successfully logged out'): void { | |||||
| this.ns.show({ | this.ns.show({ | ||||
| content: msg, | content: msg, | ||||
| cssClass: 'button-notification', | cssClass: 'button-notification', | ||||
| animation: { type: 'slide', duration: 400 }, | animation: { type: 'slide', duration: 400 }, | ||||
| position: { horizontal: 'center', vertical: 'top' }, | |||||
| position: { horizontal: 'right', vertical: 'top' }, | |||||
| type: { style: 'info', icon: true }, | type: { style: 'info', icon: true }, | ||||
| hideAfter : 2000 | hideAfter : 2000 | ||||
| }); | }); | ||||
| } | } | ||||
| public get MachineId(): string { | |||||
| return this.machineId || ''; | |||||
| } | |||||
| public set MachineId(mid) { | |||||
| if ( this.machineId === '' || this.machineId !== mid ){ | |||||
| this.machineId = mid; | |||||
| localStorage.setItem('mid', mid); | |||||
| } | |||||
| } | |||||
| public get SessionId(): string{ | |||||
| return this.loggedIn.session || ''; | |||||
| } | |||||
| } | } |
| Avatar = './assets/img/logout.png'; | Avatar = './assets/img/logout.png'; | ||||
| public items: any[] = mainMenuItems; | public items: any[] = mainMenuItems; | ||||
| private loginSub: Subscription; | private loginSub: Subscription; | ||||
| private logoutSub: Subscription; | |||||
| public showMenu = true; | public showMenu = true; | ||||
| public opened = false; | public opened = false; | ||||
| this.selectMenuShow(this.ss.loggedIn.role); | this.selectMenuShow(this.ss.loggedIn.role); | ||||
| this.loginSub = this.ss.loginSuccess.subscribe( | |||||
| this.loginSub = this.ss.loginResult.subscribe( | |||||
| (rsp: ApiV1LoginResponse) => { | (rsp: ApiV1LoginResponse) => { | ||||
| this.login = rsp.login; | this.login = rsp.login; | ||||
| this.LoggedInUser = this.ss.loggedIn.User; | |||||
| this.LoggedInUser = this.ss.CurrentUser; | |||||
| this.selectMenuShow(rsp.role); | this.selectMenuShow(rsp.role); | ||||
| } | } | ||||
| ); | ); | ||||
| this.logoutSub = this.ss.logoutEvent.subscribe( | |||||
| resp => { | |||||
| this.login = false; | |||||
| this.LoggedInUser = this.ss.CurrentUser; // always empty user | |||||
| } | |||||
| ); | |||||
| } | } | ||||
| private selectMenuShow(role: string): void { | private selectMenuShow(role: string): void { | ||||
| ngOnDestroy(): void { | ngOnDestroy(): void { | ||||
| this.loginSub.unsubscribe(); | this.loginSub.unsubscribe(); | ||||
| this.logoutSub.unsubscribe(); | |||||
| } | } | ||||
| public logout(): void{ | public logout(): void{ | ||||
| public close(action: string): void { | public close(action: string): void { | ||||
| this.opened = false; | this.opened = false; | ||||
| if ( action === 'yes') { | if ( action === 'yes') { | ||||
| // this.login = false; | |||||
| this.authService.logout(); | this.authService.logout(); | ||||
| } | } | ||||
| } | } |
| @Injectable() | @Injectable() | ||||
| export class WebSocketService extends Subject<string>{ | export class WebSocketService extends Subject<string>{ | ||||
| private connected = false; | |||||
| private sessionId = ''; | |||||
| public ws: WebSocket; | public ws: WebSocket; | ||||
| private readonly url: string; | private readonly url: string; | ||||
| public LoginEvent: Subject<WsLoginEventModel>; | public LoginEvent: Subject<WsLoginEventModel>; | ||||
| // cannot have session as service, as session use us as building block | |||||
| constructor(private config: AppConfig) { | constructor(private config: AppConfig) { | ||||
| super(); | super(); | ||||
| this.url = config.apiWsUrl; | this.url = config.apiWsUrl; | ||||
| private startWebsocket(): void { | private startWebsocket(): void { | ||||
| console.log('starting websocket now..', this.url); | console.log('starting websocket now..', this.url); | ||||
| this.ws = new WebSocket(this.url); | this.ws = new WebSocket(this.url); | ||||
| this.ws.onopen = this.onOpen.bind(this); | |||||
| this.ws.onmessage = this.onMessage.bind(this); | this.ws.onmessage = this.onMessage.bind(this); | ||||
| this.ws.onerror = this.onError.bind(this); | this.ws.onerror = this.onError.bind(this); | ||||
| this.ws.onclose = this.onClose.bind(this); | this.ws.onclose = this.onClose.bind(this); | ||||
| } | } | ||||
| // public createObservableSocket(url: string): Observable<string> { | |||||
| // this.url = url; | |||||
| // const ret = new Observable<string> ( observer => { | |||||
| // this.observer = observer; | |||||
| // }); | |||||
| // this.startWebsocket(); | |||||
| // return ret; | |||||
| // } | |||||
| public onOpen(): void { | |||||
| this.connected = true; | |||||
| if ( this.sessionId !== '' ){ | |||||
| this.registerSessionId( this.sessionId); | |||||
| } | |||||
| } | |||||
| public set SessionId( sid: string ) { | |||||
| if ( this.sessionId !== sid ){ | |||||
| this.registerSessionId(sid); | |||||
| } | |||||
| this.sessionId = sid; | |||||
| } | |||||
| private registerSessionId(sid: string): void { | |||||
| const msg = { | |||||
| t: 'register-session', | |||||
| session: sid, | |||||
| }; | |||||
| this.Send(JSON.stringify(msg)); | |||||
| } | |||||
| public Send(msg: string): void { | |||||
| if ( !this.connected ) { | |||||
| return; | |||||
| } | |||||
| try { | |||||
| this.ws.send(msg); | |||||
| }catch (e){ | |||||
| this.connected = false; | |||||
| } | |||||
| } | |||||
| public onMessage(event): void{ | public onMessage(event): void{ | ||||
| this.connected = true; | |||||
| super.next(event.data); | super.next(event.data); | ||||
| try { | try { | ||||
| const e = JSON.parse(event.data); | const e = JSON.parse(event.data); | ||||
| } | } | ||||
| public onError(event): void{ | public onError(event): void{ | ||||
| this.connected = false; | |||||
| console.log('on error ', event); | console.log('on error ', event); | ||||
| } | } | ||||
| public onClose(event): void{ | public onClose(event): void{ | ||||
| this.connected = false; | |||||
| console.log('websocket closed.. reconnect in 1s ..', event); | console.log('websocket closed.. reconnect in 1s ..', event); | ||||
| setTimeout(() => { this.startWebsocket(); } , 1000); | setTimeout(() => { this.startWebsocket(); } , 1000); | ||||
| } | } | ||||
| public sendMessage(message: any): void { | |||||
| this.ws.send(message); | |||||
| } | |||||
| } | } |
| "experimentalDecorators": true, | "experimentalDecorators": true, | ||||
| "moduleResolution": "node", | "moduleResolution": "node", | ||||
| "importHelpers": true, | "importHelpers": true, | ||||
| "target": "es2015", | |||||
| "target": "es5", | |||||
| "module": "es2020", | "module": "es2020", | ||||
| "lib": [ | "lib": [ | ||||
| "es2018", | "es2018", |