Merge branch 'master' into VM/RED-7340

This commit is contained in:
Valentin Mihai 2024-10-05 20:51:23 +03:00
commit af17c026ff
33 changed files with 1407 additions and 1278 deletions

View File

@ -9,7 +9,7 @@
.container { .container {
padding: 32px; padding: 32px;
width: 900px; width: 1000px;
max-width: 100%; max-width: 100%;
box-sizing: border-box; box-sizing: border-box;
} }

View File

@ -26,6 +26,12 @@ import {
} from '@red/domain'; } from '@red/domain';
import { annotationTypesTranslations } from '@translations/annotation-types-translations'; import { annotationTypesTranslations } from '@translations/annotation-types-translations';
interface AnnotationContent {
translation: string;
params: { [key: string]: string };
untranslatedContent: string;
}
export class AnnotationWrapper implements IListable { export class AnnotationWrapper implements IListable {
id: string; id: string;
superType: SuperType; superType: SuperType;
@ -36,7 +42,7 @@ export class AnnotationWrapper implements IListable {
numberOfComments = 0; numberOfComments = 0;
firstTopLeftPoint: IPoint; firstTopLeftPoint: IPoint;
shortContent: string; shortContent: string;
content: string; content: AnnotationContent;
value: string; value: string;
pageNumber: number; pageNumber: number;
dictionaryOperation = false; dictionaryOperation = false;
@ -279,7 +285,7 @@ export class AnnotationWrapper implements IListable {
); );
const content = this.#createContent(annotationWrapper, logEntry, isDocumine); const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content; annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content.untranslatedContent;
annotationWrapper.content = content; annotationWrapper.content = content;
const lastRelevantManualChange = logEntry.manualChanges?.at(-1); const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
@ -311,39 +317,57 @@ export class AnnotationWrapper implements IListable {
} }
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) { static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
let content = ''; let untranslatedContent = '';
const params: { [key: string]: string } = {};
if (logEntry.matchedRule) { if (logEntry.matchedRule) {
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`; params['hasRule'] = 'true';
params['matchedRule'] = logEntry.matchedRule.replace(/(^[, ]*)|([, ]*$)/g, '');
params['ruleSymbol'] = isDocumine ? ':' : '';
untranslatedContent += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
} }
if (logEntry.reason) { if (logEntry.reason) {
params['hasReason'] = 'true';
if (isDocumine && logEntry.reason.slice(-1) === '.') { if (isDocumine && logEntry.reason.slice(-1) === '.') {
logEntry.reason = logEntry.reason.slice(0, -1); logEntry.reason = logEntry.reason.slice(0, -1);
} }
if (!params['hasRule']) {
content += logEntry.reason + '\n\n'; params['reason'] = logEntry.reason.substring(0, 1).toUpperCase() + logEntry.reason.substring(1);
} else {
params['reason'] = logEntry.reason;
}
params['reason'] = params['reason'].replace(/(^[, ]*)|([, ]*$)/g, '');
untranslatedContent += logEntry.reason + '\n\n';
//remove leading and trailing commas and whitespaces //remove leading and trailing commas and whitespaces
content = content.replace(/(^[, ]*)|([, ]*$)/g, ''); untranslatedContent = untranslatedContent.replace(/(^[, ]*)|([, ]*$)/g, '');
content = content.substring(0, 1).toUpperCase() + content.substring(1); untranslatedContent = untranslatedContent.substring(0, 1).toUpperCase() + untranslatedContent.substring(1);
} }
if (annotationWrapper.legalBasis && !isDocumine) { if (annotationWrapper.legalBasis && !isDocumine) {
content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n'; params['hasLb'] = 'true';
params['legalBasis'] = annotationWrapper.legalBasis;
untranslatedContent += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
} }
if (annotationWrapper.hasBeenRemovedByManualOverride) { if (annotationWrapper.hasBeenRemovedByManualOverride) {
content += 'Removed by manual override'; params['hasOverride'] = 'true';
untranslatedContent += 'Removed by manual override';
} }
if (logEntry.section) { if (logEntry.section) {
params['hasSection'] = 'true';
params['sectionSymbol'] = isDocumine ? '' : ':';
params['shouldLower'] = untranslatedContent.length.toString();
params['section'] = logEntry.section;
let prefix = `In section${isDocumine ? '' : ':'} `; let prefix = `In section${isDocumine ? '' : ':'} `;
if (content.length) { if (untranslatedContent.length) {
prefix = ` ${prefix.toLowerCase()}`; prefix = ` ${prefix.toLowerCase()}`;
} }
content += `${prefix} "${logEntry.section}"`; untranslatedContent += `${prefix} "${logEntry.section}"`;
} }
return content; return { translation: _('annotation-content'), params: params, untranslatedContent: untranslatedContent };
} }
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) { static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {

View File

@ -21,6 +21,7 @@ import { NgForOf, NgIf } from '@angular/common';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect } from '@angular/material/select'; import { MatOption, MatSelect } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle'; import { MatSlideToggle } from '@angular/material/slide-toggle';
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
@Component({ @Component({
templateUrl: './user-profile-screen.component.html', templateUrl: './user-profile-screen.component.html',
@ -45,6 +46,7 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
protected readonly _userPreferenceService: UserPreferenceService, protected readonly _userPreferenceService: UserPreferenceService,
private readonly _changeRef: ChangeDetectorRef, private readonly _changeRef: ChangeDetectorRef,
private readonly _toaster: Toaster, private readonly _toaster: Toaster,
private readonly _pdfViewer: PdfViewer,
) { ) {
super(); super();
this._loadingService.start(); this._loadingService.start();
@ -107,6 +109,7 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
if (this.languageChanged) { if (this.languageChanged) {
await this._languageService.change(this.form.get('language').value); await this._languageService.change(this.form.get('language').value);
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
} }
if (this.themeChanged) { if (this.themeChanged) {

View File

@ -1,13 +1,13 @@
<div class="pagination noselect"> <div class="pagination noselect">
<div (click)="changePage.emit(1)" class="page-button" id="portraitPage"> <div (click)="changePage.emit(1)" class="page-button" id="portraitPage">
<mat-icon class="chevron-icon" svgIcon="iqser:nav-prev"></mat-icon> <mat-icon class="chevron-icon" svgIcon="iqser:nav-prev"></mat-icon>
Portrait {{ 'watermark-screen.pagination.portrait' | translate }}
</div> </div>
<div class="separator">/</div> <div class="separator">/</div>
<div (click)="changePage.emit(2)" class="page-button" id="landscapePage"> <div (click)="changePage.emit(2)" class="page-button" id="landscapePage">
Landscape {{ 'watermark-screen.pagination.landscape' | translate }}
<mat-icon class="chevron-icon" svgIcon="iqser:nav-next"></mat-icon> <mat-icon class="chevron-icon" svgIcon="iqser:nav-next"></mat-icon>
</div> </div>
</div> </div>

View File

@ -1,12 +1,13 @@
import { Component, EventEmitter, Output } from '@angular/core'; import { Component, EventEmitter, Output } from '@angular/core';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
@Component({ @Component({
selector: 'redaction-paginator', selector: 'redaction-paginator',
templateUrl: './paginator.component.html', templateUrl: './paginator.component.html',
styleUrls: ['./paginator.component.scss'], styleUrls: ['./paginator.component.scss'],
standalone: true, standalone: true,
imports: [MatIcon], imports: [MatIcon, TranslateModule],
}) })
export class PaginatorComponent { export class PaginatorComponent {
@Output() readonly changePage = new EventEmitter<number>(); @Output() readonly changePage = new EventEmitter<number>();

View File

@ -4,6 +4,7 @@
.container { .container {
padding: 32px; padding: 32px;
width: 1000px;
max-width: 100%; max-width: 100%;
box-sizing: border-box; box-sizing: border-box;
} }

View File

@ -119,7 +119,9 @@ export class BulkActionsService {
return; return;
} }
const fileWarnings = approvalResponse.map(response => ({ fileId: response.fileId, fileWarnings: response.fileWarnings })); const fileWarnings = approvalResponse
.filter(response => response.hasWarnings)
.map(response => ({ fileId: response.fileId, fileWarnings: response.fileWarnings }));
this._dialogService.openDialog( this._dialogService.openDialog(
'confirm', 'confirm',
{ {

View File

@ -1,161 +1,165 @@
<div @if (canPerformAnnotationActions && annotationPermissions()) {
*ngIf="canPerformAnnotationActions && annotationPermissions" <div [class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)" class="annotation-actions">
[class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)" <!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
class="annotation-actions" <ng-container *ngIf="resizing() && annotationPermissions().canResizeAnnotation">
> <iqser-circle-button
<!-- Resize Mode for annotation -> only resize accept and deny actions are available--> (action)="acceptResize()"
<ng-container *ngIf="resizing && annotationPermissions.canResizeAnnotation"> [buttonId]="
<iqser-circle-button annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-accept_resize' : 'annotations-accept_resize'
(action)="acceptResize()" "
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-accept_resize' : 'annotations-accept_resize'" [class.disabled]="!resized"
[class.disabled]="!resized" [tooltipPosition]="tooltipPosition"
[tooltipPosition]="tooltipPosition" [tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''" [type]="buttonType"
[type]="buttonType" icon="iqser:check"
icon="iqser:check" ></iqser-circle-button>
></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="cancelResize()" (action)="cancelResize()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-cancel_resize' : 'annotations-cancel_resize'" [buttonId]="
[tooltipPosition]="tooltipPosition" annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-cancel_resize' : 'annotations-cancel_resize'
[tooltip]="'annotation-actions.resize-cancel.label' | translate" "
[type]="buttonType" [tooltipPosition]="tooltipPosition"
icon="iqser:close" [tooltip]="'annotation-actions.resize-cancel.label' | translate"
></iqser-circle-button> [type]="buttonType"
</ng-container> icon="iqser:close"
></iqser-circle-button>
</ng-container>
<!-- Not resizing - standard actions --> <!-- Not resizing - standard actions -->
<ng-container *ngIf="!resizing"> <ng-container *ngIf="!resizing()">
<iqser-circle-button <iqser-circle-button
(action)="resize()" (action)="resize()"
*ngIf="canResize" *ngIf="canResize()"
[attr.help-mode-key]="helpModeKey('resize')" [attr.help-mode-key]="helpModeKey('resize')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-resize' : 'annotations-resize'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-resize' : 'annotations-resize'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize.label' | translate" [tooltip]="'annotation-actions.resize.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:resize" icon="iqser:resize"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.editRedaction(annotations)" (action)="annotationActionsService.editRedaction(annotations())"
*ngIf="canEdit" *ngIf="canEdit()"
[attr.help-mode-key]="helpModeKey('edit')" [attr.help-mode-key]="helpModeKey('edit')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-edit' : 'annotations-edit'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-edit' : 'annotations-edit'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.edit-redaction.label' | translate" [tooltip]="'annotation-actions.edit-redaction.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:edit" icon="iqser:edit"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="acceptRecommendation()" (action)="acceptRecommendation()"
*ngIf="canAcceptRecommendation" *ngIf="canAcceptRecommendation()"
[attr.help-mode-key]="helpModeKey('accept')" [attr.help-mode-key]="helpModeKey('accept')"
[buttonId]=" [buttonId]="
annotations.length === 1 annotations().length === 1
? 'annotation-' + annotations[0].id + '-accept_recommendation' ? 'annotation-' + annotations()[0].id + '-accept_recommendation'
: 'annotations-accept_recommendation' : 'annotations-accept_recommendation'
" "
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.accept-recommendation.label' | translate" [tooltip]="'annotation-actions.accept-recommendation.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:check" icon="iqser:check"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations)" (action)="annotationActionsService.convertHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()" *ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate" [tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="red:convert" icon="red:convert"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations)" (action)="annotationActionsService.removeHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()" *ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate" [tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:trash" icon="iqser:trash"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.undoDirectAction(annotations)" (action)="annotationActionsService.undoDirectAction(annotations())"
*allow="roles.redactions.deleteManual; if: annotationPermissions.canUndo" *allow="roles.redactions.deleteManual; if: annotationPermissions().canUndo"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.undo' | translate" [tooltip]="'annotation-actions.undo' | translate"
[type]="buttonType" [type]="buttonType"
icon="red:undo" icon="red:undo"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationReferencesService.show(annotations[0])" (action)="annotationReferencesService.show(annotations()[0])"
*ngIf="multiSelectService.inactive() && annotations[0].reference.length" *ngIf="multiSelectService.inactive() && annotations()[0].reference.length"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.see-references.label' | translate" [tooltip]="'annotation-actions.see-references.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="red:reference" icon="red:reference"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations)" (action)="annotationActionsService.forceAnnotation(annotations())"
*ngIf="canForceRedaction" *ngIf="canForceRedaction()"
[attr.help-mode-key]="isImageHint ? helpModeKey('redact') : helpModeKey('force')" [attr.help-mode-key]="isImageHint() ? helpModeKey('redact') : helpModeKey('force')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_redaction' : 'annotations-force_redaction'" [buttonId]="
[icon]="isImageHint ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'" annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_redaction' : 'annotations-force_redaction'
[tooltipPosition]="tooltipPosition" "
[tooltip]=" [icon]="isImageHint() ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'"
isImageHint [tooltipPosition]="tooltipPosition"
? ('annotation-actions.force-redaction.label-image-hint' | translate) [tooltip]="
: ('annotation-actions.force-redaction.label' | translate) isImageHint()
" ? ('annotation-actions.force-redaction.label-image-hint' | translate)
[type]="buttonType" : ('annotation-actions.force-redaction.label' | translate)
></iqser-circle-button> "
[type]="buttonType"
></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations, true)" (action)="annotationActionsService.forceAnnotation(annotations(), true)"
*ngIf="canForceHint" *ngIf="canForceHint()"
[attr.help-mode-key]="actionsHelpModeKey" [attr.help-mode-key]="actionsHelpModeKey"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_hint' : 'annotations-force_hint'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_hint' : 'annotations-force_hint'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.force-hint.label' | translate" [tooltip]="'annotation-actions.force-hint.label' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:thumb-up" icon="iqser:thumb-up"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="hideAnnotation()" (action)="hideAnnotation()"
*ngIf="isImage && isVisible() && !hideSkipped" *ngIf="isImage() && isVisible() && !hideSkipped()"
[attr.help-mode-key]="helpModeKey('hide')" [attr.help-mode-key]="helpModeKey('hide')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-hide' : 'annotations-hide'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-hide' : 'annotations-hide'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.hide' | translate" [tooltip]="'annotation-actions.hide' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:visibility-off" icon="iqser:visibility-off"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="showAnnotation()" (action)="showAnnotation()"
*ngIf="isImage && !isVisible() && !hideSkipped" *ngIf="isImage() && !isVisible() && !hideSkipped()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-show' : 'annotations-show'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-show' : 'annotations-show'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.show' | translate" [tooltip]="'annotation-actions.show' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:visibility" icon="iqser:visibility"
></iqser-circle-button> ></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="removeRedaction()" (action)="removeRedaction()"
*ngIf="canRemoveRedaction" *ngIf="canRemoveRedaction()"
[attr.help-mode-key]="helpModeKey('remove')" [attr.help-mode-key]="helpModeKey('remove')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-remove' : 'annotations-remove'" [buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-remove' : 'annotations-remove'"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate" [tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
[type]="buttonType" [type]="buttonType"
icon="iqser:trash" icon="iqser:trash"
></iqser-circle-button> ></iqser-circle-button>
</ng-container> </ng-container>
</div> </div>
}

View File

@ -1,4 +1,4 @@
import { Component, computed, Input, OnChanges } from '@angular/core'; import { Component, computed, input, Input, untracked } from '@angular/core';
import { CircleButtonComponent, getConfig, HelpModeService, IqserAllowDirective, IqserPermissionsService } from '@iqser/common-ui'; import { CircleButtonComponent, getConfig, HelpModeService, IqserAllowDirective, IqserPermissionsService } from '@iqser/common-ui';
import { AnnotationPermissions } from '@models/file/annotation.permissions'; import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -28,21 +28,77 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
standalone: true, standalone: true,
imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective], imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective],
}) })
export class AnnotationActionsComponent implements OnChanges { export class AnnotationActionsComponent {
#annotations: AnnotationWrapper[] = [];
readonly #isDocumine = getConfig().IS_DOCUMINE;
protected _annotationId = '';
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default; @Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
@Input() tooltipPosition: 'before' | 'above' = 'before'; @Input() tooltipPosition: 'before' | 'above' = 'before';
@Input() canPerformAnnotationActions: boolean; @Input() canPerformAnnotationActions: boolean;
@Input() alwaysVisible: boolean; @Input() alwaysVisible: boolean;
@Input() actionsHelpModeKey: string; @Input() actionsHelpModeKey: string;
readonly roles = Roles; readonly roles = Roles;
annotationPermissions: AnnotationPermissions; readonly annotations = input.required<AnnotationWrapper[], (AnnotationWrapper | undefined)[]>({
isImage = true; transform: value => value.filter(a => a !== undefined),
});
readonly isVisible = computed(() => { readonly isVisible = computed(() => {
const hidden = this._annotationManager.hidden(); const hidden = this._annotationManager.hidden();
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true); return this.annotations().reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
});
readonly somePending = computed(() => {
return this.annotations().some(a => a.pending);
});
readonly sameType = computed(() => {
const annotations = this.annotations();
const type = annotations[0].superType;
return annotations.every(a => a.superType === type);
});
readonly resizing = computed(() => {
return this.annotations().length === 1 && this.annotations()[0].id === this._annotationManager.resizingAnnotationId;
});
readonly viewerAnnotations = computed(() => {
return this._annotationManager.get(this.annotations());
});
readonly annotationPermissions = computed(() =>
AnnotationPermissions.forUser(
this._permissionsService.isApprover(this._state.dossier()),
this.annotations(),
this._state.dictionaries,
this._iqserPermissionsService,
this._state.file().excludedFromAutomaticAnalysis,
),
);
readonly hideSkipped = computed(() => this.skippedService.hideSkipped() && this.annotations().some(a => a.isSkipped));
readonly isImageHint = computed(() => this.annotations().every(a => a.IMAGE_HINT));
readonly isImage = computed(() => this.annotations().reduce((acc, a) => acc && a.isImage, true));
readonly canRemoveRedaction = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canRemoveRedaction && this.sameType(),
);
readonly canForceRedaction = computed(() => this.annotationChangesAllowed() && this.annotationPermissions().canForceRedaction);
readonly canForceHint = computed(() => this.annotationChangesAllowed() && this.annotationPermissions().canForceHint);
readonly canAcceptRecommendation = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canAcceptRecommendation,
);
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly annotationChangesAllowed = computed(
() => (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.somePending(),
);
readonly canResize = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canResizeAnnotation && this.annotations().length === 1,
);
readonly canEdit = computed(() => {
const canEditRedactions =
this.annotationPermissions().canChangeLegalBasis ||
this.annotationPermissions().canRecategorizeAnnotation ||
this.annotationPermissions().canForceHint ||
this.annotationPermissions().canForceRedaction;
return (
this.annotationChangesAllowed() &&
(this.annotations().length > 1
? this.#isDocumine
? this.annotationPermissions().canEditAnnotations
: this.annotationPermissions().canEditHints ||
this.annotationPermissions().canEditImages ||
this.annotationPermissions().canEditAnnotations
: canEditRedactions)
);
}); });
constructor( constructor(
@ -58,137 +114,54 @@ export class AnnotationActionsComponent implements OnChanges {
readonly annotationReferencesService: AnnotationReferencesService, readonly annotationReferencesService: AnnotationReferencesService,
) {} ) {}
get annotations(): AnnotationWrapper[] { get resized(): boolean {
return this.#annotations;
}
get isImageHint(): boolean {
return this.annotations.every(annotation => annotation.IMAGE_HINT);
}
@Input()
set annotations(annotations: AnnotationWrapper[]) {
this.#annotations = annotations.filter(a => a !== undefined);
this.isImage = this.#annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
this._annotationId = this.#annotations[0]?.id;
}
get canEdit(): boolean {
const canEditRedactions =
this.annotationPermissions.canChangeLegalBasis ||
this.annotationPermissions.canRecategorizeAnnotation ||
this.annotationPermissions.canForceHint ||
this.annotationPermissions.canForceRedaction;
return (
this.#annotationChangesAllowed &&
(this.annotations.length > 1
? this.#isDocumine
? this.annotationPermissions.canEditAnnotations
: this.annotationPermissions.canEditHints ||
this.annotationPermissions.canEditImages ||
this.annotationPermissions.canEditAnnotations
: canEditRedactions)
);
}
get canResize(): boolean {
return this.#annotationChangesAllowed && this.annotationPermissions.canResizeAnnotation && this.annotations.length === 1;
}
get canRemoveRedaction(): boolean {
return this.#annotationChangesAllowed && this.annotationPermissions.canRemoveRedaction && this.#sameType;
}
get canForceRedaction() {
return this.#annotationChangesAllowed && this.annotationPermissions.canForceRedaction;
}
get canForceHint() {
return this.#annotationChangesAllowed && this.annotationPermissions.canForceHint;
}
get canAcceptRecommendation() {
return this.#annotationChangesAllowed && this.annotationPermissions.canAcceptRecommendation;
}
get viewerAnnotations() {
return this._annotationManager.get(this.#annotations);
}
get resizing() {
return this.#annotations?.length === 1 && this.#annotations?.[0].id === this._annotationManager.resizingAnnotationId;
}
get resized() {
return this._annotationManager.annotationHasBeenResized; return this._annotationManager.annotationHasBeenResized;
} }
get hideSkipped() { async removeRedaction(): Promise<void> {
return this.skippedService.hideSkipped() && this.annotations.some(a => a.isSkipped); const annotations = untracked(this.annotations);
const permissions = untracked(this.annotationPermissions);
await this.annotationActionsService.removeRedaction(annotations, permissions);
} }
get #sameType() { async acceptRecommendation(): Promise<void> {
const type = this.annotations[0].superType; const annotations = untracked(this.annotations);
return this.annotations.every(a => a.superType === type); await this.annotationActionsService.convertRecommendationToAnnotation(annotations);
}
ngOnChanges(): void {
this.#setPermissions();
}
removeRedaction() {
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
}
acceptRecommendation() {
return this.annotationActionsService.convertRecommendationToAnnotation(this.annotations);
} }
hideAnnotation() { hideAnnotation() {
this._annotationManager.hide(this.viewerAnnotations); const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.hide(viewerAnnotations);
this._annotationManager.deselect(); this._annotationManager.deselect();
this._annotationManager.addToHidden(this.viewerAnnotations[0].Id); this._annotationManager.addToHidden(viewerAnnotations[0].Id);
} }
showAnnotation() { showAnnotation() {
this._annotationManager.show(this.viewerAnnotations); const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.show(viewerAnnotations);
this._annotationManager.deselect(); this._annotationManager.deselect();
this._annotationManager.removeFromHidden(this.viewerAnnotations[0].Id); this._annotationManager.removeFromHidden(viewerAnnotations[0].Id);
} }
resize() { resize() {
return this.annotationActionsService.resize(this.#annotations[0]); const annotations = untracked(this.annotations);
return this.annotationActionsService.resize(annotations[0]);
} }
acceptResize() { acceptResize() {
if (this.resized) { if (this.resized) {
return this.annotationActionsService.acceptResize(this.#annotations[0], this.annotationPermissions); const annotations = untracked(this.annotations);
const permissions = untracked(this.annotationPermissions);
return this.annotationActionsService.acceptResize(annotations[0], permissions);
} }
} }
cancelResize() { cancelResize() {
return this.annotationActionsService.cancelResize(this.#annotations[0]); const annotations = untracked(this.annotations);
return this.annotationActionsService.cancelResize(annotations[0]);
} }
helpModeKey(action: string) { helpModeKey(action: string) {
return this.#isDocumine ? `${action}_annotation` : `${this.actionsHelpModeKey}_${action}`; return this.#isDocumine ? `${action}_annotation` : `${this.actionsHelpModeKey}_${action}`;
} }
#setPermissions() {
this.annotationPermissions = AnnotationPermissions.forUser(
this._permissionsService.isApprover(this._state.dossier()),
this.#annotations,
this._state.dictionaries,
this._iqserPermissionsService,
this._state.file().excludedFromAutomaticAnalysis,
);
}
get #annotationChangesAllowed() {
return (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.#somePending;
}
get #somePending() {
return this.#annotations.some(a => a.pending);
}
} }

View File

@ -1,37 +1,42 @@
<div class="active-bar-marker"></div> <div class="active-bar-marker"></div>
<div [class.removed]="annotation.item.isRemoved" class="annotation"> <div [class.removed]="annotation().item.isRemoved" class="annotation">
<redaction-annotation-card <redaction-annotation-card
[annotation]="annotation.item" [annotation]="annotation().item"
[isSelected]="annotation.isSelected" [isSelected]="annotation().isSelected"
[matTooltip]="annotation.item.content" [matTooltip]="annotation().item.content.translation | translate: annotation().item.content.params | replaceNbsp"
matTooltipPosition="above" matTooltipPosition="above"
></redaction-annotation-card> ></redaction-annotation-card>
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper"> @if (!annotation().item.isEarmark) {
<div <div class="actions-wrapper">
*ngIf="!annotation.item.pending" @if (!annotation().item.pending) {
(click)="showComments = !showComments" <div
[matTooltip]="'comments.comments' | translate: { count: annotation.item.numberOfComments }" (click)="showComments = !showComments"
class="comments-counter" [matTooltip]="'comments.comments' | translate: { count: annotation().item.numberOfComments }"
iqserStopPropagation class="comments-counter"
matTooltipPosition="above" iqserStopPropagation
> matTooltipPosition="above"
<mat-icon svgIcon="red:comment"></mat-icon> >
{{ annotation.item.numberOfComments }} <mat-icon svgIcon="red:comment"></mat-icon>
</div> {{ annotation().item.numberOfComments }}
</div>
}
<div *ngIf="multiSelectService.inactive()" class="actions"> @if (multiSelectService.inactive()) {
<redaction-annotation-actions <div class="actions">
[annotations]="[annotation.item]" <redaction-annotation-actions
[actionsHelpModeKey]="actionsHelpModeKey" [actionsHelpModeKey]="actionsHelpModeKey()"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()" [annotations]="[annotation().item]"
></redaction-annotation-actions> [canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
</div>
}
</div> </div>
</div> }
<ng-container *ngIf="showComments"> @if (showComments) {
<redaction-comments [annotation]="annotation.item"></redaction-comments> <redaction-comments [annotation]="annotation().item"></redaction-comments>
<div <div
(click)="showComments = false" (click)="showComments = false"
@ -39,7 +44,7 @@
iqserStopPropagation iqserStopPropagation
translate="comments.hide-comments" translate="comments.hide-comments"
></div> ></div>
</ng-container> }
</div> </div>
<redaction-annotation-details [annotation]="annotation"></redaction-annotation-details> <redaction-annotation-details [annotation]="annotation()"></redaction-annotation-details>

View File

@ -1,4 +1,4 @@
import { Component, HostBinding, inject, Input, OnChanges } from '@angular/core'; import { Component, computed, effect, HostBinding, inject, input } from '@angular/core';
import { getConfig, StopPropagationDirective } from '@iqser/common-ui'; import { getConfig, StopPropagationDirective } from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ListItem } from '@models/file/list-item'; import { ListItem } from '@models/file/list-item';
@ -6,13 +6,13 @@ import { MultiSelectService } from '../../services/multi-select.service';
import { PdfProxyService } from '../../services/pdf-proxy.service'; import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ActionsHelpModeKeys } from '../../utils/constants'; import { ActionsHelpModeKeys } from '../../utils/constants';
import { AnnotationCardComponent } from '../annotation-card/annotation-card.component'; import { AnnotationCardComponent } from '../annotation-card/annotation-card.component';
import { NgIf } from '@angular/common';
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { AnnotationActionsComponent } from '../annotation-actions/annotation-actions.component'; import { AnnotationActionsComponent } from '../annotation-actions/annotation-actions.component';
import { CommentsComponent } from '../comments/comments.component'; import { CommentsComponent } from '../comments/comments.component';
import { AnnotationDetailsComponent } from '../annotation-details/annotation-details.component'; import { AnnotationDetailsComponent } from '../annotation-details/annotation-details.component';
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
@Component({ @Component({
selector: 'redaction-annotation-wrapper', selector: 'redaction-annotation-wrapper',
@ -21,7 +21,6 @@ import { AnnotationDetailsComponent } from '../annotation-details/annotation-det
standalone: true, standalone: true,
imports: [ imports: [
AnnotationCardComponent, AnnotationCardComponent,
NgIf,
MatTooltip, MatTooltip,
TranslateModule, TranslateModule,
StopPropagationDirective, StopPropagationDirective,
@ -29,28 +28,31 @@ import { AnnotationDetailsComponent } from '../annotation-details/annotation-det
AnnotationActionsComponent, AnnotationActionsComponent,
CommentsComponent, CommentsComponent,
AnnotationDetailsComponent, AnnotationDetailsComponent,
ReplaceNbspPipe,
], ],
}) })
export class AnnotationWrapperComponent implements OnChanges { export class AnnotationWrapperComponent {
readonly #isDocumine = getConfig().IS_DOCUMINE; readonly annotation = input.required<ListItem<AnnotationWrapper>>();
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active: boolean;
readonly actionsHelpModeKey = computed(() => this.#getActionsHelpModeKey(this.annotation().item));
showComments = false;
protected readonly pdfProxyService = inject(PdfProxyService); protected readonly pdfProxyService = inject(PdfProxyService);
protected readonly multiSelectService = inject(MultiSelectService); protected readonly multiSelectService = inject(MultiSelectService);
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>; readonly #isDocumine = getConfig().IS_DOCUMINE;
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey: string;
showComments = false;
ngOnChanges() { constructor() {
this.annotationId = this.annotation.item.id; effect(() => {
this.active = this.annotation.isSelected; this.active = this.annotation().isSelected;
this.actionsHelpModeKey = this.#getActionsHelpModeKey(); this.annotationId = this.annotation().item.id;
});
} }
#getActionsHelpModeKey(): string { #getActionsHelpModeKey(item: AnnotationWrapper): string {
if (!this.#isDocumine) { if (!this.#isDocumine) {
const superType = this.annotation.item.superTypeLabel?.split('.')[1]; const superType = item.superTypeLabel?.split('.')[1];
const type = this.annotation.item.type; const type = item.type;
if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) { if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) {
return ActionsHelpModeKeys[`${superType}-${type}`]; return ActionsHelpModeKeys[`${superType}-${type}`];
} }

View File

@ -1,16 +1,16 @@
<ng-container *ngFor="let annotation of annotations; let idx = index; trackBy: _trackBy"> @for (annotation of annotations(); track annotation.item.id) {
<div *ngIf="showHighlightGroup(idx) as highlightGroup" class="workload-separator"> @if (displayedHighlightGroups()[$index]; as highlightGroup) {
<redaction-highlights-separator [annotation]="annotation.item" [highlightGroup]="highlightGroup"></redaction-highlights-separator> <redaction-highlights-separator [annotation]="annotation.item" [highlightGroup]="highlightGroup"></redaction-highlights-separator>
</div> }
<redaction-annotation-wrapper <redaction-annotation-wrapper
*ngIf="!annotation.item.hiddenInWorkload"
(click)="annotationClicked(annotation.item, $event)" (click)="annotationClicked(annotation.item, $event)"
*ngIf="!annotation.item.hiddenInWorkload"
[annotation]="annotation" [annotation]="annotation"
[id]="'annotation-' + annotation.item.id"
[class.documine-wrapper]="isDocumine" [class.documine-wrapper]="isDocumine"
[id]="'annotation-' + annotation.item.id"
></redaction-annotation-wrapper> ></redaction-annotation-wrapper>
</ng-container> }
<redaction-annotation-references-list <redaction-annotation-references-list
(referenceClicked)="referenceClicked($event)" (referenceClicked)="referenceClicked($event)"

View File

@ -1,4 +1,4 @@
import { Component, computed, ElementRef, EventEmitter, Input, Output } from '@angular/core'; import { Component, computed, ElementRef, input, output } from '@angular/core';
import { getConfig, HasScrollbarDirective } from '@iqser/common-ui'; import { getConfig, HasScrollbarDirective } from '@iqser/common-ui';
import { FilterService } from '@iqser/common-ui/lib/filtering'; import { FilterService } from '@iqser/common-ui/lib/filtering';
import { IqserEventTarget } from '@iqser/common-ui/lib/utils'; import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
@ -24,13 +24,24 @@ import { AnnotationReferencesListComponent } from '../annotation-references-list
imports: [NgForOf, NgIf, HighlightsSeparatorComponent, AnnotationWrapperComponent, AnnotationReferencesListComponent], imports: [NgForOf, NgIf, HighlightsSeparatorComponent, AnnotationWrapperComponent, AnnotationReferencesListComponent],
}) })
export class AnnotationsListComponent extends HasScrollbarDirective { export class AnnotationsListComponent extends HasScrollbarDirective {
@Input({ required: true }) annotations: ListItem<AnnotationWrapper>[]; readonly annotations = input.required<ListItem<AnnotationWrapper>[]>();
@Output() readonly pagesPanelActive = new EventEmitter<boolean>(); readonly pagesPanelActive = output<boolean>();
readonly #earmarkGroups = computed(() => { readonly displayedHighlightGroups = computed(() => {
if (this._viewModeService.isEarmarks()) { const isEarMarks = this._viewModeService.isEarmarks();
return this.#getEarmarksGroups(); let result: Record<number, EarmarkGroup> = {};
if (isEarMarks) {
const annotations = this.annotations();
const earmarkGroups = this.#getEarmarksGroups(annotations);
for (let idx = 0; idx < annotations.length; ++idx) {
const group = earmarkGroups.find(h => h.startIdx === idx);
if (group) {
result[idx] = group;
}
}
} }
return [] as EarmarkGroup[];
return result;
}); });
protected readonly isDocumine = getConfig().IS_DOCUMINE; protected readonly isDocumine = getConfig().IS_DOCUMINE;
@ -77,26 +88,20 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
} }
} }
showHighlightGroup(idx: number): EarmarkGroup { #getEarmarksGroups(annotations: ListItem<AnnotationWrapper>[]) {
return this._viewModeService.isEarmarks() && this.#earmarkGroups().find(h => h.startIdx === idx);
}
protected readonly _trackBy = (index: number, listItem: ListItem<AnnotationWrapper>) => listItem.item.id;
#getEarmarksGroups() {
const earmarksGroups: EarmarkGroup[] = []; const earmarksGroups: EarmarkGroup[] = [];
if (!this.annotations?.length) { if (!annotations.length) {
return earmarksGroups; return earmarksGroups;
} }
let lastGroup: EarmarkGroup; let lastGroup: EarmarkGroup;
for (let idx = 0; idx < this.annotations.length; ++idx) { for (let idx = 0; idx < annotations.length; ++idx) {
if (idx === 0 || this.annotations[idx].item.color !== this.annotations[idx - 1].item.color) { if (idx === 0 || annotations[idx].item.color !== annotations[idx - 1].item.color) {
if (lastGroup) { if (lastGroup) {
earmarksGroups.push(lastGroup); earmarksGroups.push(lastGroup);
} }
lastGroup = { startIdx: idx, length: 1, color: this.annotations[idx].item.color }; lastGroup = { startIdx: idx, length: 1, color: annotations[idx].item.color };
} else { } else {
lastGroup.length += 1; lastGroup.length += 1;
} }

View File

@ -1,6 +1,8 @@
<div [ngStyle]="gridConfig()" class="table"> <div [ngStyle]="gridConfig()" class="table">
@for (column of _columns(); track column.label) { @for (column of _columns(); track column.label) {
<div [ngClass]="{ hide: !!column.hide }" class="col cell">{{ column.label }}</div> <div [ngClass]="{ hide: !!column.hide }" class="col cell">
<label>{{ column.label }}</label>
</div>
} }
@for (row of _data(); track $index) { @for (row of _data(); track $index) {
@for (cell of row; track cell.label) { @for (cell of row; track cell.label) {

View File

@ -12,6 +12,11 @@
top: 0; top: 0;
z-index: 1; z-index: 1;
background: white; background: white;
label {
opacity: 0.7;
font-weight: normal;
}
} }
.cell { .cell {

View File

@ -1,5 +1,5 @@
import { ActivatedRouteSnapshot, NavigationExtras, Router, RouterLink } from '@angular/router'; import { ActivatedRouteSnapshot, NavigationExtras, Router, RouterLink } from '@angular/router';
import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, untracked, ViewChild } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard'; import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import { import {
@ -101,13 +101,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
readonly roles = Roles; readonly roles = Roles;
readonly fileId = this.state.fileId; readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId; readonly dossierId = this.state.dossierId;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
@ViewChild('annotationFilterTemplate', { @ViewChild('annotationFilterTemplate', {
read: TemplateRef, read: TemplateRef,
static: false, static: false,
}) })
private readonly _filterTemplate: TemplateRef<unknown>; private readonly _filterTemplate: TemplateRef<unknown>;
#loadAllAnnotationsEnabled = false; #loadAllAnnotationsEnabled = false;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
constructor( constructor(
readonly pdf: PdfViewer, readonly pdf: PdfViewer,
@ -180,7 +180,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
effect(() => { effect(() => {
this._viewModeService.viewMode(); this._viewModeService.viewMode();
this.updateViewMode().then(); this.#updateViewMode().then();
}); });
effect(() => { effect(() => {
@ -208,7 +208,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const earmarks$ = isEarmarksViewMode$.pipe( const earmarks$ = isEarmarksViewMode$.pipe(
tap(() => this._loadingService.start()), tap(() => this._loadingService.start()),
switchMap(() => this._fileDataService.loadEarmarks()), switchMap(() => this._fileDataService.loadEarmarks()),
tap(() => this.updateViewMode().then(() => this._loadingService.stop())), tap(() => this.#updateViewMode().then(() => this._loadingService.stop())),
); );
const currentPageIfEarmarksView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe( const currentPageIfEarmarksView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe(
@ -234,7 +234,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return isChangingFromEarmarksViewMode$.pipe( return isChangingFromEarmarksViewMode$.pipe(
map(() => this._fileDataService.earmarks().get(this.pdf.currentPage()) ?? []), map(() => this._fileDataService.earmarks().get(this.pdf.currentPage()) ?? []),
map(earmarks => this.deleteAnnotations(earmarks, [])), map(earmarks => this.#deleteAnnotations(earmarks, [])),
); );
} }
@ -243,61 +243,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS); this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
} }
async updateViewMode(): Promise<void> {
this._logger.info(`[PDF] Update ${this._viewModeService.viewMode()} view mode`);
const annotations = this._annotationManager.get(a => bool(a.getCustomData('redact-manager')));
const redactions = annotations.filter(a => bool(a.getCustomData('redaction')));
switch (this._viewModeService.viewMode()) {
case ViewModes.STANDARD: {
const wrappers = this._fileDataService.annotations();
const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id);
const standardEntries = annotations
.filter(a => !bool(a.getCustomData('changeLogRemoved')) && !this._annotationManager.isHidden(a.Id))
.filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(
a =>
bool(a.getCustomData('changeLogRemoved')) ||
this._annotationManager.isHidden(a.Id) ||
(this._skippedService.hideSkipped() && bool(a.getCustomData('skipped'))),
);
this._readableRedactionsService.setAnnotationsColor(standardEntries, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(standardEntries, true);
this._annotationManager.show(standardEntries);
this._annotationManager.hide(nonStandardEntries);
break;
}
case ViewModes.DELTA: {
const changeLogEntries = annotations.filter(a => bool(a.getCustomData('changeLog')));
const nonChangeLogEntries = annotations.filter(a => !bool(a.getCustomData('changeLog')));
this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(changeLogEntries, true);
this._annotationManager.show(changeLogEntries);
this._annotationManager.hide(nonChangeLogEntries);
break;
}
case ViewModes.REDACTED: {
const nonRedactionEntries = annotations.filter(
a => !bool(a.getCustomData('redaction')) || bool(a.getCustomData('changeLogRemoved')),
);
this._readableRedactionsService.setAnnotationsColor(redactions, 'redactionColor');
this._readableRedactionsService.setAnnotationsOpacity(redactions);
this._annotationManager.show(redactions);
this._annotationManager.hide(nonRedactionEntries);
break;
}
case ViewModes.TEXT_HIGHLIGHTS: {
this._annotationManager.hide(annotations);
}
}
this._logger.info('[PDF] Rebuild filters');
this.#rebuildFilters();
this._logger.info('[PDF] Update done');
}
ngOnDetach() { ngOnDetach() {
this._viewerHeaderService.resetCompareButtons(); this._viewerHeaderService.resetCompareButtons();
this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files) this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files)
@ -412,7 +357,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const annotations$ = this._fileDataService.annotations$.pipe( const annotations$ = this._fileDataService.annotations$.pipe(
startWith([] as AnnotationWrapper[]), startWith([] as AnnotationWrapper[]),
pairwise(), pairwise(),
tap(annotations => this.deleteAnnotations(...annotations)), tap(annotations => this.#deleteAnnotations(...annotations)),
tap(() => this.#updateFiltersAfterAnnotationsChanged()),
tap(() => this.#updateViewMode()),
); );
const currentPageIfNotHighlightsView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe( const currentPageIfNotHighlightsView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe(
@ -431,12 +378,72 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return combineLatest([currentPageAnnotations$, this._documentViewer.loaded$]).pipe( return combineLatest([currentPageAnnotations$, this._documentViewer.loaded$]).pipe(
filter(([, loaded]) => loaded), filter(([, loaded]) => loaded),
map(([annotations]) => annotations), map(([annotations]) => annotations),
tap(([oldA, newA]) => this.drawChangedAnnotations(oldA, newA)?.then(() => this.updateViewMode())), switchMap(async ([oldAnnotations, newAnnotations]) => {
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)), await this.#drawChangedAnnotations(oldAnnotations, newAnnotations);
return newAnnotations;
}),
tap(newAnnotations => this.#highlightSelectedAnnotations(newAnnotations)),
); );
} }
deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) { async #updateViewMode(): Promise<void> {
const viewMode = untracked(this._viewModeService.viewMode);
this._logger.info(`[PDF] Update ${viewMode} view mode`);
const annotations = this._annotationManager.get(a => bool(a.getCustomData('redact-manager')));
const redactions = annotations.filter(a => bool(a.getCustomData('redaction')));
switch (viewMode) {
case ViewModes.STANDARD: {
const wrappers = this._fileDataService.annotations();
// TODO: const wrappers = untracked(this._fileDataService.annotations);
const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id);
const standardEntries = annotations
.filter(a => !bool(a.getCustomData('changeLogRemoved')) && !this._annotationManager.isHidden(a.Id))
.filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(
a =>
bool(a.getCustomData('changeLogRemoved')) ||
this._annotationManager.isHidden(a.Id) ||
(untracked(this._skippedService.hideSkipped) && bool(a.getCustomData('skipped'))),
);
this._readableRedactionsService.setAnnotationsColor(standardEntries, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(standardEntries, true);
this._annotationManager.show(standardEntries);
this._annotationManager.hide(nonStandardEntries);
break;
}
case ViewModes.DELTA: {
const changeLogEntries = annotations.filter(a => bool(a.getCustomData('changeLog')));
const nonChangeLogEntries = annotations.filter(a => !bool(a.getCustomData('changeLog')));
this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(changeLogEntries, true);
this._annotationManager.show(changeLogEntries);
this._annotationManager.hide(nonChangeLogEntries);
break;
}
case ViewModes.REDACTED: {
const nonRedactionEntries = annotations.filter(
a => !bool(a.getCustomData('redaction')) || bool(a.getCustomData('changeLogRemoved')),
);
this._readableRedactionsService.setAnnotationsColor(redactions, 'redactionColor');
this._readableRedactionsService.setAnnotationsOpacity(redactions);
this._annotationManager.show(redactions);
this._annotationManager.hide(nonRedactionEntries);
break;
}
case ViewModes.TEXT_HIGHLIGHTS: {
this._annotationManager.hide(annotations);
}
}
this._logger.info('[PDF] Rebuild filters');
this.#rebuildFilters();
this._logger.info('[PDF] Update done');
}
#deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
const annotationsToDelete = oldAnnotations.filter(oldAnnotation => { const annotationsToDelete = oldAnnotations.filter(oldAnnotation => {
const newAnnotation = newAnnotations.find(byId(oldAnnotation.id)); const newAnnotation = newAnnotations.find(byId(oldAnnotation.id));
return newAnnotation ? hasChanges(oldAnnotation, newAnnotation) : true; return newAnnotation ? hasChanges(oldAnnotation, newAnnotation) : true;
@ -446,11 +453,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._annotationManager.delete(annotationsToDelete); this._annotationManager.delete(annotationsToDelete);
} }
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) { async #drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]): Promise<void> {
const annotationsToDraw = this.#getAnnotationsToDraw(oldAnnotations, newAnnotations); const annotationsToDraw = this.#getAnnotationsToDraw(oldAnnotations, newAnnotations);
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw); this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
this._annotationManager.delete(annotationsToDraw); this._annotationManager.delete(annotationsToDraw);
return this.#cleanupAndRedrawAnnotations(annotationsToDraw); await this.#cleanupAndRedrawAnnotations(annotationsToDraw);
} }
async #openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) { async #openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
@ -641,7 +648,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS), filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS),
switchMap(() => { switchMap(() => {
// TODO: this switchMap is ugly, to be refactored // TODO: this switchMap is ugly, to be refactored
const annotations = this._fileDataService.annotations(); const annotations = untracked(this._fileDataService.annotations);
const showWarning = !this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning); const showWarning = !this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning);
const annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD; const annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD;
@ -654,7 +661,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(([confirmed]) => confirmed), filter(([confirmed]) => confirmed),
map(([, annotations]) => { map(([, annotations]) => {
this.#loadAllAnnotationsEnabled = true; this.#loadAllAnnotationsEnabled = true;
this.drawChangedAnnotations([], annotations).then(() => { this.#drawChangedAnnotations([], annotations).then(() => {
this._toaster.success(_('load-all-annotations-success')); this._toaster.success(_('load-all-annotations-success'));
this._viewerHeaderService.disableLoadAllAnnotations(); this._viewerHeaderService.disableLoadAllAnnotations();
}); });
@ -662,7 +669,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
) )
.subscribe(); .subscribe();
this.addActiveScreenSubscription = this._readableRedactionsService.active$.pipe(map(() => this.updateViewMode())).subscribe(); this.addActiveScreenSubscription = this._readableRedactionsService.active$
.pipe(switchMap(() => this.#updateViewMode()))
.subscribe();
this.addActiveScreenSubscription = combineLatest([this._viewModeService.viewMode$, this.state.file$, this._documentViewer.loaded$]) this.addActiveScreenSubscription = combineLatest([this._viewModeService.viewMode$, this.state.file$, this._documentViewer.loaded$])
.pipe( .pipe(
@ -701,11 +710,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._errorService.set(error); this._errorService.set(error);
} }
#cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>) { async #cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>): Promise<void> {
if (!newAnnotations.length) { if (!newAnnotations.length) {
return undefined; return undefined;
} }
await this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
}
#updateFiltersAfterAnnotationsChanged(): void {
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || []; const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
this.#rebuildFilters(); this.#rebuildFilters();
@ -714,8 +727,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.#handleDeltaAnnotationFilters(currentFilters); this.#handleDeltaAnnotationFilters(currentFilters);
}, 100); }, 100);
} }
return this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
} }
#handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) { #handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {

View File

@ -277,7 +277,6 @@ export class AnnotationActionsService {
const text = annotation.AREA ? annotation.value : isImageText; const text = annotation.AREA ? annotation.value : isImageText;
const isApprover = this._permissionsService.isApprover(dossier); const isApprover = this._permissionsService.isApprover(dossier);
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId); const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
const isUnprocessed = annotation.pending;
const data: ResizeRedactionData = { const data: ResizeRedactionData = {
redaction: annotation, redaction: annotation,
@ -308,7 +307,7 @@ export class AnnotationActionsService {
await this.cancelResize(annotation); await this.cancelResize(annotation);
const { fileId, dossierId, file } = this._state; const { fileId, dossierId } = this._state;
const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId, includeUnprocessed); const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId, includeUnprocessed);
return this.#processObsAndEmit(request); return this.#processObsAndEmit(request);
} }
@ -471,6 +470,36 @@ export class AnnotationActionsService {
// todo: might not be correct, probably shouldn't get to this point if they are not all the same // todo: might not be correct, probably shouldn't get to this point if they are not all the same
const isHint = redactions.every(r => r.isHint); const isHint = redactions.every(r => r.isHint);
const { dossierId, fileId } = this._state; const { dossierId, fileId } = this._state;
const maximumNumberEntries = 100;
if (removeFromDictionary && (body as List<IRemoveRedactionRequest>).length > maximumNumberEntries) {
const requests = body as List<IRemoveRedactionRequest>;
const splitNumber = Math.floor(requests.length / maximumNumberEntries);
const remainder = requests.length % maximumNumberEntries;
const splitRequests = [];
for (let i = 0; i < splitNumber; i++) {
splitRequests.push(requests.slice(i * maximumNumberEntries, (i + 1) * maximumNumberEntries));
}
splitRequests.push(requests.slice(splitNumber * maximumNumberEntries, splitNumber * maximumNumberEntries + remainder));
const promises = [];
for (const split of splitRequests) {
promises.push(
firstValueFrom(
this._manualRedactionService.removeRedaction(
split,
dossierId,
fileId,
removeFromDictionary,
isHint,
includeUnprocessed,
dialogResult.bulkLocal,
),
),
);
}
Promise.all(promises).finally(() => this._fileDataService.annotationsChanged());
return;
}
this.#processObsAndEmit( this.#processObsAndEmit(
this._manualRedactionService.removeRedaction( this._manualRedactionService.removeRedaction(
body, body,

View File

@ -1,5 +1,4 @@
import { computed, Injectable, Signal, signal } from '@angular/core'; import { computed, Injectable, Signal, signal } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FileDataService } from './file-data.service'; import { FileDataService } from './file-data.service';
@ -7,7 +6,6 @@ import { FileDataService } from './file-data.service';
export class AnnotationReferencesService { export class AnnotationReferencesService {
readonly references: Signal<AnnotationWrapper[]>; readonly references: Signal<AnnotationWrapper[]>;
readonly annotation: Signal<AnnotationWrapper | undefined>; readonly annotation: Signal<AnnotationWrapper | undefined>;
private readonly _annotation$ = new BehaviorSubject<AnnotationWrapper | undefined>(undefined);
readonly #annotation = signal<AnnotationWrapper | undefined>(undefined); readonly #annotation = signal<AnnotationWrapper | undefined>(undefined);
constructor(private readonly _fileDataService: FileDataService) { constructor(private readonly _fileDataService: FileDataService) {

View File

@ -1,4 +1,4 @@
import { computed, effect, inject, Injectable, NgZone } from '@angular/core'; import { computed, effect, inject, Injectable, NgZone, untracked } from '@angular/core';
import { getConfig, IqserPermissionsService } from '@iqser/common-ui'; import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
import { getCurrentUser } from '@iqser/common-ui/lib/users'; import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { isJustOne, shareDistinctLast, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils'; import { isJustOne, shareDistinctLast, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils';
@ -49,7 +49,7 @@ export class PdfProxyService {
readonly redactTextRequested$ = new Subject<ManualRedactionEntryWrapper>(); readonly redactTextRequested$ = new Subject<ManualRedactionEntryWrapper>();
readonly currentUser = getCurrentUser<User>(); readonly currentUser = getCurrentUser<User>();
readonly pageChanged$ = this._pdf.pageChanged$.pipe( readonly pageChanged$ = this._pdf.pageChanged$.pipe(
tap(() => this.#handleExcludedPageActions()), tap(pageNumber => this.#handleExcludedPageActions(pageNumber)),
tap(() => { tap(() => {
if (this._multiSelectService.inactive()) { if (this._multiSelectService.inactive()) {
this._annotationManager.deselect(); this._annotationManager.deselect();
@ -98,7 +98,7 @@ export class PdfProxyService {
effect( effect(
() => { () => {
const canPerformActions = this.canPerformActions(); const canPerformActions = this.canPerformActions();
this._pdf.isCompareMode(); const isCompareMode = this._pdf.isCompareMode();
this.#configureTextPopup(); this.#configureTextPopup();
@ -109,7 +109,8 @@ export class PdfProxyService {
this.#deactivateMultiSelect(); this.#deactivateMultiSelect();
} }
this.#handleExcludedPageActions(); const currentPage = untracked(this._pdf.currentPage);
this.#handleExcludedPageActions(currentPage);
}, },
{ allowSignalWrites: true }, { allowSignalWrites: true },
); );
@ -227,8 +228,9 @@ export class PdfProxyService {
this.redactTextRequested$.next({ manualRedactionEntry, type }); this.redactTextRequested$.next({ manualRedactionEntry, type });
} }
#handleExcludedPageActions() { #handleExcludedPageActions(currentPage: number) {
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage()); const isCurrentPageExcluded = this._state.file().isPageExcluded(currentPage);
if (!isCurrentPageExcluded) { if (!isCurrentPageExcluded) {
return; return;
} }

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { List } from '@iqser/common-ui/lib/utils'; import { List } from '@iqser/common-ui/lib/utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import { IRectangle, ISectionRectangle, IPoint, SuperTypes } from '@red/domain'; import { IPoint, IRectangle, ISectionRectangle, SuperTypes } from '@red/domain';
import { DefaultColorsService } from '@services/entity-services/default-colors.service'; import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { hexToRgb } from '@utils/functions'; import { hexToRgb } from '@utils/functions';
import { BoundingBox, Table } from '../../file-preview/services/tables.service'; import { BoundingBox, Table } from '../../file-preview/services/tables.service';
@ -169,7 +169,7 @@ export class AnnotationDrawService {
const annotation = this._pdf.textHighlight(); const annotation = this._pdf.textHighlight();
annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber); annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber);
annotation.Opacity = annotationWrapper.isRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY; annotation.Opacity = annotationWrapper.isRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
annotation.setContents(annotationWrapper.content); annotation.setContents(annotationWrapper.content.untranslatedContent);
annotation.PageNumber = pageNumber; annotation.PageNumber = pageNumber;
annotation.StrokeColor = this.convertColor(annotationWrapper.color); annotation.StrokeColor = this.convertColor(annotationWrapper.color);
annotation.Id = annotationWrapper.id; annotation.Id = annotationWrapper.id;

View File

@ -19,14 +19,14 @@ const MOVE_OPTION = 'move';
@Injectable() @Injectable()
export class REDAnnotationManager { export class REDAnnotationManager {
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly #hidden = signal(new Set<string>()); readonly #hidden = signal(new Set<string>());
readonly hidden = this.#hidden.asReadonly();
#manager: AnnotationManager; #manager: AnnotationManager;
readonly #logger = inject(NGXLogger); readonly #logger = inject(NGXLogger);
readonly #annotationSelected$ = new Subject<[Annotation[], string]>(); readonly #annotationSelected$ = new Subject<[Annotation[], string]>();
readonly annotationSelected$ = this.#annotationSelected$.asObservable(); readonly annotationSelected$ = this.#annotationSelected$.asObservable();
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly hidden = this.#hidden.asReadonly();
get selected() { get selected() {
return this.#manager.getSelectedAnnotations(); return this.#manager.getSelectedAnnotations();
@ -128,21 +128,15 @@ export class REDAnnotationManager {
annotations.forEach(annotation => this.#manager.redrawAnnotation(annotation)); annotations.forEach(annotation => this.#manager.redrawAnnotation(annotation));
} }
add(annotations: Annotation | Annotation[]) { async add(annotations: Annotation | Annotation[]): Promise<void> {
try { try {
annotations = asList(annotations); annotations = asList(annotations);
this.#manager.addAnnotations(annotations, { imported: true }); this.#manager.addAnnotations(annotations, { imported: true });
return this.#manager.drawAnnotationsFromList(annotations);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
} }
showHidden() {
const hidden = this.#getByIds([...this.hidden().values()]);
this.show(hidden);
}
#listenForAnnotationSelected() { #listenForAnnotationSelected() {
this.#manager.addEventListener('annotationSelected', async (annotations: Annotation[], action: string) => { this.#manager.addEventListener('annotationSelected', async (annotations: Annotation[], action: string) => {
this.#logger.info('[PDF] Annotation selected: ', annotations, action); this.#logger.info('[PDF] Annotation selected: ', annotations, action);

View File

@ -1,4 +1,4 @@
import { effect, inject, Injectable, signal } from '@angular/core'; import { computed, effect, inject, Injectable, signal } from '@angular/core';
import { HeaderElements } from '../../file-preview/utils/constants'; import { HeaderElements } from '../../file-preview/utils/constants';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -16,18 +16,7 @@ export class LayersService {
private readonly _pdf: PdfViewer, private readonly _pdf: PdfViewer,
private readonly _documentViewer: REDDocumentViewer, private readonly _documentViewer: REDDocumentViewer,
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
) { ) {}
effect(() => {
this.active();
this.#updateButton();
});
}
get toggleLayersBtnTitle(): string {
return this._translateService.instant(_('pdf-viewer.toggle-layers'), {
active: this.active(),
});
}
get toggleLayersBtnIcon(): string { get toggleLayersBtnIcon(): string {
return this.#icon; return this.#icon;
@ -45,13 +34,16 @@ export class LayersService {
} }
}); });
this.updateIconState();
this._documentViewer.document.setLayersArray(layers); this._documentViewer.document.setLayersArray(layers);
this._documentViewer.refreshAndUpdateView(); this._documentViewer.refreshAndUpdateView();
} }
updateIconState() { updateState() {
this.#updateIconState();
this.#updateButton();
}
#updateIconState() {
const element = this._pdf.instance.UI.iframeWindow.document.querySelector(`[data-element=${HeaderElements.TOGGLE_LAYERS}]`); const element = this._pdf.instance.UI.iframeWindow.document.querySelector(`[data-element=${HeaderElements.TOGGLE_LAYERS}]`);
if (!element) return; if (!element) return;
if (this.active()) { if (this.active()) {
@ -63,7 +55,9 @@ export class LayersService {
#updateButton() { #updateButton() {
this._pdf.instance?.UI.updateElement(HeaderElements.TOGGLE_LAYERS, { this._pdf.instance?.UI.updateElement(HeaderElements.TOGGLE_LAYERS, {
title: this.toggleLayersBtnTitle, title: this._translateService.instant(_('pdf-viewer.header.toggle-layers'), {
active: this.active(),
}),
}); });
} }
} }

View File

@ -3,7 +3,7 @@ import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-i
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { environment } from '@environments/environment'; import { environment } from '@environments/environment';
import { ErrorService, getConfig } from '@iqser/common-ui'; import { ErrorService, getConfig, LanguageService } from '@iqser/common-ui';
import { shareDistinctLast, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils'; import { shareDistinctLast, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer'; import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
@ -70,6 +70,7 @@ export class PdfViewer {
private readonly _errorService: ErrorService, private readonly _errorService: ErrorService,
private readonly _userPreferenceService: UserPreferenceService, private readonly _userPreferenceService: UserPreferenceService,
private readonly _annotationManager: REDAnnotationManager, private readonly _annotationManager: REDAnnotationManager,
private readonly _languageService: LanguageService,
) { ) {
this.totalPages = this.#totalPages.asReadonly(); this.totalPages = this.#totalPages.asReadonly();
this.isCompareMode = this.#isCompareMode.asReadonly(); this.isCompareMode = this.#isCompareMode.asReadonly();
@ -152,6 +153,7 @@ export class PdfViewer {
await this.runWithCleanup(async () => { await this.runWithCleanup(async () => {
this.#instance.UI.setTheme(this._userPreferenceService.getTheme()); this.#instance.UI.setTheme(this._userPreferenceService.getTheme());
await this.#instance.UI.setLanguage(this._languageService.currentLanguage);
this._logger.info('[PDF] Initialized'); this._logger.info('[PDF] Initialized');
this.documentViewer = this.#instance.Core.documentViewer; this.documentViewer = this.#instance.Core.documentViewer;

View File

@ -29,24 +29,13 @@ export class ReadableRedactionsService {
return this.#active$.getValue(); return this.#active$.getValue();
} }
get toggleReadableRedactionsBtnTitle(): string {
return this._translateService.instant(_('pdf-viewer.toggle-readable-redactions'), {
active: this.active,
});
}
get toggleReadableRedactionsBtnIcon(): string { get toggleReadableRedactionsBtnIcon(): string {
return this.active ? this.#enableIcon : this.#disableIcon; return this.active ? this.#enableIcon : this.#disableIcon;
} }
toggleReadableRedactions(): void { toggleReadableRedactions(): void {
this.#active$.next(!this.active); this.#active$.next(!this.active);
this.updateState();
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_READABLE_REDACTIONS, {
title: this.toggleReadableRedactionsBtnTitle,
img: this.toggleReadableRedactionsBtnIcon,
});
if (!this.active) { if (!this.active) {
this.setCustomDrawHandler(); this.setCustomDrawHandler();
} else { } else {
@ -88,4 +77,13 @@ export class ReadableRedactionsService {
annotation['FillColor'] = color; annotation['FillColor'] = color;
}); });
} }
updateState() {
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_READABLE_REDACTIONS, {
title: this._translateService.instant(_('pdf-viewer.header.toggle-readable-redactions'), {
active: this.active,
}),
img: this.toggleReadableRedactionsBtnIcon,
});
}
} }

View File

@ -15,21 +15,11 @@ export class TooltipsService {
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
) {} ) {}
get toggleTooltipsBtnTitle(): string {
return this._translateService.instant(_('pdf-viewer.toggle-tooltips'), {
active: this._userPreferenceService.getFilePreviewTooltipsPreference(),
});
}
async toggleTooltips(): Promise<void> { async toggleTooltips(): Promise<void> {
await this._userPreferenceService.toggleFilePreviewTooltipsPreference(); await this._userPreferenceService.toggleFilePreviewTooltipsPreference();
this._documentViewer.updateTooltipsVisibility(); this._documentViewer.updateTooltipsVisibility();
this.updateIconState(); this.updateState();
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, {
title: this.toggleTooltipsBtnTitle,
});
} }
updateIconState() { updateIconState() {
@ -40,4 +30,13 @@ export class TooltipsService {
element.classList.remove('active'); element.classList.remove('active');
} }
} }
updateState() {
this.updateIconState();
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, {
title: this._translateService.instant(_('pdf-viewer.header.toggle-tooltips'), {
active: this._userPreferenceService.getFilePreviewTooltipsPreference(),
}),
});
}
} }

View File

@ -16,6 +16,8 @@ import { PdfViewer } from './pdf-viewer.service';
import { ReadableRedactionsService } from './readable-redactions.service'; import { ReadableRedactionsService } from './readable-redactions.service';
import { TooltipsService } from './tooltips.service'; import { TooltipsService } from './tooltips.service';
import { UI_ROOT_PATH_FN } from '@common-ui/utils'; import { UI_ROOT_PATH_FN } from '@common-ui/utils';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { viewerHeaderButtonsTranslations } from '@translations/pdf-viewer-translations';
const divider: IHeaderElement = { const divider: IHeaderElement = {
type: 'divider', type: 'divider',
@ -90,7 +92,7 @@ export class ViewerHeaderService {
toolGroup: 'rectangleTools', toolGroup: 'rectangleTools',
dataElement: HeaderElements.SHAPE_TOOL_GROUP_BUTTON, dataElement: HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
img: this.#convertPath('/assets/icons/general/pdftron-rectangle.svg'), img: this.#convertPath('/assets/icons/general/pdftron-rectangle.svg'),
title: 'annotation.rectangle', title: viewerHeaderButtonsTranslations[HeaderElements.SHAPE_TOOL_GROUP_BUTTON],
}; };
} }
@ -99,7 +101,6 @@ export class ViewerHeaderService {
type: 'actionButton', type: 'actionButton',
element: HeaderElements.TOGGLE_TOOLTIPS, element: HeaderElements.TOGGLE_TOOLTIPS,
dataElement: HeaderElements.TOGGLE_TOOLTIPS, dataElement: HeaderElements.TOGGLE_TOOLTIPS,
title: this._tooltipsService.toggleTooltipsBtnTitle,
img: this.#convertPath('/assets/icons/general/pdftron-action-enable-tooltips.svg'), img: this.#convertPath('/assets/icons/general/pdftron-action-enable-tooltips.svg'),
onClick: () => this._ngZone.run(() => this._tooltipsService.toggleTooltips()), onClick: () => this._ngZone.run(() => this._tooltipsService.toggleTooltips()),
}; };
@ -110,7 +111,6 @@ export class ViewerHeaderService {
type: 'actionButton', type: 'actionButton',
element: HeaderElements.TOGGLE_LAYERS, element: HeaderElements.TOGGLE_LAYERS,
dataElement: HeaderElements.TOGGLE_LAYERS, dataElement: HeaderElements.TOGGLE_LAYERS,
title: this._layersService.toggleLayersBtnTitle,
img: this._layersService.toggleLayersBtnIcon, img: this._layersService.toggleLayersBtnIcon,
onClick: () => this._ngZone.run(() => this._layersService.toggleLayers()), onClick: () => this._ngZone.run(() => this._layersService.toggleLayers()),
}; };
@ -121,7 +121,6 @@ export class ViewerHeaderService {
type: 'actionButton', type: 'actionButton',
element: HeaderElements.TOGGLE_READABLE_REDACTIONS, element: HeaderElements.TOGGLE_READABLE_REDACTIONS,
dataElement: HeaderElements.TOGGLE_READABLE_REDACTIONS, dataElement: HeaderElements.TOGGLE_READABLE_REDACTIONS,
title: this._readableRedactionsService.toggleReadableRedactionsBtnTitle,
img: this._readableRedactionsService.toggleReadableRedactionsBtnIcon, img: this._readableRedactionsService.toggleReadableRedactionsBtnIcon,
onClick: () => this._ngZone.run(() => this._readableRedactionsService.toggleReadableRedactions()), onClick: () => this._ngZone.run(() => this._readableRedactionsService.toggleReadableRedactions()),
}; };
@ -130,7 +129,7 @@ export class ViewerHeaderService {
get #loadAllAnnotations(): IHeaderElement { get #loadAllAnnotations(): IHeaderElement {
return { return {
type: 'actionButton', type: 'actionButton',
title: this._translateService.instant('viewer-header.load-all-annotations'), title: viewerHeaderButtonsTranslations[HeaderElements.LOAD_ALL_ANNOTATIONS],
img: this.#convertPath('/assets/icons/general/pdftron-action-load-all-annotations.svg'), img: this.#convertPath('/assets/icons/general/pdftron-action-load-all-annotations.svg'),
onClick: () => this._ngZone.run(() => this.#events$.next({ type: ViewerEvents.LOAD_ALL_ANNOTATIONS })), onClick: () => this._ngZone.run(() => this.#events$.next({ type: ViewerEvents.LOAD_ALL_ANNOTATIONS })),
dataElement: HeaderElements.LOAD_ALL_ANNOTATIONS, dataElement: HeaderElements.LOAD_ALL_ANNOTATIONS,
@ -143,7 +142,7 @@ export class ViewerHeaderService {
element: HeaderElements.ROTATE_LEFT_BUTTON, element: HeaderElements.ROTATE_LEFT_BUTTON,
dataElement: HeaderElements.ROTATE_LEFT_BUTTON, dataElement: HeaderElements.ROTATE_LEFT_BUTTON,
img: this.#convertPath('/assets/icons/general/rotate-left.svg'), img: this.#convertPath('/assets/icons/general/rotate-left.svg'),
title: 'Rotate page left', title: viewerHeaderButtonsTranslations[HeaderElements.ROTATE_LEFT_BUTTON],
onClick: () => onClick: () =>
this._ngZone.run(() => { this._ngZone.run(() => {
this._rotationService.addRotation(RotationTypes.LEFT); this._rotationService.addRotation(RotationTypes.LEFT);
@ -203,7 +202,7 @@ export class ViewerHeaderService {
element: HeaderElements.ROTATE_RIGHT_BUTTON, element: HeaderElements.ROTATE_RIGHT_BUTTON,
dataElement: HeaderElements.ROTATE_RIGHT_BUTTON, dataElement: HeaderElements.ROTATE_RIGHT_BUTTON,
img: this.#convertPath('/assets/icons/general/rotate-right.svg'), img: this.#convertPath('/assets/icons/general/rotate-right.svg'),
title: 'Rotate page right', title: viewerHeaderButtonsTranslations[HeaderElements.ROTATE_RIGHT_BUTTON],
onClick: () => onClick: () =>
this._ngZone.run(() => { this._ngZone.run(() => {
this._rotationService.addRotation(RotationTypes.RIGHT); this._rotationService.addRotation(RotationTypes.RIGHT);
@ -218,7 +217,7 @@ export class ViewerHeaderService {
element: HeaderElements.COMPARE_BUTTON, element: HeaderElements.COMPARE_BUTTON,
dataElement: HeaderElements.COMPARE_BUTTON, dataElement: HeaderElements.COMPARE_BUTTON,
img: this.#convertPath('/assets/icons/general/pdftron-action-compare.svg'), img: this.#convertPath('/assets/icons/general/pdftron-action-compare.svg'),
title: 'Compare', title: viewerHeaderButtonsTranslations[HeaderElements.COMPARE_BUTTON],
onClick: () => onClick: () =>
this._ngZone.run(async () => { this._ngZone.run(async () => {
document.getElementById('compareFileInput').click(); document.getElementById('compareFileInput').click();
@ -310,24 +309,27 @@ export class ViewerHeaderService {
header.getItems().splice(startButtons, header.getItems().length - deleteCount, ...enabledItems); header.getItems().splice(startButtons, header.getItems().length - deleteCount, ...enabledItems);
}); });
this._pdf.instance?.UI.updateElement('selectToolButton', {
img: this.#convertPath('/assets/icons/general/pdftron-cursor.svg'),
});
if (this._pdf.instance) { if (this._pdf.instance) {
this._tooltipsService.updateIconState(); this._pdf.instance.UI.updateElement('selectToolButton', {
this._layersService.updateIconState(); img: this.#convertPath('/assets/icons/general/pdftron-cursor.svg'),
title: this._translateService.instant(viewerHeaderButtonsTranslations['selectToolButton']),
});
this._tooltipsService.updateState();
this._layersService.updateState();
this._readableRedactionsService.updateState();
const closeCompareButton = this._pdf.instance.UI.iframeWindow.document.querySelector( const closeCompareButton = this._pdf.instance.UI.iframeWindow.document.querySelector(
`[data-element=${HeaderElements.CLOSE_COMPARE_BUTTON}]`, `[data-element=${HeaderElements.CLOSE_COMPARE_BUTTON}]`,
); );
closeCompareButton?.classList.add('active'); closeCompareButton?.classList.add('active');
this.#configTooltips();
} }
} }
disableLoadAllAnnotations(): void { disableLoadAllAnnotations(): void {
this._pdf.instance.UI.updateElement(HeaderElements.LOAD_ALL_ANNOTATIONS, { this._pdf.instance.UI.updateElement(HeaderElements.LOAD_ALL_ANNOTATIONS, {
img: this.#convertPath('/assets/icons/general/pdftron-action-load-all-annotations-disabled.svg'), img: this.#convertPath('/assets/icons/general/pdftron-action-load-all-annotations-disabled.svg'),
title: this._translateService.instant('viewer-header.all-annotations-loaded'), title: this._translateService.instant(_('pdf-viewer.header.all-annotations-loaded')),
onClick: undefined, onClick: undefined,
}); });
} }
@ -370,6 +372,37 @@ export class ViewerHeaderService {
this._pdf.navigateTo(1); this._pdf.navigateTo(1);
} }
#configTooltips() {
const elements: (string | HeaderElementType)[] = [
'leftPanelButton',
'thumbnailsPanelButton',
'outlinesPanelButton',
'outlineMultiSelect',
'layersPanelButton',
'signaturePanelButton',
'zoomInButton',
'zoomOutButton',
'panToolButton',
HeaderElements.COMPARE_BUTTON,
HeaderElements.ROTATE_LEFT_BUTTON,
HeaderElements.ROTATE_RIGHT_BUTTON,
HeaderElements.LOAD_ALL_ANNOTATIONS,
HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
];
elements.forEach(element => {
if (this.#buttons.has(element as HeaderElementType)) {
this._pdf.instance.UI.updateElement(element, {
title: this._translateService.instant(this.#buttons.get(element as HeaderElementType).title),
});
} else {
this._pdf.instance.UI.updateElement(element, {
title: this._translateService.instant(viewerHeaderButtonsTranslations[element]),
});
}
});
}
#pushGroup(items: IHeaderElement[], group: HeaderElementType[]) { #pushGroup(items: IHeaderElement[], group: HeaderElementType[]) {
const enabledItems = group.filter(item => this.#isEnabled(item)); const enabledItems = group.filter(item => this.#isEnabled(item));
if (enabledItems.length) { if (enabledItems.length) {

View File

@ -2,10 +2,10 @@
:host { :host {
height: 250px; height: 250px;
margin-bottom: 16px;
} }
.scrollable { .scrollable {
margin-bottom: 8px;
overflow-y: auto; overflow-y: auto;
max-height: 240px; max-height: 240px;
@include common-mixins.scroll-bar; @include common-mixins.scroll-bar;

View File

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { GenericService, isIqserDevMode, Toaster } from '@iqser/common-ui'; import { GenericService, Toaster } from '@iqser/common-ui';
import { EntryStates, IEntityLog, IEntityLogEntry, ISectionGrid } from '@red/domain'; import { EntryStates, IEntityLog, IEntityLogEntry } from '@red/domain';
import { firstValueFrom, of } from 'rxjs'; import { firstValueFrom, of } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
@ -20,10 +20,6 @@ export class EntityLogService extends GenericService<unknown> {
return entityLog; return entityLog;
} }
getSectionGrid(dossierId: string, fileId: string) {
return this._getOne<ISectionGrid>([dossierId, fileId], 'sectionGrid');
}
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) { #filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
return entityLogEntry.filter(entry => { return entityLogEntry.filter(entry => {
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length); entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);

View File

@ -0,0 +1,22 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HeaderElements, HeaderElementType } from '../modules/file-preview/utils/constants';
export const viewerHeaderButtonsTranslations: Record<string | HeaderElementType, string> = {
leftPanelButton: _('pdf-viewer.header.left-panel-button'),
thumbnailsPanelButton: _('pdf-viewer.header.thumbnails-panel-button'),
outlinesPanelButton: _('pdf-viewer.header.outlines-panel-button'),
outlineMultiSelect: _('pdf-viewer.header.outline-multi-select'),
noOutlinesText: _('pdf-viewer.header.no-outlines-text'),
layersPanelButton: _('pdf-viewer.header.layers-panel-button'),
signaturePanelButton: _('pdf-viewer.header.signature-panel-button'),
noSignaturesText: _('pdf-viewer.header.no-signatures-text'),
zoomInButton: _('pdf-viewer.header.zoom-in-button'),
zoomOutButton: _('pdf-viewer.header.zoom-out-button'),
panToolButton: _('pdf-viewer.header.pan-tool-button'),
selectToolButton: _('pdf-viewer.header.select-tool-button'),
[HeaderElements.COMPARE_BUTTON]: _('pdf-viewer.header.compare-button'),
[HeaderElements.ROTATE_LEFT_BUTTON]: _('pdf-viewer.header.rotate-left-button'),
[HeaderElements.ROTATE_RIGHT_BUTTON]: _('pdf-viewer.header.rotate-right-button'),
[HeaderElements.SHAPE_TOOL_GROUP_BUTTON]: _('pdf-viewer.header.rectangle-tool-button'),
[HeaderElements.LOAD_ALL_ANNOTATIONS]: _('pdf-viewer.header.load-all-annotations'),
};

View File

@ -138,7 +138,7 @@
}, },
"add-edit-entity": { "add-edit-entity": {
"form": { "form": {
"case-sensitive": "Groß-/Kleinschreibung berücksichtigen", "case-sensitive": "Groß-/Kleinschreibung beachten",
"color": "Farbe {type, select, redaction{Schwärzung} hint{Hinweis} recommendation{Empfehlung} skipped{Ingorierte Schwärzung} ignored{Ignorierter Hinweis} other{}}", "color": "Farbe {type, select, redaction{Schwärzung} hint{Hinweis} recommendation{Empfehlung} skipped{Ingorierte Schwärzung} ignored{Ignorierter Hinweis} other{}}",
"color-placeholder": "#", "color-placeholder": "#",
"default-reason": "Standardgrund", "default-reason": "Standardgrund",
@ -146,7 +146,7 @@
"description": "Beschreibung", "description": "Beschreibung",
"description-placeholder": "Beschreibung eingeben", "description-placeholder": "Beschreibung eingeben",
"dossier-dictionary-only": "Nur Dossier-Wörterbuch", "dossier-dictionary-only": "Nur Dossier-Wörterbuch",
"has-dictionary": "Mit Wörterbuch", "has-dictionary": "Hat Wörterbuch",
"hint": "Hinweis", "hint": "Hinweis",
"manage-entries-in-dictionary-editor-only": "In Schwärzungs-/Bearbeitungsdialogen verfügbar", "manage-entries-in-dictionary-editor-only": "In Schwärzungs-/Bearbeitungsdialogen verfügbar",
"name": "Anzeigename", "name": "Anzeigename",
@ -207,14 +207,14 @@
"generic": "Speichern des Benutzers fehlgeschlagen." "generic": "Speichern des Benutzers fehlgeschlagen."
}, },
"form": { "form": {
"account-setup": "User account setup", "account-setup": "Konfiguration des Benutzerkontos",
"email": "E-Mail", "email": "E-Mail",
"first-name": "Vorname", "first-name": "Vorname",
"last-name": "Nachname", "last-name": "Nachname",
"reset-password": "Passwort zurücksetzen", "reset-password": "Passwort zurücksetzen",
"role": "Rolle", "role": "Rolle",
"send-email": "Do not send email requesting the user to set a password", "send-email": "Benutzer nicht per E-Mail auffordern, ein Passwort festzulegen",
"send-email-explanation": "Select this option if you use SSO. Please note that you will need to inform the user directly." "send-email-explanation": "Wählen Sie diese Option, wenn Sie SSO verwenden. Bitte beachten Sie, dass Sie den Benutzer direkt informieren müssen."
}, },
"title": "{type, select, edit{Benutzer bearbeiten} create{Neuen Benutzer erstellen} other{}}" "title": "{type, select, edit{Benutzer bearbeiten} create{Neuen Benutzer erstellen} other{}}"
}, },
@ -243,8 +243,7 @@
} }
}, },
"type": "Typ", "type": "Typ",
"type-placeholder": "Typ auswählen...", "type-placeholder": "Typ auswählen..."
"value": ""
}, },
"title": "Hinweis hinzufügen" "title": "Hinweis hinzufügen"
} }
@ -276,6 +275,9 @@
"watermarks": "Wasserzeichen" "watermarks": "Wasserzeichen"
}, },
"analysis-disabled": "", "analysis-disabled": "",
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotation-actions": { "annotation-actions": {
"accept-recommendation": { "accept-recommendation": {
"label": "Empfehlung annehmen" "label": "Empfehlung annehmen"
@ -331,14 +333,14 @@
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}", "error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet." "success": "Bild wurde einer neuen Kategorie zugeordnet."
}, },
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"remove": { "remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}", "error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt" "success": "Schwärzung wurde entfernt"
}, },
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"undo": { "undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}", "error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "Rücksetzung erfolgreich" "success": "Rücksetzung erfolgreich"
@ -351,15 +353,15 @@
"remove-highlights": { "remove-highlights": {
"label": "Ausgewählte Markierungen entfernen" "label": "Ausgewählte Markierungen entfernen"
}, },
"resize": {
"label": "Größe ändern"
},
"resize-accept": { "resize-accept": {
"label": "Neue Größe speichern" "label": "Neue Größe speichern"
}, },
"resize-cancel": { "resize-cancel": {
"label": "Größenänderung abbrechen" "label": "Größenänderung abbrechen"
}, },
"resize": {
"label": "Größe ändern"
},
"see-references": { "see-references": {
"label": "Referenzen anzeigen" "label": "Referenzen anzeigen"
}, },
@ -368,7 +370,7 @@
}, },
"annotation-changes": { "annotation-changes": {
"added-locally": "Lokal hinzugefügt", "added-locally": "Lokal hinzugefügt",
"forced-hint": "Hint wurde erzwungen", "forced-hint": "Hinweis wurde erzwungen",
"forced-redaction": "Schwärzung wurde erzwungen", "forced-redaction": "Schwärzung wurde erzwungen",
"header": "Lokale manuelle Änderungen:", "header": "Lokale manuelle Änderungen:",
"legal-basis": "Grund wurde geändert", "legal-basis": "Grund wurde geändert",
@ -377,7 +379,7 @@
"resized": "Schwärzungsbereich wurde geändert" "resized": "Schwärzungsbereich wurde geändert"
}, },
"annotation-engines": { "annotation-engines": {
"dictionary": "{isHint, select, true{Hinweis} other{Schwärzung}} basiert auf Wörterbuch", "dictionary": "Basiert auf Wörterbuch",
"dossier-dictionary": "Basiert auf Dossier-Wörterbuch", "dossier-dictionary": "Basiert auf Dossier-Wörterbuch",
"imported": "Importiert", "imported": "Importiert",
"ner": "Basiert auf KI-Modell", "ner": "Basiert auf KI-Modell",
@ -393,9 +395,6 @@
"skipped": "Ignorierte Schwärzung", "skipped": "Ignorierte Schwärzung",
"text-highlight": "Markierung" "text-highlight": "Markierung"
}, },
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotations": "Annotationen", "annotations": "Annotationen",
"archived-dossiers-listing": { "archived-dossiers-listing": {
"no-data": { "no-data": {
@ -419,7 +418,7 @@
"approver": "Genehmiger", "approver": "Genehmiger",
"approvers": "Genehmiger", "approvers": "Genehmiger",
"make-approver": "Zum Genehmiger machen", "make-approver": "Zum Genehmiger machen",
"no-reviewers": "Es wurden noch keine Prüfer zum Team hinzugefügt.", "no-reviewers": "Es wurden noch keine Mitglieder zum Dossier hinzugefügt, die nur die Prüfer-Berechtigung haben.",
"reviewers": "Prüfer", "reviewers": "Prüfer",
"search": "Suche...", "search": "Suche...",
"single-user": "Besitzer" "single-user": "Besitzer"
@ -571,7 +570,7 @@
}, },
"search": "Nach Name suchen...", "search": "Nach Name suchen...",
"table-col-names": { "table-col-names": {
"column-labels": "Column labels", "column-labels": "Spaltenbeschriftungen",
"name": "Name", "name": "Name",
"number-of-lines": "Zeilenzahl", "number-of-lines": "Zeilenzahl",
"version": "Version" "version": "Version"
@ -640,14 +639,14 @@
}, },
"confirmation-dialog": { "confirmation-dialog": {
"approve-file": { "approve-file": {
"confirmationText": "Approve anyway", "confirmationText": "Trotzdem genehmigen",
"denyText": "No, cancel", "denyText": "Nein, abbrechen",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?", "question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
"title": "Approval warning", "title": "Warnung!",
"warning-reason": { "warning-reason": {
"legal-basis-missing": "Legal basis missing", "legal-basis-missing": "Rechtsgrundlage fehlt",
"pending-changes": "Pending Changes", "pending-changes": "Änderungen stehen aus",
"unmapped-justification": "Unmapped justification" "unmapped-justification": "Nicht gemappte Begründung"
} }
}, },
"assign-file-to-me": { "assign-file-to-me": {
@ -664,7 +663,7 @@
"delete-dossier": { "delete-dossier": {
"confirmation-text": "{dossiersCount, plural, one{Dossier} other{Dossiers}} löschen", "confirmation-text": "{dossiersCount, plural, one{Dossier} other{Dossiers}} löschen",
"deny-text": "{dossiersCount, plural, one{Dossier} other{Dossiers}} behalten", "deny-text": "{dossiersCount, plural, one{Dossier} other{Dossiers}} behalten",
"question": "Möchten Sie dieses Dokument wirklich löschen?", "question": "Möchten Sie {dossiersCount, plural, one{dieses Dossier} other{diese Dossiers}} wirklich löschen?",
"title": "{dossiersCount, plural, one{{dossierName}} other{selected dossiers}} löschen" "title": "{dossiersCount, plural, one{{dossierName}} other{selected dossiers}} löschen"
}, },
"delete-file": { "delete-file": {
@ -673,7 +672,7 @@
}, },
"delete-items": { "delete-items": {
"question": "Möchten Sie {itemsCount, plural, one{dieses Element} other{diese Elemente}} wirklich löschen?", "question": "Möchten Sie {itemsCount, plural, one{dieses Element} other{diese Elemente}} wirklich löschen?",
"title": "{itemsCount, plural, one{{name}} other{Ausgewählte Elemente}} löschen" "title": "{itemsCount, plural, one{{name}} other{selected items}} löschen"
}, },
"delete-justification": { "delete-justification": {
"question": "Möchten Sie {count, plural, one{diese Begründung} other{diese Begründungen}} wirklich löschen?", "question": "Möchten Sie {count, plural, one{diese Begründung} other{diese Begründungen}} wirklich löschen?",
@ -721,7 +720,7 @@
"key": "Typ" "key": "Typ"
}, },
"table-header": { "table-header": {
"title": "{length} Standard{length, plural, one{farbe} other{farben}}" "title": "{length} Standard{length, plural, one{Farbe} other{Farben}}"
}, },
"types": { "types": {
"analysisColor": "Analyse", "analysisColor": "Analyse",
@ -763,7 +762,7 @@
"compare": { "compare": {
"compare": "Vergleichen", "compare": "Vergleichen",
"select-dictionary": "Wörterbuch auswählen", "select-dictionary": "Wörterbuch auswählen",
"select-dossier": "Wörterbuch auswählen", "select-dossier": "Dossier auswählen",
"select-dossier-template": "Dossier-Vorlage auswählen" "select-dossier-template": "Dossier-Vorlage auswählen"
}, },
"download": "Aktuelle Einträge herunterladen", "download": "Aktuelle Einträge herunterladen",
@ -853,7 +852,7 @@
"date": "Datum", "date": "Datum",
"image": "Bild", "image": "Bild",
"number": "Nummer", "number": "Nummer",
"text": "Freier Text" "text": "Text"
}, },
"dossier-attributes-listing": { "dossier-attributes-listing": {
"action": { "action": {
@ -945,7 +944,7 @@
}, },
"dossier-overview": { "dossier-overview": {
"approve": "Freigeben", "approve": "Freigeben",
"approve-disabled": "Sie können die Datei erst freigeben, wenn Sie anhand der neusten Regeln und Begriffe in den Systemwörterbüchern analysiert wurde.", "approve-disabled": "Sie können die Datei erst freigeben, wenn sie auf Basis der aktuellen Wörterbücher analysiert wurde.",
"assign-approver": "Genehmiger zuweisen", "assign-approver": "Genehmiger zuweisen",
"assign-me": "Mir zuweisen", "assign-me": "Mir zuweisen",
"assign-reviewer": "Benutzer zuweisen", "assign-reviewer": "Benutzer zuweisen",
@ -988,8 +987,8 @@
} }
}, },
"filters": { "filters": {
"label": "Name des Dokuments", "label": "Dokumentname",
"search": "Name des Dokuments..." "search": "Name des Dokuments eingeben..."
}, },
"header-actions": { "header-actions": {
"download-csv": "CSV-Bericht herunterladen", "download-csv": "CSV-Bericht herunterladen",
@ -1019,13 +1018,13 @@
"recent": "Neu ({hours} h)", "recent": "Neu ({hours} h)",
"unassigned": "Keinem Bearbeiter zugewiesen" "unassigned": "Keinem Bearbeiter zugewiesen"
}, },
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": { "reanalyse-dossier": {
"error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.", "error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Dateien für Reanalyse vorgesehen." "success": "Dateien für Reanalyse vorgesehen."
}, },
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "", "report-download": "",
"start-auto-analysis": "Auto-Analyse aktivieren", "start-auto-analysis": "Auto-Analyse aktivieren",
"stop-auto-analysis": "Auto-Analyse anhalten", "stop-auto-analysis": "Auto-Analyse anhalten",
@ -1095,6 +1094,14 @@
"total-documents": "Dokumente", "total-documents": "Dokumente",
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}" "total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
}, },
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-templates-listing": { "dossier-templates-listing": {
"action": { "action": {
"clone": "Vorlage klonen", "clone": "Vorlage klonen",
@ -1129,14 +1136,6 @@
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}" "title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
} }
}, },
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-watermark-selector": { "dossier-watermark-selector": {
"heading": "Wasserzeichen auf Dokumenten", "heading": "Wasserzeichen auf Dokumenten",
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.", "no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
@ -1168,7 +1167,7 @@
"delta-preview": "Delta-PDF", "delta-preview": "Delta-PDF",
"flatten": "Verflachte PDF", "flatten": "Verflachte PDF",
"label": "{length} Dokumenten{length, plural, one{typ} other{typen}}", "label": "{length} Dokumenten{length, plural, one{typ} other{typen}}",
"optimized-preview": "Optimized Preview PDF", "optimized-preview": "Optimierte Vorschau-PDF",
"original": "Optimierte PDF", "original": "Optimierte PDF",
"preview": "Vorschau-PDF", "preview": "Vorschau-PDF",
"redacted": "Geschwärzte PDF", "redacted": "Geschwärzte PDF",
@ -1234,10 +1233,12 @@
"save": "Speichern", "save": "Speichern",
"title": "{label} bearbeiten" "title": "{label} bearbeiten"
}, },
"entries-count": "", "entries": "{length} {length, plural, one{Eintrag} other{Einträge}}",
"false-positives": "Falsch-Positive ({count})", "false-positive-entries": "{length} {length, plural, one{Falsch-Positiver} other{Falsch-Positive}}",
"false-recommendations": "Falsche Empfehlungen ({count})", "false-positives": "Falsch-Positive",
"to-redact": "Schwärzungen ({count})" "false-recommendation-entries": "{length} {length, plural, one{falsche Empfehlung} other{falsche Empfehlungen}}",
"false-recommendations": "Falsche Empfehlungen",
"to-redact": "Schwärzungen"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1263,7 +1264,7 @@
"choose-download": "Stellen Sie Ihr Download-Paket zusammen:", "choose-download": "Stellen Sie Ihr Download-Paket zusammen:",
"dictionary": "Wörterbücher", "dictionary": "Wörterbücher",
"dossier-attributes": "Dossier-Attribute", "dossier-attributes": "Dossier-Attribute",
"dossier-dictionary": "Wörterbücher", "dossier-dictionary": "Dossier-Einträge",
"dossier-info": "Dossier-Info", "dossier-info": "Dossier-Info",
"download-package": "Download-Paket", "download-package": "Download-Paket",
"general-info": "Allgemeine Informationen", "general-info": "Allgemeine Informationen",
@ -1281,11 +1282,14 @@
"content": { "content": {
"comment": "Kommentar", "comment": "Kommentar",
"comment-placeholder": "Bemerkungen oder Notizen hinzufügen...", "comment-placeholder": "Bemerkungen oder Notizen hinzufügen...",
"custom-rectangle": "Bereichsschwärzung",
"imported": "Importierte Schwärzung",
"legal-basis": "Rechtsgrundlage", "legal-basis": "Rechtsgrundlage",
"options": { "options": {
"in-document": { "in-dossier": {
"description": "", "description": "Schwärzung in jedem Dokument in {dossierName} bearbeiten.",
"label": "" "extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
"label": "Typ in Dossier ändern"
}, },
"only-here": { "only-here": {
"description": "Bearbeiten Sie die Schwärzung nur an dieser Stelle im Dokument.", "description": "Bearbeiten Sie die Schwärzung nur an dieser Stelle im Dokument.",
@ -1328,6 +1332,15 @@
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}" "title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
} }
}, },
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"entity-rules-screen": { "entity-rules-screen": {
"error": { "error": {
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen." "generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
@ -1341,28 +1354,19 @@
"title": "Entitätsregeln-Editor", "title": "Entitätsregeln-Editor",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden" "warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
}, },
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"error": { "error": {
"deleted-entity": { "deleted-entity": {
"dossier": { "dossier": {
"action": "Zurück zur Übersicht", "action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!" "label": "Dieses Dossier wurde gelöscht!"
}, },
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": { "file": {
"action": "Zurück zum Dossier", "action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!" "label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
} }
}, },
"file-preview": { "file-preview": {
@ -1380,6 +1384,12 @@
}, },
"exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr", "exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr",
"file": "Datei", "file": "Datei",
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attribute-encoding-types": { "file-attribute-encoding-types": {
"ascii": "ASCII", "ascii": "ASCII",
"iso": "ISO-8859-1", "iso": "ISO-8859-1",
@ -1390,12 +1400,6 @@
"number": "Nummer", "number": "Nummer",
"text": "Freier Text" "text": "Freier Text"
}, },
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attributes-configurations": { "file-attributes-configurations": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
"form": { "form": {
@ -1471,7 +1475,7 @@
"delete": "Attribut löschen", "delete": "Attribut löschen",
"edit": "Attribut bearbeiten" "edit": "Attribut bearbeiten"
}, },
"add-new": "Neue Attribute", "add-new": "Neues Attribut",
"bulk-actions": { "bulk-actions": {
"delete": "Ausgewählte Attribute löschen" "delete": "Ausgewählte Attribute löschen"
}, },
@ -1490,7 +1494,7 @@
"search": "Nach Attribut-Namen suchen...", "search": "Nach Attribut-Namen suchen...",
"table-col-names": { "table-col-names": {
"csv-column": "CSV-Spalte", "csv-column": "CSV-Spalte",
"displayed-in-file-list": "In Dokumentenliste", "displayed-in-file-list": "In Dokumentenliste anzeigen",
"filterable": "Filterbar", "filterable": "Filterbar",
"name": "Name", "name": "Name",
"primary": "Primärattribut", "primary": "Primärattribut",
@ -1521,7 +1525,7 @@
}, },
"last-assignee": "{status, select, APPROVED{Freigegeben} UNDER_APPROVAL{Geprüft} other{Zuletzt geprüft}} von:", "last-assignee": "{status, select, APPROVED{Freigegeben} UNDER_APPROVAL{Geprüft} other{Zuletzt geprüft}} von:",
"no-data": { "no-data": {
"title": "An dieser Seite wurden keine Änderungen vorgenommen." "title": "Auf dieser Seite wurden keine Änderungen vorgenommen."
}, },
"quick-nav": { "quick-nav": {
"jump-first": "Zur ersten Seite springen", "jump-first": "Zur ersten Seite springen",
@ -1529,7 +1533,7 @@
}, },
"reanalyse-notification": "Reanalyse starten", "reanalyse-notification": "Reanalyse starten",
"redacted": "Vorschau", "redacted": "Vorschau",
"redacted-tooltip": "Die Vorschau ist eine Vorschau der finalen geschwärzten Version und zeigt nur Schwärzungen. Sie ist nur verfügbar, wenn keine Änderungen oder Reanalysen ausstehen.", "redacted-tooltip": "Die Vorschau ist eine Vorschau der finalen geschwärzten Version und zeigt nur Schwärzungen.",
"standard": "Standard", "standard": "Standard",
"standard-tooltip": "Standard zeigt alle Annotationstypen und ermöglicht die Bearbeitung.", "standard-tooltip": "Standard zeigt alle Annotationstypen und ermöglicht die Bearbeitung.",
"tabs": { "tabs": {
@ -1540,13 +1544,13 @@
"label": "Liste", "label": "Liste",
"no-annotations": "Es sind keine Annotationen vorhanden.", "no-annotations": "Es sind keine Annotationen vorhanden.",
"page-is": "Diese Seite ist", "page-is": "Diese Seite ist",
"reset": "setzen Sie die Filter", "reset": "Setzen Sie die Filter zurück",
"select": "Auswählen", "select": "Auswählen",
"select-all": "Alle", "select-all": "Alle",
"select-none": "Keine", "select-none": "Keine",
"show-skipped": "Ignorierte im Dokument anzeigen", "show-skipped": "Ignorierte im Dokument anzeigen",
"the-filters": "zurück.", "the-filters": "Filter",
"wrong-filters": "Keine Annotationen für die ausgewählte Filterkombination. Bitte ändern Sie die Auswahl oder" "wrong-filters": "Keine Annotationen für die ausgewählte Filterkombination. Bitte ändern Sie die Auswahl oder setzen die Filter zurück."
}, },
"document-info": { "document-info": {
"close": "Datei-Info schließen", "close": "Datei-Info schließen",
@ -1613,6 +1617,15 @@
"csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert." "csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert."
} }
}, },
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nur Hinweise",
"image": "Bilder",
"none": "Keine Annotationen",
"redaction": "Schwärzung",
"updated": "Aktualisiert"
},
"filter-menu": { "filter-menu": {
"filter-options": "Filteroptionen", "filter-options": "Filteroptionen",
"filter-types": "Filter", "filter-types": "Filter",
@ -1622,15 +1635,6 @@
"unseen-pages": "Nur Annotationen auf ungesehenen Seiten", "unseen-pages": "Nur Annotationen auf ungesehenen Seiten",
"with-comments": "Nur Annotationen mit Kommentaren" "with-comments": "Nur Annotationen mit Kommentaren"
}, },
"filter": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
"hint": "Nur Hinweise",
"image": "Bilder",
"none": "Keine Annotationen",
"redaction": "Schwärzungen",
"updated": "Aktualisiert"
},
"filters": { "filters": {
"assigned-people": "Bearbeiter", "assigned-people": "Bearbeiter",
"documents-status": "Dokumentenstatus", "documents-status": "Dokumentenstatus",
@ -1639,7 +1643,7 @@
"empty": "Leer", "empty": "Leer",
"filter-by": "Filter:", "filter-by": "Filter:",
"needs-work": "Annotationen", "needs-work": "Annotationen",
"people": "Dossier-Mitglieder" "people": "Dossier-Mitglied(er)"
}, },
"general-config-screen": { "general-config-screen": {
"actions": { "actions": {
@ -1654,7 +1658,7 @@
"auth": "Authentifizierung aktivieren", "auth": "Authentifizierung aktivieren",
"change-credentials": "Anmeldedaten ändern", "change-credentials": "Anmeldedaten ändern",
"envelope-from": "Envelope von", "envelope-from": "Envelope von",
"envelope-from-hint": "Infotext zum Feld „Ausgangsadresse“.", "envelope-from-hint": "Infotext zum Feld „Envelope von“.",
"envelope-from-placeholder": "Envelope-Absenderadresse", "envelope-from-placeholder": "Envelope-Absenderadresse",
"from": "Von", "from": "Von",
"from-display-name": "Name für Absender", "from-display-name": "Name für Absender",
@ -1922,6 +1926,13 @@
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!", "user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!" "user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
}, },
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"notifications-screen": { "notifications-screen": {
"category": { "category": {
"email-notifications": "E-Mail-Benachrichtigungen", "email-notifications": "E-Mail-Benachrichtigungen",
@ -1935,7 +1946,6 @@
"dossier": "Benachrichtigungen zu Dossiers", "dossier": "Benachrichtigungen zu Dossiers",
"other": "Andere Benachrichtigungen" "other": "Andere Benachrichtigungen"
}, },
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"options": { "options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde", "ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde",
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde", "ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
@ -1953,6 +1963,7 @@
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde", "USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere" "USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
}, },
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"schedule": { "schedule": {
"daily": "Tägliche Zusammenfassung", "daily": "Tägliche Zusammenfassung",
"instant": "Sofort", "instant": "Sofort",
@ -1960,13 +1971,6 @@
}, },
"title": "Benachrichtigungseinstellungen" "title": "Benachrichtigungseinstellungen"
}, },
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"ocr": { "ocr": {
"confirmation-dialog": { "confirmation-dialog": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
@ -2058,16 +2062,16 @@
"warnings-label": "Dialoge und Meldungen", "warnings-label": "Dialoge und Meldungen",
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen" "warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
}, },
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Ausstehend", "pending": "Ausstehend",
"processed": "Verarbeitet", "processed": "Verarbeitet",
"processing": "Verarbeitung läuft" "processing": "Verarbeitung läuft"
}, },
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"readonly": "Lesemodus", "readonly": "Lesemodus",
"readonly-archived": "Lesemodus (archiviert)", "readonly-archived": "Lesemodus (archiviert)",
"redact-text": { "redact-text": {
@ -2082,10 +2086,6 @@
"edit-text": "Text bearbeiten", "edit-text": "Text bearbeiten",
"legal-basis": "Rechtsgrundlage", "legal-basis": "Rechtsgrundlage",
"options": { "options": {
"in-document": {
"description": "",
"label": ""
},
"in-dossier": { "in-dossier": {
"description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.", "description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.",
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen", "extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
@ -2101,8 +2101,7 @@
"revert-text": "Zurück zu ursprünglicher Auswahl", "revert-text": "Zurück zu ursprünglicher Auswahl",
"type": "Typ", "type": "Typ",
"type-placeholder": "Typ auswählen...", "type-placeholder": "Typ auswählen...",
"unchanged": "Ungeändert", "unchanged": "Ungeändert"
"value": ""
}, },
"title": "Text schwärzen" "title": "Text schwärzen"
} }
@ -2126,10 +2125,6 @@
"description-bulk": "", "description-bulk": "",
"label": "In diesem Kontext aus dem Dossier entfernen" "label": "In diesem Kontext aus dem Dossier entfernen"
}, },
"in-document": {
"description": "",
"label": ""
},
"in-dossier": { "in-dossier": {
"description": "Annotieren Sie den Begriff in diesem Dossier nicht.", "description": "Annotieren Sie den Begriff in diesem Dossier nicht.",
"description-bulk": "", "description-bulk": "",
@ -2170,10 +2165,6 @@
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen", "extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
"label": "In diesem Kontext aus Dossier entfernen" "label": "In diesem Kontext aus Dossier entfernen"
}, },
"in-document": {
"description": "",
"label": ""
},
"in-dossier": { "in-dossier": {
"description": "Der Begriff wird in keinem Dokument dieses Dossiers {type, select, hint{annotiert} other{automatisch geschwärzt}}.", "description": "Der Begriff wird in keinem Dokument dieses Dossiers {type, select, hint{annotiert} other{automatisch geschwärzt}}.",
"description-bulk": "Die ausgewählten Begriffe werden in diesem Dossier nicht {type, select, hint{annotiert} other{automatisch geschwärzt}}.", "description-bulk": "Die ausgewählten Begriffe werden in diesem Dossier nicht {type, select, hint{annotiert} other{automatisch geschwärzt}}.",
@ -2316,6 +2307,12 @@
"red-user-admin": "Benutzeradmin", "red-user-admin": "Benutzeradmin",
"regular": "regulärer Benutzer" "regular": "regulärer Benutzer"
}, },
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"search-screen": { "search-screen": {
"cols": { "cols": {
"assignee": "Bearbeiter", "assignee": "Bearbeiter",
@ -2339,12 +2336,6 @@
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.", "no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}" "table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
}, },
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"seconds": "Sekunden", "seconds": "Sekunden",
"size": "Größe", "size": "Größe",
"smtp-auth-config": { "smtp-auth-config": {
@ -2418,7 +2409,7 @@
}, },
"label": "Papierkorb", "label": "Papierkorb",
"no-data": { "no-data": {
"title": "Es gibt noch keine gelöschten Elemente." "title": "Im Papierkorb befinden sich keine gelöschten Elemente."
}, },
"no-match": { "no-match": {
"title": "Die ausgewählten Filter treffen auf kein Element zu." "title": "Die ausgewählten Filter treffen auf kein Element zu."

View File

@ -265,7 +265,7 @@
"entities": "Entities", "entities": "Entities",
"entity-info": "Info", "entity-info": "Info",
"entity-rule-editor": "Entity rule editor", "entity-rule-editor": "Entity rule editor",
"false-positive": "False positive", "false-positive": "False positives",
"false-recommendations": "False recommendations", "false-recommendations": "False recommendations",
"file-attributes": "File attributes", "file-attributes": "File attributes",
"justifications": "Justifications", "justifications": "Justifications",
@ -376,6 +376,7 @@
"removed-manual": "Redaction/Hint removed", "removed-manual": "Redaction/Hint removed",
"resized": "Redaction area has been modified" "resized": "Redaction area has been modified"
}, },
"annotation-content": "{hasRule, select, true {Rule {matchedRule} matched{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n section{sectionSymbol} \"{section}\"} other {}}",
"annotation-engines": { "annotation-engines": {
"dictionary": "Based on dictionary", "dictionary": "Based on dictionary",
"dossier-dictionary": "Based on dossier dictionary", "dossier-dictionary": "Based on dossier dictionary",
@ -642,8 +643,8 @@
"approve-file": { "approve-file": {
"confirmationText": "Approve anyway", "confirmationText": "Approve anyway",
"denyText": "No, cancel", "denyText": "No, cancel",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?", "question": "This document contains unseen changes that have been added during the reanalysis.<br><br>Do you still want to approve it?",
"title": "Approval warning", "title": "Warning!",
"warning-reason": { "warning-reason": {
"legal-basis-missing": "Legal basis missing", "legal-basis-missing": "Legal basis missing",
"pending-changes": "Pending Changes", "pending-changes": "Pending Changes",
@ -945,7 +946,7 @@
}, },
"dossier-overview": { "dossier-overview": {
"approve": "Approve", "approve": "Approve",
"approve-disabled": "File can only be approved once it has been analyzed with the latest set of business rules and terms stored in the system dictionaries.", "approve-disabled": "File can only be approved once it has been analysed with the latest dictionaries.",
"assign-approver": "Assign approver", "assign-approver": "Assign approver",
"assign-me": "Assign to me", "assign-me": "Assign to me",
"assign-reviewer": "Assign user", "assign-reviewer": "Assign user",
@ -1235,9 +1236,9 @@
"title": "Edit {label}" "title": "Edit {label}"
}, },
"entries-count": "{count} {count, plural, one{entry} other{entries}}", "entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives ({count})", "false-positives": "False positives",
"false-recommendations": "False recommendations ({count})", "false-recommendations": "False recommendations",
"to-redact": "Entries ({count})" "to-redact": "To redact"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1289,7 +1290,7 @@
}, },
"only-here": { "only-here": {
"description": "Edit redaction only at this position in this document.", "description": "Edit redaction only at this position in this document.",
"label": "Change only here" "label": "Change type only here"
} }
}, },
"reason": "Reason", "reason": "Reason",
@ -1546,7 +1547,7 @@
"select-none": "None", "select-none": "None",
"show-skipped": "Show skipped in document", "show-skipped": "Show skipped in document",
"the-filters": "the filters", "the-filters": "the filters",
"wrong-filters": "No annotations for the selected filter combination. Please adjust or" "wrong-filters": "No annotations for the selected filter combination. Please adjust or or reset the filters"
}, },
"document-info": { "document-info": {
"close": "Close document info", "close": "Close document info",
@ -1628,12 +1629,12 @@
"hint": "Hints only", "hint": "Hints only",
"image": "Images", "image": "Images",
"none": "No annotations", "none": "No annotations",
"redaction": "Redacted", "redaction": "Redaction",
"updated": "Updated" "updated": "Updated"
}, },
"filters": { "filters": {
"assigned-people": "Assignee(s)", "assigned-people": "Assignee(s)",
"documents-status": "Documents state", "documents-status": "Document state",
"dossier-state": "Dossier state", "dossier-state": "Dossier state",
"dossier-templates": "Dossier templates", "dossier-templates": "Dossier templates",
"empty": "Empty", "empty": "Empty",
@ -2013,14 +2014,34 @@
"previous": "Prev" "previous": "Prev"
}, },
"pdf-viewer": { "pdf-viewer": {
"header": {
"all-annotations-loaded": "All annotations loaded",
"compare-button": "Compare",
"layers-panel-button": "Layers",
"left-panel-button": "Panel",
"load-all-annotations": "Load all annotations",
"no-outlines-text": "No outlines available",
"no-signatures-text": "This document has NO signature fields",
"outline-multi-select": "Edit",
"outlines-panel-button": "Outlines",
"pan-tool-button": "Pan",
"rectangle-tool-button": "Rectangle",
"rotate-left-button": "Rotate page left",
"rotate-right-button": "Rotate page right",
"select-tool-button": "Select",
"signature-panel-button": "Signatures",
"thumbnails-panel-button": "Thumbnails",
"toggle-layers": "{active, select, true{Disable} false{Enable} other{}} layout grid",
"toggle-readable-redactions": "Show redactions {active, select, true{as in final document} false{in preview color} other{}}",
"toggle-tooltips": "{active, select, true{Disable} false{Enable} other{}} annotation tooltips",
"zoom-in-button": "Zoom In",
"zoom-out-button": "Zoom Out"
},
"text-popup": { "text-popup": {
"actions": { "actions": {
"search": "Search for selected text" "search": "Search for selected text"
} }
}, }
"toggle-layers": "{active, select, true{Disable} false{Enable} other{}} layout grid",
"toggle-readable-redactions": "Show redactions {active, select, true{as in final document} false{in preview color} other{}}",
"toggle-tooltips": "{active, select, true{Disable} false{Enable} other{}} annotation tooltips"
}, },
"permissions-screen": { "permissions-screen": {
"dossier": { "dossier": {
@ -2158,14 +2179,14 @@
"comment-placeholder": "Add remarks or notes...", "comment-placeholder": "Add remarks or notes...",
"options": { "options": {
"do-not-recommend": { "do-not-recommend": {
"description": "Do not recommend the selected {isImage, select, image{image} other{term}} in any document of this dossier.", "description": "Do not recommend the selected term in any document of this dossier.",
"description-bulk": "Do not recommend the selected {isImage, select, image{images} other{terms}} in any document of this dossier.", "description-bulk": "Do not recommend the selected terms in any document of this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier" "label": "Remove from dossier"
}, },
"false-positive": { "false-positive": {
"description": "Mark this redaction as a false-positive. The {isImage, select, image{image} other{term}} will not be redacted in this dossier if it occurs in the same context.", "description": "Mark this redaction as a false-positive. The term will not be redacted in this dossier if it occurs in the same context.",
"description-bulk": "Mark these redactions as false-positives. The {isImage, select, image{images} other{terms}} will not be redacted in this dossier if they occur in the same context.", "description-bulk": "Mark these redactions as false-positives. The terms will not be redacted in this dossier if they occur in the same context.",
"extraOptionDescription": "Dossier template access is required to reverse this action. As a regular user you can only reverse it for this dossier.", "extraOptionDescription": "Dossier template access is required to reverse this action. As a regular user you can only reverse it for this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier in this context" "label": "Remove from dossier in this context"
@ -2175,15 +2196,15 @@
"label": "Remove from document" "label": "Remove from document"
}, },
"in-dossier": { "in-dossier": {
"description": "Do not {type, select, hint{annotate} other{auto-redact}} the selected {isImage, select, image{image} other{term}} in any document of this dossier.", "description": "Do not {type, select, hint{annotate} other{auto-redact}} the selected term in any document of this dossier.",
"description-bulk": "Do not {type, select, hint{annotate} other{auto-redact}} the selected {isImage, select, image{images} other{terms}} in this dossier.", "description-bulk": "Do not {type, select, hint{annotate} other{auto-redact}} the selected terms in this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier", "label": "Remove from dossier",
"label-bulk": "Remove from dossier" "label-bulk": "Remove from dossier"
}, },
"only-here": { "only-here": {
"description": "Do not {type, select, hint{annotate} other{redact}} the {isImage, select, image{image} other{term}} at this position in the current document.", "description": "Do not {type, select, hint{annotate} other{redact}} the term at this position in the current document.",
"description-bulk": "Do not {type, select, hint{annotate} other{redact}} the selected {isImage, select, image{images} other{terms}} at this position in the current document.", "description-bulk": "Do not {type, select, hint{annotate} other{redact}} the selected terms at this position in the current document.",
"label": "Remove here" "label": "Remove here"
} }
} }
@ -2535,10 +2556,6 @@
"view-as": "View as:", "view-as": "View as:",
"workflow": "Workflow" "workflow": "Workflow"
}, },
"viewer-header": {
"all-annotations-loaded": "All annotations loaded",
"load-all-annotations": "Load all annotations"
},
"watermark-screen": { "watermark-screen": {
"action": { "action": {
"change-success": "Watermark has been updated.", "change-success": "Watermark has been updated.",
@ -2567,6 +2584,10 @@
"orientation": "Orientation", "orientation": "Orientation",
"text-label": "Watermark text", "text-label": "Watermark text",
"text-placeholder": "Enter text" "text-placeholder": "Enter text"
},
"pagination": {
"landscape": "Landscape",
"portrait": "Portrait"
} }
}, },
"watermarks-listing": { "watermarks-listing": {

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@
"save-and-add-members": "Save and edit team" "save-and-add-members": "Save and edit team"
}, },
"errors": { "errors": {
"dossier-already-exists": "Dossier with this name already exists! If it is in the trash, you need to permanently delete it first to re-use the name. If it is an active or archived dossier, please choose a different name." "dossier-already-exists": "<strong>Dossier name already exists.</strong>\n<ul><li>If the dossier is in trash, you can delete it permanently to reuse the name.</li><li>If the dossier is active or archived, please use a different name.</li></ul>"
}, },
"form": { "form": {
"description": { "description": {
@ -159,8 +159,8 @@
"template-and-dossier-dictionaries": "" "template-and-dossier-dictionaries": ""
}, },
"success": { "success": {
"create": "Entity added!", "create": "Success: Entity created.",
"edit": "Entity updated. Please note that other users need to refresh the browser to see your changes." "edit": "<strong>Success: Entity updated.</strong><br><br>Please ask the users to refresh the browser to see the changes."
} }
}, },
"add-edit-file-attribute": { "add-edit-file-attribute": {
@ -204,7 +204,7 @@
}, },
"error": { "error": {
"email-already-used": "This e-mail address is already in use by a different user!", "email-already-used": "This e-mail address is already in use by a different user!",
"generic": "Failed to save user!" "generic": "Failed to save user."
}, },
"form": { "form": {
"account-setup": "User account setup", "account-setup": "User account setup",
@ -265,7 +265,7 @@
"entities": "Entities", "entities": "Entities",
"entity-info": "Info", "entity-info": "Info",
"entity-rule-editor": "Entity rule editor", "entity-rule-editor": "Entity rule editor",
"false-positive": "False positive", "false-positive": "False positives",
"false-recommendations": "False recommendations", "false-recommendations": "False recommendations",
"file-attributes": "File attributes", "file-attributes": "File attributes",
"justifications": "Justifications", "justifications": "Justifications",
@ -303,7 +303,7 @@
}, },
"remove": { "remove": {
"error": "Failed to remove dictionary entry: {error}", "error": "Failed to remove dictionary entry: {error}",
"success": "Dictionary entry removed!" "success": "Dictionary entry removed"
}, },
"undo": { "undo": {
"error": "Failed to undo: {error}", "error": "Failed to undo: {error}",
@ -313,15 +313,15 @@
"manual-redaction": { "manual-redaction": {
"add": { "add": {
"error": "Failed to save annotation: {error}", "error": "Failed to save annotation: {error}",
"success": "Annotation added!" "success": "Annotation added"
}, },
"force-hint": { "force-hint": {
"error": "Failed to save hint: {error}", "error": "Failed to save hint: {error}",
"success": "Hint added!" "success": "Hint added"
}, },
"force-redaction": { "force-redaction": {
"error": "Failed to save annotation: {error}", "error": "Failed to save annotation: {error}",
"success": "Annotation added!" "success": "Annotation added"
}, },
"recategorize-annotation": { "recategorize-annotation": {
"error": "", "error": "",
@ -333,11 +333,11 @@
}, },
"remove-hint": { "remove-hint": {
"error": "Failed to remove hint: {error}", "error": "Failed to remove hint: {error}",
"success": "Hint removed!" "success": "Hint removed"
}, },
"remove": { "remove": {
"error": "Failed to remove annotation: {error}", "error": "Failed to remove annotation: {error}",
"success": "Annotation removed!" "success": "Annotation removed"
}, },
"undo": { "undo": {
"error": "Failed to undo: {error}", "error": "Failed to undo: {error}",
@ -376,6 +376,7 @@
"removed-manual": "Annotation/Hint removed", "removed-manual": "Annotation/Hint removed",
"resized": "Annotation area has been modified" "resized": "Annotation area has been modified"
}, },
"annotation-content": "{hasRule, select, true {Rule {matchedRule} matched{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n section{sectionSymbol} \"{section}\"} other {}}",
"annotation-engines": { "annotation-engines": {
"dictionary": "{isHint, select, true{Hint} other{Annotation}} based on dictionary", "dictionary": "{isHint, select, true{Hint} other{Annotation}} based on dictionary",
"dossier-dictionary": "Annotation based on dossier dictionary", "dossier-dictionary": "Annotation based on dossier dictionary",
@ -481,7 +482,7 @@
}, },
"auth-error": { "auth-error": {
"heading": "Your user is successfully logged in but has no role assigned yet. Please contact your DocuMine administrator to assign appropriate roles.", "heading": "Your user is successfully logged in but has no role assigned yet. Please contact your DocuMine administrator to assign appropriate roles.",
"heading-with-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >your DocuMine administrator</a> to assign appropriate roles!", "heading-with-link": "Your user is successfully logged in but has no role assigned yet. Please ask <a href={adminUrl} target=_blank >your DocuMine administrator</a> to assign a role to you.",
"heading-with-name": "Your user is successfully logged in but has no role assigned yet. Please contact {adminName} to assign appropriate roles.", "heading-with-name": "Your user is successfully logged in but has no role assigned yet. Please contact {adminName} to assign appropriate roles.",
"heading-with-name-and-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >{adminName}</a> to assign appropriate roles.", "heading-with-name-and-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >{adminName}</a> to assign appropriate roles.",
"logout": "Logout" "logout": "Logout"
@ -599,22 +600,22 @@
"checkbox": { "checkbox": {
"documents": "All documents will be archived and cannot be put back to active" "documents": "All documents will be archived and cannot be put back to active"
}, },
"details": "Restoring an archived dossier is not possible anymore, once it got archived.", "details": "Be aware that archiving is an irreversible action. Documents archived will no longer be available for active use.",
"title": "Archive {dossierName}", "title": "Archive {dossierName}",
"toast-error": "Please confirm that you understand the ramifications of your action!", "toast-error": "Please confirm that you understand the consequences of this action.",
"warning": "Are you sure you want to archive the dossier?" "warning": "Are you sure you want to archive the dossier?"
}, },
"confirm-delete-attribute": { "confirm-delete-attribute": {
"cancel": "Keep {count, plural, one{attribute} other{attributes}}", "cancel": "Keep {count, plural, one{attribute} other{attributes}}",
"delete": "Delete {count, plural, one{attribute} other{attributes}}", "delete": "Delete {count, plural, one{attribute} other{attributes}}",
"dossier-impacted-documents": "All dossiers based on this template will be affected", "dossier-impacted-documents": "This action will affect all dossiers that use this template.",
"dossier-lost-details": "All values for this attribute will be lost", "dossier-lost-details": "The attribute values entered by users on document level will be lost.",
"file-impacted-documents": "All documents {count, plural, one{it is} other{they are}} used on will be impacted", "file-impacted-documents": "All documents {count, plural, one{it is} other{they are}} used on will be impacted",
"file-lost-details": "All inputted details on the documents will be lost", "file-lost-details": "All information entered by users on document level will be lost.",
"impacted-report": "{reportsCount} reports use the placeholder for this attribute and need to be adjusted", "impacted-report": "{reportsCount} reports currently use the placeholder for this attribute. Please update them.",
"title": "Delete {count, plural, one{{name}} other{file attributes}}", "title": "Delete {count, plural, one{{name}} other{file attributes}}",
"toast-error": "Please confirm that you understand the ramifications of your action!", "toast-error": "Please confirm that you understand the consequences of this action.",
"warning": "Warning: this cannot be undone!" "warning": "Warning: This action cannot be undone!"
}, },
"confirm-delete-dossier-state": { "confirm-delete-dossier-state": {
"cancel": "Cancel", "cancel": "Cancel",
@ -622,9 +623,9 @@
"delete-replace": "Delete and replace", "delete-replace": "Delete and replace",
"form": { "form": {
"state": "Replace state", "state": "Replace state",
"state-placeholder": "Choose another state" "state-placeholder": "Select another state"
}, },
"question": "Replace the {count, plural, one{dossier's} other{dossiers'}} state with another state", "question": "Select another state to replace the current {count, plural, one{dossier} other{dossier}} state",
"success": "Successfully deleted state!", "success": "Successfully deleted state!",
"title": "Delete dossier state", "title": "Delete dossier state",
"warning": "The {name} state is assigned to {count} {count, plural, one{dossier} other{dossiers}}." "warning": "The {name} state is assigned to {count} {count, plural, one{dossier} other{dossiers}}."
@ -635,15 +636,15 @@
"impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted", "impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted",
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted", "impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace", "title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
"toast-error": "Please confirm that you understand the ramifications of your action!", "toast-error": "Please confirm that you understand the consequences of this action.",
"warning": "Warning: this cannot be undone!" "warning": "Warning: this cannot be undone!"
}, },
"confirmation-dialog": { "confirmation-dialog": {
"approve-file": { "approve-file": {
"confirmationText": "Approve anyway", "confirmationText": "Approve anyway",
"denyText": "No, cancel", "denyText": "No, cancel",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?", "question": "This document contains unseen changes that have been added during the reanalysis.\n<br><br>Do you still want to approve it?",
"title": "Approval warning", "title": "Warning!",
"warning-reason": { "warning-reason": {
"legal-basis-missing": "Legal basis missing", "legal-basis-missing": "Legal basis missing",
"pending-changes": "Pending Changes", "pending-changes": "Pending Changes",
@ -653,19 +654,19 @@
"assign-file-to-me": { "assign-file-to-me": {
"question": { "question": {
"multiple": "At least one document is currently assigned to someone else. Are you sure you want to replace them and assign yourself to these documents?", "multiple": "At least one document is currently assigned to someone else. Are you sure you want to replace them and assign yourself to these documents?",
"single": "This document is currently assigned to someone else. Are you sure you want to replace it and assign yourself to this document?" "single": "This document is currently assigned to another user. Do you still want to assign the files to yourself?"
}, },
"title": "Re-assign user" "title": "Re-assign user"
}, },
"compare-file": { "compare-file": {
"question": "<strong>Warning!</strong> <br><br> Number of pages does not match, current document has <strong>{currentDocumentPageCount} page(s)</strong>. Uploaded document has <strong>{compareDocumentPageCount} page(s)</strong>. <br><br> Do you wish to proceed?", "question": "<strong>Warning: page count mismatch</strong><br><br> Current document: \n<strong>{currentDocumentPageCount} page(s)</strong>.<br><br>Upload document: \n<strong>{compareDocumentPageCount} page(s)</strong>. <br><be>This appears to be a different document. Do you wish to proceed?",
"title": "Compare with file: {fileName}" "title": "Compare with file: {fileName}"
}, },
"delete-dossier": { "delete-dossier": {
"confirmation-text": "Delete {dossiersCount, plural, one{dossier} other{dossiers}}", "confirmation-text": "Delete {dossiersCount, plural, one{dossier} other{dossiers}}",
"deny-text": "Keep {dossiersCount, plural, one{dossier} other{dossiers}}", "deny-text": "Keep {dossiersCount, plural, one{dossier} other{dossiers}}",
"question": "Are you sure you want to delete {dossiersCount, plural, one{this dossier} other{these dossiers}}?", "question": "Are you sure you want to delete {dossiersCount, plural, one{this dossier} other{these dossiers}}?",
"title": "Delete {dossiersCount, plural, one{{dossierName}} other{Selected Dossiers}}" "title": "Delete {dossiersCount, plural, one{{dossierName}} other{selected dossiers}}"
}, },
"delete-file": { "delete-file": {
"question": "Do you wish to proceed?", "question": "Do you wish to proceed?",
@ -688,23 +689,23 @@
}, },
"unsaved-changes": { "unsaved-changes": {
"confirmation-text": "Save and leave", "confirmation-text": "Save and leave",
"details": "If you leave the tab without saving, all the unsaved changes will be lost.", "details": "Please save your changes. If you leave now, any unsaved progress will be lost.",
"discard-changes-text": "DISCARD CHANGES", "discard-changes-text": "DISCARD CHANGES",
"question": "Are you sure you want to leave the tab? You have unsaved changes.", "question": "Do you still want to leave the tab?",
"title": "You have unsaved changes" "title": "You have unsaved changes"
}, },
"upload-report-template": { "upload-report-template": {
"alternate-confirmation-text": "Upload as multi-file report", "alternate-confirmation-text": "Upload as multi-file report",
"confirmation-text": "Upload as single-file report", "confirmation-text": "Upload as single-file report",
"deny-text": "Cancel upload", "deny-text": "Cancel upload",
"question": "Please choose if <b>{fileName}</b> is a single or multi-file report template", "question": "Please indicate if <b>{fileName}</b> is a single or multi-file report template",
"title": "Report template upload" "title": "Report template upload"
} }
}, },
"content": "Reason", "content": "Reason",
"dashboard": { "dashboard": {
"empty-template": { "empty-template": {
"description": "This template does not contain any dossiers. Start by creating a dossier to use it on.", "description": "This template does not contain any dossiers. Create a dossier that applies this ruleset.",
"new-dossier": "New dossier" "new-dossier": "New dossier"
}, },
"greeting": { "greeting": {
@ -768,13 +769,13 @@
}, },
"download": "Download current entries", "download": "Download current entries",
"error": { "error": {
"400": "Cannot update dictionary because at least one of the newly added words where recognized as a general term that appear too often in texts.", "400": "<strong>Dictionary update failed.</strong><br><br>One or more of the newly added terms have been identified as frequently used general term. Please remove these terms to proceed with the update.",
"generic": "Something went wrong... Dictionary update failed!" "generic": "Error: Dictionary update failed."
}, },
"revert-changes": "Revert", "revert-changes": "Revert",
"save-changes": "Save changes", "save-changes": "Save changes",
"search": "Search entries...", "search": "Search entries...",
"select-dictionary": "Select a dictionary above to compare with the current one.", "select-dictionary": "Select a dictionary for comparison above.",
"success": { "success": {
"generic": "Dictionary updated!" "generic": "Dictionary updated!"
} }
@ -784,11 +785,11 @@
"actions": { "actions": {
"back": "Back", "back": "Back",
"cancel": "Cancel", "cancel": "Cancel",
"certificate-not-valid-error": "Uploaded certificate is not valid!", "certificate-not-valid-error": "Uploaded certificate is invalid.",
"continue": "Continue", "continue": "Continue",
"save": "Save configurations", "save": "Save configurations",
"save-error": "Failed to save digital signature!", "save-error": "Failed to save digital signature!",
"save-success": "Digital signature certificate successfully saved!" "save-success": "Digital signature certificate saved successfully"
}, },
"forms": { "forms": {
"kms": { "kms": {
@ -810,11 +811,11 @@
}, },
"options": { "options": {
"kms": { "kms": {
"description": "Provide a corresponding PEM file containing the certificate, along with Amazon KMS credentials needed for securing the private key.", "description": "Please upload a PEM file with the certificate and provide Amazon KMS credentials to secure the private key.",
"label": "I use an Amazon KMS private key" "label": "I use an Amazon KMS private key"
}, },
"pkcs": { "pkcs": {
"description": "A PKCS#12 file is a file that bundles the private key and the X.509 certificate. The password protection is required to secure the private key. Unprotected PKCS#12 files are not supported.", "description": "A PKCS#12 file combines your private key with an X.509 certificate. Password protection is essential to secure the private key, as unprotected PKCS#12 files are not supported.",
"label": "I want to upload a PKCS#12 file" "label": "I want to upload a PKCS#12 file"
} }
}, },
@ -832,7 +833,7 @@
"remove": "Remove", "remove": "Remove",
"save": "Save changes", "save": "Save changes",
"save-error": "Failed to save digital signature!", "save-error": "Failed to save digital signature!",
"save-success": "Digital signature certificate successfully saved!" "save-success": "No digital signature certificate available.<br/>Please configure a certificate to sign redacted documents."
}, },
"no-data": { "no-data": {
"action": "Configure certificate", "action": "Configure certificate",
@ -884,7 +885,7 @@
"dossier-details": { "dossier-details": {
"assign-members": "Assign members", "assign-members": "Assign members",
"collapse": "Hide details", "collapse": "Hide details",
"document-status": "Document status", "document-status": "Document processing status",
"edit-owner": "Edit owner", "edit-owner": "Edit owner",
"expand": "Show details", "expand": "Show details",
"members": "Members", "members": "Members",
@ -896,7 +897,7 @@
"add-new": "New dossier", "add-new": "New dossier",
"archive": { "archive": {
"action": "Archive dossier", "action": "Archive dossier",
"archive-failed": "Failed to archive dossier {dossierName}!", "archive-failed": "Failed to archive dossier {dossierName}.",
"archive-succeeded": "Successfully archived dossier {dossierName}." "archive-succeeded": "Successfully archived dossier {dossierName}."
}, },
"delete": { "delete": {
@ -1235,9 +1236,9 @@
"title": "" "title": ""
}, },
"entries-count": "{count} {count, plural, one{entry} other{entries}}", "entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives ({count})", "false-positives": "False positives",
"false-recommendations": "False recommendations ({count})", "false-recommendations": "False recommendations",
"to-redact": "To redact ({count})" "to-redact": "To redact"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1367,12 +1368,12 @@
}, },
"file-preview": { "file-preview": {
"action": "Refresh", "action": "Refresh",
"label": "An unknown error occurred. Please refresh the page." "label": "<strong>Unknown error:</strong> Please refresh the page."
}, },
"http": { "http": {
"generic": "Action failed with code {status}" "generic": "Action failed. Error code: {status}"
}, },
"missing-types": "The dossier template has missing types ({missingTypes}). Data might not be displayed correctly.", "missing-types": "<strong>Dossier template incomplete:</strong> missing types ({missingTypes}) may cause data display issues.",
"offline": "Disconnected", "offline": "Disconnected",
"online": "Reconnected", "online": "Reconnected",
"reload": "Reload", "reload": "Reload",
@ -1392,7 +1393,7 @@
}, },
"file-attribute": { "file-attribute": {
"update": { "update": {
"error": "Failed to update file attribute value!", "error": "Update of file attribute value failed. Please try again.",
"success": "File attribute value has been updated successfully!" "success": "File attribute value has been updated successfully!"
} }
}, },
@ -1428,7 +1429,7 @@
"key-column": "Key column", "key-column": "Key column",
"key-column-placeholder": "Select column...", "key-column-placeholder": "Select column...",
"no-data": { "no-data": {
"title": "No file attributes defined. Select a column from the left panel to start defining file attributes." "title": "No file attributes defined. Select a CSV column from the left to start defining file attributes."
}, },
"no-hovered-column": "Preview CSV column by hovering the entry.", "no-hovered-column": "Preview CSV column by hovering the entry.",
"no-sample-data-for": "No sample data for {column}.", "no-sample-data-for": "No sample data for {column}.",
@ -1449,7 +1450,7 @@
"table-col-names": { "table-col-names": {
"name": "Name", "name": "Name",
"primary": "primary", "primary": "primary",
"primary-info-tooltip": "The value of the attribute set as primary shows up under the file name in the documents list.", "primary-info-tooltip": "The value of the attribute set as primary is diplayed below the file name in the document list.",
"read-only": "Read-only", "read-only": "Read-only",
"type": "Type" "type": "Type"
}, },
@ -1477,8 +1478,8 @@
}, },
"configurations": "Configurations", "configurations": "Configurations",
"error": { "error": {
"conflict": "File-Attribute with this name already exists!", "conflict": "File attribute name already exists. Please try another name.",
"generic": "Failed to add file attribute" "generic": "Failed to add file attribute."
}, },
"no-data": { "no-data": {
"title": "There are no file attributes yet." "title": "There are no file attributes yet."
@ -1494,7 +1495,7 @@
"filterable": "Filterable", "filterable": "Filterable",
"name": "Name", "name": "Name",
"primary": "Primary", "primary": "Primary",
"primary-info-tooltip": "The value of the attribute set as primary shows up under the file name in the documents list.", "primary-info-tooltip": "The value of the attribute set as primary is diplayed below the file name in the document list.",
"read-only": "Read-only", "read-only": "Read-only",
"type": "Input type" "type": "Input type"
}, },
@ -1508,20 +1509,20 @@
"assign-reviewer": "Assign user", "assign-reviewer": "Assign user",
"change-reviewer": "Change user", "change-reviewer": "Change user",
"delta": "Delta", "delta": "Delta",
"delta-tooltip": "The delta view shows the unseen changes since your last visit to the page. This view is only available if there is at least 1 change.", "delta-tooltip": "Delta shows the unseen changes since your last visit to the page. It is only available if there has been at least 1 change.",
"document-info": "Document info", "document-info": "Document info",
"download-original-file": "Download original file", "download-original-file": "Download original file",
"exclude-pages": "Exclude pages from extraction", "exclude-pages": "Exclude pages from extraction",
"excluded-from-redaction": "excluded", "excluded-from-redaction": "Excluded",
"fullscreen": "Full screen (F)", "fullscreen": "Full screen (F)",
"get-tables": "Draw tables", "get-tables": "Draw tables",
"highlights": { "highlights": {
"convert": "Convert earmarks", "convert": "Convert earmarks",
"remove": "Remove earmarks" "remove": "Remove earmarks"
}, },
"last-assignee": "Last assignee", "last-assignee": "{status, select, APPROVED{Approved} UNDER_APPROVAL{Reviewed} other{Last reviewed}} by:",
"no-data": { "no-data": {
"title": "This page does not contain annotations for the selected component or filter." "title": "There have been no changes to this page."
}, },
"quick-nav": { "quick-nav": {
"jump-first": "Jump to first page", "jump-first": "Jump to first page",
@ -1538,7 +1539,7 @@
"jump-to-next": "Jump to next", "jump-to-next": "Jump to next",
"jump-to-previous": "Jump to previous", "jump-to-previous": "Jump to previous",
"label": "Workload", "label": "Workload",
"no-annotations": "There are no annotations for the selected component. \n", "no-annotations": "There are no annotations for the selected component.\n",
"page-is": "This page is", "page-is": "This page is",
"reset": "reset", "reset": "reset",
"select": "Select", "select": "Select",
@ -1546,7 +1547,7 @@
"select-none": "None", "select-none": "None",
"show-skipped": "Show skipped in document", "show-skipped": "Show skipped in document",
"the-filters": "the filters", "the-filters": "the filters",
"wrong-filters": "No annotations for the selected filter combination. Please adjust or" "wrong-filters": "No annotations for the selected filter combination. Please adjust or reset the filters"
}, },
"document-info": { "document-info": {
"close": "Close document info", "close": "Close document info",
@ -1578,7 +1579,7 @@
} }
}, },
"text-highlights": "Earmarks", "text-highlights": "Earmarks",
"text-highlights-tooltip": "Shows all text-earmarks and allows removing or importing them as components", "text-highlights-tooltip": "Earmark allows removing text highlights or converting them into redactions.",
"toggle-analysis": { "toggle-analysis": {
"disable": "Disable extraction", "disable": "Disable extraction",
"enable": "Enable for extraction", "enable": "Enable for extraction",
@ -1587,7 +1588,7 @@
}, },
"file-status": { "file-status": {
"analyse": "Analyzing", "analyse": "Analyzing",
"approved": "Done", "approved": "Approved",
"error": "Re-processing required", "error": "Re-processing required",
"figure-detection-analyzing": "", "figure-detection-analyzing": "",
"full-processing": "Processing", "full-processing": "Processing",
@ -1618,7 +1619,7 @@
"filter-types": "Filter", "filter-types": "Filter",
"label": "Filter", "label": "Filter",
"pages-without-annotations": "Only pages without annotations", "pages-without-annotations": "Only pages without annotations",
"redaction-changes": "Only annotations with changes", "redaction-changes": "Only annotations with local manual changes",
"unseen-pages": "Only annotations on unseen pages", "unseen-pages": "Only annotations on unseen pages",
"with-comments": "Only annotations with comments" "with-comments": "Only annotations with comments"
}, },
@ -1693,7 +1694,7 @@
"title": "System preferences" "title": "System preferences"
}, },
"test": { "test": {
"error": "Test e-mail could not be sent! Please revise the e-mail address.", "error": "Test e-mail could not be sent. Please double-check the email address.",
"success": "Test e-mail was sent successfully!", "success": "Test e-mail was sent successfully!",
"warning": "Admin mail address not set. Test email sent to {recipientEmail} instead." "warning": "Admin mail address not set. Test email sent to {recipientEmail} instead."
}, },
@ -1705,12 +1706,12 @@
}, },
"help-mode": { "help-mode": {
"bottom-text": "Help mode", "bottom-text": "Help mode",
"clicking-anywhere-on": "<b> Clicking anywhere on the screen </b> will show you which areas are interactive. Hovering an interactive area will <b> change the mouse cursor </b> to let you know if the element is interactive.", "clicking-anywhere-on": "<b>Click anywhere on the screen </b> to display the interactive elements. When you hover over an interactive element, <b> the mouse cursor changes</b> to show the element is clickable.",
"instructions": "Open help mode instructions", "instructions": "Open help mode instructions",
"options": { "options": {
"do-not-show-again": "Do not show again" "do-not-show-again": "Do not show again"
}, },
"welcome-to-help-mode": "<b> Welcome to help mode! <br> Clicking on interactive elements will open info about them in new tab. </b>" "welcome-to-help-mode": "<b>Welcome to Help Mode!<br> Click on interactive elements to learn more about their functionality in a new tab."
}, },
"highlight-action-dialog": { "highlight-action-dialog": {
"actions": { "actions": {
@ -2013,14 +2014,34 @@
"previous": "Prev" "previous": "Prev"
}, },
"pdf-viewer": { "pdf-viewer": {
"header": {
"all-annotations-loaded": "All annotations loaded",
"compare-button": "Compare",
"layers-panel-button": "Layers",
"left-panel-button": "Panel",
"load-all-annotations": "Load all annotations",
"no-outlines-text": "No outlines available",
"no-signatures-text": "This document has NO signature fields",
"outline-multi-select": "Edit",
"outlines-panel-button": "Outlines",
"pan-tool-button": "Pan",
"rectangle-tool-button": "Rectangle",
"rotate-left-button": "Rotate page left",
"rotate-right-button": "Rotate page right",
"select-tool-button": "Select",
"signature-panel-button": "Signatures",
"thumbnails-panel-button": "Thumbnails",
"toggle-layers": "{active, select, true{Disable} false{Enable} other{}} layout grid",
"toggle-readable-redactions": "Show redactions {active, select, true{as in final document} false{in preview color} other{}}",
"toggle-tooltips": "{active, select, true{Disable} false{Enable} other{}} annotation tooltips",
"zoom-in-button": "Zoom In",
"zoom-out-button": "Zoom Out"
},
"text-popup": { "text-popup": {
"actions": { "actions": {
"search": "Search for selected text" "search": "Search for selected text"
} }
}, }
"toggle-layers": "{active, select, true{Disable} false{Enable} other{}} layout grid",
"toggle-readable-redactions": "Show components {active, select, true{as in final document} false{in preview color} other{}}",
"toggle-tooltips": "{active, select, true{Disable} false{Enable} other{}} annotation tooltips"
}, },
"permissions-screen": { "permissions-screen": {
"dossier": { "dossier": {
@ -2535,10 +2556,6 @@
"view-as": "View as:", "view-as": "View as:",
"workflow": "Workflow" "workflow": "Workflow"
}, },
"viewer-header": {
"all-annotations-loaded": "All annotations loaded",
"load-all-annotations": "Load all annotations"
},
"watermark-screen": { "watermark-screen": {
"action": { "action": {
"change-success": "Watermark has been updated!", "change-success": "Watermark has been updated!",
@ -2567,6 +2584,10 @@
"orientation": "Orientation", "orientation": "Orientation",
"text-label": "Watermark text", "text-label": "Watermark text",
"text-placeholder": "Enter text" "text-placeholder": "Enter text"
},
"pagination": {
"landscape": "",
"portrait": ""
} }
}, },
"watermarks-listing": { "watermarks-listing": {