diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts index a8faf210e..0de86597d 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts @@ -26,6 +26,7 @@ export class DossierOverviewBulkActionsComponent { readonly maxWidth = input(); readonly buttons = computed(() => this.#buttons); readonly IqserTooltipPositions = IqserTooltipPositions; + readonly #areFilesInErrorState = computed(() => this.selectedFiles().some(file => file.isError)); readonly #areRulesLocked = computed(() => this._rulesService.currentTemplateRules().timeoutDetected); readonly #allFilesAreUnderReviewOrUnassigned = computed(() => this.selectedFiles().reduce((acc, file) => acc && (file.isUnderReview || file.isNew), true), @@ -47,18 +48,34 @@ export class DossierOverviewBulkActionsComponent { ); readonly #canDelete = computed(() => this._permissionsService.canSoftDeleteFile(this.selectedFiles(), this.dossier())); readonly #canReanalyse = computed(() => this._permissionsService.canReanalyseFile(this.selectedFiles(), this.dossier())); - readonly #canDisableAutoAnalysis = computed(() => - this._permissionsService.canDisableAutoAnalysis(this.selectedFiles(), this.dossier()), + readonly #canDisableAutoAnalysis = computed( + () => this._permissionsService.canDisableAutoAnalysis(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canEnableAutoAnalysis = computed( + () => this._permissionsService.canEnableAutoAnalysis(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), ); - readonly #canEnableAutoAnalysis = computed(() => this._permissionsService.canEnableAutoAnalysis(this.selectedFiles(), this.dossier())); readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.selectedFiles(), this.dossier())); - readonly #canOcr = computed(() => this._permissionsService.canOcrFile(this.selectedFiles(), this.dossier())); - readonly #canSetToNew = computed(() => this._permissionsService.canSetToNew(this.selectedFiles(), this.dossier())); - readonly #canSetToUnderReview = computed(() => this._permissionsService.canSetUnderReview(this.selectedFiles(), this.dossier())); - readonly #canSetToUnderApproval = computed(() => this._permissionsService.canSetUnderApproval(this.selectedFiles(), this.dossier())); - readonly #isReadyForApproval = computed(() => this._permissionsService.isReadyForApproval(this.selectedFiles(), this.dossier())); - readonly #canApprove = computed(() => this._permissionsService.canBeApproved(this.selectedFiles(), this.dossier())); - readonly #canUndoApproval = computed(() => this._permissionsService.canUndoApproval(this.selectedFiles(), this.dossier())); + readonly #canOcr = computed( + () => this._permissionsService.canOcrFile(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canSetToNew = computed( + () => this._permissionsService.canSetToNew(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canSetToUnderReview = computed( + () => this._permissionsService.canSetUnderReview(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canSetToUnderApproval = computed( + () => this._permissionsService.canSetUnderApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #isReadyForApproval = computed( + () => this._permissionsService.isReadyForApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canApprove = computed( + () => this._permissionsService.canBeApproved(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); + readonly #canUndoApproval = computed( + () => this._permissionsService.canUndoApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(), + ); readonly #assignTooltip = computed(() => this.#allFilesAreUnderApproval() ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'), ); diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts index 2ec1353b6..82fcc69aa 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts @@ -126,7 +126,7 @@ export class AnnotationActionsComponent { async acceptRecommendation(): Promise { const annotations = untracked(this.annotations); - await this.annotationActionsService.convertRecommendationToAnnotation(annotations); + await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept'); } hideAnnotation() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts b/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts index 1b5deaff9..5bd7b034c 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts @@ -16,6 +16,7 @@ import { HighlightsSeparatorComponent } from '../highlights-separator/highlights import { AnnotationWrapperComponent } from '../annotation-wrapper/annotation-wrapper.component'; import { AnnotationReferencesListComponent } from '../annotation-references-list/annotation-references-list.component'; import { Clipboard } from '@angular/cdk/clipboard'; +import { isTargetInput } from '@utils/functions'; @Component({ selector: 'redaction-annotations-list', @@ -68,7 +69,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective { console.log('Selected Annotation:', annotation); } - if (($event?.target as IqserEventTarget)?.localName === 'input') { + if (isTargetInput($event)) { return; } diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts index d1b164b70..bf616ec5f 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts @@ -50,6 +50,7 @@ import { ALL_HOTKEYS } from '../../utils/constants'; import { AnnotationDrawService } from '../../../pdf-viewer/services/annotation-draw.service'; import { FileManagementService } from '@services/files/file-management.service'; import { MatDialog } from '@angular/material/dialog'; +import { isTargetInput, isTargetTextArea } from '@utils/functions'; @Component({ selector: 'redaction-file-header', @@ -178,7 +179,6 @@ export class FileHeaderComponent implements OnInit, AfterViewInit, OnDetach, OnD this.fullScreen = false; } } - @Bind() handleKeyEvent($event: KeyboardEvent) { if (this._router.url.indexOf('/file/') < 0) { @@ -210,29 +210,26 @@ export class FileHeaderComponent implements OnInit, AfterViewInit, OnDetach, OnD this._changeRef.markForCheck(); } + if ($event.key === 'F5') { + window.location.reload(); + } + + if (isTargetInput($event) || isTargetTextArea($event)) { + return; + } + if (!$event.ctrlKey && !$event.metaKey && ['f', 'F'].includes($event.key)) { - // if you type in an input, don't toggle full-screen - if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) { - return; - } this.toggleFullScreen(); return; } if (['h', 'H'].includes($event.key)) { - if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) { - return; - } this._ngZone.run(() => { window.focus(); this._helpModeService.activateHelpMode(false); }); return; } - - if ($event.key === 'F5') { - window.location.reload(); - } } #openFullScreen() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts index e7e0199c0..850108023 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts @@ -59,7 +59,7 @@ import { PageExclusionComponent } from '../page-exclusion/page-exclusion.compone import { PagesComponent } from '../pages/pages.component'; import { ReadonlyBannerComponent } from '../readonly-banner/readonly-banner.component'; import { DocumentInfoComponent } from '../document-info/document-info.component'; -import { getLast } from '@utils/functions'; +import { getLast, isTargetInput } from '@utils/functions'; import { ALL_ANNOTATIONS_PAGE } from '../../utils/constants'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; @@ -251,11 +251,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On handleKeyEvent($event: KeyboardEvent): void { const multiSelectServiceInactive = untracked(this.multiSelectService.inactive); - if ( - !ALL_HOTKEY_ARRAY.includes($event.key) || - this._dialog.openDialogs.length || - ($event.target as IqserEventTarget).localName === 'input' - ) { + if (!ALL_HOTKEY_ARRAY.includes($event.key) || this._dialog.openDialogs.length || isTargetInput($event)) { return; } @@ -342,7 +338,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On } preventKeyDefault($event: KeyboardEvent): void { - if (COMMAND_KEY_ARRAY.includes($event.key) && !(($event.target as any).localName === 'input')) { + if (COMMAND_KEY_ARRAY.includes($event.key) && !isTargetInput($event)) { $event.preventDefault(); } } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.html index a060ab00a..a2e9fbb92 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.html @@ -1,10 +1,12 @@
-
+
+ +
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.ts index c87d46f0f..d8393392c 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.ts @@ -39,6 +39,7 @@ import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-r import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; import { validatePageRange } from '../../utils/form-validators'; import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils'; +import { ActionsHelpModeKeys } from '../../utils/constants'; interface TypeSelectOptions { type: string; @@ -83,12 +84,19 @@ export class EditRedactionDialogComponent readonly isHint = this.annotations.every(annotation => annotation.HINT || annotation.IMAGE_HINT); readonly isRedacted = this.annotations.every(annotation => annotation.isRedacted); readonly isImported: boolean = this.annotations.every(annotation => annotation.imported || annotation.type === 'imported_redaction'); + readonly isSkipped: boolean = this.annotations.every(annotation => annotation.isSkipped); readonly allRectangles = this.annotations.reduce((acc, a) => acc && a.AREA, true); readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }]; readonly tableData: ValueColumn[][] = this.data.annotations.map(redaction => [ { label: redaction.value, bold: true }, { label: redaction.typeLabel }, ]); + readonly annotationsType = this.isHint + ? ActionsHelpModeKeys.hint + : this.isSkipped + ? ActionsHelpModeKeys.skipped + : ActionsHelpModeKeys.redaction; + readonly helpModeKeyByType = `${this.annotationsType}_edit_DIALOG`; options = this.allRectangles ? getRectangleRedactOptions('edit', this.data.annotations) : getEditRedactionOptions(this.isHint); legalOptions: LegalBasisOption[] = []; dictionaries: Dictionary[] = []; diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.html index cdf6b9936..a5adc3148 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.html @@ -1,6 +1,11 @@
-
+
+ +
@if (!isImageHint) { diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts index c27546dd0..d17ad99e7 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/force-redaction-dialog/force-annotation-dialog.component.ts @@ -10,9 +10,8 @@ import { IqserDialogComponent, } from '@iqser/common-ui'; import { JustificationsService } from '@services/entity-services/justifications.service'; -import { Dossier, ILegalBasisChangeRequest } from '@red/domain'; +import { ILegalBasisChangeRequest } from '@red/domain'; import { firstValueFrom } from 'rxjs'; -import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { Roles } from '@users/roles'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { @@ -38,10 +37,8 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.'; standalone: true, imports: [ ReactiveFormsModule, - NgIf, SelectedAnnotationsTableComponent, MatFormField, - MatSelectTrigger, MatSelect, MatOption, MatTooltip, @@ -50,7 +47,6 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.'; IconButtonComponent, IqserDenyDirective, CircleButtonComponent, - NgForOf, HelpButtonComponent, DetailsRadioComponent, ], @@ -68,6 +64,7 @@ export class ForceAnnotationDialogComponent { label: redaction.value, bold: true }, { label: redaction.typeLabel }, ]); + readonly isSkipped = this.data.annotations.every(annotation => annotation.isSkipped); legalOptions: LegalBasisOption[] = []; protected readonly roles = Roles; diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.html index 5c7d34dd2..dd12ee39a 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component.html @@ -1,6 +1,8 @@
-
+
+ +
@@ -59,6 +61,7 @@
+
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 c98b44dad..a89d59024 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 @@ -8,7 +8,15 @@ import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select import { MatTooltip } from '@angular/material/tooltip'; import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; -import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui'; +import { + CircleButtonComponent, + HasScrollbarDirective, + HelpButtonComponent, + IconButtonComponent, + IconButtonTypes, + IqserDenyDirective, + IqserDialogComponent, +} from '@iqser/common-ui'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { TranslateModule } from '@ngx-translate/core'; import { Dictionary, IAddRedactionRequest, SuperTypes } from '@red/domain'; @@ -30,6 +38,7 @@ import { RedactRecommendationResult, ResizeOptions, } from '../../utils/dialog-types'; +import { Roles } from '@users/roles'; @Component({ templateUrl: './redact-recommendation-dialog.component.html', @@ -51,6 +60,8 @@ import { MatDialogClose, MatSelectTrigger, MatSelect, + HelpButtonComponent, + IqserDenyDirective, ], }) export class RedactRecommendationDialogComponent @@ -75,6 +86,8 @@ export class RedactRecommendationDialogComponent reason: [null], }); + readonly helpModeKey = `recommendation_${this.data.action}_DIALOG`; + readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }]; readonly tableData: ValueColumn[][] = this.data.annotations.map(redaction => [ { label: redaction.value, bold: true }, @@ -204,4 +217,6 @@ export class RedactRecommendationDialogComponent } this.form.controls.dictionary.setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null); } + + protected readonly roles = Roles; } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.html index ddc240a4c..796e45c90 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/redact-text-dialog/redact-text-dialog.component.html @@ -1,6 +1,8 @@
-
+
+ +
@@ -134,6 +136,7 @@ />
+
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 e65ef165f..b0911e59b 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 @@ -8,7 +8,15 @@ import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select import { MatTooltip } from '@angular/material/tooltip'; import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; -import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui'; +import { + CircleButtonComponent, + HasScrollbarDirective, + HelpButtonComponent, + IconButtonComponent, + IconButtonTypes, + IqserDenyDirective, + IqserDialogComponent, +} from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { Dictionary, SuperTypes } from '@red/domain'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; @@ -55,6 +63,8 @@ const MAXIMUM_TEXT_AREA_WIDTH = 421; AsyncPipe, IconButtonComponent, MatDialogClose, + HelpButtonComponent, + IqserDenyDirective, ], }) export class RedactTextDialogComponent @@ -138,7 +148,7 @@ export class RedactTextDialogComponent get applyToAll() { return this.isSystemDefault || this._userPreferences.getAddRedactionDefaultExtraOption() === 'undefined' - ? this.data.applyToAllDossiers ?? true + ? (this.data.applyToAllDossiers ?? true) : stringToBoolean(this._userPreferences.getAddRedactionDefaultExtraOption()); } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.html index cea51e224..43055cbb2 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.html @@ -1,9 +1,13 @@
-
+
+ +
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.ts index 76da3737f..69b336f1c 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/remove-redaction-dialog/remove-redaction-dialog.component.ts @@ -76,6 +76,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent< : this.recommendation ? ANNOTATION_TYPES.RECOMMENDATION : ANNOTATION_TYPES.REDACTION; + readonly helpModeKeyByType = `${this.annotationsType}_remove_DIALOG`; readonly optionByType = { recommendation: { main: this._userPreferences.getRemoveRecommendationDefaultOption(), diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.html index 5f33edd9e..c9bf306e3 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/resize-redaction-dialog/resize-redaction-dialog.component.html @@ -1,6 +1,11 @@
-
+
+ +
@@ -48,6 +53,7 @@ >
+
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 1d8c4308e..0f3d75992 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 @@ -6,12 +6,21 @@ import { MatFormField } from '@angular/material/form-field'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select'; import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; -import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui'; +import { + CircleButtonComponent, + HasScrollbarDirective, + HelpButtonComponent, + IconButtonComponent, + IconButtonTypes, + IqserDenyDirective, + IqserDialogComponent, +} from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { getResizeRedactionOptions } from '../../utils/dialog-options'; import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedactionResult } from '../../utils/dialog-types'; +import { Roles } from '@users/roles'; @Component({ templateUrl: './resize-redaction-dialog.component.html', @@ -30,6 +39,8 @@ import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedact HasScrollbarDirective, MatDialogClose, NgIf, + HelpButtonComponent, + IqserDenyDirective, ], }) export class ResizeRedactionDialogComponent extends IqserDialogComponent< @@ -67,6 +78,14 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent< return this.data.redaction.HINT ? 'hint' : this.data.redaction.isSkippedImageHint ? 'image' : 'redaction'; } + get dialogTitleHelpKey() { + return this.data.redaction.isRecommendation + ? 'recommendation_resize_DIALOG' + : this.data.redaction.isHint + ? 'hint_resize_DIALOG' + : 'redaction_resize_DIALOG'; + } + get displayedDictionaryLabel() { const dictType = this.form.get('dictionary').value; if (dictType) { @@ -93,4 +112,6 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent< option: this.options[0], }); } + + protected readonly roles = Roles; } 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 e94a33190..651fb0949 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 @@ -225,9 +225,9 @@ export class AnnotationActionsService { this.#processObsAndEmit(request$).then(); } - async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) { + async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[], action: 'accept' | 'resize') { const { dossierId, fileId } = this._state; - const data = this.#getRedactRecommendationDialogData(recommendations) as RedactRecommendationData; + const data = this.#getRedactRecommendationDialogData(recommendations, action) as RedactRecommendationData; const dialog = this._iqserDialog.openDefault(RedactRecommendationDialogComponent, { data }); const result = await dialog.result(); if (!result) { @@ -277,7 +277,7 @@ export class AnnotationActionsService { recommendation.isRemoved = true; await this._annotationDrawService.draw([recommendation], this._skippedService.hideSkipped(), this._state.dossierTemplateId); - return this.convertRecommendationToAnnotation([recommendation]); + return this.convertRecommendationToAnnotation([recommendation], 'resize'); } const dossier = this._state.dossier(); @@ -550,7 +550,7 @@ export class AnnotationActionsService { return this._iqserDialog.openDefault(EditRedactionDialogComponent, { data }); } - #getRedactRecommendationDialogData(annotations: AnnotationWrapper[]) { + #getRedactRecommendationDialogData(annotations: AnnotationWrapper[], action: 'accept' | 'resize') { const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId); const isApprover = this._permissionsService.isApprover(this._state.dossier()); const applyDictionaryUpdatesToAllDossiersByDefault = dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault; @@ -560,6 +560,7 @@ export class AnnotationActionsService { dossierId: this._state.dossierId, applyToAllDossiers: isApprover ? applyDictionaryUpdatesToAllDossiersByDefault : false, isApprover, + action, }; } diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts index 45b8ec5d8..0fa78c9eb 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts @@ -75,7 +75,7 @@ export class PdfAnnotationActionsService { if (permissions.canAcceptRecommendation && annotationChangesAllowed) { const acceptRecommendationButton = this.#getButton('check', _('annotation-actions.accept-recommendation.label'), () => - this.#annotationActionsService.convertRecommendationToAnnotation(annotations), + this.#annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept'), ); availableActions.push(acceptRecommendationButton); } 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 05b705e84..0a697fd0a 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 @@ -97,6 +97,7 @@ export interface RedactTextResult { export type RedactRecommendationData = EditRedactionData & { applyToAllDossiers: boolean; + action?: 'resize' | 'accept'; }; export interface RedactRecommendationResult { diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts index 88fbff253..a7b6be744 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts @@ -14,6 +14,7 @@ import { PdfViewer } from './pdf-viewer.service'; import Color = Core.Annotations.Color; import DocumentViewer = Core.DocumentViewer; import Quad = Core.Math.Quad; +import { isTargetInput } from '@utils/functions'; @Injectable() export class REDDocumentViewer { @@ -71,12 +72,12 @@ export class REDDocumentViewer { return fromEvent(this.#document, 'keyUp').pipe( tap(stopAndPreventIfNotAllowed), filter($event => { - if (($event.target as HTMLElement)?.tagName?.toLowerCase() === 'input') { + if (isTargetInput($event)) { if ($event.key === 'Escape') { return true; } } - return ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input'; + return isTargetInput($event); }), filter($event => $event.key.startsWith('Arrow') || ['f', 'h', 'H', 'Escape', 'Shift'].includes($event.key)), tap(stopAndPrevent), diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts index 5be425c50..87ce5cff8 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts @@ -115,6 +115,11 @@ export class PdfViewer { return page$.pipe(map(page => this.#adjustPage(page))); } + get #searchInput() { + const iframeWindow = this.#instance.UI.iframeWindow; + return iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement; + } + activateSearch() { this.#instance.UI.searchTextFull('', this.searchOptions); } @@ -292,13 +297,13 @@ export class PdfViewer { #listenForShift() { this.#instance.UI.iframeWindow.addEventListener('keydown', e => { - e.preventDefault(); + if (e.target === this.#searchInput) return; if (e.key === 'Shift') { this.#setSelectionMode(SelectionModes.RECTANGULAR); } }); this.#instance.UI.iframeWindow.addEventListener('keyup', e => { - e.preventDefault(); + if (e.target === this.#searchInput) return; if (e.key === 'Shift') { this.#setSelectionMode(SelectionModes.STRUCTURAL); } @@ -395,13 +400,11 @@ export class PdfViewer { if (this.#isElementActive('textPopup')) { this.#instance.UI.closeElements(['textPopup']); } - const iframeWindow = this.#instance.UI.iframeWindow; - const input = iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement; - if (input) { - input.focus(); + if (this.#searchInput) { + this.#searchInput.focus(); } - if (input?.value?.length > 0) { - input.select(); + if (this.#searchInput?.value?.length > 0) { + this.#searchInput.select(); } } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts index 560e7bee2..77c0b221c 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts @@ -13,7 +13,7 @@ import { } from '@iqser/common-ui'; import { getCurrentUser } from '@iqser/common-ui/lib/users'; import { IqserTooltipPositions } from '@iqser/common-ui/lib/utils'; -import { Action, ActionTypes, ApproveResponse, Dossier, File, ProcessingFileStatuses, User } from '@red/domain'; +import { Action, ActionTypes, ApproveResponse, Dossier, File, User } from '@red/domain'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { FileAttributesService } from '@services/entity-services/file-attributes.service'; import { FileManagementService } from '@services/files/file-management.service'; @@ -69,15 +69,21 @@ export class FileActionsComponent { this.file().isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'), ); readonly #showSetToNew = computed( - () => this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(), + () => + this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow() && !this.file().isError, ); readonly #showUndoApproval = computed( - () => this._permissionsService.canUndoApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(), + () => + this._permissionsService.canUndoApproval(this.file(), this.dossier()) && + !this.isDossierOverviewWorkflow() && + !this.file().isError, ); readonly #showAssignToSelf = computed( () => this._permissionsService.canAssignToSelf(this.file(), this.dossier()) && this.isDossierOverview(), ); - readonly #showImportRedactions = computed(() => this._permissionsService.canImportRedactions(this.file(), this.dossier())); + readonly #showImportRedactions = computed( + () => this._permissionsService.canImportRedactions(this.file(), this.dossier()) && !this.file().isError, + ); readonly #showAssign = computed( () => (this._permissionsService.canAssignUser(this.file(), this.dossier()) || @@ -87,15 +93,26 @@ export class FileActionsComponent { readonly #showDelete = computed(() => this._permissionsService.canSoftDeleteFile(this.file(), this.dossier())); readonly #showOCR = computed(() => this._permissionsService.canOcrFile(this.file(), this.dossier()) && !this.file().isError); readonly #canReanalyse = computed(() => this._permissionsService.canReanalyseFile(this.file(), this.dossier())); - readonly #canEnableAutoAnalysis = computed(() => this._permissionsService.canEnableAutoAnalysis([this.file()], this.dossier())); + readonly #canEnableAutoAnalysis = computed( + () => this._permissionsService.canEnableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError, + ); readonly #showUnderReview = computed( - () => this._permissionsService.canSetUnderReview(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(), + () => + this._permissionsService.canSetUnderReview(this.file(), this.dossier()) && + !this.isDossierOverviewWorkflow() && + !this.file().isError, ); readonly #showUnderApproval = computed( - () => this._permissionsService.canSetUnderApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(), + () => + this._permissionsService.canSetUnderApproval(this.file(), this.dossier()) && + !this.isDossierOverviewWorkflow() && + !this.file().isError, ); readonly #showApprove = computed( - () => this._permissionsService.isReadyForApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(), + () => + this._permissionsService.isReadyForApproval(this.file(), this.dossier()) && + !this.isDossierOverviewWorkflow() && + !this.file().isError, ); readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.file(), this.dossier())); readonly #toggleTooltip? = computed(() => { @@ -130,7 +147,7 @@ export class FileActionsComponent { readonly #isDocumine = getConfig().IS_DOCUMINE; readonly #canDisableAutoAnalysis = computed( - () => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()), + () => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError, ); constructor( diff --git a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.html b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.html index 1e5f43d09..43c1d9467 100644 --- a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.html +++ b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.html @@ -1,6 +1,8 @@
-
+
+ +
diff --git a/apps/red-ui/src/app/utils/functions.ts b/apps/red-ui/src/app/utils/functions.ts index ea763c79a..405a3e629 100644 --- a/apps/red-ui/src/app/utils/functions.ts +++ b/apps/red-ui/src/app/utils/functions.ts @@ -1,5 +1,5 @@ import { ITrackable } from '@iqser/common-ui'; -import type { List } from '@iqser/common-ui/lib/utils'; +import type { IqserEventTarget, List } from '@iqser/common-ui/lib/utils'; import type { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { Dayjs } from 'dayjs'; import { AbstractControl } from '@angular/forms'; @@ -149,3 +149,11 @@ export function urlFileId() { export function formControlToSignal(control: AbstractControl) { return toSignal(control.valueChanges, { initialValue: control.value }); } + +export function isTargetInput(event: Event) { + return (event?.target as IqserEventTarget)?.localName === 'input'; +} + +export function isTargetTextArea(event: Event) { + return (event?.target as IqserEventTarget)?.localName === 'textarea'; +} diff --git a/apps/red-ui/src/assets/help-mode/help-mode-keys.json b/apps/red-ui/src/assets/help-mode/help-mode-keys.json index 06209d4b4..d066783ff 100644 --- a/apps/red-ui/src/assets/help-mode/help-mode-keys.json +++ b/apps/red-ui/src/assets/help-mode/help-mode-keys.json @@ -36,6 +36,11 @@ "documentKey": "new_dossier", "overlappingElements": ["USER_MENU"] }, + { + "elementKey": "new_dossier_DIALOG", + "documentKey": "new_dossier", + "overlappingElements": ["USER_MENU"] + }, { "elementKey": "open_notifications", "documentKey": "open_notifications" @@ -114,48 +119,84 @@ "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "redaction_resize_DIALOG", + "documentKey": "redaction_resize" + }, { "elementKey": "redaction_edit", "documentKey": "redaction_edit", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "redaction_edit_DIALOG", + "documentKey": "redaction_edit" + }, + { + "elementKey": "add_redaction_DIALOG", + "documentKey": "add_redaction" + }, { "elementKey": "redaction_remove", "documentKey": "redaction_remove", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "redaction_remove_DIALOG", + "documentKey": "redaction_remove" + }, { "elementKey": "recommendation_resize", "documentKey": "recommendation_resize", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "recommendation_resize_DIALOG", + "documentKey": "recommendation_resize" + }, { "elementKey": "recommendation_accept", "documentKey": "recommendation_accept", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "recommendation_accept_DIALOG", + "documentKey": "recommendation_accept" + }, { "elementKey": "recommendation_remove", "documentKey": "recommendation_remove", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "recommendation_remove_DIALOG", + "documentKey": "recommendation_remove" + }, { "elementKey": "skipped_edit", "documentKey": "skipped_edit", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "skipped_edit_DIALOG", + "documentKey": "skipped_edit" + }, { "elementKey": "skipped_force", "documentKey": "skipped_force", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "skipped_force_DIALOG", + "documentKey": "skipped_force" + }, { "elementKey": "skipped_remove", "documentKey": "skipped_remove", @@ -174,18 +215,30 @@ "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "hint_resize_DIALOG", + "documentKey": "hint_resize" + }, { "elementKey": "hint_edit", "documentKey": "hint_edit", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "hint_edit_DIALOG", + "documentKey": "hint_edit" + }, { "elementKey": "hint_remove", "documentKey": "hint_remove", "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "hint_remove_DIALOG", + "documentKey": "hint_remove" + }, { "elementKey": "hint_hide", "documentKey": "hint_hide", @@ -198,6 +251,10 @@ "scrollableParentView": "ANNOTATIONS_LIST", "overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"] }, + { + "elementKey": "hint_redact_DIALOG", + "documentKey": "hint_redact" + }, { "elementKey": "workload_in_editor", "documentKey": "workload_in_editor", diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json index c6802073e..c966525a6 100644 --- a/apps/red-ui/src/assets/i18n/redact/de.json +++ b/apps/red-ui/src/assets/i18n/redact/de.json @@ -1,7 +1,7 @@ { "accept-recommendation-dialog": { "header": { - "add-to-dictionary": "Zum Wörterbuch hinzufügen", + "add-to-dictionary": "Fügen Sie die Schwärzung auf mehreren Seiten hinzu.", "request-add-to-dictionary": "Wörterbucheintrag vorschlagen" } }, @@ -529,7 +529,7 @@ }, "edit-title": "Edit {displayName} definition", "form": { - "autogenerated-label": "Autogenerated based on the initial display name", + "autogenerated-label": "Wurde ausgehend vom initialen Anzeigenamen automatisch generiert", "description": "Beschreibung", "description-placeholder": "Beschreibung", "display-name": "Display Name", @@ -640,7 +640,7 @@ }, "confirmation-dialog": { "approve-file": { - "confirmationText": "Trotzdem genehmigen", + "confirmationText": "Dennoch freigeben", "denyText": "Nein, abbrechen", "question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.

Möchten Sie es trotzdem freigeben?", "title": "Warnung!", @@ -1616,7 +1616,7 @@ }, "file-status": { "analyse": "Analyse läuft", - "approved": "Genehmigt", + "approved": "Freigegeben", "error": "Reanalyse erforderlich", "figure-detection-analyzing": "", "full-processing": "Verarbeitung läuft", @@ -2383,7 +2383,7 @@ "red-manager": "{count, plural, one{Manager} other{Manager}}", "red-user": "Benutzer", "red-user-admin": "{count, plural, one{Benutzeradmin} other{Benutzeradmins}}", - "regular": "{count, plural, one{{regulärer Benutzer}} other{reguläre Benutzer}}" + "regular": "{count, plural, one{regulärer Benutzer} other{reguläre Benutzer}}" }, "search": { "active-dossiers": "Dokumente in aktiven Dossiers",