| import {UploadInterceptor} from './lender-uploads/upload-status.interceptor'; | import {UploadInterceptor} from './lender-uploads/upload-status.interceptor'; | ||||
| import { UploadDetailComponent } from './upload-detail/upload-detail.component'; | import { UploadDetailComponent } from './upload-detail/upload-detail.component'; | ||||
| import {SafeUrlPipe} from './pipe/safe.url.pipe'; | import {SafeUrlPipe} from './pipe/safe.url.pipe'; | ||||
| import { ImagePopupDialogComponent } from './image-popup-dialog/image-popup-dialog.component'; | |||||
| ListIncomeComponent, | ListIncomeComponent, | ||||
| UploadCardsComponent, | UploadCardsComponent, | ||||
| UploadDetailComponent, | UploadDetailComponent, | ||||
| SafeUrlPipe | |||||
| SafeUrlPipe, | |||||
| ImagePopupDialogComponent | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| BrowserModule, | BrowserModule, |
| .container.outer{ | .container.outer{ | ||||
| width:100%; | width:100%; | ||||
| max-width:100vw; | max-width:100vw; | ||||
| background: url('/assets/img/body_bg.jpg') no-repeat center center fixed; | |||||
| background: url('./assets/img/body_bg.jpg') no-repeat center center fixed; | |||||
| -webkit-background-size: cover; | -webkit-background-size: cover; | ||||
| -moz-background-size: cover; | -moz-background-size: cover; | ||||
| -o-background-size: cover; | -o-background-size: cover; |
| <kendo-dialog [title]="title" *ngIf="opened" (close)="close('cancel')" | |||||
| [minWidth]="'200px'" [minHeight]="'200px'" [width]="dialogWidth" | |||||
| [maxWidth]="'100vw'" [maxHeight]="'100vh'" | |||||
| > | |||||
| <div class="text-center"> | |||||
| <kendo-loader *ngIf="!imageLoaded" size="large"></kendo-loader> | |||||
| </div> | |||||
| <img #content [src]="url | safeUrl" (load)="onLoad()" [width]="iWidth"/> | |||||
| <kendo-dialog-actions> | |||||
| <button kendoButton (click)="zoom(+0.1)" [icon]="'zoom-in'" [disabled]="imgWidthPercent >= 200 "><span class="k-fx-hidden">.</span></button> | |||||
| <button kendoButton (click)="zoom(-0.1)" [icon]="'zoom-out'" [disabled]="imgWidthPercent <=0" ><span class="k-fx-hidden">.</span></button> | |||||
| <button kendoButton (click)="originalSize()" >1:1</button> | |||||
| <button kendoButton (click)="close('no')" [disabled]="true"></button> | |||||
| <button kendoButton (click)="close('no')" [disabled]="true">{{imgWidthPercent | percent:'1.0'}}</button> | |||||
| <button kendoButton (click)="close('yes')" [icon]="'close-circle'" primary="true">close</button> | |||||
| </kendo-dialog-actions> | |||||
| </kendo-dialog> |
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { ImagePopupDialogComponent } from './image-popup-dialog.component'; | |||||
| describe('ImagePopupDialogComponent', () => { | |||||
| let component: ImagePopupDialogComponent; | |||||
| let fixture: ComponentFixture<ImagePopupDialogComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [ ImagePopupDialogComponent ] | |||||
| }) | |||||
| .compileComponents(); | |||||
| }); | |||||
| beforeEach(() => { | |||||
| fixture = TestBed.createComponent(ImagePopupDialogComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {AfterViewInit, Component, ElementRef, HostListener, Input, OnInit, ViewChild} from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-image-popup-dialog', | |||||
| templateUrl: './image-popup-dialog.component.html', | |||||
| styleUrls: ['./image-popup-dialog.component.scss'] | |||||
| }) | |||||
| export class ImagePopupDialogComponent implements OnInit{ | |||||
| @Input() url = ''; | |||||
| @Input() title = 'Image'; | |||||
| @ViewChild('content', { read: ElementRef }) img: ElementRef; | |||||
| public opened = false; | |||||
| public imgWidthPercent = 1; | |||||
| public iWidth = 100; | |||||
| public imgOriginalWidth = 0; | |||||
| public imgOriginalHeight = 0; | |||||
| public imageLoaded = false; | |||||
| public scrHeight = 0; | |||||
| public scrWidth = 0; | |||||
| public dialogWidth = 0 ; | |||||
| public margin = 32; // dialog margin; | |||||
| constructor() { } | |||||
| ngOnInit(): void { | |||||
| this.getScreenSize(); | |||||
| this.initDialogWidth(); | |||||
| } | |||||
| @HostListener('window:resize', ['$event']) | |||||
| getScreenSize(event?): void { | |||||
| this.scrHeight = window.innerHeight; | |||||
| this.scrWidth = window.innerWidth; | |||||
| this.dialogWidth = 200; // this.scrWidth; | |||||
| // console.log(this.scrHeight, this.scrWidth); | |||||
| // console.log(this.scrHeight, this.scrWidth); | |||||
| } | |||||
| public showImg(url: string, title?: string): void { | |||||
| this.initDialogWidth(); | |||||
| this.title = title || 'Image'; | |||||
| const ts = Date.now(); | |||||
| this.url = url + '?date=' + ts; | |||||
| this.opened = true; | |||||
| } | |||||
| private initDialogWidth(): void { | |||||
| this.imageLoaded = false; | |||||
| this.dialogWidth = 200; | |||||
| this.iWidth = this.dialogWidth - this.margin; | |||||
| } | |||||
| public close(status: string): void { | |||||
| this.opened = false; | |||||
| } | |||||
| public zoom(diff: number): void { | |||||
| this.imgWidthPercent += diff; | |||||
| if (this.imgWidthPercent < 0.05) { | |||||
| this.imgWidthPercent = 0.05; | |||||
| } | |||||
| if (this.imgWidthPercent > 3){ | |||||
| this.imgWidthPercent = 3; | |||||
| } | |||||
| this.resizeWindow(); | |||||
| } | |||||
| public originalSize(): void { | |||||
| this.imgWidthPercent = 1; | |||||
| this.resizeWindow(); | |||||
| } | |||||
| private resizeWindow(): void { | |||||
| this.iWidth = this.imgOriginalWidth * this.imgWidthPercent; | |||||
| const margin = this.margin; // the default dialog padding | |||||
| if (this.iWidth + margin <= this.scrWidth) { | |||||
| this.dialogWidth = this.iWidth + margin; | |||||
| }else{ | |||||
| 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); | |||||
| } | |||||
| public onLoad(): void { | |||||
| this.imageLoaded = true; | |||||
| this.imgOriginalWidth = this.img.nativeElement.naturalWidth; | |||||
| this.imgOriginalHeight = this.img.nativeElement.naturalHeight; | |||||
| if ( this.imgOriginalWidth > this.scrWidth) { | |||||
| this.iWidth = this.scrWidth - this.margin; | |||||
| }else{ | |||||
| this.iWidth = this.imgOriginalWidth; | |||||
| } | |||||
| this.imgWidthPercent = this.iWidth / this.imgOriginalWidth; | |||||
| this.resizeWindow(); | |||||
| } | |||||
| } |
| (uploadProgress)="uploadProgress($event)" | (uploadProgress)="uploadProgress($event)" | ||||
| [showFileList]="false" | [showFileList]="false" | ||||
| [saveUrl]="uploadSaveUrl"> | [saveUrl]="uploadSaveUrl"> | ||||
| <ng-template kendoUploadFileInfoTemplate let-files let map="map"> | |||||
| <ng-template kendoUploadFileInfoTemplate let-files> | |||||
| <div *ngIf="files!==undefined && files!== null && files[0] !== null"> | <div *ngIf="files!==undefined && files!== null && files[0] !== null"> | ||||
| <div (click)="onClick(files)">Name: {{ files[0].name }}</div> | <div (click)="onClick(files)">Name: {{ files[0].name }}</div> | ||||
| <div *ngIf="files[0].validationErrors !== undefined"> Cannot upload this file {{files[0]}}</div> | <div *ngIf="files[0].validationErrors !== undefined"> Cannot upload this file {{files[0]}}</div> | ||||
| </div> | </div> | ||||
| <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | <bkp-divider-shadow-bottom></bkp-divider-shadow-bottom> | ||||
| <div class="container"> | <div class="container"> | ||||
| <div class="row justify-content-start"> | |||||
| <div class="col-lg-3 text-center" *ngFor="let p of displayedUploads"> | |||||
| <div *ngIf="!loading" class="row justify-content-start"> | |||||
| <div class="col-lg-3 text-center" *ngFor="let p of displayedUploads"> | |||||
| <app-upload-cards [uploadId]="p"></app-upload-cards> | <app-upload-cards [uploadId]="p"></app-upload-cards> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div *ngIf="loading" class="row justify-content-center"> | |||||
| <div class="col-lg-1 text-center loader"> | |||||
| <kendo-loader [size]="'large'"></kendo-loader> | |||||
| </div> | |||||
| </div> | |||||
| <div class="vertical-spacer"></div> | <div class="vertical-spacer"></div> | ||||
| </div> | </div> | ||||
| (pageChange)="onPageChange($event)"> | (pageChange)="onPageChange($event)"> | ||||
| </kendo-datapager> | </kendo-datapager> | ||||
| </div> | </div> |
| position:relative; | position:relative; | ||||
| background-color: #46495b; | background-color: #46495b; | ||||
| overflow: scroll; | overflow: scroll; | ||||
| div.row div.loader{ | |||||
| top: 45%; | |||||
| position:absolute; | |||||
| } | |||||
| } | } | ||||
| div.file-manager-bar{ | div.file-manager-bar{ |
| import {range} from '@progress/kendo-angular-dateinputs/dist/es2015/util'; | import {range} from '@progress/kendo-angular-dateinputs/dist/es2015/util'; | ||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| import {PageChangeEvent} from '@progress/kendo-angular-pager'; | import {PageChangeEvent} from '@progress/kendo-angular-pager'; | ||||
| import {ImagePopupDialogComponent} from '../image-popup-dialog/image-popup-dialog.component'; | |||||
| import {UploadAttachService} from '../service/upload.attach.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-lender-uploads', | selector: 'app-lender-uploads', | ||||
| private uploads: SuccessEvent[] = []; | private uploads: SuccessEvent[] = []; | ||||
| public value = 0 ; | public value = 0 ; | ||||
| public map: Map<string, SuccessEvent> = new Map<string, SuccessEvent>(); | public map: Map<string, SuccessEvent> = new Map<string, SuccessEvent>(); | ||||
| public loading = false; | |||||
| uploadSaveUrl = 'https://svr2021.lawipac.com:8080/api/v1/lender-upload/'; // should represent an actual API endpoint | 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 | uploadRemoveUrl = 'https://svr2021.lawipac.com:8080/api/v1/lender-upload-remove/'; // should represent an actual API endpoint | ||||
| allowedExtensions: ['.pdf', '.xls', '.xlsx'] | allowedExtensions: ['.pdf', '.xls', '.xlsx'] | ||||
| }; | }; | ||||
| constructor(private auth: AuthService, private router: Router) { | |||||
| constructor(private auth: AuthService, private router: Router, private uas: UploadAttachService) { | |||||
| } | } | ||||
| this.uploadSaveUrl = this.auth.getUrl('lender-upload/'); | this.uploadSaveUrl = this.auth.getUrl('lender-upload/'); | ||||
| this.uploadRemoveUrl = this.auth.getUrl('lender-upload-remove/'); | this.uploadRemoveUrl = this.auth.getUrl('lender-upload-remove/'); | ||||
| this.loadDisplayedUploads(); | this.loadDisplayedUploads(); | ||||
| } | } | ||||
| this.filteredUploads = this.allUploads ; | this.filteredUploads = this.allUploads ; | ||||
| this.displayedUploads = this.filteredUploads.slice(this.skip, this.skip + this.pageSize); | this.displayedUploads = this.filteredUploads.slice(this.skip, this.skip + this.pageSize); | ||||
| this.total = this.displayedUploads.length; | this.total = this.displayedUploads.length; | ||||
| } | |||||
| private loadAllUploads(): void{ | |||||
| this.loading = true; | |||||
| // this.uas.getUploadMetaList(this.skip, this.skip + this.pageSize).subscribe( | |||||
| // resp => { | |||||
| // this.loading = false; | |||||
| // } | |||||
| // ); | |||||
| } | } | ||||
| public onFilterUploads(hint: string): void { | public onFilterUploads(hint: string): void { | ||||
| this.loadDisplayedUploads(); | this.loadDisplayedUploads(); | ||||
| } | } | ||||
| } | } |
| export class AnalysisAaaModel { | |||||
| Year: number; | |||||
| Month: number; | |||||
| LoanNumber: string; | |||||
| Settlement: Date; | |||||
| LoanAmount: number; | |||||
| Balance: number; | |||||
| InTrail: number; | |||||
| constructor(payload: Partial<AnalysisAaaModel>) { | |||||
| this.Year = payload.Year || 0; | |||||
| this.Month = payload.Month || 0; | |||||
| this.LoanNumber = payload.LoanNumber || ''; | |||||
| this.Settlement = new Date(payload.Settlement); | |||||
| this.LoanAmount = payload.LoanAmount || 0; | |||||
| this.Balance = payload.Balance || 0; | |||||
| this.InTrail = payload.Year || 0; | |||||
| } | |||||
| } |
| import {UploadModel} from './upload.model'; | |||||
| import {UploadMetaModel} from './uploadMetaModel'; | |||||
| import {PayInModel} from './pay-in.model'; | import {PayInModel} from './pay-in.model'; | ||||
| import {FunderAaaTrailModel} from './funder.aaa.trail.model'; | import {FunderAaaTrailModel} from './funder.aaa.trail.model'; | ||||
| import {PayInAAARowModel} from './Pay.In.AAA.Row.model'; | |||||
| export class UploadAnalysisModel { | export class UploadAnalysisModel { | ||||
| public Id: number; | public Id: number; | ||||
| public IsDuplicate: boolean; | public IsDuplicate: boolean; | ||||
| public Uid?: string; // client side unique id when upload | public Uid?: string; // client side unique id when upload | ||||
| public Input?: UploadModel; | |||||
| public Input?: UploadMetaModel; | |||||
| constructor(payload: Partial<UploadAnalysisModel>){ | constructor(payload: Partial<UploadAnalysisModel>){ | ||||
| this.Id = payload.Id || 0 ; | this.Id = payload.Id || 0 ; | ||||
| this.IsDuplicate = payload.IsDuplicate || false; | this.IsDuplicate = payload.IsDuplicate || false; | ||||
| this.Uid = payload.Uid || '' ; | this.Uid = payload.Uid || '' ; | ||||
| this.Input = new UploadModel(payload.Input || {}); | |||||
| this.Input = new UploadMetaModel(payload.Input || {}); | |||||
| } | } | ||||
| } | } |
| export class UploadModel { | |||||
| export class UploadMetaModel { | |||||
| public Id: number; | public Id: number; | ||||
| public Mime: string; | |||||
| public Format: string; | |||||
| public FileName: string; | public FileName: string; | ||||
| public Ts: Date; | public Ts: Date; | ||||
| public By: string; | public By: string; | ||||
| public Size: number; | public Size: number; | ||||
| public Sha256: string; | public Sha256: string; | ||||
| constructor(payload: Partial<UploadModel>) { | |||||
| constructor(payload: Partial<UploadMetaModel>) { | |||||
| this.Id = payload.Id || 0; | this.Id = payload.Id || 0; | ||||
| this.Mime = payload.Mime || ''; | |||||
| this.Format = payload.Format || ''; | |||||
| this.FileName = payload.FileName || ''; | this.FileName = payload.FileName || ''; | ||||
| this.Ts = new Date (payload.Ts || new Date()); | this.Ts = new Date (payload.Ts || new Date()); | ||||
| this.By = payload.By || ''; | this.By = payload.By || ''; | ||||
| this.Size = payload.Size || 0; | this.Size = payload.Size || 0; | ||||
| this.Sha256 = payload.Sha256 || ''; | this.Sha256 = payload.Sha256 || ''; | ||||
| } | } | ||||
| get iconUrl(): string { | |||||
| const t = this.Format.replace( '/', '-'); | |||||
| return location.origin +'/assets/img/mime/' + t + '.png'; | |||||
| } | |||||
| } | } |
| import {HttpClient} from '@angular/common/http'; | import {HttpClient} from '@angular/common/http'; | ||||
| import {AuthService} from './auth.service'; | import {AuthService} from './auth.service'; | ||||
| import {Observable} from 'rxjs'; | import {Observable} from 'rxjs'; | ||||
| import {UploadModel} from '../models/upload.model'; | |||||
| import {UploadMetaModel} from '../models/uploadMetaModel'; | |||||
| import {UploadAnalysisModel} from '../models/upload.analysis.model'; | import {UploadAnalysisModel} from '../models/upload.analysis.model'; | ||||
| constructor(private http: HttpClient, private auth: AuthService) { | constructor(private http: HttpClient, private auth: AuthService) { | ||||
| } | } | ||||
| public getUploadMeta(id: number): Observable<UploadModel> { | |||||
| return this.http.get<UploadModel>(this.auth.getUrl('upload-meta/' + id)); | |||||
| public getUploadMeta(id: number): Observable<UploadMetaModel> { | |||||
| return this.http.get<UploadMetaModel>(this.auth.getUrl('upload-meta/' + id)); | |||||
| } | } | ||||
| public getUploadAnalysis(id: number): Observable<UploadAnalysisModel> { | public getUploadAnalysis(id: number): Observable<UploadAnalysisModel> { | ||||
| } | } | ||||
| public getUploadAsJpgUrl(id: number): string { | public getUploadAsJpgUrl(id: number): string { | ||||
| return this.auth.getUrl('upload-as-image/' + id); | |||||
| const ts = Date.now(); | |||||
| return this.auth.getUrl('upload-as-image/' + id + '?date=' + ts); | |||||
| } | |||||
| public getUploadAsJpgUrlDefault(): string { | |||||
| return this.auth.getUrl('upload-as-image/default'); | |||||
| } | } | ||||
| public getUploadAsPdfUrl(id: number): string { | public getUploadAsPdfUrl(id: number): string { | ||||
| const ts = Date.now(); | const ts = Date.now(); | ||||
| return this.auth.getUrl('upload-as-pdf/' + id + '?date=' + ts); | return this.auth.getUrl('upload-as-pdf/' + id + '?date=' + ts); | ||||
| } | } | ||||
| public getUploadAsPdfUrlDefault(): string { | |||||
| const ts = Date.now(); | |||||
| return this.auth.getUrl('upload-as-pdf/default' + '?date=' + ts); | |||||
| } | |||||
| public getUploadAsPdfUrlForDownload(id: number): string { | public getUploadAsPdfUrlForDownload(id: number): string { | ||||
| return this.auth.getUrl('upload-as-pdf/' + id + '?download=force'); | return this.auth.getUrl('upload-as-pdf/' + id + '?download=force'); | ||||
| } | } |
| <div class="card-wrapper"> | <div class="card-wrapper"> | ||||
| <div class="file-type-image" [ngStyle]="{'background-image': 'url(https://via.placeholder.com/32x32)'}"></div> | |||||
| <kendo-card class="upload-card-content upload-thumb" [width]="'200px'" [ngStyle]="{'background-image' : thumbImage}"> | |||||
| <div class="file-type-image" [ngStyle]="{'background-image': 'url('+ icon +')'}"></div> | |||||
| <kendo-card class="upload-card-content upload-thumb" [width]="'200px'" [ngStyle]="{'background-image' : 'url(' + thumbImage + ')'}"> | |||||
| <kendo-card-header class="k-hbox"> | <kendo-card-header class="k-hbox"> | ||||
| <p> some title </p> | <p> some title </p> | ||||
| </kendo-card> | </kendo-card> | ||||
| </div> | </div> | ||||
| <app-image-popup-dialog #imgBox></app-image-popup-dialog> |
| } | } | ||||
| .file-type-image{ | .file-type-image{ | ||||
| width: 32px; | width: 32px; | ||||
| border-radius:100%; | |||||
| // border-radius:100%; | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-size: cover; | background-size: cover; | ||||
| border:1px white solid; | |||||
| box-shadow: inset 1px 1px 0 #b7b7b7; | |||||
| // border:1px white solid; | |||||
| // box-shadow: inset 1px 1px 0 #b7b7b7; | |||||
| z-index: 2; | z-index: 2; | ||||
| width: 32px; | |||||
| height: 32px; | height: 32px; | ||||
| position: absolute; | position: absolute; | ||||
| margin-right: 10px; | margin-right: 10px; | ||||
| right : 30px; | |||||
| left : 160px; | |||||
| top: 20px; | top: 20px; | ||||
| } | } | ||||
| import {Component, Input, OnInit} from '@angular/core'; | |||||
| import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; | |||||
| import {Router} from '@angular/router'; | import {Router} from '@angular/router'; | ||||
| import {ImagePopupDialogComponent} from '../image-popup-dialog/image-popup-dialog.component'; | |||||
| import {AuthService} from '../service/auth.service'; | |||||
| import {UploadMetaModel} from '../models/uploadMetaModel'; | |||||
| import {UploadAttachService} from '../service/upload.attach.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-upload-cards', | selector: 'app-upload-cards', | ||||
| }) | }) | ||||
| export class UploadCardsComponent implements OnInit { | export class UploadCardsComponent implements OnInit { | ||||
| @Input() uploadId: number; | @Input() uploadId: number; | ||||
| @Input() icon = '../../assets/img/pdf.jpg'; | |||||
| @ViewChild('imgBox', {static: true}) imgBox: ImagePopupDialogComponent; | |||||
| public uploadMeta: UploadMetaModel = new UploadMetaModel({}); | |||||
| public thumbImage = 'url(https://svr2021.lawipac.com:8080/api/v1/upload-as-thumbnail/31)'; | |||||
| public thumbImage = 'https://svr2021.lawipac.com:8080/api/v1/upload-as-thumbnail/31'; | |||||
| public fullImage = ''; | |||||
| constructor(private router: Router) { } | |||||
| constructor(private router: Router, private auth: AuthService, private uas: UploadAttachService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| this.thumbImage = 'url(https://svr2021.lawipac.com:8080/api/v1/upload-as-thumbnail/' + this.uploadId + ')'; | |||||
| let url = this.auth.getUrl('upload-as-thumbnail/' + this.uploadId); | |||||
| this.thumbImage = url ; | |||||
| url = this.auth.getUrl('upload-as-image/' + this.uploadId); | |||||
| this.fullImage = url; | |||||
| this.uas.getUploadMeta(this.uploadId).subscribe( resp => { | |||||
| this.uploadMeta = new UploadMetaModel(resp); | |||||
| this.icon = this.uploadMeta.iconUrl; | |||||
| }); | |||||
| } | } | ||||
| public onNavigateTo(id: number ): void{ | public onNavigateTo(id: number ): void{ | ||||
| this.router.navigate(['/upload-details/' + id]); | this.router.navigate(['/upload-details/' + id]); | ||||
| } | } | ||||
| public onSearch(id: number ): void{ | public onSearch(id: number ): void{ | ||||
| this.router.navigate(['/upload-details/' + id]); | |||||
| this.popupImage(this.fullImage); | |||||
| // this.popupImage(this.thumbImage); | |||||
| } | } | ||||
| public popupImage(url: string): void { | |||||
| this.imgBox.showImg(url, this.uploadId + ' - ' + this.uploadMeta.FileName); | |||||
| } | |||||
| } | } |
| <div class="workspace"> | <div class="workspace"> | ||||
| <kendo-tabstrip [tabPosition]="'left'" class="fullheight-tab upload-details" | |||||
| (tabSelect)="onTabSelect($event)" | |||||
| [animate]="false" | |||||
| > | |||||
| <kendo-tabstrip-tab title="PreView" [selected]="true"> | |||||
| <ng-template kendoTabContent> | |||||
| <div class="dark-panel text-center" > | |||||
| <div class="main-image-content" > | |||||
| <img [src]="uploadAsPicUrl | safeUrl"> | |||||
| </div> | |||||
| </div> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab title="Content" cssClass="uploads-panel"> | |||||
| <ng-template kendoTabContent class="dark-panel"> | |||||
| <div class="dark-panel text-center"> | |||||
| <div class="main-content" [ngClass]="{'init-pdf': !initAnimation, 'ready-pdf': initAnimation}"> | |||||
| <kendo-loader *ngIf="!iframeLoaded || !initAnimation" [size]="'large'"></kendo-loader> | |||||
| <iframe #pdf height="100%" | |||||
| [ngClass]="{'init-iframe': !iframeLoaded, 'ready-iframe': iframeLoaded}" | |||||
| [src]="uploadAsPdfUrl | safeUrl" | |||||
| (loadstart)="finishedLoading('loadstart')" | |||||
| (load)="finishedLoading('load')" ></iframe> | |||||
| </div> | |||||
| </div> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab title="AI Analysis"> | |||||
| <ng-template kendoTabContent> | |||||
| <div *ngIf="!scandone" class="dark-panel text-center" > | |||||
| <div *ngIf="scanning" class="overlay" [ngClass]="{'scanning': scanning, 'fade-out': scandone }"></div> | |||||
| <div *ngif="!scandone" class="main-content" [ngClass]="{'init-pdf': !initAnimation, 'ready-pdf': initAnimation, 'fade-out': scandone}"> | |||||
| <kendo-loader *ngIf="!iframeLoaded || !initAnimation" [size]="'large'"></kendo-loader> | |||||
| <iframe #pdf height="100%" | |||||
| [ngClass]="{'init-iframe': !iframeLoaded, 'ready-iframe': iframeLoaded}" | |||||
| [src]="uploadAsPdfUrl | safeUrl" | |||||
| (loadstart)="finishedLoading('loadstart')" | |||||
| (load)="finishedLoading('load')" ></iframe> | |||||
| </div> | |||||
| </div> | |||||
| <p> {{ ua.Input.FileName }}</p> | |||||
| <p> {{ ua.Input.Id }}</p> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| </kendo-tabstrip> | |||||
| <kendo-floatingactionbutton #fab class="init-fab" [ngClass]="{'init-fab': !initAnimation, 'ready-fab': initAnimation}" | |||||
| <kendo-splitter orientation="vertical"> | |||||
| <kendo-splitter-pane [resizable]="true" > | |||||
| <div class="pane-content"> | |||||
| <kendo-tabstrip #tabs [tabPosition]="'left'" class="fullheight-tab upload-details" | |||||
| (tabSelect)="onTabSelect($event)" | |||||
| [animate]="true" | |||||
| > | |||||
| <kendo-tabstrip-tab [title]="tabTitle[0]" [selected]="true"> | |||||
| <ng-template kendoTabContent> | |||||
| <div class="dark-panel text-center preview" > | |||||
| <div *ngIf="!PreviewImgLoaded" class="text-center"> | |||||
| <kendo-loader size="large"></kendo-loader> | |||||
| </div> | |||||
| <div class="main-image-content" | |||||
| [ngClass]="{'image-not-loaded': !PreviewImgLoaded, 'image-loaded': PreviewImgLoaded }" > | |||||
| <img [src]="uploadAsPicUrl | safeUrl" (load)="onImgPreviewLoaded()"> | |||||
| </div> | |||||
| </div> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab [title]="tabTitle[1]" cssClass="uploads-panel"> | |||||
| <ng-template kendoTabContent class="dark-panel"> | |||||
| <div class="dark-panel text-center"> | |||||
| <div class="main-content" [ngClass]="{'init-pdf': !initAnimation, 'ready-pdf': initAnimation}"> | |||||
| <kendo-loader *ngIf="!iframeLoaded || !initAnimation" [size]="'large'"></kendo-loader> | |||||
| <iframe #pdf height="100%" | |||||
| [ngClass]="{'init-iframe': !iframeLoaded, 'ready-iframe': iframeLoaded}" | |||||
| [src]="uploadAsPdfUrl | safeUrl" | |||||
| (loadstart)="finishedLoading('loadstart')" | |||||
| (load)="finishedLoading('load')" ></iframe> | |||||
| </div> | |||||
| </div> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| <kendo-tabstrip-tab [title]="tabTitle[2]"> | |||||
| <ng-template kendoTabContent> | |||||
| <div *ngIf="!scanDone" class="dark-panel text-center preview" > | |||||
| <div *ngIf="!ScanImgLoaded" class="text-center"> | |||||
| <kendo-loader size="large"></kendo-loader> | |||||
| </div> | |||||
| <div *ngIf="scanning" class="overlay" | |||||
| [ngClass]="{'scanning': scanning, 'fade-out': scanDone }"> | |||||
| <hr class="red-scan-line" /> | |||||
| </div> | |||||
| <div *ngIf="!scanDone || scanning" class="main-image-content" | |||||
| [ngClass]="{'image-not-loaded': !ScanImgLoaded, 'image-loaded': ScanImgLoaded }" > | |||||
| <img [src]="uploadAsPicUrl | safeUrl" (load)="onImgScanLoaded()"> | |||||
| </div> | |||||
| </div> | |||||
| <div *ngIf="scanDone" class="analysis-result"> | |||||
| <kendo-grid *ngIf="ua.Funder ==='AAA Financial'" [data]="analysisAAA"> | |||||
| <ng-template kendoGridToolbarTemplate> | |||||
| {{ua.Funder }} | |||||
| <button kendoGridAddCommand icon="plus" >Add new Income</button> | |||||
| Show Uploads <kendo-switch></kendo-switch> | |||||
| </ng-template> | |||||
| <kendo-grid-column field="Year"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Month"> </kendo-grid-column> | |||||
| <kendo-grid-column field="LoanNumber"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Settlement"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Amount"> </kendo-grid-column> | |||||
| <kendo-grid-column field="Balance"> </kendo-grid-column> | |||||
| <kendo-grid-column field="InTrail"> </kendo-grid-column> | |||||
| </kendo-grid> | |||||
| <div *ngIf="ua.Funder ===''"> | |||||
| <kendo-grid> </kendo-grid> | |||||
| </div> | |||||
| </div> | |||||
| </ng-template> | |||||
| </kendo-tabstrip-tab> | |||||
| </kendo-tabstrip> | |||||
| </div> | |||||
| </kendo-splitter-pane> | |||||
| <kendo-splitter-pane [resizable]="true" [collapsible]="true" [collapsed]="!resizeSplitter" | |||||
| (collapsedChange)="onPayInCollapsed($event)" | |||||
| (sizeChange)="onPayInSizeChange($event)" | |||||
| [(size)]="PayInSize"> | |||||
| <div class="pane-content"> | |||||
| <h3>Outer splitter / Bottom pane</h3> | |||||
| <p>Non-resizable and non-collapsible.</p> | |||||
| </div> | |||||
| </kendo-splitter-pane> | |||||
| </kendo-splitter> | |||||
| <kendo-floatingactionbutton #fab class="init-fab" | |||||
| [ngClass]="{'init-fab': !initAnimation && !resizeSplitter, 'ready-fab': initAnimation &&!resizeSplitter}" | |||||
| [icon]="'table'" | [icon]="'table'" | ||||
| [offset]="fabOffset" | |||||
| [align]="{ horizontal: 'start', vertical: 'bottom' }" | [align]="{ horizontal: 'start', vertical: 'bottom' }" | ||||
| (click)="onClickFloatActionButton($event)" | (click)="onClickFloatActionButton($event)" | ||||
| [title]="'List all Uploads'" | [title]="'List all Uploads'" | ||||
| > | > | ||||
| </kendo-floatingactionbutton> | </kendo-floatingactionbutton> | ||||
| </div> | </div> | ||||
| div.workspace { | div.workspace { | ||||
| height: calc(100vh - 48px); | height: calc(100vh - 48px); | ||||
| overflow: hidden; | overflow: hidden; | ||||
| kendo-splitter { | |||||
| height:100%; | |||||
| .pane-content{ | |||||
| height:100%; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| .fullheight-tab{ | .fullheight-tab{ | ||||
| overflow: hidden; | overflow: hidden; | ||||
| position:relative; | position:relative; | ||||
| &.preview{ | |||||
| height: unset; | |||||
| min-height: 100%; | |||||
| } | |||||
| .main-content, | .main-content, | ||||
| .main-image-content{ | .main-image-content{ | ||||
| position:relative; | position:relative; | ||||
| .main-image-content{ | .main-image-content{ | ||||
| display:block; | display:block; | ||||
| text-align: center; | text-align: center; | ||||
| overflow: scroll; | |||||
| margin-bottom: 30px; | |||||
| img { | img { | ||||
| width:100%; | width:100%; | ||||
| } | } | ||||
| transition: all 0.1s ease-in-out; | transition: all 0.1s ease-in-out; | ||||
| } | } | ||||
| } | |||||
| kendo-floatingactionbutton { | |||||
| width: 52px; | |||||
| } | } | ||||
| .init-fab{ | .init-fab{ | ||||
| top : -10px !important; | |||||
| top : 50vh !important; | |||||
| transition: all 1s ease-in-out; | transition: all 1s ease-in-out; | ||||
| } | } | ||||
| } | } | ||||
| div.overlay { | div.overlay { | ||||
| height: 10%; | |||||
| background-color: black; | |||||
| height: 100px; | |||||
| background-color: #19c7ef; | |||||
| opacity: 0.5; | opacity: 0.5; | ||||
| z-index: 10; | z-index: 10; | ||||
| position: absolute; | |||||
| width: 100%; | width: 100%; | ||||
| background-image: url('../../assets/img/scanbar.gif'); | |||||
| background-size: cover; | |||||
| background-repeat: no-repeat; | |||||
| position: absolute; | |||||
| top:20px; | |||||
| background-blend-mode: color; // for background image | |||||
| mix-blend-mode: hard-light; // for background | |||||
| } | } | ||||
| .scanning{ | .scanning{ | ||||
| animation: scanning 1s infinite; | |||||
| animation: scanning 3s infinite; | |||||
| } | |||||
| .image-not-loaded{ | |||||
| top: 100vh; | |||||
| } | |||||
| .image-loaded{ | |||||
| top: 0; | |||||
| } | } | ||||
| @keyframes scanning { | @keyframes scanning { | ||||
| 50% { | 50% { | ||||
| transform: translateY(80vh); | |||||
| transform: translateY(100vh); | |||||
| } | } | ||||
| } | } | ||||
| @keyframes shrinking { | |||||
| 50% { | |||||
| height: 10px; | |||||
| background-color: yellow; | |||||
| } | |||||
| } | |||||
| .fade-out { | .fade-out { | ||||
| opacity: 0; | opacity: 0; | ||||
| transition: all 0.5s ease-out; | transition: all 0.5s ease-out; | ||||
| } | } | ||||
| .blink_me { | |||||
| animation: blinker 1s linear infinite; | |||||
| } | |||||
| .red-scan-line{ | |||||
| color: red; | |||||
| width: 100%; | |||||
| border-top: 3px solid red; | |||||
| box-shadow: 1px 1px 10px red; | |||||
| animation: blinker 0.1s linear infinite; | |||||
| position: absolute; | |||||
| top: 80%; | |||||
| } | |||||
| @keyframes blinker { | |||||
| 50% { | |||||
| opacity: 0.1; | |||||
| } | |||||
| } | |||||
| .scanning > .red-scan-line { | |||||
| animation: top-down 3s infinite; | |||||
| } | |||||
| @keyframes top-down { | |||||
| 0% { top:80%;} | |||||
| 49% { top:80%; } | |||||
| 50% { top: 0; } | |||||
| 99% { top: 0; } | |||||
| 100%{ top: 80%;} | |||||
| } | |||||
| div.analysis-result{ | |||||
| height: 100%; | |||||
| width: 100%; | |||||
| background-color: lightgoldenrodyellow; | |||||
| kendo-grid{ | |||||
| height: 100%; | |||||
| } | |||||
| } |
| import {Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; | |||||
| import {AfterViewChecked, AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation} from '@angular/core'; | |||||
| import {ActivatedRoute, Router} from '@angular/router'; | import {ActivatedRoute, Router} from '@angular/router'; | ||||
| import {UploadAttachService} from '../service/upload.attach.service'; | import {UploadAttachService} from '../service/upload.attach.service'; | ||||
| import {UploadAnalysisModel} from '../models/upload.analysis.model'; | import {UploadAnalysisModel} from '../models/upload.analysis.model'; | ||||
| import {UploadModel} from '../models/upload.model'; | |||||
| import {UploadMetaModel} from '../models/uploadMetaModel'; | |||||
| import {FloatingActionButtonComponent} from '@progress/kendo-angular-buttons'; | import {FloatingActionButtonComponent} from '@progress/kendo-angular-buttons'; | ||||
| import {DrawerSelectEvent} from '@progress/kendo-angular-layout'; | |||||
| import {DrawerSelectEvent, TabStripComponent} from '@progress/kendo-angular-layout'; | |||||
| import {AnalysisAaaModel} from '../models/analysis.aaa.model'; | |||||
| templateUrl: './upload-detail.component.html', | templateUrl: './upload-detail.component.html', | ||||
| styleUrls: ['./upload-detail.component.scss'] | styleUrls: ['./upload-detail.component.scss'] | ||||
| }) | }) | ||||
| export class UploadDetailComponent implements OnInit { | |||||
| export class UploadDetailComponent implements OnInit, AfterViewInit { | |||||
| @Input() uploadId: number; | @Input() uploadId: number; | ||||
| @Input() ua: UploadAnalysisModel = new UploadAnalysisModel({}); | @Input() ua: UploadAnalysisModel = new UploadAnalysisModel({}); | ||||
| @ViewChild('fab', {static: true}) fab: FloatingActionButtonComponent; | @ViewChild('fab', {static: true}) fab: FloatingActionButtonComponent; | ||||
| @ViewChild('pdf', {static: false}) pdf: ElementRef; | @ViewChild('pdf', {static: false}) pdf: ElementRef; | ||||
| @ViewChild('tabs', {static: true}) tab: TabStripComponent; | |||||
| // 'http://africau.edu/images/default/sample.pdf'; | // 'http://africau.edu/images/default/sample.pdf'; | ||||
| public uploadAsPicUrl = 'https://svr2021.lawipac.com:8080/api/v1/upload-as-pdf/default'; | public uploadAsPicUrl = 'https://svr2021.lawipac.com:8080/api/v1/upload-as-pdf/default'; | ||||
| public uploadAsPdfUrl = ''; | public uploadAsPdfUrl = ''; | ||||
| public initAnimation = false; | public initAnimation = false; | ||||
| public iframeLoaded = false; | public iframeLoaded = false; | ||||
| public scanning = false; | public scanning = false; | ||||
| public scandone = false; | |||||
| public scanDone = false; | |||||
| public analysisIsDone = false; | |||||
| public ScanImgLoaded = false; | |||||
| public PreviewImgLoaded = false; | |||||
| public resizeSplitter = false; | |||||
| public PayInSize = '300px'; | |||||
| public fabOffset = { x: '10px', y: this.PayInSize }; | |||||
| @Input() public currentTab = 0; | |||||
| public tabText: string[] = ['Preview', 'Content', 'Analysis']; | |||||
| public tabTitle: string[] = this.tabText; | |||||
| public analysisAAA: AnalysisAaaModel[] = []; | |||||
| constructor(private us: UploadAttachService, private actRoute: ActivatedRoute, private router: Router) { } | constructor(private us: UploadAttachService, private actRoute: ActivatedRoute, private router: Router) { } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| } | } | ||||
| this.loadUploadMeta(); | this.loadUploadMeta(); | ||||
| // this.loadUploadAnalysis(); | |||||
| // this.fabOffset = { y : '-1px' }; | |||||
| this.fabSlideIn(); | this.fabSlideIn(); | ||||
| // this.changeTabTitle(); | |||||
| // this.currentTab=2; | |||||
| setTimeout(() => { | |||||
| this.resizeSplitter = true; | |||||
| }, 2000); | |||||
| } | |||||
| ngAfterViewInit(): void { | |||||
| setTimeout(() => { // if I don't use this, it complains value has been changed after being checked. | |||||
| this.tab.selectTab(this.currentTab); | |||||
| }, 100); | |||||
| } | } | ||||
| private loadUploadAnalysis(): void { | private loadUploadAnalysis(): void { | ||||
| this.analysisIsDone = false; | |||||
| this.us.getUploadAnalysis(this.uploadId).subscribe( | this.us.getUploadAnalysis(this.uploadId).subscribe( | ||||
| resp => { | resp => { | ||||
| this.scandone = true; | |||||
| this.scanning = false; | |||||
| this.ua = new UploadAnalysisModel(resp); | |||||
| this.uploadId = this.ua.Id; | |||||
| this.ua = new UploadAnalysisModel(resp); | |||||
| // this.TranslateForDisplay() // TODO: use database for this structure | |||||
| this.uploadId = this.ua.Id; | |||||
| this.analysisIsDone = true; | |||||
| }, err => { | |||||
| this.analysisIsDone = true; | |||||
| }, () => { | |||||
| this.analysisIsDone = true; | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| private loadUploadMeta(): void { | private loadUploadMeta(): void { | ||||
| this.us.getUploadMeta(this.uploadId).subscribe( | this.us.getUploadMeta(this.uploadId).subscribe( | ||||
| resp => { | resp => { | ||||
| this.ua.Input = new UploadModel(resp); | |||||
| this.ua.Input = new UploadMetaModel(resp); | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| private startScanningUI(): void { | |||||
| this.resizeSplitter = false; | |||||
| this.scanning = true; | |||||
| let count = 0; | |||||
| const t = setInterval(() => { | |||||
| count ++; | |||||
| if (count > 30 && this.analysisIsDone) { // 30 should be in sync with scss 3s | |||||
| this.scanDone = true; | |||||
| this.scanning = false; | |||||
| this.resizeSplitter = true; | |||||
| clearInterval(t); | |||||
| } | |||||
| }, 100); | |||||
| } | |||||
| private fabSlideIn(): void { | private fabSlideIn(): void { | ||||
| setTimeout(() => {this.initAnimation = true; }, 1000); | setTimeout(() => {this.initAnimation = true; }, 1000); | ||||
| } | } | ||||
| } | } | ||||
| public finishedLoading(status: string ): void{ | public finishedLoading(status: string ): void{ | ||||
| if ( this.pdf === undefined ) { | |||||
| if (this.pdf === undefined) { | |||||
| return; | return; | ||||
| } | } | ||||
| const el = this.pdf.nativeElement; | |||||
| if ( el !== undefined && el.src === this.uploadAsPdfUrl) { | |||||
| console.log('yes true',this); | |||||
| this.iframeLoaded = true; | |||||
| } | |||||
| const el = this.pdf.nativeElement; | |||||
| if (el !== undefined && el.src === this.uploadAsPdfUrl) { | |||||
| console.log('yes true', this); | |||||
| this.iframeLoaded = true; | |||||
| } | |||||
| } | } | ||||
| public onTabSelect( select: DrawerSelectEvent): void { | public onTabSelect( select: DrawerSelectEvent): void { | ||||
| if ( select.index === 2 && ! this.iframeLoaded ) { | |||||
| select.preventDefault(); | |||||
| return; | |||||
| } | |||||
| if ( ! this.scandone ) { | |||||
| this.scanning = true; | |||||
| this.currentTab = select.index; | |||||
| this.changeTabTitle(); | |||||
| if ( select.index === 2 && ! this.scanDone ) { | |||||
| this.loadUploadAnalysis(); | this.loadUploadAnalysis(); | ||||
| } | } | ||||
| } | |||||
| private changeTabTitle(): void { | |||||
| this.tabTitle = []; | |||||
| for ( let i = 0; i < this.tabText.length; i++ ) { | |||||
| if ( i === this.currentTab ){ | |||||
| this.tabTitle[i] = '☒ ' + this.tabText[i]; | |||||
| }else{ | |||||
| this.tabTitle[i] = '☐ ' + this.tabText[i]; | |||||
| } | |||||
| } | |||||
| } | |||||
| public onImgPreviewLoaded(): void { | |||||
| this.PreviewImgLoaded = true; | |||||
| } | |||||
| public onImgScanLoaded(): void { | |||||
| this.ScanImgLoaded = true; | |||||
| this.startScanningUI(); | |||||
| } | |||||
| public onPayInCollapsed( closed: boolean): void { | |||||
| console.log(this); | |||||
| this.resizeSplitter = !closed; | |||||
| return ; | |||||
| } | |||||
| public onPayInSizeChange(s: string): void { | |||||
| this.resizeSplitter = true; | |||||
| this.fabOffset = {x: '10px', y: s}; | |||||
| } | } | ||||
| } | } |