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 {
padding: 32px;
width: 900px;
width: 1000px;
max-width: 100%;
box-sizing: border-box;
}

View File

@ -26,6 +26,12 @@ import {
} from '@red/domain';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
interface AnnotationContent {
translation: string;
params: { [key: string]: string };
untranslatedContent: string;
}
export class AnnotationWrapper implements IListable {
id: string;
superType: SuperType;
@ -36,7 +42,7 @@ export class AnnotationWrapper implements IListable {
numberOfComments = 0;
firstTopLeftPoint: IPoint;
shortContent: string;
content: string;
content: AnnotationContent;
value: string;
pageNumber: number;
dictionaryOperation = false;
@ -279,7 +285,7 @@ export class AnnotationWrapper implements IListable {
);
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;
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
@ -311,39 +317,57 @@ export class AnnotationWrapper implements IListable {
}
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
let content = '';
let untranslatedContent = '';
const params: { [key: string]: string } = {};
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) {
params['hasReason'] = 'true';
if (isDocumine && logEntry.reason.slice(-1) === '.') {
logEntry.reason = logEntry.reason.slice(0, -1);
}
content += logEntry.reason + '\n\n';
if (!params['hasRule']) {
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
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
content = content.substring(0, 1).toUpperCase() + content.substring(1);
untranslatedContent = untranslatedContent.replace(/(^[, ]*)|([, ]*$)/g, '');
untranslatedContent = untranslatedContent.substring(0, 1).toUpperCase() + untranslatedContent.substring(1);
}
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) {
content += 'Removed by manual override';
params['hasOverride'] = 'true';
untranslatedContent += 'Removed by manual override';
}
if (logEntry.section) {
params['hasSection'] = 'true';
params['sectionSymbol'] = isDocumine ? '' : ':';
params['shouldLower'] = untranslatedContent.length.toString();
params['section'] = logEntry.section;
let prefix = `In section${isDocumine ? '' : ':'} `;
if (content.length) {
if (untranslatedContent.length) {
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[]) {

View File

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

View File

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

View File

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

View File

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

View File

@ -119,7 +119,9 @@ export class BulkActionsService {
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(
'confirm',
{

View File

@ -1,161 +1,165 @@
<div
*ngIf="canPerformAnnotationActions && annotationPermissions"
[class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)"
class="annotation-actions"
>
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
<ng-container *ngIf="resizing && annotationPermissions.canResizeAnnotation">
<iqser-circle-button
(action)="acceptResize()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-accept_resize' : 'annotations-accept_resize'"
[class.disabled]="!resized"
[tooltipPosition]="tooltipPosition"
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
@if (canPerformAnnotationActions && annotationPermissions()) {
<div [class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)" class="annotation-actions">
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
<ng-container *ngIf="resizing() && annotationPermissions().canResizeAnnotation">
<iqser-circle-button
(action)="acceptResize()"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-accept_resize' : 'annotations-accept_resize'
"
[class.disabled]="!resized"
[tooltipPosition]="tooltipPosition"
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="cancelResize()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-cancel_resize' : 'annotations-cancel_resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize-cancel.label' | translate"
[type]="buttonType"
icon="iqser:close"
></iqser-circle-button>
</ng-container>
<iqser-circle-button
(action)="cancelResize()"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-cancel_resize' : 'annotations-cancel_resize'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize-cancel.label' | translate"
[type]="buttonType"
icon="iqser:close"
></iqser-circle-button>
</ng-container>
<!-- Not resizing - standard actions -->
<ng-container *ngIf="!resizing">
<iqser-circle-button
(action)="resize()"
*ngIf="canResize"
[attr.help-mode-key]="helpModeKey('resize')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-resize' : 'annotations-resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize.label' | translate"
[type]="buttonType"
icon="iqser:resize"
></iqser-circle-button>
<!-- Not resizing - standard actions -->
<ng-container *ngIf="!resizing()">
<iqser-circle-button
(action)="resize()"
*ngIf="canResize()"
[attr.help-mode-key]="helpModeKey('resize')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-resize' : 'annotations-resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize.label' | translate"
[type]="buttonType"
icon="iqser:resize"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.editRedaction(annotations)"
*ngIf="canEdit"
[attr.help-mode-key]="helpModeKey('edit')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-edit' : 'annotations-edit'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
[type]="buttonType"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.editRedaction(annotations())"
*ngIf="canEdit()"
[attr.help-mode-key]="helpModeKey('edit')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-edit' : 'annotations-edit'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
[type]="buttonType"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="acceptRecommendation()"
*ngIf="canAcceptRecommendation"
[attr.help-mode-key]="helpModeKey('accept')"
[buttonId]="
annotations.length === 1
? 'annotation-' + annotations[0].id + '-accept_recommendation'
: 'annotations-accept_recommendation'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="acceptRecommendation()"
*ngIf="canAcceptRecommendation()"
[attr.help-mode-key]="helpModeKey('accept')"
[buttonId]="
annotations().length === 1
? 'annotation-' + annotations()[0].id + '-accept_recommendation'
: 'annotations-accept_recommendation'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations)"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType"
icon="red:convert"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType"
icon="red:convert"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations)"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.undoDirectAction(annotations)"
*allow="roles.redactions.deleteManual; if: annotationPermissions.canUndo"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.undo' | translate"
[type]="buttonType"
icon="red:undo"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.undoDirectAction(annotations())"
*allow="roles.redactions.deleteManual; if: annotationPermissions().canUndo"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.undo' | translate"
[type]="buttonType"
icon="red:undo"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationReferencesService.show(annotations[0])"
*ngIf="multiSelectService.inactive() && annotations[0].reference.length"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.see-references.label' | translate"
[type]="buttonType"
icon="red:reference"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationReferencesService.show(annotations()[0])"
*ngIf="multiSelectService.inactive() && annotations()[0].reference.length"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.see-references.label' | translate"
[type]="buttonType"
icon="red:reference"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations)"
*ngIf="canForceRedaction"
[attr.help-mode-key]="isImageHint ? helpModeKey('redact') : helpModeKey('force')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_redaction' : 'annotations-force_redaction'"
[icon]="isImageHint ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'"
[tooltipPosition]="tooltipPosition"
[tooltip]="
isImageHint
? ('annotation-actions.force-redaction.label-image-hint' | translate)
: ('annotation-actions.force-redaction.label' | translate)
"
[type]="buttonType"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations())"
*ngIf="canForceRedaction()"
[attr.help-mode-key]="isImageHint() ? helpModeKey('redact') : helpModeKey('force')"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_redaction' : 'annotations-force_redaction'
"
[icon]="isImageHint() ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'"
[tooltipPosition]="tooltipPosition"
[tooltip]="
isImageHint()
? ('annotation-actions.force-redaction.label-image-hint' | translate)
: ('annotation-actions.force-redaction.label' | translate)
"
[type]="buttonType"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations, true)"
*ngIf="canForceHint"
[attr.help-mode-key]="actionsHelpModeKey"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_hint' : 'annotations-force_hint'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.force-hint.label' | translate"
[type]="buttonType"
icon="iqser:thumb-up"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations(), true)"
*ngIf="canForceHint()"
[attr.help-mode-key]="actionsHelpModeKey"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_hint' : 'annotations-force_hint'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.force-hint.label' | translate"
[type]="buttonType"
icon="iqser:thumb-up"
></iqser-circle-button>
<iqser-circle-button
(action)="hideAnnotation()"
*ngIf="isImage && isVisible() && !hideSkipped"
[attr.help-mode-key]="helpModeKey('hide')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-hide' : 'annotations-hide'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.hide' | translate"
[type]="buttonType"
icon="iqser:visibility-off"
></iqser-circle-button>
<iqser-circle-button
(action)="hideAnnotation()"
*ngIf="isImage() && isVisible() && !hideSkipped()"
[attr.help-mode-key]="helpModeKey('hide')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-hide' : 'annotations-hide'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.hide' | translate"
[type]="buttonType"
icon="iqser:visibility-off"
></iqser-circle-button>
<iqser-circle-button
(action)="showAnnotation()"
*ngIf="isImage && !isVisible() && !hideSkipped"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-show' : 'annotations-show'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.show' | translate"
[type]="buttonType"
icon="iqser:visibility"
></iqser-circle-button>
<iqser-circle-button
(action)="showAnnotation()"
*ngIf="isImage() && !isVisible() && !hideSkipped()"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-show' : 'annotations-show'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.show' | translate"
[type]="buttonType"
icon="iqser:visibility"
></iqser-circle-button>
<iqser-circle-button
(action)="removeRedaction()"
*ngIf="canRemoveRedaction"
[attr.help-mode-key]="helpModeKey('remove')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-remove' : 'annotations-remove'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
</ng-container>
</div>
<iqser-circle-button
(action)="removeRedaction()"
*ngIf="canRemoveRedaction()"
[attr.help-mode-key]="helpModeKey('remove')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-remove' : 'annotations-remove'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
</ng-container>
</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 { AnnotationPermissions } from '@models/file/annotation.permissions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -28,21 +28,77 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
standalone: true,
imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective],
})
export class AnnotationActionsComponent implements OnChanges {
#annotations: AnnotationWrapper[] = [];
readonly #isDocumine = getConfig().IS_DOCUMINE;
protected _annotationId = '';
export class AnnotationActionsComponent {
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
@Input() tooltipPosition: 'before' | 'above' = 'before';
@Input() canPerformAnnotationActions: boolean;
@Input() alwaysVisible: boolean;
@Input() actionsHelpModeKey: string;
readonly roles = Roles;
annotationPermissions: AnnotationPermissions;
isImage = true;
readonly annotations = input.required<AnnotationWrapper[], (AnnotationWrapper | undefined)[]>({
transform: value => value.filter(a => a !== undefined),
});
readonly isVisible = computed(() => {
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(
@ -58,137 +114,54 @@ export class AnnotationActionsComponent implements OnChanges {
readonly annotationReferencesService: AnnotationReferencesService,
) {}
get annotations(): AnnotationWrapper[] {
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() {
get resized(): boolean {
return this._annotationManager.annotationHasBeenResized;
}
get hideSkipped() {
return this.skippedService.hideSkipped() && this.annotations.some(a => a.isSkipped);
async removeRedaction(): Promise<void> {
const annotations = untracked(this.annotations);
const permissions = untracked(this.annotationPermissions);
await this.annotationActionsService.removeRedaction(annotations, permissions);
}
get #sameType() {
const type = this.annotations[0].superType;
return this.annotations.every(a => a.superType === type);
}
ngOnChanges(): void {
this.#setPermissions();
}
removeRedaction() {
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
}
acceptRecommendation() {
return this.annotationActionsService.convertRecommendationToAnnotation(this.annotations);
async acceptRecommendation(): Promise<void> {
const annotations = untracked(this.annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations);
}
hideAnnotation() {
this._annotationManager.hide(this.viewerAnnotations);
const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.hide(viewerAnnotations);
this._annotationManager.deselect();
this._annotationManager.addToHidden(this.viewerAnnotations[0].Id);
this._annotationManager.addToHidden(viewerAnnotations[0].Id);
}
showAnnotation() {
this._annotationManager.show(this.viewerAnnotations);
const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.show(viewerAnnotations);
this._annotationManager.deselect();
this._annotationManager.removeFromHidden(this.viewerAnnotations[0].Id);
this._annotationManager.removeFromHidden(viewerAnnotations[0].Id);
}
resize() {
return this.annotationActionsService.resize(this.#annotations[0]);
const annotations = untracked(this.annotations);
return this.annotationActionsService.resize(annotations[0]);
}
acceptResize() {
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() {
return this.annotationActionsService.cancelResize(this.#annotations[0]);
const annotations = untracked(this.annotations);
return this.annotationActionsService.cancelResize(annotations[0]);
}
helpModeKey(action: string) {
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.removed]="annotation.item.isRemoved" class="annotation">
<div [class.removed]="annotation().item.isRemoved" class="annotation">
<redaction-annotation-card
[annotation]="annotation.item"
[isSelected]="annotation.isSelected"
[matTooltip]="annotation.item.content"
[annotation]="annotation().item"
[isSelected]="annotation().isSelected"
[matTooltip]="annotation().item.content.translation | translate: annotation().item.content.params | replaceNbsp"
matTooltipPosition="above"
></redaction-annotation-card>
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper">
<div
*ngIf="!annotation.item.pending"
(click)="showComments = !showComments"
[matTooltip]="'comments.comments' | translate: { count: annotation.item.numberOfComments }"
class="comments-counter"
iqserStopPropagation
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.item.numberOfComments }}
</div>
@if (!annotation().item.isEarmark) {
<div class="actions-wrapper">
@if (!annotation().item.pending) {
<div
(click)="showComments = !showComments"
[matTooltip]="'comments.comments' | translate: { count: annotation().item.numberOfComments }"
class="comments-counter"
iqserStopPropagation
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation().item.numberOfComments }}
</div>
}
<div *ngIf="multiSelectService.inactive()" class="actions">
<redaction-annotation-actions
[annotations]="[annotation.item]"
[actionsHelpModeKey]="actionsHelpModeKey"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
@if (multiSelectService.inactive()) {
<div class="actions">
<redaction-annotation-actions
[actionsHelpModeKey]="actionsHelpModeKey()"
[annotations]="[annotation().item]"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
</div>
}
</div>
</div>
}
<ng-container *ngIf="showComments">
<redaction-comments [annotation]="annotation.item"></redaction-comments>
@if (showComments) {
<redaction-comments [annotation]="annotation().item"></redaction-comments>
<div
(click)="showComments = false"
@ -39,7 +44,7 @@
iqserStopPropagation
translate="comments.hide-comments"
></div>
</ng-container>
}
</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 { AnnotationWrapper } from '@models/file/annotation.wrapper';
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 { ActionsHelpModeKeys } from '../../utils/constants';
import { AnnotationCardComponent } from '../annotation-card/annotation-card.component';
import { NgIf } from '@angular/common';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { MatIcon } from '@angular/material/icon';
import { AnnotationActionsComponent } from '../annotation-actions/annotation-actions.component';
import { CommentsComponent } from '../comments/comments.component';
import { AnnotationDetailsComponent } from '../annotation-details/annotation-details.component';
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
@Component({
selector: 'redaction-annotation-wrapper',
@ -21,7 +21,6 @@ import { AnnotationDetailsComponent } from '../annotation-details/annotation-det
standalone: true,
imports: [
AnnotationCardComponent,
NgIf,
MatTooltip,
TranslateModule,
StopPropagationDirective,
@ -29,28 +28,31 @@ import { AnnotationDetailsComponent } from '../annotation-details/annotation-det
AnnotationActionsComponent,
CommentsComponent,
AnnotationDetailsComponent,
ReplaceNbspPipe,
],
})
export class AnnotationWrapperComponent implements OnChanges {
readonly #isDocumine = getConfig().IS_DOCUMINE;
export class AnnotationWrapperComponent {
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 multiSelectService = inject(MultiSelectService);
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey: string;
showComments = false;
readonly #isDocumine = getConfig().IS_DOCUMINE;
ngOnChanges() {
this.annotationId = this.annotation.item.id;
this.active = this.annotation.isSelected;
this.actionsHelpModeKey = this.#getActionsHelpModeKey();
constructor() {
effect(() => {
this.active = this.annotation().isSelected;
this.annotationId = this.annotation().item.id;
});
}
#getActionsHelpModeKey(): string {
#getActionsHelpModeKey(item: AnnotationWrapper): string {
if (!this.#isDocumine) {
const superType = this.annotation.item.superTypeLabel?.split('.')[1];
const type = this.annotation.item.type;
const superType = item.superTypeLabel?.split('.')[1];
const type = item.type;
if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) {
return ActionsHelpModeKeys[`${superType}-${type}`];
}

View File

@ -1,16 +1,16 @@
<ng-container *ngFor="let annotation of annotations; let idx = index; trackBy: _trackBy">
<div *ngIf="showHighlightGroup(idx) as highlightGroup" class="workload-separator">
@for (annotation of annotations(); track annotation.item.id) {
@if (displayedHighlightGroups()[$index]; as highlightGroup) {
<redaction-highlights-separator [annotation]="annotation.item" [highlightGroup]="highlightGroup"></redaction-highlights-separator>
</div>
}
<redaction-annotation-wrapper
*ngIf="!annotation.item.hiddenInWorkload"
(click)="annotationClicked(annotation.item, $event)"
*ngIf="!annotation.item.hiddenInWorkload"
[annotation]="annotation"
[id]="'annotation-' + annotation.item.id"
[class.documine-wrapper]="isDocumine"
[id]="'annotation-' + annotation.item.id"
></redaction-annotation-wrapper>
</ng-container>
}
<redaction-annotation-references-list
(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 { FilterService } from '@iqser/common-ui/lib/filtering';
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
@ -24,13 +24,24 @@ import { AnnotationReferencesListComponent } from '../annotation-references-list
imports: [NgForOf, NgIf, HighlightsSeparatorComponent, AnnotationWrapperComponent, AnnotationReferencesListComponent],
})
export class AnnotationsListComponent extends HasScrollbarDirective {
@Input({ required: true }) annotations: ListItem<AnnotationWrapper>[];
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
readonly #earmarkGroups = computed(() => {
if (this._viewModeService.isEarmarks()) {
return this.#getEarmarksGroups();
readonly annotations = input.required<ListItem<AnnotationWrapper>[]>();
readonly pagesPanelActive = output<boolean>();
readonly displayedHighlightGroups = computed(() => {
const isEarMarks = this._viewModeService.isEarmarks();
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;
@ -77,26 +88,20 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
}
}
showHighlightGroup(idx: number): EarmarkGroup {
return this._viewModeService.isEarmarks() && this.#earmarkGroups().find(h => h.startIdx === idx);
}
protected readonly _trackBy = (index: number, listItem: ListItem<AnnotationWrapper>) => listItem.item.id;
#getEarmarksGroups() {
#getEarmarksGroups(annotations: ListItem<AnnotationWrapper>[]) {
const earmarksGroups: EarmarkGroup[] = [];
if (!this.annotations?.length) {
if (!annotations.length) {
return earmarksGroups;
}
let lastGroup: EarmarkGroup;
for (let idx = 0; idx < this.annotations.length; ++idx) {
if (idx === 0 || this.annotations[idx].item.color !== this.annotations[idx - 1].item.color) {
for (let idx = 0; idx < annotations.length; ++idx) {
if (idx === 0 || annotations[idx].item.color !== annotations[idx - 1].item.color) {
if (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 {
lastGroup.length += 1;
}

View File

@ -1,6 +1,8 @@
<div [ngStyle]="gridConfig()" class="table">
@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 (cell of row; track cell.label) {

View File

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

View File

@ -1,5 +1,5 @@
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 { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import {
@ -101,13 +101,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
readonly roles = Roles;
readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
@ViewChild('annotationFilterTemplate', {
read: TemplateRef,
static: false,
})
private readonly _filterTemplate: TemplateRef<unknown>;
#loadAllAnnotationsEnabled = false;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
constructor(
readonly pdf: PdfViewer,
@ -180,7 +180,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
effect(() => {
this._viewModeService.viewMode();
this.updateViewMode().then();
this.#updateViewMode().then();
});
effect(() => {
@ -208,7 +208,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const earmarks$ = isEarmarksViewMode$.pipe(
tap(() => this._loadingService.start()),
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(
@ -234,7 +234,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return isChangingFromEarmarksViewMode$.pipe(
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);
}
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() {
this._viewerHeaderService.resetCompareButtons();
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(
startWith([] as AnnotationWrapper[]),
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(
@ -431,12 +378,72 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return combineLatest([currentPageAnnotations$, this._documentViewer.loaded$]).pipe(
filter(([, loaded]) => loaded),
map(([annotations]) => annotations),
tap(([oldA, newA]) => this.drawChangedAnnotations(oldA, newA)?.then(() => this.updateViewMode())),
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
switchMap(async ([oldAnnotations, 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 newAnnotation = newAnnotations.find(byId(oldAnnotation.id));
return newAnnotation ? hasChanges(oldAnnotation, newAnnotation) : true;
@ -446,11 +453,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._annotationManager.delete(annotationsToDelete);
}
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
async #drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]): Promise<void> {
const annotationsToDraw = this.#getAnnotationsToDraw(oldAnnotations, newAnnotations);
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
this._annotationManager.delete(annotationsToDraw);
return this.#cleanupAndRedrawAnnotations(annotationsToDraw);
await this.#cleanupAndRedrawAnnotations(annotationsToDraw);
}
async #openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
@ -641,7 +648,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS),
switchMap(() => {
// 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 annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD;
@ -654,7 +661,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(([confirmed]) => confirmed),
map(([, annotations]) => {
this.#loadAllAnnotationsEnabled = true;
this.drawChangedAnnotations([], annotations).then(() => {
this.#drawChangedAnnotations([], annotations).then(() => {
this._toaster.success(_('load-all-annotations-success'));
this._viewerHeaderService.disableLoadAllAnnotations();
});
@ -662,7 +669,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
)
.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$])
.pipe(
@ -701,11 +710,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._errorService.set(error);
}
#cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>) {
async #cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>): Promise<void> {
if (!newAnnotations.length) {
return undefined;
}
await this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
}
#updateFiltersAfterAnnotationsChanged(): void {
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
this.#rebuildFilters();
@ -714,8 +727,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.#handleDeltaAnnotationFilters(currentFilters);
}, 100);
}
return this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
}
#handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {

View File

@ -277,7 +277,6 @@ export class AnnotationActionsService {
const text = annotation.AREA ? annotation.value : isImageText;
const isApprover = this._permissionsService.isApprover(dossier);
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
const isUnprocessed = annotation.pending;
const data: ResizeRedactionData = {
redaction: annotation,
@ -308,7 +307,7 @@ export class AnnotationActionsService {
await this.cancelResize(annotation);
const { fileId, dossierId, file } = this._state;
const { fileId, dossierId } = this._state;
const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId, includeUnprocessed);
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
const isHint = redactions.every(r => r.isHint);
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._manualRedactionService.removeRedaction(
body,

View File

@ -1,5 +1,4 @@
import { computed, Injectable, Signal, signal } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FileDataService } from './file-data.service';
@ -7,7 +6,6 @@ import { FileDataService } from './file-data.service';
export class AnnotationReferencesService {
readonly references: Signal<AnnotationWrapper[]>;
readonly annotation: Signal<AnnotationWrapper | undefined>;
private readonly _annotation$ = new BehaviorSubject<AnnotationWrapper | undefined>(undefined);
readonly #annotation = signal<AnnotationWrapper | undefined>(undefined);
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 { getCurrentUser } from '@iqser/common-ui/lib/users';
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 currentUser = getCurrentUser<User>();
readonly pageChanged$ = this._pdf.pageChanged$.pipe(
tap(() => this.#handleExcludedPageActions()),
tap(pageNumber => this.#handleExcludedPageActions(pageNumber)),
tap(() => {
if (this._multiSelectService.inactive()) {
this._annotationManager.deselect();
@ -98,7 +98,7 @@ export class PdfProxyService {
effect(
() => {
const canPerformActions = this.canPerformActions();
this._pdf.isCompareMode();
const isCompareMode = this._pdf.isCompareMode();
this.#configureTextPopup();
@ -109,7 +109,8 @@ export class PdfProxyService {
this.#deactivateMultiSelect();
}
this.#handleExcludedPageActions();
const currentPage = untracked(this._pdf.currentPage);
this.#handleExcludedPageActions(currentPage);
},
{ allowSignalWrites: true },
);
@ -227,8 +228,9 @@ export class PdfProxyService {
this.redactTextRequested$.next({ manualRedactionEntry, type });
}
#handleExcludedPageActions() {
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage());
#handleExcludedPageActions(currentPage: number) {
const isCurrentPageExcluded = this._state.file().isPageExcluded(currentPage);
if (!isCurrentPageExcluded) {
return;
}

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { List } from '@iqser/common-ui/lib/utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
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 { hexToRgb } from '@utils/functions';
import { BoundingBox, Table } from '../../file-preview/services/tables.service';
@ -169,7 +169,7 @@ export class AnnotationDrawService {
const annotation = this._pdf.textHighlight();
annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber);
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.StrokeColor = this.convertColor(annotationWrapper.color);
annotation.Id = annotationWrapper.id;

View File

@ -19,14 +19,14 @@ const MOVE_OPTION = 'move';
@Injectable()
export class REDAnnotationManager {
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly #hidden = signal(new Set<string>());
readonly hidden = this.#hidden.asReadonly();
#manager: AnnotationManager;
readonly #logger = inject(NGXLogger);
readonly #annotationSelected$ = new Subject<[Annotation[], string]>();
readonly annotationSelected$ = this.#annotationSelected$.asObservable();
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly hidden = this.#hidden.asReadonly();
get selected() {
return this.#manager.getSelectedAnnotations();
@ -128,21 +128,15 @@ export class REDAnnotationManager {
annotations.forEach(annotation => this.#manager.redrawAnnotation(annotation));
}
add(annotations: Annotation | Annotation[]) {
async add(annotations: Annotation | Annotation[]): Promise<void> {
try {
annotations = asList(annotations);
this.#manager.addAnnotations(annotations, { imported: true });
return this.#manager.drawAnnotationsFromList(annotations);
} catch (e) {
console.log(e);
}
}
showHidden() {
const hidden = this.#getByIds([...this.hidden().values()]);
this.show(hidden);
}
#listenForAnnotationSelected() {
this.#manager.addEventListener('annotationSelected', async (annotations: Annotation[], action: string) => {
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 { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
@ -16,18 +16,7 @@ export class LayersService {
private readonly _pdf: PdfViewer,
private readonly _documentViewer: REDDocumentViewer,
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 {
return this.#icon;
@ -45,13 +34,16 @@ export class LayersService {
}
});
this.updateIconState();
this._documentViewer.document.setLayersArray(layers);
this._documentViewer.refreshAndUpdateView();
}
updateIconState() {
updateState() {
this.#updateIconState();
this.#updateButton();
}
#updateIconState() {
const element = this._pdf.instance.UI.iframeWindow.document.querySelector(`[data-element=${HeaderElements.TOGGLE_LAYERS}]`);
if (!element) return;
if (this.active()) {
@ -63,7 +55,9 @@ export class LayersService {
#updateButton() {
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 { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
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 { TranslateService } from '@ngx-translate/core';
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
@ -70,6 +70,7 @@ export class PdfViewer {
private readonly _errorService: ErrorService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _annotationManager: REDAnnotationManager,
private readonly _languageService: LanguageService,
) {
this.totalPages = this.#totalPages.asReadonly();
this.isCompareMode = this.#isCompareMode.asReadonly();
@ -152,6 +153,7 @@ export class PdfViewer {
await this.runWithCleanup(async () => {
this.#instance.UI.setTheme(this._userPreferenceService.getTheme());
await this.#instance.UI.setLanguage(this._languageService.currentLanguage);
this._logger.info('[PDF] Initialized');
this.documentViewer = this.#instance.Core.documentViewer;

View File

@ -29,24 +29,13 @@ export class ReadableRedactionsService {
return this.#active$.getValue();
}
get toggleReadableRedactionsBtnTitle(): string {
return this._translateService.instant(_('pdf-viewer.toggle-readable-redactions'), {
active: this.active,
});
}
get toggleReadableRedactionsBtnIcon(): string {
return this.active ? this.#enableIcon : this.#disableIcon;
}
toggleReadableRedactions(): void {
this.#active$.next(!this.active);
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_READABLE_REDACTIONS, {
title: this.toggleReadableRedactionsBtnTitle,
img: this.toggleReadableRedactionsBtnIcon,
});
this.updateState();
if (!this.active) {
this.setCustomDrawHandler();
} else {
@ -88,4 +77,13 @@ export class ReadableRedactionsService {
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,
) {}
get toggleTooltipsBtnTitle(): string {
return this._translateService.instant(_('pdf-viewer.toggle-tooltips'), {
active: this._userPreferenceService.getFilePreviewTooltipsPreference(),
});
}
async toggleTooltips(): Promise<void> {
await this._userPreferenceService.toggleFilePreviewTooltipsPreference();
this._documentViewer.updateTooltipsVisibility();
this.updateIconState();
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, {
title: this.toggleTooltipsBtnTitle,
});
this.updateState();
}
updateIconState() {
@ -40,4 +30,13 @@ export class TooltipsService {
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 { TooltipsService } from './tooltips.service';
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 = {
type: 'divider',
@ -90,7 +92,7 @@ export class ViewerHeaderService {
toolGroup: 'rectangleTools',
dataElement: HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
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',
element: HeaderElements.TOGGLE_TOOLTIPS,
dataElement: HeaderElements.TOGGLE_TOOLTIPS,
title: this._tooltipsService.toggleTooltipsBtnTitle,
img: this.#convertPath('/assets/icons/general/pdftron-action-enable-tooltips.svg'),
onClick: () => this._ngZone.run(() => this._tooltipsService.toggleTooltips()),
};
@ -110,7 +111,6 @@ export class ViewerHeaderService {
type: 'actionButton',
element: HeaderElements.TOGGLE_LAYERS,
dataElement: HeaderElements.TOGGLE_LAYERS,
title: this._layersService.toggleLayersBtnTitle,
img: this._layersService.toggleLayersBtnIcon,
onClick: () => this._ngZone.run(() => this._layersService.toggleLayers()),
};
@ -121,7 +121,6 @@ export class ViewerHeaderService {
type: 'actionButton',
element: HeaderElements.TOGGLE_READABLE_REDACTIONS,
dataElement: HeaderElements.TOGGLE_READABLE_REDACTIONS,
title: this._readableRedactionsService.toggleReadableRedactionsBtnTitle,
img: this._readableRedactionsService.toggleReadableRedactionsBtnIcon,
onClick: () => this._ngZone.run(() => this._readableRedactionsService.toggleReadableRedactions()),
};
@ -130,7 +129,7 @@ export class ViewerHeaderService {
get #loadAllAnnotations(): IHeaderElement {
return {
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'),
onClick: () => this._ngZone.run(() => this.#events$.next({ type: ViewerEvents.LOAD_ALL_ANNOTATIONS })),
dataElement: HeaderElements.LOAD_ALL_ANNOTATIONS,
@ -143,7 +142,7 @@ export class ViewerHeaderService {
element: HeaderElements.ROTATE_LEFT_BUTTON,
dataElement: HeaderElements.ROTATE_LEFT_BUTTON,
img: this.#convertPath('/assets/icons/general/rotate-left.svg'),
title: 'Rotate page left',
title: viewerHeaderButtonsTranslations[HeaderElements.ROTATE_LEFT_BUTTON],
onClick: () =>
this._ngZone.run(() => {
this._rotationService.addRotation(RotationTypes.LEFT);
@ -203,7 +202,7 @@ export class ViewerHeaderService {
element: HeaderElements.ROTATE_RIGHT_BUTTON,
dataElement: HeaderElements.ROTATE_RIGHT_BUTTON,
img: this.#convertPath('/assets/icons/general/rotate-right.svg'),
title: 'Rotate page right',
title: viewerHeaderButtonsTranslations[HeaderElements.ROTATE_RIGHT_BUTTON],
onClick: () =>
this._ngZone.run(() => {
this._rotationService.addRotation(RotationTypes.RIGHT);
@ -218,7 +217,7 @@ export class ViewerHeaderService {
element: HeaderElements.COMPARE_BUTTON,
dataElement: HeaderElements.COMPARE_BUTTON,
img: this.#convertPath('/assets/icons/general/pdftron-action-compare.svg'),
title: 'Compare',
title: viewerHeaderButtonsTranslations[HeaderElements.COMPARE_BUTTON],
onClick: () =>
this._ngZone.run(async () => {
document.getElementById('compareFileInput').click();
@ -310,24 +309,27 @@ export class ViewerHeaderService {
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) {
this._tooltipsService.updateIconState();
this._layersService.updateIconState();
this._pdf.instance.UI.updateElement('selectToolButton', {
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(
`[data-element=${HeaderElements.CLOSE_COMPARE_BUTTON}]`,
);
closeCompareButton?.classList.add('active');
this.#configTooltips();
}
}
disableLoadAllAnnotations(): void {
this._pdf.instance.UI.updateElement(HeaderElements.LOAD_ALL_ANNOTATIONS, {
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,
});
}
@ -370,6 +372,37 @@ export class ViewerHeaderService {
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[]) {
const enabledItems = group.filter(item => this.#isEnabled(item));
if (enabledItems.length) {

View File

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

View File

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { GenericService, isIqserDevMode, Toaster } from '@iqser/common-ui';
import { EntryStates, IEntityLog, IEntityLogEntry, ISectionGrid } from '@red/domain';
import { GenericService, Toaster } from '@iqser/common-ui';
import { EntryStates, IEntityLog, IEntityLogEntry } from '@red/domain';
import { firstValueFrom, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@ -20,10 +20,6 @@ export class EntityLogService extends GenericService<unknown> {
return entityLog;
}
getSectionGrid(dossierId: string, fileId: string) {
return this._getOne<ISectionGrid>([dossierId, fileId], 'sectionGrid');
}
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
return entityLogEntry.filter(entry => {
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": {
"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-placeholder": "#",
"default-reason": "Standardgrund",
@ -146,7 +146,7 @@
"description": "Beschreibung",
"description-placeholder": "Beschreibung eingeben",
"dossier-dictionary-only": "Nur Dossier-Wörterbuch",
"has-dictionary": "Mit Wörterbuch",
"has-dictionary": "Hat Wörterbuch",
"hint": "Hinweis",
"manage-entries-in-dictionary-editor-only": "In Schwärzungs-/Bearbeitungsdialogen verfügbar",
"name": "Anzeigename",
@ -207,14 +207,14 @@
"generic": "Speichern des Benutzers fehlgeschlagen."
},
"form": {
"account-setup": "User account setup",
"account-setup": "Konfiguration des Benutzerkontos",
"email": "E-Mail",
"first-name": "Vorname",
"last-name": "Nachname",
"reset-password": "Passwort zurücksetzen",
"role": "Rolle",
"send-email": "Do not send email requesting the user to set a password",
"send-email-explanation": "Select this option if you use SSO. Please note that you will need to inform the user directly."
"send-email": "Benutzer nicht per E-Mail auffordern, ein Passwort festzulegen",
"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{}}"
},
@ -243,8 +243,7 @@
}
},
"type": "Typ",
"type-placeholder": "Typ auswählen...",
"value": ""
"type-placeholder": "Typ auswählen..."
},
"title": "Hinweis hinzufügen"
}
@ -276,6 +275,9 @@
"watermarks": "Wasserzeichen"
},
"analysis-disabled": "",
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotation-actions": {
"accept-recommendation": {
"label": "Empfehlung annehmen"
@ -331,14 +333,14 @@
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
},
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt"
},
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "Rücksetzung erfolgreich"
@ -351,15 +353,15 @@
"remove-highlights": {
"label": "Ausgewählte Markierungen entfernen"
},
"resize": {
"label": "Größe ändern"
},
"resize-accept": {
"label": "Neue Größe speichern"
},
"resize-cancel": {
"label": "Größenänderung abbrechen"
},
"resize": {
"label": "Größe ändern"
},
"see-references": {
"label": "Referenzen anzeigen"
},
@ -368,7 +370,7 @@
},
"annotation-changes": {
"added-locally": "Lokal hinzugefügt",
"forced-hint": "Hint wurde erzwungen",
"forced-hint": "Hinweis wurde erzwungen",
"forced-redaction": "Schwärzung wurde erzwungen",
"header": "Lokale manuelle Änderungen:",
"legal-basis": "Grund wurde geändert",
@ -377,7 +379,7 @@
"resized": "Schwärzungsbereich wurde geändert"
},
"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",
"imported": "Importiert",
"ner": "Basiert auf KI-Modell",
@ -393,9 +395,6 @@
"skipped": "Ignorierte Schwärzung",
"text-highlight": "Markierung"
},
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotations": "Annotationen",
"archived-dossiers-listing": {
"no-data": {
@ -419,7 +418,7 @@
"approver": "Genehmiger",
"approvers": "Genehmiger",
"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",
"search": "Suche...",
"single-user": "Besitzer"
@ -571,7 +570,7 @@
},
"search": "Nach Name suchen...",
"table-col-names": {
"column-labels": "Column labels",
"column-labels": "Spaltenbeschriftungen",
"name": "Name",
"number-of-lines": "Zeilenzahl",
"version": "Version"
@ -640,14 +639,14 @@
},
"confirmation-dialog": {
"approve-file": {
"confirmationText": "Approve anyway",
"denyText": "No, cancel",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?",
"title": "Approval warning",
"confirmationText": "Trotzdem genehmigen",
"denyText": "Nein, abbrechen",
"question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
"title": "Warnung!",
"warning-reason": {
"legal-basis-missing": "Legal basis missing",
"pending-changes": "Pending Changes",
"unmapped-justification": "Unmapped justification"
"legal-basis-missing": "Rechtsgrundlage fehlt",
"pending-changes": "Änderungen stehen aus",
"unmapped-justification": "Nicht gemappte Begründung"
}
},
"assign-file-to-me": {
@ -664,7 +663,7 @@
"delete-dossier": {
"confirmation-text": "{dossiersCount, plural, one{Dossier} other{Dossiers}} löschen",
"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"
},
"delete-file": {
@ -673,7 +672,7 @@
},
"delete-items": {
"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": {
"question": "Möchten Sie {count, plural, one{diese Begründung} other{diese Begründungen}} wirklich löschen?",
@ -721,7 +720,7 @@
"key": "Typ"
},
"table-header": {
"title": "{length} Standard{length, plural, one{farbe} other{farben}}"
"title": "{length} Standard{length, plural, one{Farbe} other{Farben}}"
},
"types": {
"analysisColor": "Analyse",
@ -763,7 +762,7 @@
"compare": {
"compare": "Vergleichen",
"select-dictionary": "Wörterbuch auswählen",
"select-dossier": "Wörterbuch auswählen",
"select-dossier": "Dossier auswählen",
"select-dossier-template": "Dossier-Vorlage auswählen"
},
"download": "Aktuelle Einträge herunterladen",
@ -853,7 +852,7 @@
"date": "Datum",
"image": "Bild",
"number": "Nummer",
"text": "Freier Text"
"text": "Text"
},
"dossier-attributes-listing": {
"action": {
@ -945,7 +944,7 @@
},
"dossier-overview": {
"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-me": "Mir zuweisen",
"assign-reviewer": "Benutzer zuweisen",
@ -988,8 +987,8 @@
}
},
"filters": {
"label": "Name des Dokuments",
"search": "Name des Dokuments..."
"label": "Dokumentname",
"search": "Name des Dokuments eingeben..."
},
"header-actions": {
"download-csv": "CSV-Bericht herunterladen",
@ -1019,13 +1018,13 @@
"recent": "Neu ({hours} h)",
"unassigned": "Keinem Bearbeiter zugewiesen"
},
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": {
"error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Dateien für Reanalyse vorgesehen."
},
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "",
"start-auto-analysis": "Auto-Analyse aktivieren",
"stop-auto-analysis": "Auto-Analyse anhalten",
@ -1095,6 +1094,14 @@
"total-documents": "Dokumente",
"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": {
"action": {
"clone": "Vorlage klonen",
@ -1129,14 +1136,6 @@
"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": {
"heading": "Wasserzeichen auf Dokumenten",
"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",
"flatten": "Verflachte PDF",
"label": "{length} Dokumenten{length, plural, one{typ} other{typen}}",
"optimized-preview": "Optimized Preview PDF",
"optimized-preview": "Optimierte Vorschau-PDF",
"original": "Optimierte PDF",
"preview": "Vorschau-PDF",
"redacted": "Geschwärzte PDF",
@ -1234,10 +1233,12 @@
"save": "Speichern",
"title": "{label} bearbeiten"
},
"entries-count": "",
"false-positives": "Falsch-Positive ({count})",
"false-recommendations": "Falsche Empfehlungen ({count})",
"to-redact": "Schwärzungen ({count})"
"entries": "{length} {length, plural, one{Eintrag} other{Einträge}}",
"false-positive-entries": "{length} {length, plural, one{Falsch-Positiver} other{Falsch-Positive}}",
"false-positives": "Falsch-Positive",
"false-recommendation-entries": "{length} {length, plural, one{falsche Empfehlung} other{falsche Empfehlungen}}",
"false-recommendations": "Falsche Empfehlungen",
"to-redact": "Schwärzungen"
},
"general-info": {
"form": {
@ -1263,7 +1264,7 @@
"choose-download": "Stellen Sie Ihr Download-Paket zusammen:",
"dictionary": "Wörterbücher",
"dossier-attributes": "Dossier-Attribute",
"dossier-dictionary": "Wörterbücher",
"dossier-dictionary": "Dossier-Einträge",
"dossier-info": "Dossier-Info",
"download-package": "Download-Paket",
"general-info": "Allgemeine Informationen",
@ -1281,11 +1282,14 @@
"content": {
"comment": "Kommentar",
"comment-placeholder": "Bemerkungen oder Notizen hinzufügen...",
"custom-rectangle": "Bereichsschwärzung",
"imported": "Importierte Schwärzung",
"legal-basis": "Rechtsgrundlage",
"options": {
"in-document": {
"description": "",
"label": ""
"in-dossier": {
"description": "Schwärzung in jedem Dokument in {dossierName} bearbeiten.",
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
"label": "Typ in Dossier ändern"
},
"only-here": {
"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}}"
}
},
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"entity-rules-screen": {
"error": {
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
@ -1341,28 +1354,19 @@
"title": "Entitätsregeln-Editor",
"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": {
"deleted-entity": {
"dossier": {
"action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
}
},
"file-preview": {
@ -1380,6 +1384,12 @@
},
"exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr",
"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": {
"ascii": "ASCII",
"iso": "ISO-8859-1",
@ -1390,12 +1400,6 @@
"number": "Nummer",
"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": {
"cancel": "Abbrechen",
"form": {
@ -1471,7 +1475,7 @@
"delete": "Attribut löschen",
"edit": "Attribut bearbeiten"
},
"add-new": "Neue Attribute",
"add-new": "Neues Attribut",
"bulk-actions": {
"delete": "Ausgewählte Attribute löschen"
},
@ -1490,7 +1494,7 @@
"search": "Nach Attribut-Namen suchen...",
"table-col-names": {
"csv-column": "CSV-Spalte",
"displayed-in-file-list": "In Dokumentenliste",
"displayed-in-file-list": "In Dokumentenliste anzeigen",
"filterable": "Filterbar",
"name": "Name",
"primary": "Primärattribut",
@ -1521,7 +1525,7 @@
},
"last-assignee": "{status, select, APPROVED{Freigegeben} UNDER_APPROVAL{Geprüft} other{Zuletzt geprüft}} von:",
"no-data": {
"title": "An dieser Seite wurden keine Änderungen vorgenommen."
"title": "Auf dieser Seite wurden keine Änderungen vorgenommen."
},
"quick-nav": {
"jump-first": "Zur ersten Seite springen",
@ -1529,7 +1533,7 @@
},
"reanalyse-notification": "Reanalyse starten",
"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-tooltip": "Standard zeigt alle Annotationstypen und ermöglicht die Bearbeitung.",
"tabs": {
@ -1540,13 +1544,13 @@
"label": "Liste",
"no-annotations": "Es sind keine Annotationen vorhanden.",
"page-is": "Diese Seite ist",
"reset": "setzen Sie die Filter",
"reset": "Setzen Sie die Filter zurück",
"select": "Auswählen",
"select-all": "Alle",
"select-none": "Keine",
"show-skipped": "Ignorierte im Dokument anzeigen",
"the-filters": "zurück.",
"wrong-filters": "Keine Annotationen für die ausgewählte Filterkombination. Bitte ändern Sie die Auswahl oder"
"the-filters": "Filter",
"wrong-filters": "Keine Annotationen für die ausgewählte Filterkombination. Bitte ändern Sie die Auswahl oder setzen die Filter zurück."
},
"document-info": {
"close": "Datei-Info schließen",
@ -1613,6 +1617,15 @@
"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-options": "Filteroptionen",
"filter-types": "Filter",
@ -1622,15 +1635,6 @@
"unseen-pages": "Nur Annotationen auf ungesehenen Seiten",
"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": {
"assigned-people": "Bearbeiter",
"documents-status": "Dokumentenstatus",
@ -1639,7 +1643,7 @@
"empty": "Leer",
"filter-by": "Filter:",
"needs-work": "Annotationen",
"people": "Dossier-Mitglieder"
"people": "Dossier-Mitglied(er)"
},
"general-config-screen": {
"actions": {
@ -1654,7 +1658,7 @@
"auth": "Authentifizierung aktivieren",
"change-credentials": "Anmeldedaten ändern",
"envelope-from": "Envelope von",
"envelope-from-hint": "Infotext zum Feld „Ausgangsadresse“.",
"envelope-from-hint": "Infotext zum Feld „Envelope von“.",
"envelope-from-placeholder": "Envelope-Absenderadresse",
"from": "Von",
"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-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": {
"category": {
"email-notifications": "E-Mail-Benachrichtigungen",
@ -1935,7 +1946,6 @@
"dossier": "Benachrichtigungen zu Dossiers",
"other": "Andere Benachrichtigungen"
},
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger 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_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": {
"daily": "Tägliche Zusammenfassung",
"instant": "Sofort",
@ -1960,13 +1971,6 @@
},
"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": {
"confirmation-dialog": {
"cancel": "Abbrechen",
@ -2058,16 +2062,16 @@
"warnings-label": "Dialoge und Meldungen",
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"processing-status": {
"ocr": "OCR",
"pending": "Ausstehend",
"processed": "Verarbeitet",
"processing": "Verarbeitung läuft"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"readonly": "Lesemodus",
"readonly-archived": "Lesemodus (archiviert)",
"redact-text": {
@ -2082,10 +2086,6 @@
"edit-text": "Text bearbeiten",
"legal-basis": "Rechtsgrundlage",
"options": {
"in-document": {
"description": "",
"label": ""
},
"in-dossier": {
"description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.",
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
@ -2101,8 +2101,7 @@
"revert-text": "Zurück zu ursprünglicher Auswahl",
"type": "Typ",
"type-placeholder": "Typ auswählen...",
"unchanged": "Ungeändert",
"value": ""
"unchanged": "Ungeändert"
},
"title": "Text schwärzen"
}
@ -2126,10 +2125,6 @@
"description-bulk": "",
"label": "In diesem Kontext aus dem Dossier entfernen"
},
"in-document": {
"description": "",
"label": ""
},
"in-dossier": {
"description": "Annotieren Sie den Begriff in diesem Dossier nicht.",
"description-bulk": "",
@ -2170,10 +2165,6 @@
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
"label": "In diesem Kontext aus Dossier entfernen"
},
"in-document": {
"description": "",
"label": ""
},
"in-dossier": {
"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}}.",
@ -2316,6 +2307,12 @@
"red-user-admin": "Benutzeradmin",
"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": {
"cols": {
"assignee": "Bearbeiter",
@ -2339,12 +2336,6 @@
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
"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",
"size": "Größe",
"smtp-auth-config": {
@ -2418,7 +2409,7 @@
},
"label": "Papierkorb",
"no-data": {
"title": "Es gibt noch keine gelöschten Elemente."
"title": "Im Papierkorb befinden sich keine gelöschten Elemente."
},
"no-match": {
"title": "Die ausgewählten Filter treffen auf kein Element zu."

View File

@ -265,7 +265,7 @@
"entities": "Entities",
"entity-info": "Info",
"entity-rule-editor": "Entity rule editor",
"false-positive": "False positive",
"false-positive": "False positives",
"false-recommendations": "False recommendations",
"file-attributes": "File attributes",
"justifications": "Justifications",
@ -376,6 +376,7 @@
"removed-manual": "Redaction/Hint removed",
"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": {
"dictionary": "Based on dictionary",
"dossier-dictionary": "Based on dossier dictionary",
@ -642,8 +643,8 @@
"approve-file": {
"confirmationText": "Approve anyway",
"denyText": "No, cancel",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?",
"title": "Approval warning",
"question": "This document contains unseen changes that have been added during the reanalysis.<br><br>Do you still want to approve it?",
"title": "Warning!",
"warning-reason": {
"legal-basis-missing": "Legal basis missing",
"pending-changes": "Pending Changes",
@ -945,7 +946,7 @@
},
"dossier-overview": {
"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-me": "Assign to me",
"assign-reviewer": "Assign user",
@ -1235,9 +1236,9 @@
"title": "Edit {label}"
},
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives ({count})",
"false-recommendations": "False recommendations ({count})",
"to-redact": "Entries ({count})"
"false-positives": "False positives",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
},
"general-info": {
"form": {
@ -1289,7 +1290,7 @@
},
"only-here": {
"description": "Edit redaction only at this position in this document.",
"label": "Change only here"
"label": "Change type only here"
}
},
"reason": "Reason",
@ -1546,7 +1547,7 @@
"select-none": "None",
"show-skipped": "Show skipped in document",
"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": {
"close": "Close document info",
@ -1628,12 +1629,12 @@
"hint": "Hints only",
"image": "Images",
"none": "No annotations",
"redaction": "Redacted",
"redaction": "Redaction",
"updated": "Updated"
},
"filters": {
"assigned-people": "Assignee(s)",
"documents-status": "Documents state",
"documents-status": "Document state",
"dossier-state": "Dossier state",
"dossier-templates": "Dossier templates",
"empty": "Empty",
@ -2013,14 +2014,34 @@
"previous": "Prev"
},
"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": {
"actions": {
"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": {
"dossier": {
@ -2158,14 +2179,14 @@
"comment-placeholder": "Add remarks or notes...",
"options": {
"do-not-recommend": {
"description": "Do not recommend the selected {isImage, select, image{image} other{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": "Do not recommend the selected term in any document of this dossier.",
"description-bulk": "Do not recommend the selected terms in any document of this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier"
},
"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-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": "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 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.",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier in this context"
@ -2175,15 +2196,15 @@
"label": "Remove from document"
},
"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-bulk": "Do not {type, select, hint{annotate} other{auto-redact}} the selected {isImage, select, image{images} other{terms}} in 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 terms in this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier",
"label-bulk": "Remove from dossier"
},
"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-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": "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 terms at this position in the current document.",
"label": "Remove here"
}
}
@ -2535,10 +2556,6 @@
"view-as": "View as:",
"workflow": "Workflow"
},
"viewer-header": {
"all-annotations-loaded": "All annotations loaded",
"load-all-annotations": "Load all annotations"
},
"watermark-screen": {
"action": {
"change-success": "Watermark has been updated.",
@ -2567,6 +2584,10 @@
"orientation": "Orientation",
"text-label": "Watermark text",
"text-placeholder": "Enter text"
},
"pagination": {
"landscape": "Landscape",
"portrait": "Portrait"
}
},
"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"
},
"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": {
"description": {
@ -159,8 +159,8 @@
"template-and-dossier-dictionaries": ""
},
"success": {
"create": "Entity added!",
"edit": "Entity updated. Please note that other users need to refresh the browser to see your changes."
"create": "Success: Entity created.",
"edit": "<strong>Success: Entity updated.</strong><br><br>Please ask the users to refresh the browser to see the changes."
}
},
"add-edit-file-attribute": {
@ -204,7 +204,7 @@
},
"error": {
"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": {
"account-setup": "User account setup",
@ -265,7 +265,7 @@
"entities": "Entities",
"entity-info": "Info",
"entity-rule-editor": "Entity rule editor",
"false-positive": "False positive",
"false-positive": "False positives",
"false-recommendations": "False recommendations",
"file-attributes": "File attributes",
"justifications": "Justifications",
@ -303,7 +303,7 @@
},
"remove": {
"error": "Failed to remove dictionary entry: {error}",
"success": "Dictionary entry removed!"
"success": "Dictionary entry removed"
},
"undo": {
"error": "Failed to undo: {error}",
@ -313,15 +313,15 @@
"manual-redaction": {
"add": {
"error": "Failed to save annotation: {error}",
"success": "Annotation added!"
"success": "Annotation added"
},
"force-hint": {
"error": "Failed to save hint: {error}",
"success": "Hint added!"
"success": "Hint added"
},
"force-redaction": {
"error": "Failed to save annotation: {error}",
"success": "Annotation added!"
"success": "Annotation added"
},
"recategorize-annotation": {
"error": "",
@ -333,11 +333,11 @@
},
"remove-hint": {
"error": "Failed to remove hint: {error}",
"success": "Hint removed!"
"success": "Hint removed"
},
"remove": {
"error": "Failed to remove annotation: {error}",
"success": "Annotation removed!"
"success": "Annotation removed"
},
"undo": {
"error": "Failed to undo: {error}",
@ -376,6 +376,7 @@
"removed-manual": "Annotation/Hint removed",
"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": {
"dictionary": "{isHint, select, true{Hint} other{Annotation}} based on dictionary",
"dossier-dictionary": "Annotation based on dossier dictionary",
@ -481,7 +482,7 @@
},
"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-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-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"
@ -599,22 +600,22 @@
"checkbox": {
"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}",
"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?"
},
"confirm-delete-attribute": {
"cancel": "Keep {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-lost-details": "All values for this attribute will be lost",
"dossier-impacted-documents": "This action will affect all dossiers that use this template.",
"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-lost-details": "All inputted details on the documents will be lost",
"impacted-report": "{reportsCount} reports use the placeholder for this attribute and need to be adjusted",
"file-lost-details": "All information entered by users on document level will be lost.",
"impacted-report": "{reportsCount} reports currently use the placeholder for this attribute. Please update them.",
"title": "Delete {count, plural, one{{name}} other{file attributes}}",
"toast-error": "Please confirm that you understand the ramifications of your action!",
"warning": "Warning: this cannot be undone!"
"toast-error": "Please confirm that you understand the consequences of this action.",
"warning": "Warning: This action cannot be undone!"
},
"confirm-delete-dossier-state": {
"cancel": "Cancel",
@ -622,9 +623,9 @@
"delete-replace": "Delete and replace",
"form": {
"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!",
"title": "Delete dossier state",
"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-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
"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!"
},
"confirmation-dialog": {
"approve-file": {
"confirmationText": "Approve anyway",
"denyText": "No, cancel",
"question": "Do you still want to approve the {questionLength, plural, one{document} other{documents}}?",
"title": "Approval warning",
"question": "This document contains unseen changes that have been added during the reanalysis.\n<br><br>Do you still want to approve it?",
"title": "Warning!",
"warning-reason": {
"legal-basis-missing": "Legal basis missing",
"pending-changes": "Pending Changes",
@ -653,19 +654,19 @@
"assign-file-to-me": {
"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?",
"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"
},
"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}"
},
"delete-dossier": {
"confirmation-text": "Delete {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}}?",
"title": "Delete {dossiersCount, plural, one{{dossierName}} other{Selected Dossiers}}"
"title": "Delete {dossiersCount, plural, one{{dossierName}} other{selected dossiers}}"
},
"delete-file": {
"question": "Do you wish to proceed?",
@ -688,23 +689,23 @@
},
"unsaved-changes": {
"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",
"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"
},
"upload-report-template": {
"alternate-confirmation-text": "Upload as multi-file report",
"confirmation-text": "Upload as single-file report",
"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"
}
},
"content": "Reason",
"dashboard": {
"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"
},
"greeting": {
@ -768,13 +769,13 @@
},
"download": "Download current entries",
"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.",
"generic": "Something went wrong... Dictionary update failed!"
"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": "Error: Dictionary update failed."
},
"revert-changes": "Revert",
"save-changes": "Save changes",
"search": "Search entries...",
"select-dictionary": "Select a dictionary above to compare with the current one.",
"select-dictionary": "Select a dictionary for comparison above.",
"success": {
"generic": "Dictionary updated!"
}
@ -784,11 +785,11 @@
"actions": {
"back": "Back",
"cancel": "Cancel",
"certificate-not-valid-error": "Uploaded certificate is not valid!",
"certificate-not-valid-error": "Uploaded certificate is invalid.",
"continue": "Continue",
"save": "Save configurations",
"save-error": "Failed to save digital signature!",
"save-success": "Digital signature certificate successfully saved!"
"save-success": "Digital signature certificate saved successfully"
},
"forms": {
"kms": {
@ -810,11 +811,11 @@
},
"options": {
"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"
},
"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"
}
},
@ -832,7 +833,7 @@
"remove": "Remove",
"save": "Save changes",
"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": {
"action": "Configure certificate",
@ -884,7 +885,7 @@
"dossier-details": {
"assign-members": "Assign members",
"collapse": "Hide details",
"document-status": "Document status",
"document-status": "Document processing status",
"edit-owner": "Edit owner",
"expand": "Show details",
"members": "Members",
@ -896,7 +897,7 @@
"add-new": "New dossier",
"archive": {
"action": "Archive dossier",
"archive-failed": "Failed to archive dossier {dossierName}!",
"archive-failed": "Failed to archive dossier {dossierName}.",
"archive-succeeded": "Successfully archived dossier {dossierName}."
},
"delete": {
@ -1235,9 +1236,9 @@
"title": ""
},
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives ({count})",
"false-recommendations": "False recommendations ({count})",
"to-redact": "To redact ({count})"
"false-positives": "False positives",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
},
"general-info": {
"form": {
@ -1367,12 +1368,12 @@
},
"file-preview": {
"action": "Refresh",
"label": "An unknown error occurred. Please refresh the page."
"label": "<strong>Unknown error:</strong> Please refresh the page."
},
"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",
"online": "Reconnected",
"reload": "Reload",
@ -1392,7 +1393,7 @@
},
"file-attribute": {
"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!"
}
},
@ -1428,7 +1429,7 @@
"key-column": "Key column",
"key-column-placeholder": "Select column...",
"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-sample-data-for": "No sample data for {column}.",
@ -1449,7 +1450,7 @@
"table-col-names": {
"name": "Name",
"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",
"type": "Type"
},
@ -1477,8 +1478,8 @@
},
"configurations": "Configurations",
"error": {
"conflict": "File-Attribute with this name already exists!",
"generic": "Failed to add file attribute"
"conflict": "File attribute name already exists. Please try another name.",
"generic": "Failed to add file attribute."
},
"no-data": {
"title": "There are no file attributes yet."
@ -1494,7 +1495,7 @@
"filterable": "Filterable",
"name": "Name",
"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",
"type": "Input type"
},
@ -1508,20 +1509,20 @@
"assign-reviewer": "Assign user",
"change-reviewer": "Change user",
"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",
"download-original-file": "Download original file",
"exclude-pages": "Exclude pages from extraction",
"excluded-from-redaction": "excluded",
"excluded-from-redaction": "Excluded",
"fullscreen": "Full screen (F)",
"get-tables": "Draw tables",
"highlights": {
"convert": "Convert earmarks",
"remove": "Remove earmarks"
},
"last-assignee": "Last assignee",
"last-assignee": "{status, select, APPROVED{Approved} UNDER_APPROVAL{Reviewed} other{Last reviewed}} by:",
"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": {
"jump-first": "Jump to first page",
@ -1538,7 +1539,7 @@
"jump-to-next": "Jump to next",
"jump-to-previous": "Jump to previous",
"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",
"reset": "reset",
"select": "Select",
@ -1546,7 +1547,7 @@
"select-none": "None",
"show-skipped": "Show skipped in document",
"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": {
"close": "Close document info",
@ -1578,7 +1579,7 @@
}
},
"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": {
"disable": "Disable extraction",
"enable": "Enable for extraction",
@ -1587,7 +1588,7 @@
},
"file-status": {
"analyse": "Analyzing",
"approved": "Done",
"approved": "Approved",
"error": "Re-processing required",
"figure-detection-analyzing": "",
"full-processing": "Processing",
@ -1618,7 +1619,7 @@
"filter-types": "Filter",
"label": "Filter",
"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",
"with-comments": "Only annotations with comments"
},
@ -1693,7 +1694,7 @@
"title": "System preferences"
},
"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!",
"warning": "Admin mail address not set. Test email sent to {recipientEmail} instead."
},
@ -1705,12 +1706,12 @@
},
"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",
"options": {
"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": {
"actions": {
@ -2013,14 +2014,34 @@
"previous": "Prev"
},
"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": {
"actions": {
"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": {
"dossier": {
@ -2535,10 +2556,6 @@
"view-as": "View as:",
"workflow": "Workflow"
},
"viewer-header": {
"all-annotations-loaded": "All annotations loaded",
"load-all-annotations": "Load all annotations"
},
"watermark-screen": {
"action": {
"change-success": "Watermark has been updated!",
@ -2567,6 +2584,10 @@
"orientation": "Orientation",
"text-label": "Watermark text",
"text-placeholder": "Enter text"
},
"pagination": {
"landscape": "",
"portrait": ""
}
},
"watermarks-listing": {