linked redaction file screen to backend colors and dictionary data
This commit is contained in:
parent
b4888b49ce
commit
f1f3dda4e3
@ -52,5 +52,11 @@
|
|||||||
"secure": false,
|
"secure": false,
|
||||||
"changeOrigin": true,
|
"changeOrigin": true,
|
||||||
"logLevel": "debug"
|
"logLevel": "debug"
|
||||||
|
},
|
||||||
|
"/color": {
|
||||||
|
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||||
|
"secure": false,
|
||||||
|
"changeOrigin": true,
|
||||||
|
"logLevel": "debug"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
<div [ngClass]="type" class="icon">
|
<div class="icon" [class.hint]="typeValue.hint" [style.background-color]="typeValue.hexColor">
|
||||||
<span>{{ type[0] }}</span>
|
<span>{{ typeValue.type[0] }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -40,33 +40,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hint,
|
.hint,
|
||||||
.comment,
|
|
||||||
.ignore {
|
.ignore {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hint,
|
|
||||||
.redaction,
|
|
||||||
.comment {
|
|
||||||
background-color: $grey-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.request {
|
|
||||||
background-color: $blue-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ignore {
|
.ignore {
|
||||||
background-color: $grey-5;
|
background-color: $grey-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hint_only {
|
|
||||||
background-color: $orange-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vertebrate {
|
|
||||||
background-color: $green-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.names {
|
|
||||||
background-color: $yellow-2;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { TypeValue } from '@redaction/red-ui-http';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-annotation-icon',
|
selector: 'redaction-annotation-icon',
|
||||||
@ -6,7 +7,7 @@ import { Component, Input, OnInit } from '@angular/core';
|
|||||||
styleUrls: ['./annotation-icon.component.scss']
|
styleUrls: ['./annotation-icon.component.scss']
|
||||||
})
|
})
|
||||||
export class AnnotationIconComponent implements OnInit {
|
export class AnnotationIconComponent implements OnInit {
|
||||||
@Input() public type: 'hint' | 'redaction' | 'suggestion' | 'ignore' | 'comment' | 'request';
|
@Input() typeValue: TypeValue;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
|||||||
@ -38,9 +38,8 @@ export class ManualRedactionDialogComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.isDocumentAdmin =
|
this.isDocumentAdmin =
|
||||||
(this._appStateService.isActiveProjectOwner ||
|
this._appStateService.isActiveProjectOwner && this._userService.user.isManager;
|
||||||
this._appStateService.isActiveFileDocumentReviewer) &&
|
this.isDocumentAdmin = false;
|
||||||
this._userService.user.isManager;
|
|
||||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||||
this.redactionForm = this._formBuilder.group({
|
this.redactionForm = this._formBuilder.group({
|
||||||
addToDictionary: [false],
|
addToDictionary: [false],
|
||||||
|
|||||||
@ -145,8 +145,9 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
[type]="key"
|
[typeValue]="appStateService.getDictionaryTypeValue(key)"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
|
||||||
{{ 'file-preview.filter-menu.' + key + '.label' | translate }}
|
{{ 'file-preview.filter-menu.' + key + '.label' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -162,15 +163,11 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
[type]="key + ' ' + subkey"
|
[typeValue]="
|
||||||
|
appStateService.getDictionaryTypeValue(subkey)
|
||||||
|
"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
{{
|
{{ appStateService.getDictionaryLabel(subkey) }}
|
||||||
'file-preview.filter-menu.' +
|
|
||||||
key +
|
|
||||||
'.' +
|
|
||||||
subkey +
|
|
||||||
'.label' | translate
|
|
||||||
}}
|
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +220,11 @@
|
|||||||
(click)="selectAnnotation(annotation)"
|
(click)="selectAnnotation(annotation)"
|
||||||
>
|
>
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
[type]="getType(annotation) + ' ' + getDictionary(annotation)"
|
[typeValue]="
|
||||||
|
appStateService.getDictionaryTypeValue(
|
||||||
|
getDictionary(annotation)
|
||||||
|
)
|
||||||
|
"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -8,7 +8,12 @@ import {
|
|||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ManualRedactionEntry, ReanalysisControllerService } from '@redaction/red-ui-http';
|
import {
|
||||||
|
DictionaryControllerService,
|
||||||
|
ManualRedactionEntry,
|
||||||
|
ReanalysisControllerService,
|
||||||
|
TypeValue
|
||||||
|
} from '@redaction/red-ui-http';
|
||||||
import { AppStateService } from '../../../state/app-state.service';
|
import { AppStateService } from '../../../state/app-state.service';
|
||||||
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||||
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
||||||
@ -56,6 +61,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
private readonly _activatedRoute: ActivatedRoute,
|
private readonly _activatedRoute: ActivatedRoute,
|
||||||
private readonly _dialogService: DialogService,
|
private readonly _dialogService: DialogService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
|
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _fileDownloadService: FileDownloadService,
|
private readonly _fileDownloadService: FileDownloadService,
|
||||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||||
@ -67,7 +73,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
this.fileId = params.fileId;
|
this.fileId = params.fileId;
|
||||||
this.appStateService.activateFile(this.projectId, this.fileId);
|
this.appStateService.activateFile(this.projectId, this.fileId);
|
||||||
});
|
});
|
||||||
this.filters = _filtersService.filters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get user() {
|
public get user() {
|
||||||
@ -91,7 +96,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
// PDFTRON cache fix
|
this.filters = this._filtersService.getFilters(this.appStateService.dictionaryData);
|
||||||
this._reloadFiles();
|
this._reloadFiles();
|
||||||
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
|
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
|
||||||
if (fileStatus.fileId === this.fileId) {
|
if (fileStatus.fileId === this.fileId) {
|
||||||
@ -273,7 +278,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAllFilters(filter: AnnotationFilters, value: boolean, rootKey?: string) {
|
public setAllFilters(filter: string, value: boolean, rootKey?: string) {
|
||||||
if (rootKey) {
|
if (rootKey) {
|
||||||
this.filters[rootKey] = value;
|
this.filters[rootKey] = value;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AnnotationFilters } from '../../../utils/types';
|
import { AnnotationFilters } from '../../../utils/types';
|
||||||
|
import { TypeValue } from '@redaction/red-ui-http';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -8,18 +9,23 @@ export class FiltersService {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
private _filters: AnnotationFilters = {
|
private _filters: AnnotationFilters = {
|
||||||
hint: {
|
hint: {},
|
||||||
hint_only: false,
|
redaction: {},
|
||||||
vertebrate: false,
|
request: false,
|
||||||
names: false
|
|
||||||
},
|
|
||||||
redaction: false,
|
|
||||||
comment: false,
|
|
||||||
suggestion: false,
|
|
||||||
ignore: false
|
ignore: false
|
||||||
};
|
};
|
||||||
|
|
||||||
public get filters(): AnnotationFilters {
|
public getFilters(dictionaryData: { [key: string]: TypeValue }): AnnotationFilters {
|
||||||
return JSON.parse(JSON.stringify(this._filters));
|
const filtersCopy = JSON.parse(JSON.stringify(this._filters));
|
||||||
|
for (let key of Object.keys(dictionaryData)) {
|
||||||
|
const typeValue = dictionaryData[key];
|
||||||
|
if (typeValue.hint === true) {
|
||||||
|
filtersCopy.hint[key] = false;
|
||||||
|
}
|
||||||
|
if (typeValue.hint === false) {
|
||||||
|
filtersCopy.redaction[key] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtersCopy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class AppStateGuard implements CanActivate {
|
|||||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
await this._userService.loadAllUsersIfNecessary();
|
await this._userService.loadAllUsersIfNecessary();
|
||||||
await this._appStateService.loadAllProjectsIfNecessary();
|
await this._appStateService.loadAllProjectsIfNecessary();
|
||||||
|
await this._appStateService.loadDictionaryDataIfNecessary();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,22 @@
|
|||||||
import { EventEmitter, Injectable } from '@angular/core';
|
import { EventEmitter, Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
DictionaryControllerService,
|
||||||
FileStatus,
|
FileStatus,
|
||||||
FileUploadControllerService,
|
FileUploadControllerService,
|
||||||
Project,
|
Project,
|
||||||
ProjectControllerService,
|
ProjectControllerService,
|
||||||
ReanalysisControllerService,
|
ReanalysisControllerService,
|
||||||
StatusControllerService
|
StatusControllerService,
|
||||||
|
TypeValue
|
||||||
} from '@redaction/red-ui-http';
|
} from '@redaction/red-ui-http';
|
||||||
import { NotificationService, NotificationType } from '../notification/notification.service';
|
import { NotificationService, NotificationType } from '../notification/notification.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { UserService } from '../user/user.service';
|
import { UserService } from '../user/user.service';
|
||||||
import { interval } from 'rxjs';
|
import { forkJoin, interval } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { download } from '../utils/file-download-utils';
|
import { download } from '../utils/file-download-utils';
|
||||||
|
import { humanize } from '../utils/functions';
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
projects: ProjectWrapper[];
|
projects: ProjectWrapper[];
|
||||||
@ -37,6 +40,7 @@ export class ProjectWrapper {
|
|||||||
})
|
})
|
||||||
export class AppStateService {
|
export class AppStateService {
|
||||||
private _appState: AppState;
|
private _appState: AppState;
|
||||||
|
private _dictionaryData: { [key: string]: TypeValue } = null;
|
||||||
|
|
||||||
public fileStatusChanged = new EventEmitter<FileStatus>();
|
public fileStatusChanged = new EventEmitter<FileStatus>();
|
||||||
|
|
||||||
@ -48,6 +52,7 @@ export class AppStateService {
|
|||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
|
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||||
private readonly _statusControllerService: StatusControllerService
|
private readonly _statusControllerService: StatusControllerService
|
||||||
) {
|
) {
|
||||||
this._appState = {
|
this._appState = {
|
||||||
@ -90,6 +95,19 @@ export class AppStateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get dictionaryData() {
|
||||||
|
return this._dictionaryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDictionaryColor(type: string) {
|
||||||
|
const color = this._dictionaryData[type].hexColor;
|
||||||
|
return color ? color : this._dictionaryData['default'].hexColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDictionaryLabel(type: string) {
|
||||||
|
return this._dictionaryData[type]['label'];
|
||||||
|
}
|
||||||
|
|
||||||
get isActiveFileDocumentReviewer() {
|
get isActiveFileDocumentReviewer() {
|
||||||
return this._appState.activeFile?.currentReviewer === this._userService.userId;
|
return this._appState.activeFile?.currentReviewer === this._userService.userId;
|
||||||
}
|
}
|
||||||
@ -331,4 +349,53 @@ export class AppStateService {
|
|||||||
download(data, 'redaction-report-' + file.filename + '.docx');
|
download(data, 'redaction-report-' + file.filename + '.docx');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadDictionaryDataIfNecessary() {
|
||||||
|
if (!this._dictionaryData) {
|
||||||
|
this._dictionaryData = {};
|
||||||
|
const typeObs = this._dictionaryControllerService.getAllTypes().pipe(
|
||||||
|
tap((typesResponse) => {
|
||||||
|
for (let type of typesResponse.types) {
|
||||||
|
this._dictionaryData[type.type] = type;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const colorsObs = this._dictionaryControllerService.getColors().pipe(
|
||||||
|
tap((colors) => {
|
||||||
|
this._dictionaryData['request'] = {
|
||||||
|
hexColor: colors.requestAdd,
|
||||||
|
type: 'request'
|
||||||
|
};
|
||||||
|
this._dictionaryData['ignore'] = {
|
||||||
|
hexColor: colors.notRedacted,
|
||||||
|
type: 'ignore'
|
||||||
|
};
|
||||||
|
this._dictionaryData['default'] = {
|
||||||
|
hexColor: colors.defaultColor,
|
||||||
|
type: 'default'
|
||||||
|
};
|
||||||
|
this._dictionaryData['add'] = { hexColor: colors.requestAdd, type: 'add' };
|
||||||
|
this._dictionaryData['remove'] = {
|
||||||
|
hexColor: colors.requestRemove,
|
||||||
|
type: 'remove'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await forkJoin([typeObs, colorsObs]).toPromise();
|
||||||
|
|
||||||
|
this._dictionaryData['hint'] = { hexColor: '#283241', type: 'hint' };
|
||||||
|
this._dictionaryData['redaction'] = { hexColor: '#283241', type: 'redaction' };
|
||||||
|
for (let key of Object.keys(this._dictionaryData)) {
|
||||||
|
this._dictionaryData[key]['label'] = humanize(key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this._dictionaryData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDictionaryTypeValue(key: string) {
|
||||||
|
const data = this._dictionaryData[key];
|
||||||
|
return data ? data : this._dictionaryData['default'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,8 +72,7 @@ export class AnnotationUtils {
|
|||||||
annotations: [],
|
annotations: [],
|
||||||
hint: 0,
|
hint: 0,
|
||||||
redaction: 0,
|
redaction: 0,
|
||||||
comment: 0,
|
request: 0,
|
||||||
suggestion: 0,
|
|
||||||
ignore: 0
|
ignore: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,3 +4,11 @@ export function groupBy(xs: any[], key: string) {
|
|||||||
return rv;
|
return rv;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function humanize(str: string) {
|
||||||
|
let frags = str.split(/[ \-_]+/);
|
||||||
|
for (let i = 0; i < frags.length; i++) {
|
||||||
|
frags[i] = frags[i].charAt(0).toUpperCase() + frags[i].slice(1);
|
||||||
|
}
|
||||||
|
return frags.join(' ');
|
||||||
|
}
|
||||||
|
|||||||
4
apps/red-ui/src/app/utils/types.d.ts
vendored
4
apps/red-ui/src/app/utils/types.d.ts
vendored
@ -2,8 +2,6 @@ import { FileStatus } from '@redaction/red-ui-http';
|
|||||||
|
|
||||||
export type Color = FileStatus.StatusEnum | ProjectStatus.StatusEnum;
|
export type Color = FileStatus.StatusEnum | ProjectStatus.StatusEnum;
|
||||||
|
|
||||||
export type AnnotationType = 'hint' | 'redaction' | 'suggestion' | 'comment' | 'ignore';
|
|
||||||
|
|
||||||
export class SortingOption {
|
export class SortingOption {
|
||||||
label: string;
|
label: string;
|
||||||
order: string;
|
order: string;
|
||||||
@ -11,5 +9,5 @@ export class SortingOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AnnotationFilters {
|
export class AnnotationFilters {
|
||||||
[key: AnnotationType]: boolean;
|
[key: string]: boolean | {};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -442,26 +442,14 @@
|
|||||||
"filter-types": {
|
"filter-types": {
|
||||||
"label": "Filter types"
|
"label": "Filter types"
|
||||||
},
|
},
|
||||||
"hint": {
|
|
||||||
"label": "Hint annotation",
|
|
||||||
"hint_only": {
|
|
||||||
"label": "Hint only"
|
|
||||||
},
|
|
||||||
"vertebrate": {
|
|
||||||
"label": "Vertebrate"
|
|
||||||
},
|
|
||||||
"names": {
|
|
||||||
"label": "Names"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"redaction": {
|
"redaction": {
|
||||||
"label": "Redaction"
|
"label": "Redaction"
|
||||||
},
|
},
|
||||||
"comment": {
|
"hint": {
|
||||||
"label": "Comment annotation"
|
"label": "Hint"
|
||||||
},
|
},
|
||||||
"suggestion": {
|
"request": {
|
||||||
"label": "Suggested redaction"
|
"label": "Redaction Request"
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"label": "Ignored redaction"
|
"label": "Ignored redaction"
|
||||||
|
|||||||
@ -17,6 +17,9 @@ server {
|
|||||||
location /project {
|
location /project {
|
||||||
proxy_pass $API_URL;
|
proxy_pass $API_URL;
|
||||||
}
|
}
|
||||||
|
location /color {
|
||||||
|
proxy_pass $API_URL;
|
||||||
|
}
|
||||||
location /user {
|
location /user {
|
||||||
proxy_pass $API_URL;
|
proxy_pass $API_URL;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user