Merge branch 'master' into VM/RED-10301

This commit is contained in:
Valentin Mihai 2024-12-03 12:39:06 +02:00
commit fb68e63bd0
27 changed files with 260 additions and 84 deletions

View File

@ -26,6 +26,7 @@ export class DossierOverviewBulkActionsComponent {
readonly maxWidth = input<number>();
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'),
);

View File

@ -126,7 +126,7 @@ export class AnnotationActionsComponent {
async acceptRecommendation(): Promise<void> {
const annotations = untracked(this.annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept');
}
hideAnnotation() {

View File

@ -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;
}

View File

@ -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() {

View File

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

View File

@ -1,10 +1,12 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div
[translateParams]="{ type: isImage ? 'image' : isHint ? 'hint' : 'redaction' }"
[translate]="'edit-redaction.dialog.title'"
class="dialog-header heading-l"
></div>
<div class="dialog-header heading-l">
<span
[translateParams]="{ type: isImage ? 'image' : isHint ? 'hint' : 'redaction' }"
[translate]="'edit-redaction.dialog.title'"
[attr.help-mode-key]="helpModeKeyByType"
></span>
</div>
<div [class.image-dialog]="isRedacted && isImage" [class.rectangle-dialog]="allRectangles" class="dialog-content redaction">
<div *ngIf="!isImage && redactedTexts.length && !allRectangles" class="iqser-input-group">

View File

@ -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[] = [];

View File

@ -1,6 +1,11 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-header heading-l" [translate]="dialogTitle"></div>
<div class="dialog-header heading-l" [translate]="dialogTitle">
<span
[translate]="dialogTitle"
[attr.help-mode-key]="isSkipped ? 'skipped_force_DIALOG' : isImageHint ? 'hint_redact_DIALOG' : ''"
></span>
</div>
<div class="dialog-content force-annotation">
@if (!isImageHint) {

View File

@ -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;

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'redact-text.dialog.title'" [attr.help-mode-key]="helpModeKey"></span>
</div>
<div class="dialog-content redaction">
<div class="iqser-input-group">
@ -59,6 +61,7 @@
</iqser-icon-button>
<div [translate]="'redact-text.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

@ -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;
}

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'redact-text.dialog.title'" [attr.help-mode-key]="'add_redaction_DIALOG'"></span>
</div>
<div class="dialog-content redaction">
<div class="iqser-input-group w-full selected-text-group">
@ -134,6 +136,7 @@
/>
<div [translate]="'redact-text.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

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

View File

@ -1,9 +1,13 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div
[innerHTML]="(isBulk ? 'remove-redaction.dialog.title-bulk' : 'remove-redaction.dialog.title') | translate: typeTranslationArg"
class="dialog-header heading-l"
></div>
<div class="dialog-header heading-l">
<span
[innerHTML]="
(isBulk ? 'remove-redaction.dialog.title-bulk' : 'remove-redaction.dialog.title') | translate: typeTranslationArg
"
[attr.help-mode-key]="helpModeKeyByType"
></span>
</div>
<div [ngStyle]="{ height: dialogContentHeight + redactedTextsAreaHeight + 'px' }" class="dialog-content redaction">
<div class="iqser-input-group">

View File

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

View File

@ -1,6 +1,11 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span
[innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }"
[attr.help-mode-key]="dialogTitleHelpKey"
></span>
</div>
<div class="dialog-content redaction">
<ng-container *ngIf="!redaction.isImage && !redaction.AREA">
@ -48,6 +53,7 @@
></iqser-icon-button>
<div [translate]="'resize-redaction.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

@ -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;
}

View File

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

View File

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

View File

@ -97,6 +97,7 @@ export interface RedactTextResult {
export type RedactRecommendationData = EditRedactionData & {
applyToAllDossiers: boolean;
action?: 'resize' | 'accept';
};
export interface RedactRecommendationResult {

View File

@ -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<KeyboardEvent>(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<KeyboardEvent>(stopAndPrevent),

View File

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

View File

@ -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(

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form [formGroup]="form">
<div [translate]="'add-dossier-dialog.header-new'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'add-dossier-dialog.header-new'" [attr.help-mode-key]="'new_dossier_DIALOG'"></span>
</div>
<div *ngIf="config.IS_DOCUMINE && !availableReportTypes.length" class="inline-dialog-toast toast-warning">
<div [translate]="'add-dossier-dialog.no-report-types-warning'"></div>

View File

@ -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<T>(control: AbstractControl<T>) {
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';
}

View File

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

View File

@ -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.<br><br>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",