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 23a115d14..a5e7058b4 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 @@ -16,7 +16,7 @@ import { import { CommentsApiService } from '@services/comments-api.service'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { PermissionsService } from '@services/permissions.service'; -import { firstValueFrom, Observable, zip } from 'rxjs'; +import { firstValueFrom, Observable } from 'rxjs'; import { getFirstRelevantTextPart } from '../../../utils'; import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service'; import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service'; @@ -32,6 +32,7 @@ import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dial import { RemoveRedactionOptions } from '../utils/dialog-options'; import { EditRedactionData, + EditRedactResult, RemoveRedactionData, RemoveRedactionPermissions, RemoveRedactionResult, @@ -101,47 +102,34 @@ export class AnnotationActionsService { }; const result = await this.#getEditRedactionDialog(data).result(); - - const requests: Observable[] = []; - if (!result) { return; } - if ( - !this.#isDocumine && - (!annotations.every(annotation => annotation.legalBasis === result.legalBasis) || - !annotations.every(annotation => annotation.section === result.section)) - ) { - const changeLegalBasisBody = annotations.map(annotation => ({ - annotationId: annotation.id, - legalBasis: result.legalBasis, - section: result.section ?? annotation.section, - value: result.value ?? annotation.value, - })); - requests.push( - this._manualRedactionService.changeLegalBasis( - changeLegalBasisBody, - dossierId, - fileId, - file().excludedFromAutomaticAnalysis && isUnprocessed, - ), - ); - } - if (result.type && !annotations.every(annotation => annotation.type === result.type)) { - const recategorizeBody: List = annotations.map(annotation => ({ - annotationId: annotation.id, - type: result.type ?? annotation.type, - })); - requests.push( - this._manualRedactionService.recategorizeRedactions( + const recategorizeBody: List = annotations.map(annotation => { + const body = { annotationId: annotation.id, type: result.type ?? annotation.type }; + if (!this.#isDocumine) { + return { + ...body, + legalBasis: result.legalBasis, + section: result.section ?? annotation.section, + value: result.value ?? annotation.value, + }; + } + return body; + }); + + await this.#processObsAndEmit( + this._manualRedactionService + .recategorizeRedactions( recategorizeBody, dossierId, fileId, + this.#getChangedFields(annotations, result), file().excludedFromAutomaticAnalysis && isUnprocessed, - ), - ); - } + ) + .pipe(log()), + ); if (result.comment) { try { @@ -152,11 +140,6 @@ export class AnnotationActionsService { this._toaster.rawError(error.error.message); } } - - if (!requests.length) { - return; - } - await this.#processObsAndEmit(zip(requests).pipe(log())); } async removeRedaction(redactions: AnnotationWrapper[], permissions: AnnotationPermissions) { @@ -498,4 +481,23 @@ export class AnnotationActionsService { isApprover, }; } + + #getChangedFields(annotations: AnnotationWrapper[], result: EditRedactResult) { + const changedFields = []; + if (result.type && !annotations.every(annotation => annotation.type === result.type)) { + changedFields.push('type'); + } + + if (this.#isDocumine) { + return { changes: changedFields.join(', ') }; + } + + if (result.legalBasis && !annotations.every(annotation => annotation.legalBasis === result.legalBasis)) { + changedFields.push('reason'); + } + if (typeof result.section === 'string' && !annotations.every(annotation => annotation.section === result.section)) { + changedFields.push('paragraph/location'); + } + return { changes: changedFields.join(', ') }; + } } diff --git a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index e8c7799b1..a86a57ef7 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -18,7 +18,7 @@ import type { import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations'; import { Roles } from '@users/roles'; import { NGXLogger } from 'ngx-logger'; -import { EMPTY, of, OperatorFunction, pipe } from 'rxjs'; +import { EMPTY, of, pipe } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; function getResponseType(error: boolean, isConflict: boolean) { @@ -62,12 +62,18 @@ export class ManualRedactionService extends GenericService { return this.addAnnotation(recommendations, dossierId, fileId); } - changeLegalBasis(body: List, dossierId: string, fileId: string, includeUnprocessed = false) { - return this.legalBasisChange(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-legal-basis')); - } - - recategorizeRedactions(body: List, dossierId: string, fileId: string, includeUnprocessed = false) { - return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-type')); + recategorizeRedactions( + body: List, + dossierId: string, + fileId: string, + successMessageParameters?: { + [key: string]: string; + }, + includeUnprocessed = false, + ) { + return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe( + this.#showToast('recategorize-annotation', false, successMessageParameters), + ); } addAnnotation( @@ -131,13 +137,6 @@ export class ManualRedactionService extends GenericService { ); } - legalBasisChange(body: List, dossierId: string, fileId: string, includeUnprocessed = false) { - return this._post( - body, - `${this.#bulkRedaction}/legalBasisChange/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`, - ).pipe(this.#log('Legal basis change', body)); - } - undo(annotationIds: List, dossierId: string, fileId: string) { const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`; return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds)); @@ -165,7 +164,11 @@ export class ManualRedactionService extends GenericService { }); } - #showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false) { + #showToast( + action: ManualRedactionActions | DictionaryActions, + isDictionary = false, + successMessageParameters?: { [key: string]: string }, + ) { return pipe( catchError((error: unknown) => { const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict; @@ -175,7 +178,12 @@ export class ManualRedactionService extends GenericService { }); return EMPTY; }), - tap(() => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' })), + tap(() => + this._toaster.success(getMessage(action, isDictionary), { + params: successMessageParameters, + positionClass: 'toast-file-preview', + }), + ), ); } diff --git a/apps/red-ui/src/app/translations/annotation-actions-translations.ts b/apps/red-ui/src/app/translations/annotation-actions-translations.ts index fe1b0f8b6..b08871dea 100644 --- a/apps/red-ui/src/app/translations/annotation-actions-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-actions-translations.ts @@ -28,10 +28,6 @@ export const manualRedactionActionsTranslations: Record{type}", + "label": "No longer annotate as ''{type}''", "label-bulk": "" }, "only-here": { - "description": "Do not annotate the term at this position in the current document.", + "description": "Do not annotate ''{value}'' at this position in the current document.", "description-bulk": "Do not auto-annotate the selected terms at this position in the current document.", "label": "Remove here" } @@ -2046,8 +2042,8 @@ "comment-placeholder": "Add remarks or mentions...", "options": { "do-not-recommend": { - "description": "Do not recommend the selected term in any document of this dossier.", - "description-bulk": "Do not recommend the selected terms in any document of this dossier.", + "description": "Do not recommend the selected term in any document of the current dossier.", + "description-bulk": "Do not recommend the selected terms in any document of the current dossier.", "extraOptionLabel": "Apply to all active and future dossiers", "label": "Remove from dossier" }, @@ -2059,7 +2055,7 @@ "label": "Remove from dossier in this context" }, "in-dossier": { - "description": "Do not {type, select, hint{annotate} other{redact}} the selected term in any document of this dossier.", + "description": "Do not {type, select, hint{annotate} other{redact}} the selected term in any document of the current dossier.", "description-bulk": "Do not auto-redact the selected term in this dossier.", "extraOptionLabel": "Apply to all active and future dossiers", "label": "Remove from dossier", diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json index 3dbebf3fd..a698f9b92 100644 --- a/apps/red-ui/src/assets/i18n/scm/de.json +++ b/apps/red-ui/src/assets/i18n/scm/de.json @@ -288,14 +288,6 @@ "error": "Fehler beim Speichern der Schwärzung: {error}", "success": "Schwärzung hinzugefügt!" }, - "change-legal-basis": { - "error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}", - "success": "In der Anmerkung genannte Begründung wurde bearbeitet." - }, - "change-type": { - "error": "", - "success": "" - }, "force-hint": { "error": "Failed to save hint: {error}", "success": "Hint added!" @@ -304,6 +296,10 @@ "error": "Die Schwärzung konnte nicht gespeichert werden!", "success": "Schwärzung eingefügt!" }, + "recategorize-annotation": { + "error": "", + "success": "" + }, "recategorize-image": { "error": "Rekategorisierung des Bildes gescheitert: {error}", "success": "Bild wurde einer neuen Kategorie zugeordnet." diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json index e9c929495..514016196 100644 --- a/apps/red-ui/src/assets/i18n/scm/en.json +++ b/apps/red-ui/src/assets/i18n/scm/en.json @@ -288,14 +288,6 @@ "error": "Failed to save annotation: {error}", "success": "Annotation added!" }, - "change-legal-basis": { - "error": "Failed to edit annotation reason: {error}", - "success": "Annotation reason was edited." - }, - "change-type": { - "error": "", - "success": "" - }, "force-hint": { "error": "Failed to save hint: {error}", "success": "Hint added!" @@ -304,6 +296,10 @@ "error": "Failed to save annotation: {error}", "success": "Annotation added!" }, + "recategorize-annotation": { + "error": "", + "success": "" + }, "recategorize-image": { "error": "Failed to recategorize image: {error}", "success": "Image recategorized." diff --git a/libs/red-domain/src/lib/annotations/types.ts b/libs/red-domain/src/lib/annotations/types.ts index b9c5f79b0..97355218a 100644 --- a/libs/red-domain/src/lib/annotations/types.ts +++ b/libs/red-domain/src/lib/annotations/types.ts @@ -6,12 +6,11 @@ export type ManualRedactionActions = | 'add' | 'remove' | 'remove-hint' - | 'change-legal-basis' | 'recategorize-image' | 'undo' | 'force-redaction' | 'force-hint' - | 'change-type'; + | 'recategorize-annotation'; export const AnnotationIconTypes = { square: 'square', diff --git a/libs/red-domain/src/lib/redaction-log/recategorization.request.ts b/libs/red-domain/src/lib/redaction-log/recategorization.request.ts index 6c374d977..5892fd7a4 100644 --- a/libs/red-domain/src/lib/redaction-log/recategorization.request.ts +++ b/libs/red-domain/src/lib/redaction-log/recategorization.request.ts @@ -2,4 +2,7 @@ export interface IRecategorizationRequest { readonly annotationId?: string; readonly comment?: string; readonly type?: string; + readonly legalBasis?: string; + readonly section?: string; + readonly value?: string; }