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 { 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 = [

View File

@ -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, AcceptRecommendationData, AcceptRecommendationReturnType>(
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<IAddRedactionRequest> = 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<IAddRedactionRequest> = 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<unknown>) {
async #processObsAndEmit(obs: Observable<unknown>) {
// 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) {

View File

@ -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<DialogType> {
@ -60,6 +62,9 @@ export class FilePreviewDialogService extends DialogService<DialogType> {
component: RssDialogComponent,
dialogConfig: { width: '90vw' },
},
falsePositive: {
component: FalsePositiveDialogComponent,
},
};
constructor(protected readonly _dialog: MatDialog) {

View File

@ -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!"

View File

@ -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!"