From 83abade9e5a668bf5f0f15a7599dfd8d439f659c Mon Sep 17 00:00:00 2001 From: Patrick Sun Date: Tue, 6 Apr 2021 17:31:59 +1000 Subject: [PATCH] uploads list works. --- src/app/app-routing.module.ts | 6 +- src/app/app.component.scss | 15 ++- src/app/app.module.ts | 16 ++- .../lender-uploads.component.html | 44 +++++- .../lender-uploads.component.scss | 68 ++++++++++ .../lender-uploads.component.ts | 96 +++++++++++++- .../upload-status.interceptor.ts | 34 +++++ .../list-all-people.component.scss | 1 + .../list-all-people.component.ts | 9 +- .../list-all-rewards.component.scss | 1 + .../list-income/list-income.component.html | 37 ++++++ .../list-income/list-income.component.scss | 7 + .../list-income/list-income.component.spec.ts | 25 ++++ src/app/list-income/list-income.component.ts | 21 +++ .../trail-income/trail-income.component.ts | 32 ++--- src/app/main-menu-items.ts | 2 + src/app/models/Pay.In.AAA.Row.model.ts | 18 +++ src/app/models/funder.aaa.trail.model.ts | 15 +++ src/app/models/loan.model.ts | 28 +--- src/app/models/pay-in.model.ts | 40 ++++-- src/app/models/upload.analysis.model.ts | 50 +++++++ src/app/models/upload.model.ts | 22 +++ src/app/pay-in/pay-in.component.html | 92 ++++++++++++- src/app/pay-in/pay-in.component.scss | 9 ++ src/app/pay-in/pay-in.component.ts | 125 +++++++++++++++++- src/app/pipe/safe.url.pipe.ts | 14 ++ src/app/service/upload.attach.service.ts | 38 ++++++ .../upload-cards/upload-cards.component.html | 25 ++++ .../upload-cards/upload-cards.component.scss | 73 ++++++++++ .../upload-cards.component.spec.ts | 25 ++++ .../upload-cards/upload-cards.component.ts | 22 +++ .../upload-detail.component.html | 17 +++ .../upload-detail.component.scss | 22 +++ .../upload-detail.component.spec.ts | 25 ++++ .../upload-detail/upload-detail.component.ts | 63 +++++++++ 35 files changed, 1064 insertions(+), 73 deletions(-) create mode 100644 src/app/lender-uploads/upload-status.interceptor.ts create mode 100644 src/app/list-income/list-income.component.html create mode 100644 src/app/list-income/list-income.component.scss create mode 100644 src/app/list-income/list-income.component.spec.ts create mode 100644 src/app/list-income/list-income.component.ts create mode 100644 src/app/models/Pay.In.AAA.Row.model.ts create mode 100644 src/app/models/funder.aaa.trail.model.ts create mode 100644 src/app/models/upload.analysis.model.ts create mode 100644 src/app/models/upload.model.ts create mode 100644 src/app/pipe/safe.url.pipe.ts create mode 100644 src/app/service/upload.attach.service.ts create mode 100644 src/app/upload-cards/upload-cards.component.html create mode 100644 src/app/upload-cards/upload-cards.component.scss create mode 100644 src/app/upload-cards/upload-cards.component.spec.ts create mode 100644 src/app/upload-cards/upload-cards.component.ts create mode 100644 src/app/upload-detail/upload-detail.component.html create mode 100644 src/app/upload-detail/upload-detail.component.scss create mode 100644 src/app/upload-detail/upload-detail.component.spec.ts create mode 100644 src/app/upload-detail/upload-detail.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 5f59f22..cebffca 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -24,6 +24,8 @@ import {PeopleAddComponent} from './people-add/people-add.component'; import {SettingsComponent} from './settings/settings.component'; import {ProfileComponent} from './profile/profile.component'; import {ListAllPeopleComponent} from './list-all-people/list-all-people.component'; +import {ListIncomeComponent} from './list-income/list-income.component'; +import {UploadDetailComponent} from './upload-detail/upload-detail.component'; const routes: Routes = [ @@ -54,7 +56,9 @@ const routes: Routes = [ {path : 'people-add', component: PeopleAddComponent, canActivate: [AuthGuard] }, {path : 'profile', component: ProfileComponent, canActivate: [AuthGuard] }, {path : 'profile/:id', component: ProfileComponent, canActivate: [AuthGuard] }, - {path : 'e403', component: E403Component, }, + {path : 'upload-details/:id', component: UploadDetailComponent, canActivate: [AuthGuard] }, + {path : 'list-income', component: ListIncomeComponent, canActivate: [AuthGuard] }, + {path : 'e403', component: E403Component }, ]; @NgModule({ diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 5446797..0e67a0e 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -25,16 +25,21 @@ } #topBar .main-menu-item , #topBar .k-icon, #topBar .k-menu-item { - color:black !important; + color:black !important; } .k-menu:not(.k-context-menu) > .k-item > .k-state-active { - font-weight: bold; - background-color: lightblue; + font-weight: bold; + background-color: lightblue; } .k-popup.k-menu-popup .k-menu-item:hover { color: black; - font-weight: bold; - background-color: lightblue; + font-weight: bold; + background-color: lightblue; +} + + +.upload-details div[role="tabpanel"]{ + padding:0px; } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1ede24a..5c8736d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -88,6 +88,11 @@ import { AdminProfileComponent } from './profile/admin-profile/admin-profile.com import { ChangePasswordComponent } from './profile/change-password/change-password.component'; import { ProgressBarModule } from '@progress/kendo-angular-progressbar'; import { PagerModule } from '@progress/kendo-angular-pager'; +import { ListIncomeComponent } from './list-income/list-income.component'; +import { UploadCardsComponent } from './upload-cards/upload-cards.component'; +import {UploadInterceptor} from './lender-uploads/upload-status.interceptor'; +import { UploadDetailComponent } from './upload-detail/upload-detail.component'; +import {SafeUrlPipe} from './pipe/safe.url.pipe'; @@ -146,7 +151,11 @@ import { PagerModule } from '@progress/kendo-angular-pager'; MessageBoxComponent, UserProfileComponent, AdminProfileComponent, - ChangePasswordComponent + ChangePasswordComponent, + ListIncomeComponent, + UploadCardsComponent, + UploadDetailComponent, + SafeUrlPipe ], imports: [ BrowserModule, @@ -193,6 +202,11 @@ import { PagerModule } from '@progress/kendo-angular-pager'; provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true + }, + { + provide: HTTP_INTERCEPTORS, + useClass: UploadInterceptor, + multi: true } ], bootstrap: [AppComponent] diff --git a/src/app/lender-uploads/lender-uploads.component.html b/src/app/lender-uploads/lender-uploads.component.html index 34551fa..fdf3839 100644 --- a/src/app/lender-uploads/lender-uploads.component.html +++ b/src/app/lender-uploads/lender-uploads.component.html @@ -1 +1,43 @@ -

lender-uploads works!

+
+
+
+ + +
+
Name: {{ files[0].name }}
+
Cannot upload this file {{files[0]}}
+
fuck all {{map.get(files[0].uid).response.body.Funder}}
+
+
+
+
+
+ + +
+
+ +
+
+
+ +
+
+
+
+ + + +
diff --git a/src/app/lender-uploads/lender-uploads.component.scss b/src/app/lender-uploads/lender-uploads.component.scss index e69de29..e898e2d 100644 --- a/src/app/lender-uploads/lender-uploads.component.scss +++ b/src/app/lender-uploads/lender-uploads.component.scss @@ -0,0 +1,68 @@ +div.workarea{ + height: calc(100vh - 48px); + position:relative; + background-color: #46495b; +} + +div.file-manager-bar{ + position: relative; + height: 48px; + div.upload-area{ + width: 60%; + display: inline-block; + position:absolute; + } + + div.search-area{ + position:absolute; + right:0; + top:0; + width: 40%; + display: inline-block; + height: 48px; + background-color:grey; + border-left: 5px solid darkgrey; + border-right: 5px solid darkgrey; + .search-people{ + width: 100%; + bottom: 0px; + position: absolute; + height: 100%; + border:0px; + padding-right: 48px; + padding-left: 10px; + background-color: lightgoldenrodyellow; + } + .search-people:focus { + outline:none; + border:0px; + } + kendo-icon{ + position: absolute; + right:10px; + margin: 5px; + color: lightgrey; + } + kendo-icon:hover{ + color: darkgreen; + } + } + + .contact-image { + width: 22px; + height: 22px; + margin-right: 8px; + border-radius: 50%; + } + +} + +.vertical-spacer{ + height: 150px; +} + +.bottom-pager { + position: fixed; + bottom: 0px; +} + diff --git a/src/app/lender-uploads/lender-uploads.component.ts b/src/app/lender-uploads/lender-uploads.component.ts index b0441da..25c53a3 100644 --- a/src/app/lender-uploads/lender-uploads.component.ts +++ b/src/app/lender-uploads/lender-uploads.component.ts @@ -1,15 +1,105 @@ -import { Component, OnInit } from '@angular/core'; +import {AfterViewInit, Component, ElementRef, EventEmitter, OnInit, Output, ViewChild, ViewEncapsulation} from '@angular/core'; +import {FileInfo, FileRestrictions, SuccessEvent, UploadComponent, UploadEvent, UploadProgressEvent} from '@progress/kendo-angular-upload'; +import {AuthService} from '../service/auth.service'; +import {range} from '@progress/kendo-angular-dateinputs/dist/es2015/util'; +import {Router} from '@angular/router'; +import {PageChangeEvent} from '@progress/kendo-angular-pager'; @Component({ selector: 'app-lender-uploads', templateUrl: './lender-uploads.component.html', - styleUrls: ['./lender-uploads.component.scss'] + styleUrls: ['./lender-uploads.component.scss'], }) export class LenderUploadsComponent implements OnInit { + @Output() success: EventEmitter = new EventEmitter(); + @Output() click: EventEmitter = new EventEmitter(); + @Output() complete: EventEmitter = new EventEmitter(); + @Output() upload: EventEmitter = new EventEmitter(); + + + private uploads: SuccessEvent[] = []; + public value = 0 ; + public map: Map = new Map(); + + uploadSaveUrl = 'https://svr2021.lawipac.com:8080/api/v1/lender-upload/'; // should represent an actual API endpoint + uploadRemoveUrl = 'https://svr2021.lawipac.com:8080/api/v1/lender-upload-remove/'; // should represent an actual API endpoint + + public allUploads = [...range(30, 44 )]; + public displayedUploads: any[] = []; + public filteredUploads: any[] = []; + + + public skip = 0; + public pageSize = 12; + public total = 0; + + myRestrictions: FileRestrictions = { + allowedExtensions: ['.pdf', '.xls', '.xlsx'] + }; + + constructor(private auth: AuthService, private router: Router) { + } - constructor() { } ngOnInit(): void { + this.uploadSaveUrl = this.auth.getUrl('lender-upload/'); + this.uploadRemoveUrl = this.auth.getUrl('lender-upload-remove/'); + this.loadDisplayedUploads(); + } + + + + public onClick( files: any): void{ + this.click.emit(files[0]); + } + + public onSuccess(ss: SuccessEvent ): void { + this.uploads.push(ss); + this.map.set(ss.files[0].uid, ss); + this.success.emit(ss); + this.allUploads.unshift(this.allUploads.length + 100); } + public onUpload(ss: UploadEvent ): void { + this.upload.emit(true); + } + + public onComplete(): void { + this.upload.emit(false); + } + + public hasResp(uid): boolean { + let found = false; + this.uploads.every(v => { + if ( v.files[0].uid === uid ) { + found = true; + return false; // stop search + } + }); + return found; + } + + public uploadProgress(prog: UploadProgressEvent): void { + this.value = prog.percentComplete.valueOf(); + } + public show( i: number): void{ + this.router.navigate(['/upload-details/' + i]); + } + + + public onPageChange(e: PageChangeEvent): void { + this.skip = e.skip; + this.pageSize = e.take; + this.loadDisplayedUploads(); + } + + private loadDisplayedUploads(): void { + this.filteredUploads = this.allUploads ; + this.displayedUploads = this.filteredUploads.slice(this.skip, this.skip + this.pageSize); + this.total = this.displayedUploads.length; + } + + public onFilterUploads(hint: string): void { + this.loadDisplayedUploads(); + } } diff --git a/src/app/lender-uploads/upload-status.interceptor.ts b/src/app/lender-uploads/upload-status.interceptor.ts new file mode 100644 index 0000000..e565962 --- /dev/null +++ b/src/app/lender-uploads/upload-status.interceptor.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpProgressEvent, HttpEventType, HttpResponse } from '@angular/common/http'; +import { Observable, of, concat } from 'rxjs'; +import {AuthService} from '../service/auth.service'; +import {delay, tap} from 'rxjs/operators'; + +/* + Mocked backend service. + For further details, check + https://angular.io/guide/http#writing-an-interceptor +*/ + +@Injectable() +export class UploadInterceptor implements HttpInterceptor { + constructor(private auth: AuthService){} + intercept(req: HttpRequest, next: HttpHandler): Observable> { + if (req.url === this.auth.getUrl('lender-upload')) { + return next.handle(req).pipe( + tap(event => { + // console.log(event); + if (event.type === HttpEventType.UploadProgress){ + console.log('progress', event); + } + }) + ); + } + + if (req.url === this.auth.getUrl('lender-upload-remove')) { + // ok, not processng anything + console.log('lender-upload-remove'); + } + return next.handle(req); + } +} diff --git a/src/app/list-all-people/list-all-people.component.scss b/src/app/list-all-people/list-all-people.component.scss index a51a349..6a6d1f9 100644 --- a/src/app/list-all-people/list-all-people.component.scss +++ b/src/app/list-all-people/list-all-people.component.scss @@ -1,6 +1,7 @@ div.workarea{ height: calc(100vh - 48px); position:relative; + background-color: #46495b; } kendo-multicolumncombobox{ diff --git a/src/app/list-all-people/list-all-people.component.ts b/src/app/list-all-people/list-all-people.component.ts index e5dd2e5..895acf8 100644 --- a/src/app/list-all-people/list-all-people.component.ts +++ b/src/app/list-all-people/list-all-people.component.ts @@ -1,15 +1,14 @@ -import {Component, OnInit, ViewChild} from '@angular/core'; -import {PeopleModel, RelevantPeopleModel} from '../models/people.model'; +import {Component, OnInit, ViewEncapsulation} from '@angular/core'; +import {PeopleModel} from '../models/people.model'; import {PeopleService} from '../service/people.service'; import {AuthService} from '../service/auth.service'; import {ClonerService} from '../service/clone.service'; -import {ComboBoxComponent, PopupSettings, VirtualizationSettings} from '@progress/kendo-angular-dropdowns'; -import { PageChangeEvent } from '@progress/kendo-angular-pager'; +import {PageChangeEvent} from '@progress/kendo-angular-pager'; @Component({ selector: 'app-list-all-people', templateUrl: './list-all-people.component.html', - styleUrls: ['./list-all-people.component.scss'] + styleUrls: ['./list-all-people.component.scss'], }) export class ListAllPeopleComponent implements OnInit { diff --git a/src/app/list-all-rewards/list-all-rewards.component.scss b/src/app/list-all-rewards/list-all-rewards.component.scss index a0aebbc..409d76a 100644 --- a/src/app/list-all-rewards/list-all-rewards.component.scss +++ b/src/app/list-all-rewards/list-all-rewards.component.scss @@ -7,6 +7,7 @@ kendo-grid{ flex: 0 1 auto; + height: 100%; } diff --git a/src/app/list-income/list-income.component.html b/src/app/list-income/list-income.component.html new file mode 100644 index 0000000..4b9a1fb --- /dev/null +++ b/src/app/list-income/list-income.component.html @@ -0,0 +1,37 @@ +
+ + + +
+

Outer splitter / Middle pane

+

Resizable only.

+
+
+ + + + + + +
+ +
+
+ + +
+
+ Type: {{u.files[0].extension}} + Name: {{u.files[0].name}} + Funder: Name: {{u.response.body.Funder}} +
+
+
+
+ + + + +
+
+
diff --git a/src/app/list-income/list-income.component.scss b/src/app/list-income/list-income.component.scss new file mode 100644 index 0000000..e7d3929 --- /dev/null +++ b/src/app/list-income/list-income.component.scss @@ -0,0 +1,7 @@ +div.income-container { + height: calc(100vh - 48px); +} + +div.pane-content{ + height:100%; +} diff --git a/src/app/list-income/list-income.component.spec.ts b/src/app/list-income/list-income.component.spec.ts new file mode 100644 index 0000000..f7f8382 --- /dev/null +++ b/src/app/list-income/list-income.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListIncomeComponent } from './list-income.component'; + +describe('ListIncomeComponent', () => { + let component: ListIncomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ListIncomeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ListIncomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/list-income/list-income.component.ts b/src/app/list-income/list-income.component.ts new file mode 100644 index 0000000..0ed3a87 --- /dev/null +++ b/src/app/list-income/list-income.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit } from '@angular/core'; +import {SuccessEvent} from '@progress/kendo-angular-upload'; + +@Component({ + selector: 'app-list-income', + templateUrl: './list-income.component.html', + styleUrls: ['./list-income.component.scss'] +}) +export class ListIncomeComponent implements OnInit { + public uploads: SuccessEvent[] = []; + + constructor() { } + + ngOnInit(): void { + } + + public onSuccess(e: SuccessEvent): void { + this.uploads.push (e); + console.log(e); + } +} diff --git a/src/app/loan-edit/trail-income/trail-income.component.ts b/src/app/loan-edit/trail-income/trail-income.component.ts index 441e49b..770b76d 100644 --- a/src/app/loan-edit/trail-income/trail-income.component.ts +++ b/src/app/loan-edit/trail-income/trail-income.component.ts @@ -47,7 +47,7 @@ export class TrailIncomeComponent implements OnInit { let balance = -1; let offsetBalance = -1; if ( this.Loan.PayIn.length > 0) { - const idx = this.Loan.PayIn.length -1; + const idx = this.Loan.PayIn.length - 1; balance = this.Loan.PayIn[idx].Balance; offsetBalance = this.Loan.PayIn[idx].OffsetBalance; } @@ -60,7 +60,7 @@ export class TrailIncomeComponent implements OnInit { Trail: 168, Ts: new Date(), Balance: balance, - OffsetBalance: offsetBalance + OffsetBalance: offsetBalance, }); sender.addRow(this.formGroup); @@ -98,19 +98,19 @@ export class TrailIncomeComponent implements OnInit { if ( !this.showBalance) { v.Balance = -1; } if ( !this.showOffsetBalance) { v.OffsetBalance = -1; } - const pi = new PayInModel( - v.Id, - this.Loan.Amount, - v.Balance, - this.Loan.Lender, - this.Loan.Id, - this.Loan.LenderLoanNumber, - v.OffsetBalance, - this.Loan.Settlement, - v.Trail, - v.Ts, - 0, - ); + const pi = new PayInModel({}); + pi.Id = v.Id; + pi.Amount = this.Loan.Amount; + pi.Balance = v.Balance; + pi.Lender = this.Loan.Lender; + pi.LoanId = this.Loan.Id; + pi.LoanNumber = this.Loan.LenderLoanNumber; + pi.OffsetBalance = v.OffsetBalance; + pi.Settlement = this.Loan.Settlement; + pi.Trail = v.Trail; + pi.Ts = new Date(v.Ts); + pi.UploadId = v.UploadId; + console.log('saving PayIn', pi); this.ls.savePayIn(pi, isNew).subscribe( @@ -152,7 +152,7 @@ export class TrailIncomeComponent implements OnInit { } public visitUploads(pi: PayInModel): void { - this.router.navigate(['./uploads/' + pi.Uploads]); + this.router.navigate(['./uploads/' + pi.UploadId]); } } diff --git a/src/app/main-menu-items.ts b/src/app/main-menu-items.ts index 27fe433..035fabf 100644 --- a/src/app/main-menu-items.ts +++ b/src/app/main-menu-items.ts @@ -23,6 +23,8 @@ export const mainMenuItems: any[] = [ { text: 'Income', icon: 'dollar', url: './#pay-in' }, { text: '--', separator: 'true' }, { text: 'Uploads', icon: 'dollar', url: './#lender-uploads' }, + { text: 'Uploads by Id', icon: 'dollar', url: './#upload-details/30' }, + { text: 'list income', icon: 'dollar', url: './#list-income' }, ] }, { diff --git a/src/app/models/Pay.In.AAA.Row.model.ts b/src/app/models/Pay.In.AAA.Row.model.ts new file mode 100644 index 0000000..49a5ace --- /dev/null +++ b/src/app/models/Pay.In.AAA.Row.model.ts @@ -0,0 +1,18 @@ + + +export class PayInAAARowModel { + + public LoanNumber: string; + public Settlement: Date; + public LoanAmount: number; + public Balance: number; + public InTrail: number; + + constructor(payload: Partial){ + this.LoanNumber = payload.LoanNumber || ''; + this.Settlement = new Date(payload.Settlement); + this.LoanAmount = payload.LoanAmount || 0; + this.Balance = payload.Balance || 0; + this.InTrail = payload.InTrail || 0; + } +} diff --git a/src/app/models/funder.aaa.trail.model.ts b/src/app/models/funder.aaa.trail.model.ts new file mode 100644 index 0000000..820411f --- /dev/null +++ b/src/app/models/funder.aaa.trail.model.ts @@ -0,0 +1,15 @@ +import {PayInAAARowModel} from './Pay.In.AAA.Row.model'; + +export class FunderAaaTrailModel{ + public Period: Date; // valid only year, and month + public Rows: PayInAAARowModel[]; + constructor(payload: Partial) { + this.Period = new Date(payload.Period); + this.Rows = []; + if ( payload.Rows ){ + payload.Rows.forEach( v => { + this.Rows.push (new PayInAAARowModel(v)); + }); + } + } +} diff --git a/src/app/models/loan.model.ts b/src/app/models/loan.model.ts index 8c8fa03..c92d126 100644 --- a/src/app/models/loan.model.ts +++ b/src/app/models/loan.model.ts @@ -89,19 +89,7 @@ export class LoanModel { private setPayIn(v: any[]): void{ this.PayIn = []; v.forEach( pi =>{ - this.PayIn.push(new PayInModel( - pi.Id, - pi.Amount, - pi.Balance, - pi.Lender, - pi.LoanId, - pi.LoanNumber, - pi.OffsetBalance, - new Date(pi.Settlement), - pi.Trail, - new Date(pi.Ts), - pi.Uploads, - )); + this.PayIn.push(new PayInModel(pi)); }); } @@ -111,19 +99,7 @@ export class LoanModel { const na = this.PayIn.filter(v => v.Id !== pi.Id ); // add the incoming one na.unshift( - new PayInModel( - pi.Id, - pi.Amount, - pi.Balance, - pi.Lender, - pi.LoanId, - pi.LoanNumber, - pi.OffsetBalance, - new Date(pi.Settlement), - pi.Trail, - new Date(pi.Ts), - pi.Uploads, - ) + new PayInModel(pi) ); // update array element this.PayIn = na; diff --git a/src/app/models/pay-in.model.ts b/src/app/models/pay-in.model.ts index a49a568..e8be8d1 100644 --- a/src/app/models/pay-in.model.ts +++ b/src/app/models/pay-in.model.ts @@ -1,16 +1,30 @@ export class PayInModel { - constructor( - public Id: number, - public Amount: number, - public Balance: number, - public Lender: string, - public LoanId: string, - public LoanNumber: string, - public OffsetBalance: number, - public Settlement: Date, - public Trail: number, - public Ts: Date, - public Uploads: number - ){} + public Id: number; + public Amount: number; + public Balance: number; + public Lender: string; + public LoanId: string; + public LoanNumber: string; + public OffsetBalance: number; + public Settlement: Date; + public Trail: number; + public Ts: Date; + public UploadId: number; + constructor(payload: Partial){ + if ( payload === null ) { + payload = {}; + } + this.Id = payload.Id || 0; + this.Amount = payload.Amount || 0; + this.Balance = payload.Balance || 0; + this.Lender = payload.Lender || ''; + this.LoanId = payload.LoanId || '' ; + this.LoanNumber = payload.LoanNumber || ''; + this.OffsetBalance = payload.OffsetBalance || 0; + this.Settlement = new Date(payload.Settlement); + this.Trail = payload.Trail || 0 ; + this.Ts = new Date(payload.Ts); + this.UploadId = payload.UploadId; + } } diff --git a/src/app/models/upload.analysis.model.ts b/src/app/models/upload.analysis.model.ts new file mode 100644 index 0000000..1514f1d --- /dev/null +++ b/src/app/models/upload.analysis.model.ts @@ -0,0 +1,50 @@ +import {UploadModel} from './upload.model'; +import {PayInModel} from './pay-in.model'; +import {FunderAaaTrailModel} from './funder.aaa.trail.model'; +import {PayInAAARowModel} from './Pay.In.AAA.Row.model'; + +export class UploadAnalysisModel { + public Id: number; + public Funder: string; + public Mime: string; + public PayIn: PayInModel[]; + + public AAA?: FunderAaaTrailModel[]; + + public IsDuplicate: boolean; + public Uid?: string; // client side unique id when upload + public Upload?: UploadModel; + + constructor(payload: Partial){ + this.Id = payload.Id || 0 ; + this.Funder = payload.Funder || ''; + this.Mime = payload.Mime || ''; + if ( payload.PayIn && payload.PayIn.length > 0 ) { + this.PayIn = []; + }else{ + this.PayIn = []; + if ( payload.PayIn ) { + payload.PayIn.forEach( + v => { + const pi = new PayInModel(v); + this.PayIn.push(pi); + } + ); + } + } + + this.AAA = []; + if ( payload.AAA ) { + + payload.AAA.forEach( v => { + const r = new FunderAaaTrailModel(v); + this.AAA.push(r); + }); + } + + this.IsDuplicate = payload.IsDuplicate || false; + this.Uid = payload.Uid || '' ; + this.Upload = new UploadModel(payload.Upload || {}); + } + +} diff --git a/src/app/models/upload.model.ts b/src/app/models/upload.model.ts new file mode 100644 index 0000000..0cb33f1 --- /dev/null +++ b/src/app/models/upload.model.ts @@ -0,0 +1,22 @@ + +export class UploadModel { + public Id: number; + public Mime: string; + public FileName: string; + public Ts: Date; + public By: string; + public LastModified: Date; + public Size: number; + public Sha256: string; + + constructor(payload: Partial) { + this.Id = payload.Id || 0; + this.Mime = payload.Mime || ''; + this.FileName = payload.FileName || ''; + this.Ts = new Date (payload.Ts || new Date()); + this.By = payload.By || ''; + this.LastModified = new Date(payload.LastModified || new Date()); + this.Size = payload.Size || 0; + this.Sha256 = payload.Sha256 || ''; + } +} diff --git a/src/app/pay-in/pay-in.component.html b/src/app/pay-in/pay-in.component.html index b25a474..b3e760a 100644 --- a/src/app/pay-in/pay-in.component.html +++ b/src/app/pay-in/pay-in.component.html @@ -1 +1,91 @@ -

pay-in works!

+ + + +   Show Uploads   + + + + + + + + + + + + + + + + + + + + {{ dataItem.Ts | date: 'yyyy-MM-dd' }} + + + + + +
{{ dataItem.Balance | currency}}
+
unknown
+
+ + + + + + + + + +
+ + + + +
{{ dataItem.OffsetBalance | currency}}
+
unknown
+
+ + + + + + + + +
+ + + + +

-

+
+
+ +
diff --git a/src/app/pay-in/pay-in.component.scss b/src/app/pay-in/pay-in.component.scss index e69de29..ba606f0 100644 --- a/src/app/pay-in/pay-in.component.scss +++ b/src/app/pay-in/pay-in.component.scss @@ -0,0 +1,9 @@ +kendo-grid { + height: calc(100vh - 48px); +} + +.balance { + display: inline-block; + margin-left: 0px !important; + width: calc(100% - 60px) !important; +} diff --git a/src/app/pay-in/pay-in.component.ts b/src/app/pay-in/pay-in.component.ts index bf22de7..6c28fa2 100644 --- a/src/app/pay-in/pay-in.component.ts +++ b/src/app/pay-in/pay-in.component.ts @@ -1,4 +1,15 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, Input, OnInit} from '@angular/core'; +import {PayInModel} from '../models/pay-in.model'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; + +const createFormGroup = dataItem => new FormGroup({ + Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required), + Trail : new FormControl(dataItem.Trail, Validators.required), + Ts: new FormControl(dataItem.Ts, Validators.required), + Balance: new FormControl(dataItem.Balance, Validators.required), + OffsetBalance: new FormControl(dataItem.OffsetBalance, Validators.required), +}); + @Component({ selector: 'app-pay-in', @@ -6,10 +17,122 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./pay-in.component.scss'] }) export class PayInComponent implements OnInit { + @Input() public AllPayIn: PayInModel[] = []; + public formGroup: FormGroup; + private editedRowIndex: number; + + public trailIncome: FormGroup; + public showBalance = true; + public showOffsetBalance = true; + + public splitButtonItems: Array = [{ + text: 'Keep Text Only', + icon: 'paste-plain-text', + click: () => { console.log('Keep Text Only click handler'); } + }, { + text: 'Paste as HTML', + icon: 'paste-as-html' + }, { + text: 'Paste Markdown', + icon: 'paste-markdown' + }, { + text: 'Set Default Paste' + }]; constructor() { } ngOnInit(): void { } + + public addHandler({ sender }): void { + this.closeEditor(sender); + + let balance = -1; + let offsetBalance = -1; + // if ( this.Loan.PayIn.length > 0) { + // const idx = this.Loan.PayIn.length -1; + // balance = this.Loan.PayIn[idx].Balance; + // offsetBalance = this.Loan.PayIn[idx].OffsetBalance; + // } + + // this.showBalance = balance >= 0 ; + // this.showOffsetBalance = offsetBalance >= 0 ; + + this.formGroup = createFormGroup({ + Id: 0, + Trail: 168, + Ts: new Date(), + Balance: balance, + OffsetBalance: offsetBalance + }); + + sender.addRow(this.formGroup); + } + + public editHandler({ sender, rowIndex, dataItem }): void { + this.closeEditor(sender); + + this.showBalance = dataItem.Balance >= 0 ; + this.showOffsetBalance = dataItem.OffsetBalance >= 0 ; + this.formGroup = createFormGroup(dataItem); + this.editedRowIndex = rowIndex; + sender.editRow(rowIndex, this.formGroup); + + } + + public cancelHandler({ sender, rowIndex }): void { + console.log(sender); + this.closeEditor(sender, rowIndex); + } + + public saveHandler({ sender, rowIndex, formGroup, isNew }): void { + const v = formGroup.getRawValue(); + + if ( !this.showBalance) { v.Balance = -1; } + if ( !this.showOffsetBalance) { v.OffsetBalance = -1; } + + // const pi = new PayInModel( + // v.Id, + // this.Loan.Amount, + // v.Balance, + // this.Loan.Lender, + // this.Loan.Id, + // this.Loan.LenderLoanNumber, + // v.OffsetBalance, + // this.Loan.Settlement, + // v.Trail, + // v.Ts, + // 0, + // ); + + //console.log('saving PayIn', pi); + // this.ls.savePayIn(pi, isNew).subscribe( + // (resp: PayInModel) => { + // this.Loan.cuPayIn(resp); + // }, + // err => { + // this.errorOccurred.emit('Error saving Income'); + // } + // ); + + sender.closeRow(rowIndex); + } + + public removeHandler({ dataItem }): void { + console.log(dataItem); + + // const na = this.Loan.PayIn.filter(v => { + // return v.Id !== dataItem.Id; + // }); + // + // this.Loan.PayIn = na; + // this.ls.removePayIn(dataItem.Id); + } + + private closeEditor(grid, rowIndex = this.editedRowIndex): void{ + grid.closeRow(rowIndex); + this.editedRowIndex = undefined; + this.formGroup = undefined; + } } diff --git a/src/app/pipe/safe.url.pipe.ts b/src/app/pipe/safe.url.pipe.ts new file mode 100644 index 0000000..38188f8 --- /dev/null +++ b/src/app/pipe/safe.url.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; + +@Pipe({ + name: 'safeUrl' +}) +export class SafeUrlPipe implements PipeTransform { + + constructor(private sanitizer: DomSanitizer) { } + public transform(url): SafeResourceUrl { + return this.sanitizer.bypassSecurityTrustResourceUrl(url); + } + +} diff --git a/src/app/service/upload.attach.service.ts b/src/app/service/upload.attach.service.ts new file mode 100644 index 0000000..a5c58f0 --- /dev/null +++ b/src/app/service/upload.attach.service.ts @@ -0,0 +1,38 @@ +import {Injectable} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; +import {AuthService} from './auth.service'; +import {Observable} from 'rxjs'; +import {UploadModel} from '../models/upload.model'; +import {UploadAnalysisModel} from '../models/upload.analysis.model'; + + +@Injectable({providedIn: 'root'}) +export class UploadAttachService { + constructor(private http: HttpClient, private auth: AuthService) { + } + + public getUploadMeta(id: number): Observable { + return this.http.get(this.auth.getUrl('upload-meta/' + id)); + } + + public getUploadAnalysis(id: number): Observable { + return this.http.get(this.auth.getUrl('upload-analysis/' + id)); + } + + public getUploadAsJpgUrl(id: number): string { + return this.auth.getUrl('upload-as-image/' + id); + } + + public getUploadAsPdfUrl(id: number): string { + const ts = Date.now(); + return this.auth.getUrl('upload-as-pdf/' + id + '?date=' + ts); + } + public getUploadAsPdfUrlForDownload(id: number): string { + return this.auth.getUrl('upload-as-pdf/' + id + '?download=force'); + } + + public getUploadAsThumbnailUrl(id: number): string { + return this.auth.getUrl('upload-as-thumbnail/' + id ); + } + +} diff --git a/src/app/upload-cards/upload-cards.component.html b/src/app/upload-cards/upload-cards.component.html new file mode 100644 index 0000000..18ec02e --- /dev/null +++ b/src/app/upload-cards/upload-cards.component.html @@ -0,0 +1,25 @@ +
+ + + +
+

card.headerTitle

+

card.headerSubtitle

+
+
+ +

upload-cards works!

+

this is upload id {{ uploadId }}

+
+ +

footer

+
+
+
+ → +
+
+
+ + +
diff --git a/src/app/upload-cards/upload-cards.component.scss b/src/app/upload-cards/upload-cards.component.scss new file mode 100644 index 0000000..386bcc5 --- /dev/null +++ b/src/app/upload-cards/upload-cards.component.scss @@ -0,0 +1,73 @@ +div.card-wrapper{ + width:260px; + margin-bottom:20px; + width: 100%; + height: 100%; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + + .upload-card-content { + &:before { + content: ""; + position: absolute; +// z-index: -1; + top: -16px; + right: -16px; + background: #00838d; + height: 32px; + width: 32px; + border-radius: 32px; + transform: scale(1); + transform-origin: 50% 50%; + transition: transform 0.25s ease-out; + } + + kendo-card-header, + kendo-card-body, + kendo-card-footer{ + z-index:2; + } ; + + &:hover:before { + transform: scale(21); + } + + &:hover { + p { + z-index: 100; + transition: all 0.3s ease-out; + color: rgba(255, 255, 255, 0.8); + } + h1 { + transition: all 0.3s ease-out; + color: #fcfcfc; + } + } + + } + + + .go-corner { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + width: 32px; + height: 32px; + overflow: hidden; + top: 0; + right: 0; + background-color: #00838d; + border-radius: 0 4px 0 32px; + } + + .go-arrow { + margin-top: -4px; + margin-right: -4px; + color: white; + font-family: courier, sans; + } +} + diff --git a/src/app/upload-cards/upload-cards.component.spec.ts b/src/app/upload-cards/upload-cards.component.spec.ts new file mode 100644 index 0000000..26f4615 --- /dev/null +++ b/src/app/upload-cards/upload-cards.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UploadCardsComponent } from './upload-cards.component'; + +describe('UploadCardsComponent', () => { + let component: UploadCardsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UploadCardsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadCardsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/upload-cards/upload-cards.component.ts b/src/app/upload-cards/upload-cards.component.ts new file mode 100644 index 0000000..d74becf --- /dev/null +++ b/src/app/upload-cards/upload-cards.component.ts @@ -0,0 +1,22 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; + +@Component({ + selector: 'app-upload-cards', + templateUrl: './upload-cards.component.html', + styleUrls: ['./upload-cards.component.scss'] +}) +export class UploadCardsComponent implements OnInit { + @Input() uploadId: number; + + @Input() public card: any; + + constructor(private router: Router) { } + + ngOnInit(): void { + } + + public onNavigateTo(id: number ): void{ + this.router.navigate(['/upload-details/' + id]); + } +} diff --git a/src/app/upload-detail/upload-detail.component.html b/src/app/upload-detail/upload-detail.component.html new file mode 100644 index 0000000..2da00ce --- /dev/null +++ b/src/app/upload-detail/upload-detail.component.html @@ -0,0 +1,17 @@ +
+ + + +
+ +

No data

+
+
+
+ + +

pane 2

+
+
+
+
diff --git a/src/app/upload-detail/upload-detail.component.scss b/src/app/upload-detail/upload-detail.component.scss new file mode 100644 index 0000000..ea2009f --- /dev/null +++ b/src/app/upload-detail/upload-detail.component.scss @@ -0,0 +1,22 @@ +div.workspace { + height: calc(100vh - 48px); +} + +.fullheight-tab{ + height:100%; +} + +.main-content{ + min-height:100%; + margin:0; + box-shadow: 1px 1px 10px black; +} + +.dark-panel { + width:100%; + height:100%; + background-color: darkgrey; + padding: 10px 10px 10px 10px; + scroll-behavior: auto; + overflow: hidden; +} diff --git a/src/app/upload-detail/upload-detail.component.spec.ts b/src/app/upload-detail/upload-detail.component.spec.ts new file mode 100644 index 0000000..afa66e0 --- /dev/null +++ b/src/app/upload-detail/upload-detail.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UploadDetailComponent } from './upload-detail.component'; + +describe('UploadDetailComponent', () => { + let component: UploadDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UploadDetailComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/upload-detail/upload-detail.component.ts b/src/app/upload-detail/upload-detail.component.ts new file mode 100644 index 0000000..2a9d517 --- /dev/null +++ b/src/app/upload-detail/upload-detail.component.ts @@ -0,0 +1,63 @@ +import {Component, Input, OnInit, ViewEncapsulation} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {UploadAttachService} from '../service/upload.attach.service'; +import {UploadAnalysisModel} from '../models/upload.analysis.model'; +import {UploadModel} from '../models/upload.model'; +import {range} from '@progress/kendo-angular-dateinputs/dist/es2015/util'; + + + +@Component({ + selector: 'app-upload-detail', + templateUrl: './upload-detail.component.html', + styleUrls: ['./upload-detail.component.scss'] +}) +export class UploadDetailComponent implements OnInit { + @Input() uploadId: number; + @Input() ua: UploadAnalysisModel = new UploadAnalysisModel({}); + // 'http://africau.edu/images/default/sample.pdf'; + public uploadAsPicUrl = 'https://svr2021.lawipac.com:8080/api/v1/upload-as-pdf/default'; + public uploadAsPdfUrl = ''; + + + + constructor(private us: UploadAttachService, private actRoute: ActivatedRoute, private router: Router) { } + + ngOnInit(): void { + const id = this.actRoute.snapshot.params.id; + if ( id !== undefined && id > 0 ) { + this.uploadId = id; + } + + this.ua.Id = this.uploadId; + this.uploadAsPicUrl = this.us.getUploadAsJpgUrl(this.ua.Id); + this.uploadAsPdfUrl = this.us.getUploadAsPdfUrl(this.ua.Id); + + this.loadUploadMeta(); + // this.loadUploadAnalysis(); + } + + private loadUploadAnalysis(): void { + this.us.getUploadAnalysis(this.uploadId).subscribe( + resp => { + this.ua = new UploadAnalysisModel(resp); + this.uploadId = this.ua.Id; + this.uploadAsPicUrl = this.us.getUploadAsJpgUrl(this.ua.Id); + this.uploadAsPdfUrl = this.us.getUploadAsPdfUrl(this.ua.Id); + console.log(this); + } + ); + } + private loadUploadMeta(): void { + this.us.getUploadMeta(this.uploadId).subscribe( + resp => { + this.ua.Upload = new UploadModel(resp); + console.log(resp, this); + // this.ua.Upload = new UploadModel(resp); + // this.loadUploadAnalysis(); + } + ); + } + + +}