File Details
This commit is contained in:
parent
1f0d33136e
commit
7575b01b7e
@ -1,9 +1,7 @@
|
||||
import { AuthErrorComponent } from './components/auth-error/auth-error.component';
|
||||
import { AuthGuard } from './modules/auth/auth.guard';
|
||||
import { PdfViewerScreenComponent } from './components/pdf-viewer-screen/pdf-viewer-screen.component';
|
||||
import { CompositeRouteGuard } from './guards/composite-route.guard';
|
||||
import { RedRoleGuard } from './modules/auth/red-role.guard';
|
||||
import { HtmlDebugScreenComponent } from './components/html-debug-screen/html-debug-screen.component';
|
||||
import { BaseScreenComponent } from './components/base-screen/base-screen.component';
|
||||
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
||||
import { NgModule } from '@angular/core';
|
||||
@ -23,22 +21,6 @@ const routes = [
|
||||
component: AuthErrorComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'pdf-preview/:projectId/:fileId',
|
||||
component: PdfViewerScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'html-debug/:projectId/:fileId',
|
||||
component: HtmlDebugScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'main/my-profile',
|
||||
component: BaseScreenComponent,
|
||||
|
||||
@ -22,8 +22,6 @@ import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/materia
|
||||
import { ToastComponent } from './components/toast/toast.component';
|
||||
import { HttpCacheInterceptor } from '@redaction/red-cache';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
import { PdfViewerScreenComponent } from './components/pdf-viewer-screen/pdf-viewer-screen.component';
|
||||
import { HtmlDebugScreenComponent } from './components/html-debug-screen/html-debug-screen.component';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { DownloadsListScreenComponent } from './components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
@ -49,7 +47,7 @@ function cleanupBaseUrl(baseUrl: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const screens = [BaseScreenComponent, PdfViewerScreenComponent, HtmlDebugScreenComponent, DownloadsListScreenComponent, UserProfileScreenComponent];
|
||||
const screens = [BaseScreenComponent, DownloadsListScreenComponent, UserProfileScreenComponent];
|
||||
|
||||
const components = [AppComponent, LogoComponent, AuthErrorComponent, ToastComponent, NotificationsComponent, ...screens];
|
||||
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
<section [innerHTML]="htmlData"></section>
|
||||
<redaction-full-page-loading-indicator [displayed]="loading"></redaction-full-page-loading-indicator>
|
||||
@ -1,7 +0,0 @@
|
||||
section {
|
||||
height: calc(100vh - 40px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DebugControllerService, FileManagementControllerService } from '@redaction/red-ui-http';
|
||||
import { FileStatusWrapper } from '../../models/file/file-status.wrapper';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-html-debug-screen',
|
||||
templateUrl: './html-debug-screen.component.html',
|
||||
styleUrls: ['./html-debug-screen.component.scss']
|
||||
})
|
||||
export class HtmlDebugScreenComponent {
|
||||
private _fileId: string;
|
||||
private _projectId: string;
|
||||
|
||||
htmlData: any;
|
||||
loading: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _debugControllerService: DebugControllerService,
|
||||
private readonly _fileManagementControllerService: FileManagementControllerService
|
||||
) {
|
||||
this._activatedRoute.params.subscribe((params) => {
|
||||
this._fileId = params.fileId;
|
||||
this._projectId = params.projectId;
|
||||
this._loadDebugHTML();
|
||||
});
|
||||
}
|
||||
|
||||
private _loadDebugHTML() {
|
||||
this.loading = true;
|
||||
const fileStatus = new FileStatusWrapper(
|
||||
{
|
||||
projectId: this._projectId,
|
||||
fileId: this._fileId,
|
||||
lastProcessed: new Date().toISOString()
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
this._fileManagementControllerService
|
||||
.downloadAnnotatedFile(fileStatus.projectId, fileStatus.fileId, true, fileStatus.lastUploaded, 'body')
|
||||
.pipe(
|
||||
mergeMap((fileData) => {
|
||||
return this._debugControllerService.debugHtmlTablesForm(fileData, true);
|
||||
})
|
||||
)
|
||||
.subscribe((data) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this.htmlData = reader.result;
|
||||
this.loading = false;
|
||||
};
|
||||
reader.readAsText(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
<div #viewer class="viewer"></div>
|
||||
@ -1,4 +0,0 @@
|
||||
.viewer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FileStatusWrapper } from '../../models/file/file-status.wrapper';
|
||||
import { FileManagementControllerService } from '@redaction/red-ui-http';
|
||||
import { BASE_HREF } from '../../tokens';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-pdf-viewer-screen',
|
||||
templateUrl: './pdf-viewer-screen.component.html',
|
||||
styleUrls: ['./pdf-viewer-screen.component.scss']
|
||||
})
|
||||
export class PdfViewerScreenComponent implements OnInit {
|
||||
private _instance: WebViewerInstance;
|
||||
@ViewChild('viewer', { static: true })
|
||||
private _viewer: ElementRef;
|
||||
|
||||
private _fileId: string;
|
||||
private _projectId: string;
|
||||
private _fileData: any;
|
||||
|
||||
constructor(
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _fileManagementControllerService: FileManagementControllerService
|
||||
) {
|
||||
this._activatedRoute.params.subscribe((params) => {
|
||||
this._fileId = params.fileId;
|
||||
this._projectId = params.projectId;
|
||||
this._loadFile();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._loadViewer();
|
||||
}
|
||||
|
||||
private _loadViewer() {
|
||||
WebViewer(
|
||||
{
|
||||
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
|
||||
isReadOnly: true,
|
||||
path: this._baseHref + '/assets/wv-resources',
|
||||
css: this._baseHref + '/assets/pdftron/stylesheet.css'
|
||||
},
|
||||
this._viewer.nativeElement
|
||||
).then((instance) => {
|
||||
this._instance = instance;
|
||||
|
||||
instance.docViewer.on('documentLoaded', () => {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
if (this._fileData) {
|
||||
this._loadDocumentIntoViewer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _loadFile() {
|
||||
const fileStatus = new FileStatusWrapper(
|
||||
{
|
||||
projectId: this._projectId,
|
||||
fileId: this._fileId,
|
||||
lastProcessed: new Date().toISOString()
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
this._fileManagementControllerService
|
||||
.downloadAnnotatedFile(fileStatus.projectId, fileStatus.fileId, true, fileStatus.lastUploaded, 'body')
|
||||
.subscribe((data) => {
|
||||
this._fileData = data;
|
||||
this._loadDocumentIntoViewer();
|
||||
});
|
||||
}
|
||||
|
||||
private _loadDocumentIntoViewer() {
|
||||
if (this._instance) {
|
||||
this._instance.loadDocument(this._fileData, {
|
||||
filename: 'document.pdf'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,27 @@
|
||||
import { FileStatus } from '@redaction/red-ui-http';
|
||||
import { FileAttributeConfig, FileStatus } from '@redaction/red-ui-http';
|
||||
import { StatusSorter } from '../../utils/sorters/status-sorter';
|
||||
|
||||
export class FileStatusWrapper {
|
||||
constructor(public fileStatus: FileStatus, public reviewerName: string) {}
|
||||
primaryAttribute: string;
|
||||
|
||||
searchField: string;
|
||||
|
||||
constructor(public fileStatus: FileStatus, public reviewerName: string, fileAttributesConfig?: FileAttributeConfig[]) {
|
||||
this.searchField = fileStatus.filename;
|
||||
|
||||
if (fileAttributesConfig) {
|
||||
const primary = fileAttributesConfig.find((c) => c.primaryAttribute);
|
||||
if (primary && fileStatus.fileAttributes?.attributeIdToValue) {
|
||||
this.primaryAttribute = fileStatus.fileAttributes?.attributeIdToValue[primary.id];
|
||||
this.searchField += ' ' + this.primaryAttribute;
|
||||
}
|
||||
|
||||
if (!this.primaryAttribute) {
|
||||
// Fallback here
|
||||
this.primaryAttribute = '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get analysisDuration() {
|
||||
return this.fileStatus.analysisDuration;
|
||||
@ -52,6 +71,14 @@ export class FileStatusWrapper {
|
||||
return this.fileStatus.filename;
|
||||
}
|
||||
|
||||
get hasAnnotationComments() {
|
||||
return this.fileStatus.hasAnnotationComments;
|
||||
}
|
||||
|
||||
get ocrTime() {
|
||||
return this.fileStatus.lastOCRTime;
|
||||
}
|
||||
|
||||
get hasHints() {
|
||||
return this.fileStatus.hasHints;
|
||||
}
|
||||
|
||||
@ -158,24 +158,6 @@
|
||||
tooltip="file-preview.download-original-file"
|
||||
tooltipPosition="below"
|
||||
></redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
*ngIf="userPreferenceService.areDevFeaturesEnabled"
|
||||
(action)="openSSRFilePreview()"
|
||||
icon="red:new-tab"
|
||||
type="primary"
|
||||
class="ml-8"
|
||||
tooltip="file-preview.new-tab-ssr"
|
||||
tooltipPosition="below"
|
||||
></redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
*ngIf="userPreferenceService.areDevFeaturesEnabled"
|
||||
(action)="openHTMLDebug()"
|
||||
icon="red:html-file"
|
||||
type="primary"
|
||||
class="ml-8"
|
||||
tooltip="file-preview.html-debug"
|
||||
tooltipPosition="below"
|
||||
></redaction-circle-button>
|
||||
<!-- End Dev Mode Features-->
|
||||
|
||||
<redaction-circle-button
|
||||
|
||||
@ -181,6 +181,17 @@
|
||||
</div>
|
||||
<!-- <span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span>-->
|
||||
</div>
|
||||
<div class="small-label stats-subtitle" *ngIf="fileStatus.primaryAttribute">
|
||||
<div class="primary-attribute">
|
||||
{{ fileStatus.primaryAttribute }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:ocr"></mat-icon>
|
||||
{{ fileStatus.ocrTime ? (fileStatus.ocrTime | date: 'mediumDate') : ('project-overview.no-ocr' | translate) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@ -121,3 +121,7 @@ cdk-virtual-scroll-viewport {
|
||||
color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-attribute {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strateg
|
||||
styleUrls: ['./project-overview-screen.component.scss']
|
||||
})
|
||||
export class ProjectOverviewScreenComponent extends BaseListingComponent<FileStatusWrapper> implements OnInit, OnDestroy, OnDetach, OnAttach {
|
||||
protected readonly _searchKey = 'filename';
|
||||
protected readonly _searchKey = 'searchField';
|
||||
protected readonly _selectionKey = 'fileId';
|
||||
protected readonly _sortKey = 'project-overview';
|
||||
|
||||
@ -42,7 +42,6 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
|
||||
public peopleFilters: FilterModel[];
|
||||
public needsWorkFilters: FilterModel[];
|
||||
public collapsedDetails = false;
|
||||
public searchForm: FormGroup;
|
||||
|
||||
detailsContainerFilters: {
|
||||
needsWorkFilters: FilterModel[];
|
||||
|
||||
@ -2,6 +2,7 @@ import { EventEmitter, Injectable } from '@angular/core';
|
||||
import {
|
||||
DictionaryControllerService,
|
||||
FileAttributeConfig,
|
||||
FileAttributesConfig,
|
||||
FileAttributesControllerService,
|
||||
FileStatus,
|
||||
Project,
|
||||
@ -17,8 +18,8 @@ import { NotificationService, NotificationType } from '../services/notification.
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Event, NavigationEnd, ResolveStart, Router } from '@angular/router';
|
||||
import { UserService } from '../services/user.service';
|
||||
import { forkJoin, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { forkJoin, Observable, of } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { humanize } from '../utils/functions';
|
||||
import { FileStatusWrapper } from '../models/file/file-status.wrapper';
|
||||
import { ProjectWrapper } from './model/project.wrapper';
|
||||
@ -243,12 +244,17 @@ export class AppStateService {
|
||||
});
|
||||
|
||||
const fileData = await this._statusControllerService.getFileStatusForProjects(mappedProjects.map((p) => p.projectId)).toPromise();
|
||||
|
||||
for (const projectId of Object.keys(fileData)) {
|
||||
this._processFiles(
|
||||
mappedProjects.find((p) => p.projectId === projectId),
|
||||
fileData[projectId],
|
||||
emitEvents
|
||||
);
|
||||
const project = mappedProjects.find((p) => p.projectId === projectId);
|
||||
const fileAttributesConfig = await this._fileAttributesService
|
||||
.getFileAttributesConfiguration(project.ruleSetId)
|
||||
.pipe(catchError(() => of({})))
|
||||
.toPromise();
|
||||
|
||||
console.log(fileAttributesConfig);
|
||||
|
||||
this._processFiles(project, fileData[projectId], emitEvents, fileAttributesConfig);
|
||||
}
|
||||
|
||||
this._appState.projects = mappedProjects;
|
||||
@ -266,7 +272,11 @@ export class AppStateService {
|
||||
const oldProcessedDate = this.activeFile.lastProcessed;
|
||||
const activeFile = await this._statusControllerService.getFileStatus(this.activeProjectId, this.activeFileId).toPromise();
|
||||
|
||||
const activeFileWrapper = new FileStatusWrapper(activeFile, this._userService.getNameForId(activeFile.currentReviewer));
|
||||
const activeFileWrapper = new FileStatusWrapper(
|
||||
activeFile,
|
||||
this._userService.getNameForId(activeFile.currentReviewer),
|
||||
this._appState.activeFileAttributesConfig
|
||||
);
|
||||
this.activeProject.files = this.activeProject.files.map((file) => (file.fileId === activeFileWrapper.fileId ? activeFileWrapper : file));
|
||||
|
||||
await this.updateDictionaryVersion();
|
||||
@ -282,12 +292,17 @@ export class AppStateService {
|
||||
if (!project) {
|
||||
project = this.activeProject;
|
||||
}
|
||||
const files = await this._statusControllerService.getProjectStatus(project.project.projectId).toPromise();
|
||||
const files = await this._statusControllerService.getProjectStatus(project.projectId).toPromise();
|
||||
|
||||
return this._processFiles(project, files, emitEvents);
|
||||
const fileAttributesConfig = await this._fileAttributesService
|
||||
.getFileAttributesConfiguration(project.ruleSetId)
|
||||
.pipe(catchError(() => of({})))
|
||||
.toPromise();
|
||||
|
||||
return this._processFiles(project, files, emitEvents, fileAttributesConfig);
|
||||
}
|
||||
|
||||
private _processFiles(project: ProjectWrapper, files: FileStatus[], emitEvents: boolean = true) {
|
||||
private _processFiles(project: ProjectWrapper, files: FileStatus[], emitEvents: boolean = true, fileAttributesConfig: FileAttributesConfig) {
|
||||
const oldFiles = [...project.files];
|
||||
|
||||
const fileStatusChangedEvent = [];
|
||||
@ -298,7 +313,11 @@ export class AppStateService {
|
||||
for (const oldFile of oldFiles) {
|
||||
if (oldFile.fileId === file.fileId) {
|
||||
// emit when analysis count changed
|
||||
const fileStatusWrapper = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
|
||||
const fileStatusWrapper = new FileStatusWrapper(
|
||||
file,
|
||||
this._userService.getNameForId(file.currentReviewer),
|
||||
fileAttributesConfig?.fileAttributeConfigs
|
||||
);
|
||||
if (JSON.stringify(oldFile) !== JSON.stringify(fileStatusWrapper)) {
|
||||
fileStatusChangedEvent.push(fileStatusWrapper);
|
||||
}
|
||||
@ -311,12 +330,14 @@ export class AppStateService {
|
||||
}
|
||||
// emit for new file
|
||||
if (!found) {
|
||||
const fsw = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
|
||||
const fsw = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer), fileAttributesConfig?.fileAttributeConfigs);
|
||||
fileStatusChangedEvent.push(fsw);
|
||||
}
|
||||
}
|
||||
|
||||
project.files = files.map((f) => new FileStatusWrapper(f, this._userService.getNameForId(f.currentReviewer)));
|
||||
project.files = files.map(
|
||||
(f) => new FileStatusWrapper(f, this._userService.getNameForId(f.currentReviewer), fileAttributesConfig?.fileAttributeConfigs)
|
||||
);
|
||||
this._computeStats();
|
||||
|
||||
if (emitEvents) {
|
||||
@ -343,12 +364,6 @@ export class AppStateService {
|
||||
this._router.navigate(['/main/projects']);
|
||||
return;
|
||||
}
|
||||
this._fileAttributesService
|
||||
.getFileAttributesConfiguration(this.getProjectById(projectId).ruleSetId)
|
||||
.toPromise()
|
||||
.then((data) => {
|
||||
this._appState.activeFileAttributesConfig = data.fileAttributeConfigs;
|
||||
});
|
||||
}
|
||||
|
||||
activateFile(projectId: string, fileId: string) {
|
||||
|
||||
@ -192,6 +192,7 @@
|
||||
"collapse": "Hide Details"
|
||||
},
|
||||
"project-overview": {
|
||||
"no-ocr": "No OCR",
|
||||
"no-data": {
|
||||
"title": "There are no documents yet.",
|
||||
"action": "Upload Document"
|
||||
|
||||
@ -48,6 +48,10 @@ export interface FileStatus {
|
||||
* The file's name.
|
||||
*/
|
||||
filename?: string;
|
||||
/**
|
||||
* Shows if this file has comments on annotations.
|
||||
*/
|
||||
hasAnnotationComments?: boolean;
|
||||
/**
|
||||
* Shows if any hints were found during the analysis.
|
||||
*/
|
||||
@ -68,6 +72,10 @@ export interface FileStatus {
|
||||
* Shows if there is any change between the previous and current analysis.
|
||||
*/
|
||||
hasUpdates?: boolean;
|
||||
/**
|
||||
* Shows if this file has been OCRed by us. Last Time of OCR.
|
||||
*/
|
||||
lastOCRTime?: string;
|
||||
/**
|
||||
* Shows the last date of a successful analysis.
|
||||
*/
|
||||
@ -109,31 +117,30 @@ export interface FileStatus {
|
||||
*/
|
||||
uploader?: string;
|
||||
}
|
||||
|
||||
export namespace FileStatus {
|
||||
export type StatusEnum =
|
||||
| 'UNPROCESSED'
|
||||
| 'REPROCESS'
|
||||
| 'FULLREPROCESS'
|
||||
| 'PROCESSING'
|
||||
| 'OCR_PROCESSING'
|
||||
| 'ERROR'
|
||||
| 'UNASSIGNED'
|
||||
| 'UNDER_REVIEW'
|
||||
| 'EXCLUDED'
|
||||
| 'UNDER_APPROVAL'
|
||||
| 'APPROVED';
|
||||
| 'APPROVED'
|
||||
| 'FULLREPROCESS'
|
||||
| 'OCR_PROCESSING'
|
||||
| 'EXCLUDED';
|
||||
export const StatusEnum = {
|
||||
UNPROCESSED: 'UNPROCESSED' as StatusEnum,
|
||||
FULLREPROCESS: 'FULLREPROCESS' as StatusEnum,
|
||||
REPROCESS: 'REPROCESS' as StatusEnum,
|
||||
PROCESSING: 'PROCESSING' as StatusEnum,
|
||||
OCR_PROCESSING: 'OCR_PROCESSING' as StatusEnum,
|
||||
ERROR: 'ERROR' as StatusEnum,
|
||||
UNASSIGNED: 'UNASSIGNED' as StatusEnum,
|
||||
UNDERREVIEW: 'UNDER_REVIEW' as StatusEnum,
|
||||
UNDERAPPROVAL: 'UNDER_APPROVAL' as StatusEnum,
|
||||
APPROVED: 'APPROVED' as StatusEnum,
|
||||
FULLREPROCESS: 'FULLREPROCESS' as StatusEnum,
|
||||
OCR_PROCESSING: 'OCR_PROCESSING' as StatusEnum,
|
||||
EXCLUDED: 'EXCLUDED' as StatusEnum
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user