diff --git a/apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts b/apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts index b5090ec0d..1ce97d73e 100644 --- a/apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts +++ b/apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts @@ -2,9 +2,15 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { addHintTranslations } from '@translations/add-hint-translations'; import { redactTextTranslations } from '@translations/redact-text-translations'; import { removeRedactionTranslations } from '@translations/remove-redaction-translations'; -import { ForceAnnotationOptions, RedactOrHintOptions, RemoveRedactionOptions } from '../../file-preview/utils/dialog-types'; +import { + ForceAnnotationOptions, + RectangleRedactOptions, + RedactOrHintOptions, + RemoveRedactionOptions, +} from '../../file-preview/utils/dialog-types'; export const SystemDefaults = { + RECTANGLE_REDACT_DEFAULT: RectangleRedactOptions.ONLY_THIS_PAGE, ADD_REDACTION_DEFAULT: RedactOrHintOptions.IN_DOSSIER, ADD_HINT_DEFAULT: RedactOrHintOptions.IN_DOSSIER, FORCE_REDACTION_DEFAULT: ForceAnnotationOptions.ONLY_HERE, diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/add-hint-dialog/add-hint-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/add-hint-dialog/add-hint-dialog.component.ts index 92258751a..2826b01a4 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/add-hint-dialog/add-hint-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/add-hint-dialog/add-hint-dialog.component.ts @@ -160,7 +160,7 @@ export class AddHintDialogComponent extends IqserDialogComponent): void { - this.#applyToAllDossiers = option.extraOption.checked; + this.#applyToAllDossiers = option.additionalCheck.checked; this.#setDictionaries(); if (this.#applyToAllDossiers && this.form.get('dictionary').value) { @@ -176,7 +176,7 @@ export class AddHintDialogComponent extends IqserDialogComponent d.type === selectedDictionaryType); - this.options[1].extraOption.disabled = selectedDictionary.dossierDictionaryOnly; + this.options[1].additionalCheck.disabled = selectedDictionary.dossierDictionaryOnly; } } @@ -223,7 +223,7 @@ export class AddHintDialogComponent extends IqserDialogComponent
+ + +
@@ -91,27 +94,6 @@
- -
- - {{ 'manual-annotation.dialog.content.apply-on-multiple-pages' | translate }} - - -
- - - {{ 'manual-annotation.dialog.content.apply-on-multiple-pages-hint' | translate }} -
-
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index c4630f766..4755981ef 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @@ -25,6 +25,12 @@ import { MatFormField } from '@angular/material/form-field'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select'; import { MatTooltip } from '@angular/material/tooltip'; import { MatCheckbox } from '@angular/material/checkbox'; +import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; +import { RectangleRedactOption, RectangleRedactOptions, RedactOrHintOption } from '../../utils/dialog-types'; +import { getRectangleRedactOptions } from '../../utils/dialog-options'; +import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; +import { SystemDefaults } from '../../../account/utils/dialog-defaults'; +import { validatePageRange } from '../../utils/form-validators'; export interface LegalBasisOption { label?: string; @@ -53,19 +59,21 @@ export const NON_READABLE_CONTENT = 'non-readable content'; IqserDenyDirective, MatCheckbox, IconButtonComponent, + DetailsRadioComponent, ], providers: [ManualRedactionService], }) export class ManualAnnotationDialogComponent extends BaseDialogComponent implements OnInit { readonly #dossier: Dossier; - readonly roles = Roles; - isDictionaryRequest: boolean; - isFalsePositiveRequest: boolean; - isEditingSelectedText = false; - applyOnMultiplePages = false; - manualRedactionTypeExists = true; - possibleDictionaries: Dictionary[] = []; - legalOptions: LegalBasisOption[] = []; + protected readonly roles = Roles; + protected readonly options: DetailsRadioOption[]; + protected isDictionaryRequest: boolean; + protected isFalsePositiveRequest: boolean; + protected isEditingSelectedText = false; + protected applyOnMultiplePages = false; + protected manualRedactionTypeExists = true; + protected possibleDictionaries: Dictionary[] = []; + protected legalOptions: LegalBasisOption[] = []; constructor( readonly iqserPermissionsService: IqserPermissionsService, @@ -85,10 +93,20 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme this.manualRedactionTypeExists = this._dictionaryService.hasManualType(this.#dossier.dossierTemplateId); + this.options = getRectangleRedactOptions(); + this.form = this.#getForm(); this.initialFormValue = this.form.getRawValue(); } + extraOptionChanged(option: DetailsRadioOption): void { + if (option.value === RectangleRedactOptions.MULTIPLE_PAGES) { + setTimeout(() => { + this.form.get('option')?.updateValueAndValidity(); + }, 0); + } + } + get title() { return this._manualRedactionService.getTitle(this.data.manualRedactionEntryWrapper.type); } @@ -200,6 +218,7 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme comment: [null], classification: [NON_READABLE_CONTENT], multiplePages: '', + option: [this.#getOption(SystemDefaults.RECTANGLE_REDACT_DEFAULT), validatePageRange()], }); } @@ -229,6 +248,10 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme : this.form.get('selectedText').value; } + #getOption(option: RectangleRedactOption): DetailsRadioOption { + return this.options.find(o => o.value === option); + } + #selectReason() { if (this.legalOptions.length === 1) { this.form.get('reason').setValue(this.legalOptions[0]); diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.ts index 85a109583..163b8363c 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.ts @@ -128,7 +128,7 @@ export class RedactRecommendationDialogComponent } extraOptionChanged(option: DetailsRadioOption): void { - this.#applyToAllDossiers = option.extraOption.checked; + this.#applyToAllDossiers = option.additionalCheck.checked; this.#setDictionaries(); if (this.#applyToAllDossiers && this.form.controls.dictionary.value) { @@ -144,7 +144,7 @@ export class RedactRecommendationDialogComponent if (!this.#applyToAllDossiers) { const selectedDictionaryType = this.form.controls.dictionary.value; const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryType); - this.options[0].extraOption.disabled = selectedDictionary.dossierDictionaryOnly; + this.options[0].additionalCheck.disabled = selectedDictionary.dossierDictionaryOnly; } } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.ts index 63332bb0a..6a9d46536 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.ts @@ -150,7 +150,7 @@ export class RedactTextDialogComponent } extraOptionChanged(option: DetailsRadioOption): void { - this.#applyToAllDossiers = option.extraOption.checked; + this.#applyToAllDossiers = option.additionalCheck.checked; this.#setDictionaries(); if (this.#applyToAllDossiers && this.form.controls.dictionary.value) { @@ -166,7 +166,7 @@ export class RedactTextDialogComponent if (!this.#applyToAllDossiers) { const selectedDictionaryType = this.form.controls.dictionary.value; const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryType); - this.options[2].extraOption.disabled = selectedDictionary.dossierDictionaryOnly; + this.options[2].additionalCheck.disabled = selectedDictionary.dossierDictionaryOnly; } } @@ -230,7 +230,7 @@ export class RedactTextDialogComponent #resetValues() { this.#applyToAllDossiers = this.applyToAll; - this.options[2].extraOption.checked = this.#applyToAllDossiers; + this.options[2].additionalCheck.checked = this.#applyToAllDossiers; if (this.dictionaryRequest) { this.form.controls.reason.setValue(null); this.form.controls.dictionary.setValue(null); diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.ts index c51877fb3..a5b174c13 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.ts @@ -82,7 +82,7 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent< super.close({ comment: formValue.comment, updateDictionary, - addToAllDossiers: !!formValue.option?.extraOption?.checked, + addToAllDossiers: !!formValue.option?.additionalCheck?.checked, }); } 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 9bdb9b7db..05b6ae67f 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 @@ -435,7 +435,7 @@ export class AnnotationActionsService { type: redaction.type, positions: redaction.positions, addToDictionary: true, - addToAllDossiers: !!dialogResult.option.extraOption?.checked || !!dialogResult.applyToAllDossiers, + addToAllDossiers: !!dialogResult.option.additionalCheck?.checked || !!dialogResult.applyToAllDossiers, reason: 'False Positive', dictionaryEntryType: redaction.isRecommendation ? DictionaryEntryTypes.FALSE_RECOMMENDATION @@ -561,7 +561,7 @@ export class AnnotationActionsService { value: redaction.value, comment: dialogResult.comment, removeFromDictionary: dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER, - removeFromAllDossiers: !!dialogResult.option.extraOption?.checked || !!dialogResult.applyToAllDossiers, + removeFromAllDossiers: !!dialogResult.option.additionalCheck?.checked || !!dialogResult.applyToAllDossiers, })); } } diff --git a/apps/red-ui/src/app/modules/file-preview/utils/dialog-options.ts b/apps/red-ui/src/app/modules/file-preview/utils/dialog-options.ts index 00cbf3ce6..74e0a7720 100644 --- a/apps/red-ui/src/app/modules/file-preview/utils/dialog-options.ts +++ b/apps/red-ui/src/app/modules/file-preview/utils/dialog-options.ts @@ -8,6 +8,8 @@ import { removeRedactionTranslations } from '@translations/remove-redaction-tran import { resizeRedactionTranslations } from '@translations/resize-redaction-translations'; import { ForceAnnotationOption, + RectangleRedactOption, + RectangleRedactOptions, RedactOrHintOption, RedactOrHintOptions, RemoveRedactionData, @@ -16,6 +18,7 @@ import { ResizeOptions, ResizeRedactionOption, } from './dialog-types'; +import { rectangleRedactTranslations } from '@translations/rectangle-redact-translations'; const PIN_ICON = 'red:push-pin'; const DOCUMENT_ICON = 'iqser:document'; @@ -43,7 +46,7 @@ export const getEditRedactionOptions = ( descriptionParams: { dossierName: dossierName }, icon: FOLDER_ICON, value: ResizeOptions.IN_DOSSIER, - extraOption: { + additionalCheck: { label: editRedactionTranslations.inDossier.extraOptionLabel, checked: applyToAllDossiers, hidden: dossierDictionaryOnly, @@ -95,7 +98,7 @@ export const getRedactOrHintOptions = ( icon: FOLDER_ICON, value: ResizeOptions.IN_DOSSIER, disabled: isPageExcluded, - extraOption: { + additionalCheck: { label: translations.inDossier.extraOptionLabel, checked: applyToAllDossiers, hidden: !isApprover, @@ -105,6 +108,29 @@ export const getRedactOrHintOptions = ( return options; }; +export const getRectangleRedactOptions = (): DetailsRadioOption[] => { + return [ + { + label: rectangleRedactTranslations.onlyThisPage.label, + description: rectangleRedactTranslations.onlyThisPage.description, + icon: PIN_ICON, + value: RectangleRedactOptions.ONLY_THIS_PAGE, + }, + { + label: rectangleRedactTranslations.multiplePages.label, + description: rectangleRedactTranslations.multiplePages.description, + icon: DOCUMENT_ICON, + value: RectangleRedactOptions.MULTIPLE_PAGES, + additionalInput: { + label: rectangleRedactTranslations.multiplePages.extraOptionLabel, + description: rectangleRedactTranslations.multiplePages.extraOptionDescription, + placeholder: rectangleRedactTranslations.multiplePages.extraOptionPlaceholder, + value: '', + }, + }, + ]; +}; + export const getResizeRedactionOptions = ( redaction: AnnotationWrapper, dossier: Dossier, @@ -136,7 +162,7 @@ export const getResizeRedactionOptions = ( tooltip: !dictBasedType ? translations.inDossier.tooltip : null, icon: FOLDER_ICON, value: RedactOrHintOptions.IN_DOSSIER, - extraOption: { + additionalCheck: { label: translations.inDossier.extraOptionLabel, checked: applyToAllDossiers, hidden: !isApprover, @@ -190,7 +216,7 @@ export const getRemoveRedactionOptions = ( }, icon: FOLDER_ICON, value: RemoveRedactionOptions.IN_DOSSIER, - extraOption: !isDocumine + additionalCheck: !isDocumine ? { label: translations.IN_DOSSIER.extraOptionLabel, checked: applyToAllDossiers, @@ -212,7 +238,7 @@ export const getRemoveRedactionOptions = ( }, icon: FOLDER_ICON, value: RemoveRedactionOptions.DO_NOT_RECOMMEND, - extraOption: !isDocumine + additionalCheck: !isDocumine ? { label: translations.DO_NOT_RECOMMEND.extraOptionLabel, checked: applyToAllDossiers, @@ -232,7 +258,7 @@ export const getRemoveRedactionOptions = ( }, icon: REMOVE_FROM_DICT_ICON, value: RemoveRedactionOptions.FALSE_POSITIVE, - extraOption: !isDocumine + additionalCheck: !isDocumine ? { label: translations.FALSE_POSITIVE.extraOptionLabel, checked: applyToAllDossiers, diff --git a/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts b/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts index d7efb27eb..284469ae9 100644 --- a/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts +++ b/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts @@ -11,6 +11,13 @@ export const RedactOrHintOptions = { export type RedactOrHintOption = keyof typeof RedactOrHintOptions; +export const RectangleRedactOptions = { + ONLY_THIS_PAGE: 'ONLY_THIS_PAGE', + MULTIPLE_PAGES: 'MULTIPLE_PAGES', +} as const; + +export type RectangleRedactOption = keyof typeof RectangleRedactOptions; + export const ForceAnnotationOptions = { ONLY_HERE: 'ONLY_HERE', IN_DOCUMENT: 'IN_DOCUMENT', diff --git a/apps/red-ui/src/app/modules/file-preview/utils/form-validators.ts b/apps/red-ui/src/app/modules/file-preview/utils/form-validators.ts new file mode 100644 index 000000000..233aed553 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/utils/form-validators.ts @@ -0,0 +1,12 @@ +import { AbstractControl, ValidatorFn } from '@angular/forms'; + +export const validatePageRange = (): ValidatorFn => { + return (control: AbstractControl): { [key: string]: any } | null => { + const option = control.value; + if (option?.additionalInput) { + const validRange = /^(\d+(-\d+)?)(,\d+(-\d+)?)*$/.test(option.additionalInput.value); + return validRange ? null : { invalidRange: true }; + } + return null; + }; +}; diff --git a/apps/red-ui/src/app/translations/rectangle-redact-translations.ts b/apps/red-ui/src/app/translations/rectangle-redact-translations.ts new file mode 100644 index 000000000..2feb6c67b --- /dev/null +++ b/apps/red-ui/src/app/translations/rectangle-redact-translations.ts @@ -0,0 +1,16 @@ +import { DialogOption } from '@translations/redact-text-translations'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; + +export const rectangleRedactTranslations: Record<'onlyThisPage' | 'multiplePages', DialogOption> = { + onlyThisPage: { + label: _('manual-annotation.dialog.content.options.only-this-page.label'), + description: _('manual-annotation.dialog.content.options.only-this-page.description'), + }, + multiplePages: { + label: _('manual-annotation.dialog.content.options.multiple-pages.label'), + description: _('manual-annotation.dialog.content.options.multiple-pages.description'), + extraOptionLabel: _('manual-annotation.dialog.content.options.multiple-pages.extraOptionLabel'), + extraOptionDescription: _('manual-annotation.dialog.content.options.multiple-pages.extraOptionDescription'), + extraOptionPlaceholder: _('manual-annotation.dialog.content.options.multiple-pages.extraOptionPlaceholder'), + }, +} as const; diff --git a/apps/red-ui/src/app/translations/redact-text-translations.ts b/apps/red-ui/src/app/translations/redact-text-translations.ts index a5166ca86..2b334471b 100644 --- a/apps/red-ui/src/app/translations/redact-text-translations.ts +++ b/apps/red-ui/src/app/translations/redact-text-translations.ts @@ -7,6 +7,7 @@ export interface DialogOption { descriptionBulk?: string; extraOptionLabel?: string; extraOptionDescription?: string; + extraOptionPlaceholder?: string; } export const redactTextTranslations: Record<'onlyHere' | 'inDocument' | 'inDossier', DialogOption> = { diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json index bdbaa2f35..eabffcfef 100644 --- a/apps/red-ui/src/assets/i18n/redact/de.json +++ b/apps/red-ui/src/assets/i18n/redact/de.json @@ -1878,6 +1878,19 @@ "dictionary": "Wörterbuch", "edit-selected-text": "Ausgewählten Text bearbeiten", "legalBasis": "Rechtsgrundlage", + "options": { + "multiple-pages": { + "description": "", + "extraOptionDescription": "", + "extraOptionLabel": "", + "extraOptionPlaceholder": "", + "label": "" + }, + "only-this-page": { + "description": "", + "label": "" + } + }, "reason": "Grund", "reason-placeholder": "Grund auswählen...", "rectangle": "Bereichsschwärzung", diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json index 6c771c61b..ab30f44eb 100644 --- a/apps/red-ui/src/assets/i18n/redact/en.json +++ b/apps/red-ui/src/assets/i18n/redact/en.json @@ -1878,6 +1878,19 @@ "dictionary": "Dictionary", "edit-selected-text": "Edit selected text", "legalBasis": "Legal basis", + "options": { + "multiple-pages": { + "description": "Edit redaction the range of pages", + "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", + "extraOptionLabel": "Range", + "extraOptionPlaceholder": "e.g. 1-20,22,32", + "label": "Apply on multiple pages" + }, + "only-this-page": { + "description": "Edit redaction only at this position in this document", + "label": "Apply only on this page" + } + }, "reason": "Reason", "reason-placeholder": "Select a reason...", "rectangle": "Custom rectangle", diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json index ef13dcc84..dd2b7ee47 100644 --- a/apps/red-ui/src/assets/i18n/scm/de.json +++ b/apps/red-ui/src/assets/i18n/scm/de.json @@ -1878,6 +1878,19 @@ "dictionary": "Wörterbuch", "edit-selected-text": "Edit selected text", "legalBasis": "Rechtsgrundlage", + "options": { + "multiple-pages": { + "description": "", + "extraOptionDescription": "", + "extraOptionLabel": "", + "extraOptionPlaceholder": "", + "label": "" + }, + "only-this-page": { + "description": "", + "label": "" + } + }, "reason": "Begründung", "reason-placeholder": "Wählen Sie eine Begründung aus ...", "rectangle": "Benutzerdefinierter Bereich", diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json index 1f9e9b480..96f6e6184 100644 --- a/apps/red-ui/src/assets/i18n/scm/en.json +++ b/apps/red-ui/src/assets/i18n/scm/en.json @@ -1878,6 +1878,19 @@ "dictionary": "Dictionary", "edit-selected-text": "Edit selected text", "legalBasis": "Legal Basis", + "options": { + "multiple-pages": { + "description": "Edit redaction the range of pages", + "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", + "extraOptionLabel": "Range", + "extraOptionPlaceholder": "e.g. 1-20,22,32", + "label": "Apply on multiple pages" + }, + "only-this-page": { + "description": "Edit redaction only at this position in this document", + "label": "Apply only on this page" + } + }, "reason": "Reason", "reason-placeholder": "Select a reason ...", "rectangle": "Custom Rectangle",