RED-6506: Added confirmation dialog for false positive action.

This commit is contained in:
Nicoleta Panaghiu 2023-04-03 15:23:11 +03:00
parent a5b7b76d46
commit cd2f51c30e
7 changed files with 126 additions and 29 deletions

View File

@ -0,0 +1,31 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-header heading-l" translate="false-positive-dialog.header"></div>
<div class="dialog-content">
<ul>
<li
*ngFor="let value of data"
[innerHTML]="'false-positive-dialog.content.body-text' | translate : { value: value.text, context: value.context }"
></li>
</ul>
<div class="iqser-input-group w-300">
<label translate="false-positive-dialog.content.comment"></label>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
</div>
<div class="dialog-actions">
<iqser-icon-button
[disabled]="!form.valid"
[label]="'false-positive-dialog.actions.save' | translate"
[submit]="true"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
<div class="all-caps-label cancel" mat-dialog-close translate="false-positive-dialog.actions.cancel"></div>
</div>
</form>
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
</section>

View File

@ -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<FalsePositiveDialogComponent>,
@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());
}
}

View File

@ -64,6 +64,7 @@ import { SuggestionsService } from './services/suggestions.service';
import { PagesComponent } from './components/pages/pages.component'; import { PagesComponent } from './components/pages/pages.component';
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module';
import { FalsePositiveDialogComponent } from './dialogs/false-positive-dialog/false-positive-dialog.component';
const routes: IqserRoutes = [ const routes: IqserRoutes = [
{ {
@ -87,6 +88,7 @@ const dialogs = [
DocumentInfoDialogComponent, DocumentInfoDialogComponent,
ImportRedactionsDialogComponent, ImportRedactionsDialogComponent,
RssDialogComponent, RssDialogComponent,
FalsePositiveDialogComponent,
]; ];
const components = [ const components = [

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ManualRedactionService } from './manual-redaction.service'; import { ManualRedactionService } from './manual-redaction.service';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Observable } from 'rxjs'; import { firstValueFrom, Observable } from 'rxjs';
import { getFirstRelevantTextPart } from '../../../utils'; import { getFirstRelevantTextPart } from '../../../utils';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import { import {
@ -50,7 +50,7 @@ export class AnnotationActionsService {
const { dossierId, fileId } = this._state; const { dossierId, fileId } = this._state;
const ids = annotations.map(a => a.id); const ids = annotations.map(a => a.id);
const request = this._manualRedactionService.approve(ids, dossierId, fileId); const request = this._manualRedactionService.approve(ids, dossierId, fileId);
this.#processObsAndEmit(request); this.#processObsAndEmit(request).then();
} }
removeHighlights(highlights: AnnotationWrapper[]): void { removeHighlights(highlights: AnnotationWrapper[]): void {
@ -72,7 +72,7 @@ export class AnnotationActionsService {
fileId, fileId,
annotations[0].isModifyDictionary, annotations[0].isModifyDictionary,
), ),
); ).then();
} }
forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) { forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) {
@ -86,7 +86,7 @@ export class AnnotationActionsService {
fileId, fileId,
annotations[0].isIgnoredHint, annotations[0].isIgnoredHint,
), ),
); ).then();
}); });
} }
@ -104,7 +104,7 @@ export class AnnotationActionsService {
value: data.value, 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.#processObsAndEmit(
this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, removeFromDictionary, annotations[0].isHint), this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, removeFromDictionary, annotations[0].isHint),
); ).then();
}); });
} }
@ -138,7 +138,7 @@ export class AnnotationActionsService {
type, type,
comment, 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, fileId,
modifyDictionary, modifyDictionary,
), ),
); ).then();
} }
convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) { async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) {
const { dossierId, fileId } = this._state; const { dossierId, fileId } = this._state;
const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>( const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>(
AcceptRecommendationDialogComponent, AcceptRecommendationDialogComponent,
@ -163,7 +163,7 @@ export class AnnotationActionsService {
); );
// TODO: remove observables // TODO: remove observables
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations)); 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) { if (isJustOne(annotations) && this._annotationManager.resizingAnnotationId === annotations[0].id) {
this.cancelResize(annotations[0]).then(); this.cancelResize(annotations[0]).then();
} }
@ -231,20 +231,24 @@ export class AnnotationActionsService {
} }
markAsFalsePositive(annotations: AnnotationWrapper[]) { markAsFalsePositive(annotations: AnnotationWrapper[]) {
const requests: List<IAddRedactionRequest> = annotations.map(annotation => ({ const data = annotations.map(annotation => ({ text: annotation.value, context: this._getFalsePositiveText(annotation) }));
sourceId: annotation.id, this._dialogService.openDialog('falsePositive', data, (result: { comment: string }) => {
value: this._getFalsePositiveText(annotation), const requests: List<IAddRedactionRequest> = annotations.map(annotation => ({
type: annotation.type, sourceId: annotation.id,
positions: annotation.positions, value: this._getFalsePositiveText(annotation),
addToDictionary: true, type: annotation.type,
reason: 'False Positive', positions: annotation.positions,
dictionaryEntryType: annotation.isRecommendation addToDictionary: true,
? DictionaryEntryTypes.FALSE_RECOMMENDATION reason: 'False Positive',
: DictionaryEntryTypes.FALSE_POSITIVE, dictionaryEntryType: annotation.isRecommendation
})); ? DictionaryEntryTypes.FALSE_RECOMMENDATION
const { dossierId, fileId } = this._state; : 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) { #generateRectangle(annotationWrapper: AnnotationWrapper) {
@ -279,12 +283,15 @@ export class AnnotationActionsService {
}; };
} }
#processObsAndEmit(obs: Observable<unknown>) { async #processObsAndEmit(obs: Observable<unknown>) {
// TODO: remove observables and use promises instead // TODO: remove observables and use promises instead
obs.subscribe({ await firstValueFrom(obs)
next: () => this._fileDataService.annotationsChanged(), .then(() => this._fileDataService.annotationsChanged())
error: () => this._fileDataService.annotationsChanged(), .catch(() => this._fileDataService.annotationsChanged());
}); // obs.subscribe({
// next: () => this._fileDataService.annotationsChanged(),
// error: () => this._fileDataService.annotationsChanged(),
// });
} }
private _getFalsePositiveText(annotation: AnnotationWrapper) { private _getFalsePositiveText(annotation: AnnotationWrapper) {

View File

@ -10,6 +10,7 @@ import { ConfirmationDialogComponent, DialogConfig, DialogService } from '@iqser
import { ResizeAnnotationDialogComponent } from '../dialogs/resize-annotation-dialog/resize-annotation-dialog.component'; import { ResizeAnnotationDialogComponent } from '../dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component'; import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component';
import { RssDialogComponent } from '../dialogs/rss-dialog/rss-dialog.component'; import { RssDialogComponent } from '../dialogs/rss-dialog/rss-dialog.component';
import { FalsePositiveDialogComponent } from '../dialogs/false-positive-dialog/false-positive-dialog.component';
type DialogType = type DialogType =
| 'confirm' | 'confirm'
@ -21,7 +22,8 @@ type DialogType =
| 'resizeAnnotation' | 'resizeAnnotation'
| 'forceAnnotation' | 'forceAnnotation'
| 'manualAnnotation' | 'manualAnnotation'
| 'highlightAction'; | 'highlightAction'
| 'falsePositive';
@Injectable() @Injectable()
export class FilePreviewDialogService extends DialogService<DialogType> { export class FilePreviewDialogService extends DialogService<DialogType> {
@ -60,6 +62,9 @@ export class FilePreviewDialogService extends DialogService<DialogType> {
component: RssDialogComponent, component: RssDialogComponent,
dialogConfig: { width: '90vw' }, dialogConfig: { width: '90vw' },
}, },
falsePositive: {
component: FalsePositiveDialogComponent,
},
}; };
constructor(protected readonly _dialog: MatDialog) { constructor(protected readonly _dialog: MatDialog) {

View File

@ -1994,6 +1994,17 @@
"annotations": "", "annotations": "",
"title": "" "title": ""
}, },
"false-positive-dialog": {
"actions": {
"cancel": "",
"save": ""
},
"content": {
"comment": "",
"body-text": ""
},
"header": ""
},
"rules-screen": { "rules-screen": {
"error": { "error": {
"generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!" "generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!"

View File

@ -1994,6 +1994,17 @@
"annotations": "", "annotations": "",
"title": "Structured Component Management" "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": { "rules-screen": {
"error": { "error": {
"generic": "Something went wrong... Rules update failed!" "generic": "Something went wrong... Rules update failed!"