diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.html new file mode 100644 index 000000000..4655e7d22 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.html @@ -0,0 +1,31 @@ +
+
+
+ +
+
    +
  • +
+
+ + +
+
+ +
+ + +
+
+
+ + +
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.ts new file mode 100644 index 000000000..d25da3c8e --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/false-positive-dialog/false-positive-dialog.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { BaseDialogComponent } from '@iqser/common-ui'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +export interface FalsePositiveDialogInput { + text: string; + context: string; +} + +@Component({ + templateUrl: './false-positive-dialog.component.html', +}) +export class FalsePositiveDialogComponent extends BaseDialogComponent implements OnInit { + constructor( + protected readonly _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: FalsePositiveDialogInput[], + ) { + super(_dialogRef); + } + + ngOnInit() { + const controlsConfig = { comment: [null] }; + this.form = this._formBuilder.group(controlsConfig); + this.initialFormValue = this.form.getRawValue(); + } + + save(): void { + this._dialogRef.close(this.form.getRawValue()); + } +} diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts index 91386b77f..922f8700c 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts @@ -64,6 +64,7 @@ import { SuggestionsService } from './services/suggestions.service'; import { PagesComponent } from './components/pages/pages.component'; import { SharedModule } from '@shared/shared.module'; import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; +import { FalsePositiveDialogComponent } from './dialogs/false-positive-dialog/false-positive-dialog.component'; const routes: IqserRoutes = [ { @@ -87,6 +88,7 @@ const dialogs = [ DocumentInfoDialogComponent, ImportRedactionsDialogComponent, RssDialogComponent, + FalsePositiveDialogComponent, ]; const components = [ diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts index 1d4c5b12a..fcefcf3ce 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ManualRedactionService } from './manual-redaction.service'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { Observable } from 'rxjs'; +import { firstValueFrom, Observable } from 'rxjs'; import { getFirstRelevantTextPart } from '../../../utils'; import { Core } from '@pdftron/webviewer'; import { @@ -50,7 +50,7 @@ export class AnnotationActionsService { const { dossierId, fileId } = this._state; const ids = annotations.map(a => a.id); const request = this._manualRedactionService.approve(ids, dossierId, fileId); - this.#processObsAndEmit(request); + this.#processObsAndEmit(request).then(); } removeHighlights(highlights: AnnotationWrapper[]): void { @@ -72,7 +72,7 @@ export class AnnotationActionsService { fileId, annotations[0].isModifyDictionary, ), - ); + ).then(); } forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) { @@ -86,7 +86,7 @@ export class AnnotationActionsService { fileId, annotations[0].isIgnoredHint, ), - ); + ).then(); }); } @@ -104,7 +104,7 @@ export class AnnotationActionsService { value: data.value, })); - this.#processObsAndEmit(this._manualRedactionService.changeLegalBasis(body, dossierId, fileId)); + this.#processObsAndEmit(this._manualRedactionService.changeLegalBasis(body, dossierId, fileId)).then(); }, ); } @@ -125,7 +125,7 @@ export class AnnotationActionsService { })); this.#processObsAndEmit( this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, removeFromDictionary, annotations[0].isHint), - ); + ).then(); }); } @@ -138,7 +138,7 @@ export class AnnotationActionsService { type, comment, })); - this.#processObsAndEmit(this._manualRedactionService.recategorizeImage(body, dossierId, fileId)); + this.#processObsAndEmit(this._manualRedactionService.recategorizeImage(body, dossierId, fileId)).then(); }); } @@ -152,10 +152,10 @@ export class AnnotationActionsService { fileId, modifyDictionary, ), - ); + ).then(); } - convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) { + async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; const dialogRef = this._dialog.open( AcceptRecommendationDialogComponent, @@ -163,7 +163,7 @@ export class AnnotationActionsService { ); // TODO: remove observables const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations)); - dialogClosed.subscribe(({ annotations, comment: commentText }) => { + await firstValueFrom(dialogClosed).then(({ annotations, comment: commentText }) => { if (isJustOne(annotations) && this._annotationManager.resizingAnnotationId === annotations[0].id) { this.cancelResize(annotations[0]).then(); } @@ -231,20 +231,24 @@ export class AnnotationActionsService { } markAsFalsePositive(annotations: AnnotationWrapper[]) { - const requests: List = annotations.map(annotation => ({ - sourceId: annotation.id, - value: this._getFalsePositiveText(annotation), - type: annotation.type, - positions: annotation.positions, - addToDictionary: true, - reason: 'False Positive', - dictionaryEntryType: annotation.isRecommendation - ? DictionaryEntryTypes.FALSE_RECOMMENDATION - : DictionaryEntryTypes.FALSE_POSITIVE, - })); - const { dossierId, fileId } = this._state; + const data = annotations.map(annotation => ({ text: annotation.value, context: this._getFalsePositiveText(annotation) })); + this._dialogService.openDialog('falsePositive', data, (result: { comment: string }) => { + const requests: List = annotations.map(annotation => ({ + sourceId: annotation.id, + value: this._getFalsePositiveText(annotation), + type: annotation.type, + positions: annotation.positions, + addToDictionary: true, + reason: 'False Positive', + dictionaryEntryType: annotation.isRecommendation + ? DictionaryEntryTypes.FALSE_RECOMMENDATION + : DictionaryEntryTypes.FALSE_POSITIVE, + comment: result.comment ? { text: result.comment } : null, + })); + const { dossierId, fileId } = this._state; - this.#processObsAndEmit(this._manualRedactionService.addAnnotation(requests, dossierId, fileId)); + this.#processObsAndEmit(this._manualRedactionService.addAnnotation(requests, dossierId, fileId)).then(); + }); } #generateRectangle(annotationWrapper: AnnotationWrapper) { @@ -279,12 +283,15 @@ export class AnnotationActionsService { }; } - #processObsAndEmit(obs: Observable) { + async #processObsAndEmit(obs: Observable) { // TODO: remove observables and use promises instead - obs.subscribe({ - next: () => this._fileDataService.annotationsChanged(), - error: () => this._fileDataService.annotationsChanged(), - }); + await firstValueFrom(obs) + .then(() => this._fileDataService.annotationsChanged()) + .catch(() => this._fileDataService.annotationsChanged()); + // obs.subscribe({ + // next: () => this._fileDataService.annotationsChanged(), + // error: () => this._fileDataService.annotationsChanged(), + // }); } private _getFalsePositiveText(annotation: AnnotationWrapper) { diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-preview-dialog.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-preview-dialog.service.ts index 49a7df222..e8abdb76b 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/file-preview-dialog.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/file-preview-dialog.service.ts @@ -10,6 +10,7 @@ import { ConfirmationDialogComponent, DialogConfig, DialogService } from '@iqser import { ResizeAnnotationDialogComponent } from '../dialogs/resize-annotation-dialog/resize-annotation-dialog.component'; import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component'; import { RssDialogComponent } from '../dialogs/rss-dialog/rss-dialog.component'; +import { FalsePositiveDialogComponent } from '../dialogs/false-positive-dialog/false-positive-dialog.component'; type DialogType = | 'confirm' @@ -21,7 +22,8 @@ type DialogType = | 'resizeAnnotation' | 'forceAnnotation' | 'manualAnnotation' - | 'highlightAction'; + | 'highlightAction' + | 'falsePositive'; @Injectable() export class FilePreviewDialogService extends DialogService { @@ -60,6 +62,9 @@ export class FilePreviewDialogService extends DialogService { component: RssDialogComponent, dialogConfig: { width: '90vw' }, }, + falsePositive: { + component: FalsePositiveDialogComponent, + }, }; constructor(protected readonly _dialog: MatDialog) { diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json index f282d792a..f3bb93680 100644 --- a/apps/red-ui/src/assets/i18n/redact/de.json +++ b/apps/red-ui/src/assets/i18n/redact/de.json @@ -1994,6 +1994,17 @@ "annotations": "", "title": "" }, + "false-positive-dialog": { + "actions": { + "cancel": "", + "save": "" + }, + "content": { + "comment": "", + "body-text": "" + }, + "header": "" + }, "rules-screen": { "error": { "generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!" diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json index 39db74751..8ca3dd376 100644 --- a/apps/red-ui/src/assets/i18n/redact/en.json +++ b/apps/red-ui/src/assets/i18n/redact/en.json @@ -1994,6 +1994,17 @@ "annotations": "", "title": "Structured Component Management" }, + "false-positive-dialog": { + "actions": { + "cancel": "Cancel", + "save": "Yes, proceed" + }, + "content": { + "comment": "Comment", + "body-text": "''{value}'' is a false positive in this context: {context}" + }, + "header": "False Positive" + }, "rules-screen": { "error": { "generic": "Something went wrong... Rules update failed!"