Reworked flows for add request and add redaction for user / manager
This commit is contained in:
parent
30c15f2e60
commit
615462893f
@ -47,7 +47,6 @@ import { LogoComponent } from './logo/logo.component';
|
||||
import { CompositeRouteGuard } from './utils/composite-route.guard';
|
||||
import { AppStateGuard } from './state/app-state.guard';
|
||||
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { ManualRedactionDialogComponent } from './dialogs/manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
|
||||
@ -61,6 +60,7 @@ import { MatNativeDateModule } from '@angular/material/core';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { ProjectMemberGuard } from './auth/project-member-guard.service';
|
||||
import { HumanizePipe } from './utils/humanize.pipe';
|
||||
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -83,7 +83,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
StatusBarComponent,
|
||||
LogoComponent,
|
||||
SimpleDoughnutChartComponent,
|
||||
ManualRedactionDialogComponent,
|
||||
ManualAnnotationDialogComponent,
|
||||
AnnotationIconComponent,
|
||||
AuthErrorComponent,
|
||||
HumanizePipe
|
||||
|
||||
@ -5,7 +5,6 @@ import {
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
ManualRedactionControllerService,
|
||||
ManualRedactionEntry,
|
||||
Project
|
||||
} from '@redaction/red-ui-http';
|
||||
import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component';
|
||||
@ -14,10 +13,10 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { AppStateService } from '../state/app-state.service';
|
||||
import { AddEditProjectDialogComponent } from './add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { AssignOwnerDialogComponent } from './assign-owner-dialog/assign-owner-dialog.component';
|
||||
import { ManualRedactionDialogComponent } from './manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { Annotations } from '@pdftron/webviewer';
|
||||
import { ManualRedactionEntryWrapper } from '../screens/file/model/manual-redaction-entry.wrapper';
|
||||
import { AnnotationWrapper } from '../screens/file/model/annotation.wrapper';
|
||||
import { ManualAnnotationDialogComponent } from './manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { ManualAnnotationService } from '../screens/file/service/manual-annotation.service';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '600px',
|
||||
@ -35,6 +34,7 @@ export class DialogService {
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService
|
||||
) {}
|
||||
|
||||
@ -83,8 +83,8 @@ export class DialogService {
|
||||
public openManualRedactionDialog(
|
||||
$event: ManualRedactionEntryWrapper,
|
||||
cb?: Function
|
||||
): MatDialogRef<ManualRedactionDialogComponent> {
|
||||
const ref = this._dialog.open(ManualRedactionDialogComponent, {
|
||||
): MatDialogRef<ManualAnnotationDialogComponent> {
|
||||
const ref = this._dialog.open(ManualAnnotationDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true,
|
||||
data: $event
|
||||
@ -99,51 +99,25 @@ export class DialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public acceptSuggestionAnnotation(
|
||||
public acceptSuggestion(
|
||||
$event: MouseEvent,
|
||||
annotation: AnnotationWrapper,
|
||||
projectId: string,
|
||||
fileId: string
|
||||
annotation: AnnotationWrapper
|
||||
): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService
|
||||
.approveRequest(projectId, fileId, annotation.uuid)
|
||||
.subscribe(
|
||||
() => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.confirm-annotation.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.confirm-annotation.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
this._manualAnnotationService.acceptSuggestion(annotation).subscribe(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotation(
|
||||
public rejectSuggestion(
|
||||
$event: MouseEvent,
|
||||
annotation: AnnotationWrapper,
|
||||
projectId: string,
|
||||
fileId: string
|
||||
annotation: AnnotationWrapper
|
||||
): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
|
||||
@ -154,29 +128,7 @@ export class DialogService {
|
||||
|
||||
ref.afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService
|
||||
.undo(projectId, fileId, annotation.uuid)
|
||||
.subscribe(
|
||||
(ok) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.remove-annotation.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.remove-annotation.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
this._manualAnnotationService.rejectSuggestion(annotation).subscribe(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
</div>
|
||||
{{ manualRedactionEntryWrapper.manualRedactionEntry.value }}
|
||||
|
||||
<div class="red-input-group">
|
||||
<div
|
||||
class="red-input-group"
|
||||
[class.hidden]="this.manualRedactionEntryWrapper.type === 'DICTIONARY'"
|
||||
>
|
||||
<label translate="manual-redaction.dialog.content.reason.label"></label>
|
||||
<input formControlName="reason" name="reason" type="text" rows="2" />
|
||||
</div>
|
||||
@ -29,15 +32,6 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="red-input-group"
|
||||
[class.hidden]="this.manualRedactionEntryWrapper.type === 'HINT'"
|
||||
>
|
||||
<mat-checkbox color="primary" formControlName="addToDictionary">{{
|
||||
'manual-redaction.dialog.content.dictionary.add.label' | translate
|
||||
}}</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
@ -0,0 +1,102 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AddRedactionRequest, TypeValue } from '@redaction/red-ui-http';
|
||||
import { NotificationService } from '../../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
|
||||
import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-manual-annotation-dialog',
|
||||
templateUrl: './manual-annotation-dialog.component.html',
|
||||
styleUrls: ['./manual-annotation-dialog.component.scss']
|
||||
})
|
||||
export class ManualAnnotationDialogComponent implements OnInit {
|
||||
dictionaryOptions: TypeValue[] = [];
|
||||
redactionForm: FormGroup;
|
||||
isDocumentAdmin: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
public dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.isDocumentAdmin = this._appStateService.isActiveProjectOwner;
|
||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||
this.redactionForm = this._formBuilder.group({
|
||||
reason:
|
||||
this.manualRedactionEntryWrapper.type === 'DICTIONARY'
|
||||
? null
|
||||
: [null, Validators.required],
|
||||
dictionary: [null, Validators.required],
|
||||
comment: commentField
|
||||
});
|
||||
|
||||
for (const key of Object.keys(this._appStateService.dictionaryData)) {
|
||||
const dictionaryData = this._appStateService.dictionaryData[key];
|
||||
if (!dictionaryData.virtual) {
|
||||
if (this.manualRedactionEntryWrapper.type === 'DICTIONARY') {
|
||||
this.dictionaryOptions.push(dictionaryData);
|
||||
}
|
||||
if (!dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'REDACTION') {
|
||||
this.dictionaryOptions.push(dictionaryData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleAddRedaction() {
|
||||
this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry);
|
||||
|
||||
if (this.manualRedactionEntryWrapper.type === 'DICTIONARY') {
|
||||
this._manualAnnotationService
|
||||
.makeDictionaryEntry(this.manualRedactionEntryWrapper.manualRedactionEntry)
|
||||
.subscribe(
|
||||
(response) => {
|
||||
this.dialogRef.close(this.manualRedactionEntryWrapper);
|
||||
},
|
||||
() => {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this._manualAnnotationService
|
||||
.makeRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry)
|
||||
.subscribe(
|
||||
(response) => {
|
||||
this.dialogRef.close(this.manualRedactionEntryWrapper);
|
||||
},
|
||||
() => {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this._manualAnnotationService.getTitle(this.manualRedactionEntryWrapper.type);
|
||||
}
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
|
||||
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
|
||||
addRedactionRequest.addToDictionary =
|
||||
this.manualRedactionEntryWrapper.type === 'DICTIONARY';
|
||||
addRedactionRequest.reason = this.redactionForm.get('reason').value;
|
||||
// todo fix this in backend
|
||||
if (!addRedactionRequest.reason) {
|
||||
addRedactionRequest.reason = 'ADD_TO_DICTIONARY_REQUEST';
|
||||
}
|
||||
const commentValue = this.redactionForm.get('comment').value;
|
||||
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
|
||||
}
|
||||
}
|
||||
@ -1,161 +0,0 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import {
|
||||
AddRedactionRequest,
|
||||
DictionaryControllerService,
|
||||
ManualRedactionControllerService,
|
||||
TypeValue
|
||||
} from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-manual-redaction-dialog',
|
||||
templateUrl: './manual-redaction-dialog.component.html',
|
||||
styleUrls: ['./manual-redaction-dialog.component.scss']
|
||||
})
|
||||
export class ManualRedactionDialogComponent implements OnInit {
|
||||
dictionaryOptions: TypeValue[] = [];
|
||||
redactionForm: FormGroup;
|
||||
isDocumentAdmin: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
public dialogRef: MatDialogRef<ManualRedactionDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.isDocumentAdmin = this._appStateService.isActiveProjectOwner;
|
||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||
this.redactionForm = this._formBuilder.group({
|
||||
addToDictionary: [false],
|
||||
reason: [null, Validators.required],
|
||||
dictionary: [null, Validators.required],
|
||||
comment: commentField
|
||||
});
|
||||
|
||||
for (const key of Object.keys(this._appStateService.dictionaryData)) {
|
||||
const dictionaryData = this._appStateService.dictionaryData[key];
|
||||
if (!dictionaryData.virtual) {
|
||||
if (dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'HINT') {
|
||||
this.dictionaryOptions.push(dictionaryData);
|
||||
}
|
||||
if (!dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'REDACTION') {
|
||||
this.dictionaryOptions.push(dictionaryData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleAddRedaction() {
|
||||
if (this.isDocumentAdmin) {
|
||||
this.addManualRedaction();
|
||||
} else {
|
||||
this.suggestManualRedaction();
|
||||
}
|
||||
}
|
||||
|
||||
suggestManualRedaction() {
|
||||
const mre = Object.assign({}, this.manualRedactionEntryWrapper.manualRedactionEntry);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService
|
||||
.requestAddRedaction(
|
||||
mre,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.subscribe(
|
||||
(ok) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
this.dialogRef.close({ mode: 'suggestion', request: mre });
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
addManualRedaction() {
|
||||
const mre = Object.assign({}, this.manualRedactionEntryWrapper.manualRedactionEntry);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService
|
||||
.addRedaction(
|
||||
mre,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.subscribe(
|
||||
(ok) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
this.dialogRef.close({ mode: 'redaction', request: mre });
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
if (this.isDocumentAdmin) {
|
||||
if (this.manualRedactionEntryWrapper.type === 'HINT') {
|
||||
return 'manual-redaction.dialog.header.hint.label';
|
||||
} else {
|
||||
return 'manual-redaction.dialog.header.redaction.label';
|
||||
}
|
||||
} else {
|
||||
if (this.manualRedactionEntryWrapper.type === 'HINT') {
|
||||
return 'manual-redaction.dialog.header.request-hint.label';
|
||||
} else {
|
||||
return 'manual-redaction.dialog.header.request-redaction.label';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
|
||||
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
|
||||
addRedactionRequest.addToDictionary =
|
||||
this.manualRedactionEntryWrapper.type === 'HINT'
|
||||
? true
|
||||
: this.redactionForm.get('addToDictionary').value;
|
||||
addRedactionRequest.reason = this.redactionForm.get('reason').value;
|
||||
const commentValue = this.redactionForm.get('comment').value;
|
||||
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
|
||||
}
|
||||
}
|
||||
@ -267,14 +267,13 @@
|
||||
>
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="acceptSuggestionAnnotation($event, annotation)"
|
||||
class="confirm"
|
||||
(click)="acceptSuggestion($event, annotation)"
|
||||
>
|
||||
<mat-icon svgIcon="red:check-alt"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="suggestRemoveAnnotation($event, annotation)"
|
||||
(click)="rejectSuggestion($event, annotation)"
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
|
||||
@ -26,6 +26,7 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wrapper';
|
||||
import { hexToRgb } from '../../../utils/functions';
|
||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||
import { ManualAnnotationService } from '../service/manual-annotation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -60,6 +61,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _router: Router,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _filtersService: FiltersService,
|
||||
@ -193,11 +195,12 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogRef = this._dialogService.openManualRedactionDialog(
|
||||
$event,
|
||||
(response: any) => {
|
||||
const manualRedactionEntry: ManualRedactionEntry = response.request;
|
||||
(response: ManualRedactionEntryWrapper) => {
|
||||
const manualRedactionEntry: ManualRedactionEntry =
|
||||
response.manualRedactionEntry;
|
||||
|
||||
const annotManager = this.activeViewer.annotManager;
|
||||
const originalQuads = manualRedactionEntry.quads;
|
||||
const originalQuads = $event.quads;
|
||||
for (const key of Object.keys(originalQuads)) {
|
||||
const pageNumber = parseInt(key, 10);
|
||||
const highlight = new this.activeViewer.Annotations.TextHighlightAnnotation();
|
||||
@ -205,7 +208,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
highlight.StrokeColor = this._getColor(manualRedactionEntry);
|
||||
highlight.setContents(manualRedactionEntry.reason);
|
||||
highlight.Quads = originalQuads[key];
|
||||
highlight.Id = this._computeId($event.type, manualRedactionEntry);
|
||||
highlight.Id = this._computeId(response.type, manualRedactionEntry);
|
||||
annotManager.addAnnotation(highlight, true);
|
||||
annotManager.redrawAnnotation(highlight);
|
||||
}
|
||||
@ -259,25 +262,15 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
|
||||
public acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogRef = this._dialogService.acceptSuggestionAnnotation(
|
||||
$event,
|
||||
annotation,
|
||||
this.projectId,
|
||||
this.fileId
|
||||
);
|
||||
this._dialogRef = this._dialogService.acceptSuggestion($event, annotation);
|
||||
});
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
|
||||
public rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogRef = this._dialogService.suggestRemoveAnnotation(
|
||||
$event,
|
||||
annotation,
|
||||
this.projectId,
|
||||
this.fileId
|
||||
);
|
||||
this._dialogRef = this._dialogService.rejectSuggestion($event, annotation);
|
||||
});
|
||||
}
|
||||
|
||||
@ -477,9 +470,21 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
private _computeId(type: 'HINT' | 'REDACTION', request: ManualRedactionEntry) {
|
||||
const prefix = this.appStateService.isActiveProjectOwner ? type.toLowerCase() : 'request';
|
||||
return prefix + ':' + request.type + ':' + new Date().getTime();
|
||||
private _computeId(type: 'DICTIONARY' | 'REDACTION', request: ManualRedactionEntry) {
|
||||
// if owner or not set the request prefix in the id
|
||||
const prefix = this.appStateService.isActiveProjectOwner ? '' : 'request:add:';
|
||||
|
||||
if (prefix.length > 0) {
|
||||
return prefix + request.type + ':' + new Date().getTime();
|
||||
} else {
|
||||
const dictionaryType: 'hint' | 'redaction' =
|
||||
type === 'REDACTION'
|
||||
? 'redaction'
|
||||
: this.appStateService.getDictionaryTypeValue(request.type).hint
|
||||
? 'hint'
|
||||
: 'redaction';
|
||||
return dictionaryType + ':' + request.type + ':' + new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
private _getColor(manualRedactionEntry: ManualRedactionEntry) {
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { ManualRedactionEntry } from '@redaction/red-ui-http';
|
||||
|
||||
export class ManualRedactionEntryWrapper {
|
||||
public mode: 'REQUEST' | 'ACTUAL';
|
||||
|
||||
constructor(
|
||||
public readonly quads: any,
|
||||
public readonly manualRedactionEntry: ManualRedactionEntry,
|
||||
public readonly type: 'HINT' | 'REDACTION'
|
||||
public readonly type: 'DICTIONARY' | 'REDACTION'
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -12,7 +12,12 @@ import {
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { FileStatus, ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
|
||||
import {
|
||||
FileStatus,
|
||||
ManualRedactionEntry,
|
||||
ManualRedactions,
|
||||
Rectangle
|
||||
} from '@redaction/red-ui-http';
|
||||
import WebViewer, { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { FileDownloadService } from '../service/file-download.service';
|
||||
@ -22,6 +27,7 @@ import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wra
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||
import { AnnotationUtils } from '../../../utils/annotation-utils';
|
||||
import { ManualAnnotationService } from '../service/manual-annotation.service';
|
||||
|
||||
export interface ViewerState {
|
||||
displayMode?: any;
|
||||
@ -58,12 +64,14 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
@ViewChild('viewer', { static: true }) viewer: ElementRef;
|
||||
instance: WebViewerInstance;
|
||||
private _manualAnnotations: ManualRedactions;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _fileDownloadService: FileDownloadService,
|
||||
private readonly _appConfigService: AppConfigService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _ngZone: NgZone
|
||||
) {}
|
||||
|
||||
@ -87,6 +95,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
200
|
||||
);
|
||||
});
|
||||
this._manualAnnotationService.getManualAnnotations().subscribe((manualAnnotation) => {
|
||||
this._manualAnnotations = manualAnnotation;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
@ -195,14 +206,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
if (this._appStateService.isActiveFileDocumentReviewer) {
|
||||
this.instance.textPopup.add(<any>{
|
||||
type: 'actionButton',
|
||||
img: '/assets/icons/general/add-hint.svg',
|
||||
img: '/assets/icons/general/add-dictionary.svg',
|
||||
title: this._translateService.instant(
|
||||
'pdf-viewer.text-popup.actions.suggestion-hint.label'
|
||||
this._manualAnnotationService.getTitle('DICTIONARY')
|
||||
),
|
||||
onClick: () => {
|
||||
const mre = this._getManualRedactionEntry();
|
||||
this.manualAnnotationRequested.emit(
|
||||
new ManualRedactionEntryWrapper(mre, 'HINT')
|
||||
new ManualRedactionEntryWrapper(
|
||||
this.instance.docViewer.getSelectedTextQuads(),
|
||||
mre,
|
||||
'DICTIONARY'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -210,12 +225,16 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
type: 'actionButton',
|
||||
img: '/assets/icons/general/add-redaction.svg',
|
||||
title: this._translateService.instant(
|
||||
'pdf-viewer.text-popup.actions.suggestion-redaction.label'
|
||||
this._manualAnnotationService.getTitle('REDACTION')
|
||||
),
|
||||
onClick: () => {
|
||||
const mre = this._getManualRedactionEntry();
|
||||
this.manualAnnotationRequested.emit(
|
||||
new ManualRedactionEntryWrapper(mre, 'REDACTION')
|
||||
new ManualRedactionEntryWrapper(
|
||||
this.instance.docViewer.getSelectedTextQuads(),
|
||||
mre,
|
||||
'REDACTION'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -231,7 +250,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
entry.positions.push(this.toPosition(parseInt(key, 10), quad));
|
||||
}
|
||||
}
|
||||
entry.quads = selectedQuads;
|
||||
entry.value = text;
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -0,0 +1,185 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import {
|
||||
DictionaryControllerService,
|
||||
ManualRedactionControllerService,
|
||||
ManualRedactionEntry
|
||||
} from '@redaction/red-ui-http';
|
||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||
import { NotificationService, NotificationType } from '../../../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ManualAnnotationService {
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
) {}
|
||||
|
||||
public makeDictionaryEntry(manualRedactionEntry: ManualRedactionEntry) {
|
||||
if (this._appStateService.isActiveProjectOwner) {
|
||||
return this._makeDictionaryEntry(manualRedactionEntry);
|
||||
} else {
|
||||
return this._makeDictionaryRequest(manualRedactionEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
|
||||
if (this._appStateService.isActiveProjectOwner) {
|
||||
return this._makeRedaction(manualRedactionEntry);
|
||||
} else {
|
||||
return this._makeRedactionRequest(manualRedactionEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public acceptSuggestion(annotationWrapper: AnnotationWrapper) {
|
||||
// for only here - approve the request
|
||||
return this._manualRedactionControllerService
|
||||
.approveRequest(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFile.fileId,
|
||||
annotationWrapper.uuid
|
||||
)
|
||||
.pipe(
|
||||
tap(
|
||||
() => {
|
||||
// if approved - and not only here, make a dictionary entry
|
||||
if (annotationWrapper.id.indexOf(':only_here:') < 0) {
|
||||
this.getManualAnnotations().subscribe((annotations) => {
|
||||
annotations.entriesToAdd.forEach((a) => {
|
||||
// found the annotation
|
||||
if (annotationWrapper.id.indexOf(a.id) >= 0) {
|
||||
console.log(a);
|
||||
this._makeDictionaryEntry(a).subscribe(() => {});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this._notify('manual-annotation.reject-request.success');
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this._notify('manual-annotation.reject-request.error');
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public rejectSuggestion(annotationWrapper: AnnotationWrapper) {
|
||||
return this._manualRedactionControllerService
|
||||
.undo(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFile.fileId,
|
||||
annotationWrapper.uuid
|
||||
)
|
||||
.pipe(
|
||||
tap(
|
||||
() => {
|
||||
this._notify('manual-annotation.reject-request.success');
|
||||
},
|
||||
() => {
|
||||
this._notify('manual-annotation.reject-request.error');
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public removeRedaction(annotationWrapper: AnnotationWrapper) {}
|
||||
|
||||
private _makeDictionaryRequest(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._makeRedactionRequest(manualRedactionEntry);
|
||||
}
|
||||
|
||||
private _makeDictionaryEntry(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._dictionaryControllerService
|
||||
.addEntry([manualRedactionEntry.value], manualRedactionEntry.type)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.dictionary-add.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.dictionary-add.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _makeRedactionRequest(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._manualRedactionControllerService
|
||||
.requestAddRedaction(
|
||||
manualRedactionEntry,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.redaction-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.redaction-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._manualRedactionControllerService
|
||||
.addRedaction(
|
||||
manualRedactionEntry,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.redaction-add.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.redaction-add.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _notify(key: string, type: NotificationType = NotificationType.SUCCESS) {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(key),
|
||||
null,
|
||||
type
|
||||
);
|
||||
}
|
||||
|
||||
getTitle(type: 'DICTIONARY' | 'REDACTION') {
|
||||
if (this._appStateService.isActiveProjectOwner) {
|
||||
if (type === 'DICTIONARY') {
|
||||
return 'manual-redaction.dialog.header.dictionary.label';
|
||||
} else {
|
||||
return 'manual-redaction.dialog.header.redaction.label';
|
||||
}
|
||||
} else {
|
||||
if (type === 'DICTIONARY') {
|
||||
return 'manual-redaction.dialog.header.request-dictionary.label';
|
||||
} else {
|
||||
return 'manual-redaction.dialog.header.request-redaction.label';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getManualAnnotations() {
|
||||
return this._manualRedactionControllerService.getManualRedaction(
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -35,14 +35,14 @@
|
||||
},
|
||||
"dialog": {
|
||||
"header": {
|
||||
"hint": {
|
||||
"label": "Add Hint"
|
||||
"dictionary": {
|
||||
"label": "Add to dictionary"
|
||||
},
|
||||
"redaction": {
|
||||
"label": "Add Redaction"
|
||||
},
|
||||
"request-hint": {
|
||||
"label": "Request Hint"
|
||||
"request-dictionary": {
|
||||
"label": "Request add to dictionary"
|
||||
},
|
||||
"request-redaction": {
|
||||
"label": "Request Redaction"
|
||||
|
||||
68
apps/red-ui/src/assets/icons/general/add-dictionary.svg
Normal file
68
apps/red-ui/src/assets/icons/general/add-dictionary.svg
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<path d="M217.528,96h-14.4L183.44,32h-27.824l-19.688,64h-14.4v16h9.48l-9.128,29.648l15.288,4.704L147.744,112h43.568
|
||||
l10.568,34.352l15.288-4.704L208.048,112h9.48V96z M152.664,96l14.776-48h4.176l14.768,48H152.664z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<path d="M294.672,88c6.656-5.864,10.856-14.456,10.856-24c0-17.648-14.352-32-32-32h-32v112h32c17.648,0,32-14.352,32-32
|
||||
C305.528,102.456,301.328,93.864,294.672,88z M273.528,128h-16V96h16c8.824,0,16,7.176,16,16S282.352,128,273.528,128z
|
||||
M273.528,80h-16V48h16c8.824,0,16,7.176,16,16S282.352,80,273.528,80z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<path d="M377.528,48V32c-30.88,0-56,25.12-56,56s25.12,56,56,56v-16c-22.056,0-40-17.944-40-40
|
||||
C337.528,65.944,355.472,48,377.528,48z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<path d="M97.528,160v144h304V160H97.528z M385.528,288h-272V176h272V288z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="105.528" y="320" width="288" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="105.528" y="352" width="288" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="377.528" y="384" width="16" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="105.528" y="384" width="256" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<path d="M494.472,32h-60.944V0h-392c-22.056,0-40,17.944-40,40v416c0,22.056,17.944,40,40,40h368h24v-16h-24
|
||||
c-13.232,0-24-10.768-24-24s10.768-24,24-24h16h8V112h60.944l-20-40L494.472,32z M377.736,480H41.528c-13.232,0-24-10.768-24-24
|
||||
s10.768-24,24-24h336.208c-5.08,6.704-8.208,14.96-8.208,24C369.528,465.04,372.656,473.296,377.736,480z M417.528,416h-8h-328
|
||||
v-32h-16v32h-24c-9,0-17.312,2.992-24,8.016V40c0-13.232,10.768-24,24-24h24v352h16V16h336V416z M468.584,96h-35.056V48h35.056
|
||||
l-12,24L468.584,96z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="65.528" y="448" width="288" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="rgb(136,142,149)">
|
||||
<g>
|
||||
<rect x="33.528" y="448" width="16" height="16"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -9,22 +9,19 @@
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
import { Comment } from './comment';
|
||||
import { Rectangle } from './rectangle';
|
||||
|
||||
export interface ManualRedactionEntry {
|
||||
addToDictionary?: boolean;
|
||||
comments?: Array<Comment>;
|
||||
id?: string;
|
||||
legalBasis?: string;
|
||||
positions?: Array<Rectangle>;
|
||||
reason?: string;
|
||||
status?: ManualRedactionEntry.StatusEnum;
|
||||
type?: string;
|
||||
user?: string;
|
||||
value?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export namespace ManualRedactionEntry {
|
||||
export type StatusEnum = 'REQUESTED' | 'APPROVED' | 'DECLINED';
|
||||
export const StatusEnum = {
|
||||
|
||||
@ -9,10 +9,12 @@
|
||||
* https://github.com/swagger-api/swagger-codegen.git
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
import { Comment } from './comment';
|
||||
import { IdRemoval } from './idRemoval';
|
||||
import { ManualRedactionEntry } from './manualRedactionEntry';
|
||||
|
||||
export interface ManualRedactions {
|
||||
comments?: { [key: string]: Array<Comment> };
|
||||
entriesToAdd?: Array<ManualRedactionEntry>;
|
||||
idsToRemove?: Array<IdRemoval>;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user