Compare commits

...

33 Commits

Author SHA1 Message Date
Valentin Mihai
53cfc669d6 RED-9944 - Action Items don't appear in document area in webviewer when bulk-select is still unintenionally active 2024-10-28 16:56:07 +02:00
Nicoleta Panaghiu
540649edbd RED-10258: try to get app to fetch the added fonts. 2024-10-28 14:30:16 +02:00
Valentin Mihai
32369d7121 RED-10256 - Bulk-local: Changes should not be filtered + Remove for image-based redactions 2024-10-28 13:57:48 +02:00
Nicoleta Panaghiu
a9a935b90d RED-10258: added missing fonts to pdftron. 2024-10-28 13:46:11 +02:00
Kilian Schüttler
491977cb49 Merge branch 'RED-10264-bp' into 'release/4.951.x'
RED-10264: remove includeUnprocessed from all manual change calls

See merge request redactmanager/red-ui!644
2024-10-28 12:40:39 +01:00
Kilian Schuettler
b620605613 RED-10264: remove includeUnprocessed from all manual change calls 2024-10-25 13:50:54 +02:00
Nicoleta Panaghiu
a28f5fd727 RED-3800: manual localazy sync. 2024-10-25 14:27:42 +03:00
Nicoleta Panaghiu
2e2eaf476d RED-9447, RED-10244: disable dossier template field if state is changed. 2024-10-25 13:39:10 +03:00
Dan Percic
b0d38e1b0f Merge branch 'feature/RED-10260-bp' into 'release/4.951.x'
feature/RED-10260: add quoteChar to componentMapping

See merge request redactmanager/red-ui!641
2024-10-24 10:00:04 +02:00
Kilian Schüttler
d4991f6806 feature/RED-10260: add quoteChar to componentMapping 2024-10-24 10:00:04 +02:00
Valentin Mihai
9d18113f56 RED-7340 - Rectangle redactions: Use bulk-local redactions + New dialog design 2024-10-23 23:48:02 +03:00
Valentin Mihai
d04f1b7b0b RED-9585 - Incorrect capitalization in German translation + missing singular/plural distinction 2024-10-23 22:25:10 +03:00
Nicoleta Panaghiu
b1948622fe RED-8277: update common ui. 2024-10-23 16:11:06 +03:00
Dan Percic
eb4368c3ce Merge branch 'RED-10072' into 'release/4.951.x'
Revert "RED-10072: AI description field and toggle for entities"

See merge request redactmanager/red-ui!638
2024-10-23 11:20:10 +02:00
Maverick Studer
9772145239 Revert "RED-10072: AI description field and toggle for entities" 2024-10-23 11:20:10 +02:00
Nicoleta Panaghiu
183b19c9da RED-10255: fixed force ignored hint action. 2024-10-22 19:19:50 +03:00
Nicoleta Panaghiu
4c9b487c78 RED-10227: fixed audit log date filters. 2024-10-22 18:59:58 +03:00
Nicoleta Panaghiu
ba311ad8e3 RED-8277: update common ui. 2024-10-22 18:03:08 +03:00
Nicoleta Panaghiu
62d4d18eaa RED-10220: description and legalBasis fields are no longer required. 2024-10-22 17:41:27 +03:00
Valentin Mihai
7f8dca3098 RED-9944 - Action Items don't appear in document area in webviewer when bulk-select is still unintenionally active 2024-10-22 17:23:32 +03:00
Valentin Mihai
289ee7f61d RED-9944 - Action Items don't appear in document area in webviewer when bulk-select is still unintenionally active 2024-10-22 16:42:15 +03:00
Nicoleta Panaghiu
bf1f25c0dd RED-10220: added support for justification technical name. 2024-10-22 12:46:48 +03:00
Valentin Mihai
773f2bfe9f RED-7340 - When a user has entered an invalid page range string, display a read border around the input field 2024-10-21 18:16:47 +03:00
Nicoleta Panaghiu
ac1780ade4 RED-3800: manual localazy sync. 2024-10-18 18:06:01 +03:00
Valentin Mihai
5f309bffe0 RED-9944 - Action Items don't appear in document area in webviewer when bulk-select is still unintenionally active 2024-10-18 14:35:52 +03:00
Nicoleta Panaghiu
611f293e64 RED-8277: made it possible to open dossiers and files in a new tab. 2024-10-17 18:10:25 +03:00
Valentin Mihai
bc7919551e Merge remote-tracking branch 'origin/release/4.951.x' into release/4.951.x 2024-10-16 23:12:59 +03:00
Valentin Mihai
4e3e64f2eb RED-9944 - cancel the bulk-selection mode when a user clicks somewhere else 2024-10-16 23:12:35 +03:00
Valentin Mihai
1233761ac3 RED-7340 - prefill the range fields in the edit and remove dialogs 2024-10-16 23:07:56 +03:00
Valentin Mihai
fd3b99a785 RED-7340 - prefill the range fields in the edit and remove dialogs 2024-10-16 14:42:40 +03:00
Nicoleta Panaghiu
f9361a2e82 RED-10180: sync localazy translation. 2024-10-16 14:10:23 +03:00
Nicoleta Panaghiu
4acb4d4eb7 RED-10190: fixed notification padding. 2024-10-16 14:01:22 +03:00
Valentin Mihai
7d4d2889da RED-7340 - Rectangle redactions: Use bulk-local redactions + New dialog design 2024-10-16 11:57:13 +03:00
57 changed files with 1264 additions and 959 deletions

View File

@ -22,7 +22,7 @@
} }
.mat-mdc-menu-item.notification { .mat-mdc-menu-item.notification {
padding: 8px 26px 10px 8px; padding: 8px 26px 10px 8px !important;
margin: 2px 0 0 0; margin: 2px 0 0 0;
height: fit-content; height: fit-content;
position: relative; position: relative;

View File

@ -23,7 +23,7 @@
<div class="mt-44"> <div class="mt-44">
<redaction-donut-chart <redaction-donut-chart
[config]="chartConfig" [config]="chartConfig()"
[radius]="63" [radius]="63"
[strokeWidth]="15" [strokeWidth]="15"
[subtitles]="['user-stats.chart.users' | translate]" [subtitles]="['user-stats.chart.users' | translate]"

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, input, Input, output, Output } from '@angular/core';
import { DonutChartConfig } from '@red/domain'; import { DonutChartConfig } from '@red/domain';
import { CircleButtonComponent } from '@iqser/common-ui'; import { CircleButtonComponent } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@ -12,6 +12,6 @@ import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.
imports: [CircleButtonComponent, TranslateModule, DonutChartComponent], imports: [CircleButtonComponent, TranslateModule, DonutChartComponent],
}) })
export class UsersStatsComponent { export class UsersStatsComponent {
@Output() toggleCollapse = new EventEmitter(); readonly chartConfig = input.required<DonutChartConfig[]>();
@Input() chartConfig: DonutChartConfig[]; readonly toggleCollapse = output();
} }

View File

@ -17,7 +17,7 @@ import { RouterHistoryService } from '@services/router-history.service';
import { auditCategoriesTranslations } from '@translations/audit-categories-translations'; import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
import { Roles } from '@users/roles'; import { Roles } from '@users/roles';
import { applyIntervalConstraints } from '@utils/date-inputs-utils'; import { applyIntervalConstraints } from '@utils/date-inputs-utils';
import { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { AdminDialogService } from '../../services/admin-dialog.service'; import { AdminDialogService } from '../../services/admin-dialog.service';
import { AuditService } from '../../services/audit.service'; import { AuditService } from '../../services/audit.service';
@ -139,16 +139,9 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
const promises = []; const promises = [];
const category = this.form.get('category').value; const category = this.form.get('category').value;
const userId = this.form.get('userId').value; const userId = this.form.get('userId').value;
const from = this.form.get('from').value; const from = this.form.get('from').value ? dayjs(this.form.get('from').value).startOf('day').toISOString() : null;
let to = this.form.get('to').value; const to = this.form.get('to').value ? dayjs(this.form.get('to').value).endOf('day').toISOString() : null;
if (to) {
const hoursLeft = new Date(to).getHours();
const minutesLeft = new Date(to).getMinutes();
to = to
.clone()
.add(24 - hoursLeft - 1, 'h')
.add(60 - minutesLeft - 1);
}
const logsRequestBody: IAuditSearchRequest = { const logsRequestBody: IAuditSearchRequest = {
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
page: page, page: page,

View File

@ -42,6 +42,15 @@
type="text" type="text"
/> />
</div> </div>
<div class="iqser-input-group required w-150">
<label translate="add-edit-component-mapping.form.quote-char"></label>
<input
[placeholder]="'add-edit-component-mapping.form.quote-char-placeholder' | translate"
formControlName="quoteChar"
name="quoteChar"
type="text"
/>
</div>
<div class="iqser-input-group required w-150"> <div class="iqser-input-group required w-150">
<label translate="add-edit-component-mapping.form.encoding-type"></label> <label translate="add-edit-component-mapping.form.encoding-type"></label>

View File

@ -17,12 +17,14 @@ interface DialogData {
dossierTemplateId: string; dossierTemplateId: string;
mapping: IComponentMapping; mapping: IComponentMapping;
} }
interface DialogResult { interface DialogResult {
id: string; id: string;
name: string; name: string;
file: Blob; file: Blob;
encoding: string; encoding: string;
delimiter: string; delimiter: string;
quoteChar: string;
fileName?: string; fileName?: string;
} }
@ -72,14 +74,14 @@ export class AddEditComponentMappingDialogComponent
const file = new Blob([fileContent.body as Blob], { type: 'text/csv' }); const file = new Blob([fileContent.body as Blob], { type: 'text/csv' });
this.form.get('file').setValue(file); this.form.get('file').setValue(file);
this.initialFormValue = this.form.getRawValue(); this.initialFormValue = this.form.getRawValue();
this.#disableEncodingAndDelimiter(); this.#disableEncodingAndQuoteCharAndDelimiter();
} }
} }
changeFile(file: File) { changeFile(file: File) {
this.form.get('file').setValue(file); this.form.get('file').setValue(file);
this.form.get('fileName').setValue(file?.name); this.form.get('fileName').setValue(file?.name);
this.#enableEncodingAndDelimiter(); this.#enableEncodingAndQuoteCharAndDelimiter();
} }
save() { save() {
@ -93,16 +95,19 @@ export class AddEditComponentMappingDialogComponent
fileName: [this.data?.mapping?.fileName, Validators.required], fileName: [this.data?.mapping?.fileName, Validators.required],
encoding: this.encodingTypeOptions.find(e => e === this.data?.mapping?.encoding) ?? this.encodingTypeOptions[0], encoding: this.encodingTypeOptions.find(e => e === this.data?.mapping?.encoding) ?? this.encodingTypeOptions[0],
delimiter: [this.data?.mapping?.delimiter ?? ',', Validators.required], delimiter: [this.data?.mapping?.delimiter ?? ',', Validators.required],
quoteChar: [this.data?.mapping?.quoteChar ?? '"', Validators.required],
}); });
} }
#disableEncodingAndDelimiter() { #disableEncodingAndQuoteCharAndDelimiter() {
this.form.get('encoding').disable(); this.form.get('encoding').disable();
this.form.get('delimiter').disable(); this.form.get('delimiter').disable();
this.form.get('quoteChar').disable();
} }
#enableEncodingAndDelimiter() { #enableEncodingAndQuoteCharAndDelimiter() {
this.form.get('encoding').enable(); this.form.get('encoding').enable();
this.form.get('delimiter').enable(); this.form.get('delimiter').enable();
this.form.get('quoteChar').enable();
} }
} }

View File

@ -99,8 +99,8 @@ export default class ComponentMappingsScreenComponent extends ListingComponent<C
const result = await dialog.result(); const result = await dialog.result();
if (result) { if (result) {
this._loadingService.start(); this._loadingService.start();
const { id, name, encoding, delimiter, fileName } = result; const { id, name, encoding, delimiter, fileName, quoteChar } = result;
const newMapping = { id, name, encoding, delimiter, fileName }; const newMapping = { id, name, encoding, delimiter, fileName, quoteChar };
await firstValueFrom( await firstValueFrom(
this._componentMappingService.createUpdateComponentMapping(this.#dossierTemplateId, newMapping, result.file), this._componentMappingService.createUpdateComponentMapping(this.#dossierTemplateId, newMapping, result.file),
); );

View File

@ -17,8 +17,17 @@
type="text" type="text"
/> />
</div> </div>
<div class="iqser-input-group">
<label translate="add-edit-entity.form.technical-name"></label>
<div class="technical-name">{{ this.technicalName() || '-' }}</div>
<span
[translateParams]="{ type: data.justification ? 'edit' : 'create' }"
[translate]="'add-edit-entity.form.technical-name-hint'"
class="hint"
></span>
</div>
<div class="iqser-input-group required w-400"> <div class="iqser-input-group w-400">
<label translate="add-edit-justification.form.reason"></label> <label translate="add-edit-justification.form.reason"></label>
<input <input
[placeholder]="'add-edit-justification.form.reason-placeholder' | translate" [placeholder]="'add-edit-justification.form.reason-placeholder' | translate"
@ -28,7 +37,7 @@
/> />
</div> </div>
<div class="iqser-input-group required w-400"> <div class="iqser-input-group w-400">
<label translate="add-edit-justification.form.description"></label> <label translate="add-edit-justification.form.description"></label>
<textarea <textarea
[placeholder]="'add-edit-justification.form.description-placeholder' | translate" [placeholder]="'add-edit-justification.form.description-placeholder' | translate"

View File

@ -1,11 +1,13 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { ChangeDetectionStrategy, Component, computed, Inject, untracked } from '@angular/core';
import { ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms'; import { ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Justification } from '@red/domain'; import { Justification } from '@red/domain';
import { JustificationsService } from '@services/entity-services/justifications.service'; import { JustificationsService } from '@services/entity-services/justifications.service';
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui'; import { BaseDialogComponent, CircleButtonComponent, HasScrollbarDirective, IconButtonComponent } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { formControlToSignal } from '@utils/functions';
import { toSignal } from '@angular/core/rxjs-interop';
interface DialogData { interface DialogData {
justification?: Justification; justification?: Justification;
@ -16,9 +18,29 @@ interface DialogData {
templateUrl: './add-edit-justification-dialog.component.html', templateUrl: './add-edit-justification-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true, standalone: true,
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent], imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent, HasScrollbarDirective],
}) })
export class AddEditJustificationDialogComponent extends BaseDialogComponent { export class AddEditJustificationDialogComponent extends BaseDialogComponent {
readonly form = this.#getForm();
readonly name = formControlToSignal(this.form.controls['name']);
readonly allJustifications = toSignal(this._justificationService.all$);
readonly technicalName = computed(() => {
if (this.data.justification) {
return this.data.justification.technicalName;
}
if (!this.name()) {
return null;
}
let currentTechnicalName = Justification.toTechnicalName(this.name());
const existingTechnicalNames = untracked(this.allJustifications).map(justification => justification.technicalName);
let suffix = 1;
while (existingTechnicalNames.includes(currentTechnicalName)) {
currentTechnicalName =
currentTechnicalName === '_' ? `${currentTechnicalName}${suffix++}` : [currentTechnicalName, suffix++].join('_');
}
return currentTechnicalName;
});
constructor( constructor(
private readonly _justificationService: JustificationsService, private readonly _justificationService: JustificationsService,
protected readonly _dialogRef: MatDialogRef<AddEditJustificationDialogComponent>, protected readonly _dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
@ -26,7 +48,6 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
) { ) {
super(_dialogRef, !!data.justification); super(_dialogRef, !!data.justification);
this.form = this._getForm();
this.initialFormValue = this.form.getRawValue(); this.initialFormValue = this.form.getRawValue();
} }
@ -34,7 +55,8 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
const dossierTemplateId = this.data.dossierTemplateId; const dossierTemplateId = this.data.dossierTemplateId;
this._loadingService.start(); this._loadingService.start();
try { try {
await firstValueFrom(this._justificationService.createOrUpdate(this.form.getRawValue() as Justification, dossierTemplateId)); const formValue = { ...this.form.getRawValue(), technicalName: this.technicalName() };
await firstValueFrom(this._justificationService.createOrUpdate(formValue as Justification, dossierTemplateId));
await firstValueFrom(this._justificationService.loadAll(dossierTemplateId)); await firstValueFrom(this._justificationService.loadAll(dossierTemplateId));
this._dialogRef.close(true); this._dialogRef.close(true);
} catch (error) { } catch (error) {
@ -43,11 +65,12 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
this._loadingService.stop(); this._loadingService.stop();
} }
private _getForm(): UntypedFormGroup { #getForm(): UntypedFormGroup {
return this._formBuilder.group({ return this._formBuilder.group({
name: [{ value: this.data.justification?.name, disabled: !!this.data.justification }, Validators.required], name: [{ value: this.data.justification?.name, disabled: !!this.data.justification }, Validators.required],
reason: [this.data.justification?.reason, Validators.required], reason: [this.data.justification?.reason],
description: [this.data.justification?.description, Validators.required], description: [this.data.justification?.description],
technicalName: [this.data.justification?.technicalName ?? null],
}); });
} }
} }

View File

@ -147,6 +147,7 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
handleClick($event: MouseEvent) { handleClick($event: MouseEvent) {
$event.stopPropagation(); $event.stopPropagation();
$event.preventDefault();
} }
ngOnDestroy() { ngOnDestroy() {
@ -154,12 +155,14 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
} }
handleFieldClick($event: MouseEvent) { handleFieldClick($event: MouseEvent) {
$event.preventDefault();
if (!this.fileNameColumn) { if (!this.fileNameColumn) {
this.editFileAttribute($event); this.editFileAttribute($event);
} }
} }
editFileAttribute($event: MouseEvent) { editFileAttribute($event: MouseEvent) {
$event.preventDefault();
if ( if (
!this.file.isInitialProcessing && !this.file.isInitialProcessing &&
this.permissionsService.canEditFileAttributes(this.file, this.dossier) && this.permissionsService.canEditFileAttributes(this.file, this.dossier) &&

View File

@ -588,4 +588,16 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this.displayedPages.every((value, index) => value === newDisplayedPages[index]) this.displayedPages.every((value, index) => value === newDisplayedPages[index])
); );
} }
@HostListener('click', ['$event'])
clickInsideWorkloadView($event: MouseEvent) {
$event?.stopPropagation();
}
@HostListener('document: click')
clickOutsideWorkloadView() {
if (this.multiSelectService.active() && !this._dialog.openDialogs.length) {
this.multiSelectService.deactivate();
}
}
} }

View File

@ -46,12 +46,13 @@ export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;
readonly options: DetailsRadioOption<RemoveAnnotationOption>[]; readonly options: DetailsRadioOption<RemoveAnnotationOption>[];
readonly redactedTexts: string[]; readonly redactedTexts: string[];
readonly isImage = this.data.redactions.reduce((acc, next) => acc && next.isImage, true);
form!: UntypedFormGroup; form!: UntypedFormGroup;
constructor(private readonly _formBuilder: FormBuilder) { constructor(private readonly _formBuilder: FormBuilder) {
super(); super();
this.options = getRemoveRedactionOptions(this.data, this.data.applyToAllDossiers, true); this.options = getRemoveRedactionOptions(this.data, this.data.applyToAllDossiers, this.isImage, true);
this.redactedTexts = this.data.redactions.map(annotation => annotation.value); this.redactedTexts = this.data.redactions.map(annotation => annotation.value);
this.form = this.#getForm(); this.form = this.#getForm();
} }

View File

@ -6,7 +6,7 @@
class="dialog-header heading-l" class="dialog-header heading-l"
></div> ></div>
<div [class.fixed-height]="isRedacted && isImage" class="dialog-content redaction"> <div [class.image-dialog]="isRedacted && isImage" [class.rectangle-dialog]="allRectangles" class="dialog-content redaction">
<div *ngIf="!isImage && redactedTexts.length && !allRectangles" class="iqser-input-group"> <div *ngIf="!isImage && redactedTexts.length && !allRectangles" class="iqser-input-group">
<redaction-selected-annotations-table <redaction-selected-annotations-table
[columns]="tableColumns" [columns]="tableColumns"
@ -107,7 +107,7 @@
formControlName="comment" formControlName="comment"
iqserHasScrollbar iqserHasScrollbar
name="comment" name="comment"
rows="4" rows="3"
type="text" type="text"
></textarea> ></textarea>
</div> </div>

View File

@ -1,8 +1,16 @@
.dialog-content { .dialog-content {
padding-top: 8px; padding-top: 8px;
&.fixed-height { &.rectangle-dialog {
height: 386px; height: 600px;
}
&.image-dialog {
height: 346px;
}
.rectangle-dialog,
.image-dialog {
overflow-y: auto; overflow-y: auto;
} }
} }

View File

@ -37,7 +37,7 @@ import {
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option'; import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { validatePageRange } from '../../utils/form-validators'; import { validatePageRange } from '../../utils/form-validators';
import { parseRectanglePosition, parseSelectedPageNumbers } from '../../utils/enhance-manual-redaction-request.utils'; import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
interface TypeSelectOptions { interface TypeSelectOptions {
type: string; type: string;
@ -103,6 +103,14 @@ export class EditRedactionDialogComponent
private readonly _dictionaryService: DictionaryService, private readonly _dictionaryService: DictionaryService,
) { ) {
super(); super();
if (this.allRectangles) {
prefillPageRange(
this.data.annotations[0],
this.data.allFileAnnotations,
this.options as DetailsRadioOption<RectangleRedactOption>[],
);
}
} }
get displayedDictionaryLabel() { get displayedDictionaryLabel() {
@ -255,7 +263,10 @@ export class EditRedactionDialogComponent
disabled: this.isImported, disabled: this.isImported,
}), }),
section: new FormControl<string>({ value: sameSection ? this.annotations[0].section : null, disabled: this.isImported }), section: new FormControl<string>({ value: sameSection ? this.annotations[0].section : null, disabled: this.isImported }),
option: new FormControl<DetailsRadioOption<EditRedactionOption | RectangleRedactOption>>(this.options[0], validatePageRange()), option: new FormControl<DetailsRadioOption<EditRedactionOption | RectangleRedactOption>>(
this.options[0],
validatePageRange(this.data.file.numberOfPages),
),
value: new FormControl<string>(this.allRectangles ? this.annotations[0].value : null), value: new FormControl<string>(this.allRectangles ? this.annotations[0].value : null),
}); });
} }

View File

@ -1,14 +1,13 @@
import { Component, Inject, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { import {
BaseDialogComponent,
CircleButtonComponent, CircleButtonComponent,
getConfig, getConfig,
HasScrollbarDirective, HasScrollbarDirective,
HelpButtonComponent, HelpButtonComponent,
IconButtonComponent, IconButtonComponent,
IqserDenyDirective, IqserDenyDirective,
IqserDialogComponent,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { JustificationsService } from '@services/entity-services/justifications.service'; import { JustificationsService } from '@services/entity-services/justifications.service';
import { Dossier, ILegalBasisChangeRequest } from '@red/domain'; import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
@ -21,20 +20,19 @@ import {
ValueColumn, ValueColumn,
} from '../../components/selected-annotations-table/selected-annotations-table.component'; } from '../../components/selected-annotations-table/selected-annotations-table.component';
import { NgForOf, NgIf } from '@angular/common'; import { NgForOf, NgIf } from '@angular/common';
import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { ForceAnnotationData, ForceAnnotationOption, ForceAnnotationResult, LegalBasisOption } from '../../utils/dialog-types';
import { getForceAnnotationOptions } from '../../utils/dialog-options';
import { SystemDefaults } from '../../../account/utils/dialog-defaults';
import { MatFormField } from '@angular/material/form-field';
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { ForceAnnotationOption, LegalBasisOption } from '../../utils/dialog-types';
import { getForceAnnotationOptions } from '../../utils/dialog-options';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component'; import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { SystemDefaults } from '../../../account/utils/dialog-defaults';
const DOCUMINE_LEGAL_BASIS = 'n-a.'; const DOCUMINE_LEGAL_BASIS = 'n-a.';
@Component({ @Component({
selector: 'redaction-force-annotation-dialog',
templateUrl: './force-annotation-dialog.component.html', templateUrl: './force-annotation-dialog.component.html',
styleUrls: ['./force-annotation-dialog.component.scss'], styleUrls: ['./force-annotation-dialog.component.scss'],
standalone: true, standalone: true,
@ -57,12 +55,16 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.';
DetailsRadioComponent, DetailsRadioComponent,
], ],
}) })
export class ForceAnnotationDialogComponent extends BaseDialogComponent implements OnInit { export class ForceAnnotationDialogComponent
extends IqserDialogComponent<ForceAnnotationDialogComponent, ForceAnnotationData, ForceAnnotationResult>
implements OnInit
{
readonly isDocumine = getConfig().IS_DOCUMINE; readonly isDocumine = getConfig().IS_DOCUMINE;
readonly options: DetailsRadioOption<ForceAnnotationOption>[]; readonly options: DetailsRadioOption<ForceAnnotationOption>[];
readonly form: FormGroup;
readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }]; readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }];
readonly tableData: ValueColumn[][] = this._data.annotations.map(redaction => [ readonly tableData: ValueColumn[][] = this.data.annotations.map(redaction => [
{ label: redaction.value, bold: true }, { label: redaction.value, bold: true },
{ label: redaction.typeLabel }, { label: redaction.typeLabel },
]); ]);
@ -72,21 +74,23 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
constructor( constructor(
private readonly _justificationsService: JustificationsService, private readonly _justificationsService: JustificationsService,
protected readonly _dialogRef: MatDialogRef<ForceAnnotationDialogComponent>, private readonly _formBuilder: FormBuilder,
@Inject(MAT_DIALOG_DATA)
private readonly _data: { readonly dossier: Dossier; readonly hint: boolean; annotations: AnnotationWrapper[] },
) { ) {
super(_dialogRef); super();
this.options = getForceAnnotationOptions(this.isDocumine, this.isHintDialog); this.options = getForceAnnotationOptions(this.isDocumine, this.isHintDialog, this.isImageDialog);
this.form = this.#getForm(); this.form = this.#getForm();
} }
get isImageHint() { get isImageHint() {
return this._data.annotations.every(annotation => annotation.IMAGE_HINT); return this.data.annotations.every(annotation => annotation.IMAGE_HINT);
} }
get isHintDialog() { get isHintDialog() {
return this._data.hint; return this.data.hint;
}
get isImageDialog() {
return this.data.image;
} }
get disabled(): boolean { get disabled(): boolean {
@ -103,7 +107,7 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
async ngOnInit() { async ngOnInit() {
if (!this.isDocumine) { if (!this.isDocumine) {
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this._data.dossier.dossierTemplateId)); const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this.data.dossier.dossierTemplateId));
this.legalOptions = data.map(lbm => ({ this.legalOptions = data.map(lbm => ({
legalBasis: lbm.reason, legalBasis: lbm.reason,
@ -114,8 +118,8 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
this.legalOptions.sort((a, b) => a.label.localeCompare(b.label)); this.legalOptions.sort((a, b) => a.label.localeCompare(b.label));
// Set pre-existing reason if it exists // Set pre-existing reason if it exists
const existingReason = this.legalOptions.find(option => option.legalBasis === this._data.annotations[0].legalBasis); const existingReason = this.legalOptions.find(option => option.legalBasis === this.data.annotations[0].legalBasis);
if (!this._data.hint && existingReason) { if (!this.data.hint && existingReason) {
this.form.patchValue({ reason: existingReason }, { emitEvent: false }); this.form.patchValue({ reason: existingReason }, { emitEvent: false });
} }
} }
@ -123,12 +127,12 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
} }
save() { save() {
this._dialogRef.close(this.#createForceRedactionRequest()); this.close(this.#createForceRedactionRequest());
} }
#getForm(): UntypedFormGroup { #getForm(): UntypedFormGroup {
return this._formBuilder.group({ return this._formBuilder.group({
reason: this._data.hint ? ['Forced Hint'] : [null, !this.isDocumine ? Validators.required : null], reason: this.data.hint ? ['Forced Hint'] : [null, !this.isDocumine ? Validators.required : null],
comment: [null], comment: [null],
option: this.options.find(o => o.value === SystemDefaults.FORCE_REDACTION_DEFAULT), option: this.options.find(o => o.value === SystemDefaults.FORCE_REDACTION_DEFAULT),
}); });
@ -140,7 +144,7 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
request.legalBasis = !this.isDocumine ? this.form.get('reason').value.legalBasis : DOCUMINE_LEGAL_BASIS; request.legalBasis = !this.isDocumine ? this.form.get('reason').value.legalBasis : DOCUMINE_LEGAL_BASIS;
request.comment = this.form.get('comment').value; request.comment = this.form.get('comment').value;
request.reason = this.form.get('reason').value.description; request.reason = this.form.get('reason').value.description;
request.option = this.form.get('option').value.value; request.option = this.form.get('option').value?.value;
return request; return request;
} }

View File

@ -2,7 +2,7 @@
<form (submit)="save()" [formGroup]="form"> <form (submit)="save()" [formGroup]="form">
<div [translate]="'manual-annotation.dialog.header.redaction'" class="dialog-header heading-l"></div> <div [translate]="'manual-annotation.dialog.header.redaction'" class="dialog-header heading-l"></div>
<div class="dialog-content"> <div class="dialog-content redaction">
<iqser-details-radio <iqser-details-radio
[options]="options" [options]="options"
(extraOptionChanged)="extraOptionChanged($event)" (extraOptionChanged)="extraOptionChanged($event)"
@ -43,7 +43,7 @@
<div class="iqser-input-group w-450"> <div class="iqser-input-group w-450">
<label [translate]="'manual-annotation.dialog.content.comment'"></label> <label [translate]="'manual-annotation.dialog.content.comment'"></label>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea> <textarea formControlName="comment" iqserHasScrollbar name="comment" rows="3" type="text"></textarea>
</div> </div>
</div> </div>

View File

@ -3,8 +3,8 @@
} }
.dialog-content { .dialog-content {
height: 650px; height: 600px;
overflow-y: auto; padding-top: 8px;
} }
.apply-on-multiple-pages { .apply-on-multiple-pages {

View File

@ -130,7 +130,7 @@ export class RectangleAnnotationDialog
reason: [null, Validators.required], reason: [null, Validators.required],
comment: [null], comment: [null],
classification: [NON_READABLE_CONTENT], classification: [NON_READABLE_CONTENT],
option: [this.#getOption(SystemDefaults.RECTANGLE_REDACT_DEFAULT), validatePageRange()], option: [this.#getOption(SystemDefaults.RECTANGLE_REDACT_DEFAULT), validatePageRange(this.data.file.numberOfPages)],
}); });
} }

View File

@ -36,7 +36,7 @@ import {
} from '../../utils/dialog-types'; } from '../../utils/dialog-types';
import { isJustOne } from '@common-ui/utils'; import { isJustOne } from '@common-ui/utils';
import { validatePageRange } from '../../utils/form-validators'; import { validatePageRange } from '../../utils/form-validators';
import { parseRectanglePosition, parseSelectedPageNumbers } from '../../utils/enhance-manual-redaction-request.utils'; import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
@Component({ @Component({
templateUrl: './remove-redaction-dialog.component.html', templateUrl: './remove-redaction-dialog.component.html',
@ -66,6 +66,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
readonly recommendation = this.data.redactions.every(redaction => redaction.isRecommendation); readonly recommendation = this.data.redactions.every(redaction => redaction.isRecommendation);
readonly hint = this.data.redactions.every(redaction => redaction.isHint); readonly hint = this.data.redactions.every(redaction => redaction.isHint);
readonly annotationsType = this.hint ? 'hint' : this.recommendation ? 'recommendation' : 'redaction'; readonly annotationsType = this.hint ? 'hint' : this.recommendation ? 'recommendation' : 'redaction';
readonly isImage = this.data.redactions.reduce((acc, next) => acc && next.isImage, true);
readonly optionByType = { readonly optionByType = {
recommendation: { recommendation: {
main: this._userPreferences.getRemoveRecommendationDefaultOption(), main: this._userPreferences.getRemoveRecommendationDefaultOption(),
@ -107,12 +108,13 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
: getRemoveRedactionOptions( : getRemoveRedactionOptions(
this.data, this.data,
this.isSystemDefault || this.isExtraOptionSystemDefault ? this.#applyToAllDossiers : this.extraOptionPreference, this.isSystemDefault || this.isExtraOptionSystemDefault ? this.#applyToAllDossiers : this.extraOptionPreference,
this.isImage,
); );
readonly skipped = this.data.redactions.some(annotation => annotation.isSkipped); readonly skipped = this.data.redactions.some(annotation => annotation.isSkipped);
readonly redactedTexts = this.data.redactions.map(annotation => annotation.value); readonly redactedTexts = this.data.redactions.map(annotation => annotation.value);
form: UntypedFormGroup = this._formBuilder.group({ form: UntypedFormGroup = this._formBuilder.group({
comment: [null], comment: [null],
option: [this.defaultOption, validatePageRange(true)], option: [this.defaultOption, validatePageRange(this.data.file.numberOfPages, true)],
}); });
readonly selectedOption = toSignal(this.form.get('option').valueChanges.pipe(map(value => value.value))); readonly selectedOption = toSignal(this.form.get('option').valueChanges.pipe(map(value => value.value)));
@ -140,6 +142,14 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
private readonly _userPreferences: UserPreferenceService, private readonly _userPreferences: UserPreferenceService,
) { ) {
super(); super();
if (this.#allRectangles) {
prefillPageRange(
this.data.redactions[0],
this.data.allFileRedactions,
this.options as DetailsRadioOption<RectangleRedactOption>[],
);
}
} }
get hasFalsePositiveOption() { get hasFalsePositiveOption() {
@ -177,12 +187,9 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
} }
save(): void { save(): void {
const optionValue = this.form.controls.option.value.value; const optionValue = this.form.controls.option?.value?.value;
const pageNumbers = parseSelectedPageNumbers( const optionInputValue = this.form.controls.option?.value?.additionalInput?.value;
this.form.get('option').value.additionalInput?.value, const pageNumbers = parseSelectedPageNumbers(optionInputValue, this.data.file, this.data.redactions[0]);
this.data.file,
this.data.redactions[0],
);
const position = parseRectanglePosition(this.data.redactions[0]); const position = parseRectanglePosition(this.data.redactions[0]);
this.close({ this.close({

View File

@ -248,11 +248,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files) this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files)
super.ngOnDetach(); super.ngOnDetach();
this.pdf.instance.UI.hotkeys.off('esc'); this.pdf.instance.UI.hotkeys.off('esc');
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
this._changeRef.markForCheck(); this._changeRef.markForCheck();
} }
ngOnDestroy() { ngOnDestroy() {
this.pdf.instance.UI.hotkeys.off('esc'); this.pdf.instance.UI.hotkeys.off('esc');
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
super.ngOnDestroy(); super.ngOnDestroy();
} }
@ -277,6 +279,29 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
} }
@Bind()
handleViewerClick(event: MouseEvent) {
this._ngZone.run(() => {
if (event.isTrusted) {
const clickedElement = event.target as HTMLElement;
const editingAnnotation =
(clickedElement as HTMLImageElement).src?.includes('edit.svg') || clickedElement.getAttribute('aria-label') === 'Edit';
if (this._multiSelectService.active() && !editingAnnotation) {
if (
clickedElement.querySelector('#selectionrect') ||
clickedElement.id === `pageWidgetContainer${this.pdf.currentPage()}`
) {
if (!this._annotationManager.selected.length) {
this._multiSelectService.deactivate();
}
} else {
this._multiSelectService.deactivate();
}
}
}
});
}
async ngOnAttach(previousRoute: ActivatedRouteSnapshot) { async ngOnAttach(previousRoute: ActivatedRouteSnapshot) {
if (!this.state.file().canBeOpened) { if (!this.state.file().canBeOpened) {
return this.#navigateToDossier(); return this.#navigateToDossier();
@ -307,6 +332,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.#restoreOldFilters(); this.#restoreOldFilters();
this.pdf.instance.UI.hotkeys.on('esc', this.handleEscInsideViewer); this.pdf.instance.UI.hotkeys.on('esc', this.handleEscInsideViewer);
this._viewerHeaderService.resetLayers(); this._viewerHeaderService.resetLayers();
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
this.pdf.instance.UI.iframeWindow.document.addEventListener('click', this.handleViewerClick);
} }
async openRectangleAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) { async openRectangleAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {

View File

@ -7,6 +7,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import { import {
DictionaryEntryTypes, DictionaryEntryTypes,
DownloadFileTypes,
EarmarkOperation, EarmarkOperation,
type IBulkLocalRemoveRequest, type IBulkLocalRemoveRequest,
IBulkRecategorizationRequest, IBulkRecategorizationRequest,
@ -51,6 +52,7 @@ import { FilePreviewStateService } from './file-preview-state.service';
import { ManualRedactionService } from './manual-redaction.service'; import { ManualRedactionService } from './manual-redaction.service';
import { SkippedService } from './skipped.service'; import { SkippedService } from './skipped.service';
import { NON_READABLE_CONTENT } from '../dialogs/rectangle-annotation-dialog/rectangle-annotation-dialog.component'; import { NON_READABLE_CONTENT } from '../dialogs/rectangle-annotation-dialog/rectangle-annotation-dialog.component';
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
@Injectable() @Injectable()
export class AnnotationActionsService { export class AnnotationActionsService {
@ -81,41 +83,49 @@ export class AnnotationActionsService {
this._dialogService.openDialog('highlightAction', data); this._dialogService.openDialog('highlightAction', data);
} }
forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) { async forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) {
const { dossierId, fileId } = this._state; const { dossierId, fileId } = this._state;
const data = { dossier: this._state.dossier(), annotations, hint }; const image = annotations.every(a => a.isImage);
this._dialogService.openDialog('forceAnnotation', data, (request: ILegalBasisChangeRequest) => { const data = { dossier: this._state.dossier(), annotations, hint, image };
let obs$: Observable<unknown>;
if (request.option === ForceAnnotationOptions.ONLY_HERE) { const dialogRef = this._iqserDialog.openDefault(ForceAnnotationDialogComponent, { data });
obs$ = this._manualRedactionService.bulkForce( const result = await dialogRef.result();
annotations.map(a => ({ ...request, annotationId: a.id })),
dossierId, if (!result) {
fileId, return;
annotations[0].isIgnoredHint, }
);
} else { let obs$: Observable<unknown>;
const addAnnotationRequest = annotations.map(a => ({ if (result.option === ForceAnnotationOptions.ONLY_HERE || hint || image) {
comment: { text: request.comment }, obs$ = this._manualRedactionService.bulkForce(
legalBasis: request.legalBasis, annotations.map(a => ({ ...result, annotationId: a.id })),
reason: request.reason, dossierId,
positions: a.positions, fileId,
type: a.type, annotations[0].isIgnoredHint,
value: a.value, );
})); } else {
obs$ = this._manualRedactionService.addAnnotation(addAnnotationRequest, dossierId, fileId, { const addAnnotationRequest = annotations.map(a => ({
hint, comment: result.comment,
bulkLocal: true, legalBasis: result.legalBasis,
}); reason: result.reason,
} positions: a.positions,
this.#processObsAndEmit(obs$).then(); type: a.type,
}); value: a.value,
}));
obs$ = this._manualRedactionService.addAnnotation(addAnnotationRequest, dossierId, fileId, {
hint,
bulkLocal: true,
});
}
this.#processObsAndEmit(obs$).then();
} }
async editRedaction(annotations: AnnotationWrapper[]) { async editRedaction(annotations: AnnotationWrapper[]) {
const { dossierId, file } = this._state; const { dossierId, file } = this._state;
const includeUnprocessed = annotations.every(annotation => this.#includeUnprocessed(annotation, true)); const allFileAnnotations = this._fileDataService.annotations();
const data = { const data = {
annotations, annotations,
allFileAnnotations,
dossierId, dossierId,
file: file(), file: file(),
}; };
@ -145,15 +155,11 @@ export class AnnotationActionsService {
return body; return body;
}); });
} else { } else {
const originTypes = annotations.map(a => a.type);
const originLegalBases = annotations.map(a => a.legalBasis);
recategorizeBody = { recategorizeBody = {
value: annotations[0].value, value: annotations[0].value,
type: result.type, type: result.type,
legalBasis: result.legalBasis, legalBasis: result.legalBasis,
section: result.section, section: result.section,
originTypes,
originLegalBases,
rectangle: annotations[0].AREA, rectangle: annotations[0].AREA,
pageNumbers: result.pageNumbers, pageNumbers: result.pageNumbers,
position: result.position, position: result.position,
@ -168,7 +174,6 @@ export class AnnotationActionsService {
dossierId, dossierId,
file().id, file().id,
this.#getChangedFields(annotations, result), this.#getChangedFields(annotations, result),
includeUnprocessed,
result.option === RedactOrHintOptions.IN_DOCUMENT || !!result.pageNumbers.length, result.option === RedactOrHintOptions.IN_DOCUMENT || !!result.pageNumbers.length,
) )
.pipe(log()), .pipe(log()),
@ -184,9 +189,11 @@ export class AnnotationActionsService {
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId); const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
const isApprover = this._permissionsService.isApprover(this._state.dossier()); const isApprover = this._permissionsService.isApprover(this._state.dossier());
const { file } = this._state; const { file } = this._state;
const allFileRedactions = this._fileDataService.annotations();
const data = { const data = {
redactions, redactions,
allFileRedactions,
file: file(), file: file(),
dossier: this._state.dossier(), dossier: this._state.dossier(),
falsePositiveContext: redactions.map(r => this.#getFalsePositiveText(r)), falsePositiveContext: redactions.map(r => this.#getFalsePositiveText(r)),
@ -202,8 +209,8 @@ export class AnnotationActionsService {
} }
if ( if (
result.option.value === RemoveRedactionOptions.FALSE_POSITIVE || result.option?.value === RemoveRedactionOptions.FALSE_POSITIVE ||
result.option.value === RemoveRedactionOptions.DO_NOT_RECOMMEND result.option?.value === RemoveRedactionOptions.DO_NOT_RECOMMEND
) { ) {
this.#setAsFalsePositive(redactions, result); this.#setAsFalsePositive(redactions, result);
} else { } else {
@ -265,7 +272,6 @@ export class AnnotationActionsService {
async acceptResize(annotation: AnnotationWrapper, permissions: AnnotationPermissions): Promise<void> { async acceptResize(annotation: AnnotationWrapper, permissions: AnnotationPermissions): Promise<void> {
const textAndPositions = await this.#extractTextAndPositions(annotation.id); const textAndPositions = await this.#extractTextAndPositions(annotation.id);
const includeUnprocessed = this.#includeUnprocessed(annotation);
if (annotation.isRecommendation) { if (annotation.isRecommendation) {
const recommendation = { const recommendation = {
...annotation, ...annotation,
@ -316,7 +322,7 @@ export class AnnotationActionsService {
await this.cancelResize(annotation); await this.cancelResize(annotation);
const { fileId, dossierId } = this._state; const { fileId, dossierId } = this._state;
const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId, includeUnprocessed); const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId);
return this.#processObsAndEmit(request); return this.#processObsAndEmit(request);
} }
@ -472,8 +478,7 @@ export class AnnotationActionsService {
} }
#removeRedaction(redactions: AnnotationWrapper[], dialogResult: RemoveRedactionResult) { #removeRedaction(redactions: AnnotationWrapper[], dialogResult: RemoveRedactionResult) {
const removeFromDictionary = dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER; const removeFromDictionary = dialogResult.option?.value === RemoveRedactionOptions.IN_DOSSIER;
const includeUnprocessed = redactions.every(redaction => this.#includeUnprocessed(redaction, true));
const body = this.#getRemoveRedactionBody(redactions, dialogResult); const body = this.#getRemoveRedactionBody(redactions, dialogResult);
// todo: might not be correct, probably shouldn't get to this point if they are not all the same // todo: might not be correct, probably shouldn't get to this point if they are not all the same
const isHint = redactions.every(r => r.isHint); const isHint = redactions.every(r => r.isHint);
@ -501,7 +506,6 @@ export class AnnotationActionsService {
fileId, fileId,
removeFromDictionary, removeFromDictionary,
isHint, isHint,
includeUnprocessed,
dialogResult.bulkLocal, dialogResult.bulkLocal,
), ),
), ),
@ -517,7 +521,6 @@ export class AnnotationActionsService {
fileId, fileId,
removeFromDictionary, removeFromDictionary,
isHint, isHint,
includeUnprocessed,
dialogResult.bulkLocal || !!dialogResult.pageNumbers.length, dialogResult.bulkLocal || !!dialogResult.pageNumbers.length,
), ),
).then(); ).then();
@ -576,20 +579,6 @@ export class AnnotationActionsService {
return { changes: changedFields.join(', ') }; return { changes: changedFields.join(', ') };
} }
//TODO this is temporary, based on RED-8950. Should be removed when a better solution will be found
#includeUnprocessed(annotation: AnnotationWrapper, isRemoveOrRecategorize = false) {
const processed = annotation.entry.manualChanges.at(-1)?.processed;
if (!processed) {
const autoAnalysisDisabled = this._state.file().excludedFromAutomaticAnalysis;
const addedLocallyWhileDisabled = annotation.manual;
if (autoAnalysisDisabled) {
return addedLocallyWhileDisabled;
}
return isRemoveOrRecategorize && addedLocallyWhileDisabled;
}
return false;
}
#getRemoveRedactionBody( #getRemoveRedactionBody(
redactions: AnnotationWrapper[], redactions: AnnotationWrapper[],
dialogResult: RemoveRedactionResult, dialogResult: RemoveRedactionResult,
@ -611,8 +600,8 @@ export class AnnotationActionsService {
annotationId: redaction.id, annotationId: redaction.id,
value: redaction.value, value: redaction.value,
comment: dialogResult.comment, comment: dialogResult.comment,
removeFromDictionary: dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER, removeFromDictionary: dialogResult.option?.value === RemoveRedactionOptions.IN_DOSSIER,
removeFromAllDossiers: !!dialogResult.option.additionalCheck?.checked || !!dialogResult.applyToAllDossiers, removeFromAllDossiers: !!dialogResult.option?.additionalCheck?.checked || !!dialogResult.applyToAllDossiers,
})); }));
} }
} }

View File

@ -3,10 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
import { ConfirmationDialogComponent, DialogConfig, DialogService } from '@iqser/common-ui'; import { ConfirmationDialogComponent, DialogConfig, DialogService } from '@iqser/common-ui';
import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component'; import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/document-info-dialog.component'; import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/document-info-dialog.component';
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component'; import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component';
type DialogType = 'confirm' | 'documentInfo' | 'changeLegalBasis' | 'forceAnnotation' | 'highlightAction'; type DialogType = 'confirm' | 'documentInfo' | 'changeLegalBasis' | 'highlightAction';
@Injectable() @Injectable()
export class FilePreviewDialogService extends DialogService<DialogType> { export class FilePreviewDialogService extends DialogService<DialogType> {
@ -22,9 +21,6 @@ export class FilePreviewDialogService extends DialogService<DialogType> {
changeLegalBasis: { changeLegalBasis: {
component: ChangeLegalBasisDialogComponent, component: ChangeLegalBasisDialogComponent,
}, },
forceAnnotation: {
component: ForceAnnotationDialogComponent,
},
highlightAction: { highlightAction: {
component: HighlightActionDialogComponent, component: HighlightActionDialogComponent,
}, },

View File

@ -75,13 +75,10 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
body: List<IRecategorizationRequest> | IBulkRecategorizationRequest, body: List<IRecategorizationRequest> | IBulkRecategorizationRequest,
dossierId: string, dossierId: string,
fileId: string, fileId: string,
successMessageParameters?: { successMessageParameters?: { [p: string]: string },
[key: string]: string;
},
includeUnprocessed = false,
bulkLocal = false, bulkLocal = false,
) { ) {
return this.#recategorize(body, dossierId, fileId, includeUnprocessed, bulkLocal).pipe( return this.#recategorize(body, dossierId, fileId, bulkLocal).pipe(
this.#showToast('recategorize-annotation', false, successMessageParameters), this.#showToast('recategorize-annotation', false, successMessageParameters),
); );
} }
@ -117,10 +114,9 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
fileId: string, fileId: string,
removeFromDictionary = false, removeFromDictionary = false,
isHint = false, isHint = false,
includeUnprocessed = false,
bulkLocal = false, bulkLocal = false,
) { ) {
return this.#remove(body, dossierId, fileId, includeUnprocessed, bulkLocal).pipe( return this.#remove(body, dossierId, fileId, bulkLocal).pipe(
this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary), this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary),
); );
} }
@ -154,36 +150,23 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
return this._post(body, `${this.#bulkRedaction}/force/${dossierId}/${fileId}`).pipe(this.#log('Force redaction', body)); return this._post(body, `${this.#bulkRedaction}/force/${dossierId}/${fileId}`).pipe(this.#log('Force redaction', body));
} }
resize(body: List<IResizeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) { resize(body: List<IResizeRequest>, dossierId: string, fileId: string) {
return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe( return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}`).pipe(this.#log('Resize', body));
this.#log('Resize', body),
);
} }
#recategorize( #recategorize(
body: List<IRecategorizationRequest> | IBulkRecategorizationRequest, body: List<IRecategorizationRequest> | IBulkRecategorizationRequest,
dossierId: string, dossierId: string,
fileId: string, fileId: string,
includeUnprocessed = false,
bulkLocal = false, bulkLocal = false,
) { ) {
const bulkPath = bulkLocal ? this.#bulkLocal : this.#bulkRedaction; const bulkPath = bulkLocal ? this.#bulkLocal : this.#bulkRedaction;
return this._post(body, `${bulkPath}/recategorize/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe( return this._post(body, `${bulkPath}/recategorize/${dossierId}/${fileId}`).pipe(this.#log('Recategorize', body));
this.#log('Recategorize', body),
);
} }
#remove( #remove(body: List<IRemoveRedactionRequest> | IBulkLocalRemoveRequest, dossierId: string, fileId: string, bulkLocal = false) {
body: List<IRemoveRedactionRequest> | IBulkLocalRemoveRequest,
dossierId: string,
fileId: string,
includeUnprocessed = false,
bulkLocal = false,
) {
const bulkPath = bulkLocal ? this.#bulkLocal : this.#bulkRedaction; const bulkPath = bulkLocal ? this.#bulkLocal : this.#bulkRedaction;
return this._post(body, `${bulkPath}/remove/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`).pipe( return this._post(body, `${bulkPath}/remove/${dossierId}/${fileId}`).pipe(this.#log('Remove', body));
this.#log('Remove', body),
);
} }
#log(action: string, body: unknown) { #log(action: string, body: unknown) {

View File

@ -118,6 +118,7 @@ export const getRectangleRedactOptions = (action: 'add' | 'edit' | 'remove' = 'a
description: translations.multiplePages.extraOptionDescription, description: translations.multiplePages.extraOptionDescription,
placeholder: translations.multiplePages.extraOptionPlaceholder, placeholder: translations.multiplePages.extraOptionPlaceholder,
value: '', value: '',
errorCode: 'invalidRange',
}, },
}, },
]; ];
@ -131,22 +132,21 @@ export const getResizeRedactionOptions = (
isApprover: boolean, isApprover: boolean,
canResizeInDictionary: boolean, canResizeInDictionary: boolean,
): DetailsRadioOption<ResizeRedactionOption>[] => { ): DetailsRadioOption<ResizeRedactionOption>[] => {
if (isRss || !canResizeInDictionary) {
return [];
}
const translations = resizeRedactionTranslations; const translations = resizeRedactionTranslations;
const options: DetailsRadioOption<ResizeRedactionOption>[] = [ const dictBasedType = redaction.isModifyDictionary;
return [
{ {
label: translations.onlyHere.label, label: translations.onlyHere.label,
description: translations.onlyHere.description, description: translations.onlyHere.description,
icon: PIN_ICON, icon: PIN_ICON,
value: ResizeOptions.ONLY_HERE, value: ResizeOptions.ONLY_HERE,
}, },
]; {
if (isRss) {
return options;
}
if (canResizeInDictionary) {
const dictBasedType = redaction.isModifyDictionary;
options.push({
label: translations.inDossier.label, label: translations.inDossier.label,
description: translations.inDossier.description, description: translations.inDossier.description,
descriptionParams: { dossierName: dossier.dossierName }, descriptionParams: { dossierName: dossier.dossierName },
@ -159,14 +159,14 @@ export const getResizeRedactionOptions = (
checked: applyToAllDossiers, checked: applyToAllDossiers,
hidden: !isApprover, hidden: !isApprover,
}, },
}); },
} ];
return options;
}; };
export const getRemoveRedactionOptions = ( export const getRemoveRedactionOptions = (
data: RemoveRedactionData, data: RemoveRedactionData,
applyToAllDossiers: boolean, applyToAllDossiers: boolean,
isImage: boolean,
isDocumine: boolean = false, isDocumine: boolean = false,
): DetailsRadioOption<RemoveRedactionOption>[] => { ): DetailsRadioOption<RemoveRedactionOption>[] => {
const translations = isDocumine ? removeAnnotationTranslations : removeRedactionTranslations; const translations = isDocumine ? removeAnnotationTranslations : removeRedactionTranslations;
@ -174,7 +174,7 @@ export const getRemoveRedactionOptions = (
const isBulk = redactions.length > 1; const isBulk = redactions.length > 1;
const options: DetailsRadioOption<RemoveRedactionOption>[] = []; const options: DetailsRadioOption<RemoveRedactionOption>[] = [];
if (permissions.canRemoveOnlyHere) { if (permissions.canRemoveOnlyHere && !isImage) {
options.push({ options.push({
label: translations.ONLY_HERE.label, label: translations.ONLY_HERE.label,
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description, description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
@ -190,9 +190,6 @@ export const getRemoveRedactionOptions = (
options.push({ options.push({
label: removeRedactionTranslations.IN_DOCUMENT.label, label: removeRedactionTranslations.IN_DOCUMENT.label,
description: removeRedactionTranslations.IN_DOCUMENT.description, description: removeRedactionTranslations.IN_DOCUMENT.description,
descriptionParams: {
isImage: redactions[0].isImage ? 'image' : redactions[0].typeLabel,
},
icon: DOCUMENT_ICON, icon: DOCUMENT_ICON,
value: RemoveRedactionOptions.IN_DOCUMENT, value: RemoveRedactionOptions.IN_DOCUMENT,
}); });
@ -264,8 +261,12 @@ export const getRemoveRedactionOptions = (
return options; return options;
}; };
export const getForceAnnotationOptions = (isDocumine: boolean, isHint: boolean): DetailsRadioOption<ForceAnnotationOption>[] => { export const getForceAnnotationOptions = (
if (isDocumine || isHint) { isDocumine: boolean,
isHint: boolean,
isImage: boolean,
): DetailsRadioOption<ForceAnnotationOption>[] => {
if (isDocumine || isHint || isImage) {
return []; return [];
} }

View File

@ -64,6 +64,7 @@ export interface RedactTextData {
export interface EditRedactionData { export interface EditRedactionData {
annotations: AnnotationWrapper[]; annotations: AnnotationWrapper[];
allFileAnnotations?: AnnotationWrapper[];
dossierId: string; dossierId: string;
file: File; file: File;
isApprover?: boolean; isApprover?: boolean;
@ -72,6 +73,21 @@ export interface EditRedactionData {
export type AddAnnotationData = RedactTextData; export type AddAnnotationData = RedactTextData;
export type AddHintData = RedactTextData; export type AddHintData = RedactTextData;
export interface ForceAnnotationData {
readonly dossier: Dossier;
readonly annotations: AnnotationWrapper[];
readonly hint: boolean;
readonly image: boolean;
}
export interface ForceAnnotationResult {
readonly annotationId?: string;
readonly comment?: string;
readonly legalBasis?: string;
readonly reason?: string;
readonly option?: ForceAnnotationOption;
}
export interface RedactTextResult { export interface RedactTextResult {
redaction: IManualRedactionEntry; redaction: IManualRedactionEntry;
dictionary: Dictionary; dictionary: Dictionary;
@ -135,6 +151,7 @@ export interface RemoveRedactionPermissions {
export interface RemoveRedactionData { export interface RemoveRedactionData {
redactions: AnnotationWrapper[]; redactions: AnnotationWrapper[];
allFileRedactions?: AnnotationWrapper[];
dossier: Dossier; dossier: Dossier;
file?: File; file?: File;
falsePositiveContext: string[]; falsePositiveContext: string[];

View File

@ -1,7 +1,8 @@
import { Dictionary, File, IAddRedactionRequest, IEntityLogEntryPosition, SuperType } from '@red/domain'; import { Dictionary, File, IAddRedactionRequest, IEntityLogEntryPosition, SuperType } from '@red/domain';
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
import { LegalBasisOption } from './dialog-types'; import { LegalBasisOption, RectangleRedactOption, RectangleRedactOptions } from './dialog-types';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
export interface EnhanceRequestData { export interface EnhanceRequestData {
readonly type: SuperType | null; readonly type: SuperType | null;
@ -79,3 +80,60 @@ export const parseRectanglePosition = (annotation: AnnotationWrapper) => {
pageNumber: position.page, pageNumber: position.page,
} as IEntityLogEntryPosition; } as IEntityLogEntryPosition;
}; };
export const prefillPageRange = (
annotation: AnnotationWrapper,
allFileAnnotations: AnnotationWrapper[],
options: DetailsRadioOption<RectangleRedactOption>[],
) => {
const option = options.find(o => o.value === RectangleRedactOptions.MULTIPLE_PAGES);
const pages = extractPages(annotation, allFileAnnotations);
option.additionalInput.value = toRangeString(pages);
};
const extractPages = (annotation: AnnotationWrapper, allFileAnnotations: AnnotationWrapper[]): number[] => {
return allFileAnnotations.reduce((pages, a) => {
const position = a.positions[0];
const annotationPosition = annotation.positions[0];
if (
position.height === annotationPosition.height &&
position.width === annotationPosition.width &&
position.topLeft.x === annotationPosition.topLeft.x &&
position.topLeft.y === annotationPosition.topLeft.y
) {
pages.push(position.page);
}
return pages;
}, []);
};
const toRangeString = (pages: number[]): string => {
if (pages.length === 0) {
return '';
}
let ranges = [];
let start = pages[0];
let end = pages[0];
for (let i = 1; i < pages.length; i++) {
if (pages[i] === end + 1) {
end = pages[i];
} else {
if (start === end) {
ranges.push(`${start}`);
} else {
ranges.push(`${start}-${end}`);
}
start = end = pages[i];
}
}
if (start === end) {
ranges.push(`${start}`);
} else {
ranges.push(`${start}-${end}`);
}
return ranges.join(',');
};

View File

@ -1,12 +1,26 @@
import { AbstractControl, ValidatorFn } from '@angular/forms'; import { AbstractControl, ValidatorFn } from '@angular/forms';
export const validatePageRange = (allowEmpty = false): ValidatorFn => { export const validatePageRange = (numberOfPages: number, allowEmpty = false): ValidatorFn => {
return (control: AbstractControl): { [key: string]: any } | null => { return (control: AbstractControl): { [key: string]: any } | null => {
const option = control.value; const option = control.value;
if (option?.additionalInput) { if (option?.additionalInput) {
const value = option.additionalInput.value; const value = option.additionalInput.value;
const validRange = /^(\d+(-\d+)?)(,\d+(-\d+)?)*$/.test(value); const validRange = /^(\d+(-\d+)?)(,\d+(-\d+)?)*$/.test(value);
return validRange || (!value.length && allowEmpty) ? null : { invalidRange: true };
if (!validRange && !(value.length === 0 && allowEmpty)) {
return { invalidRange: true };
}
const ranges = value.split(',');
const isWithinRange = ranges.every(range => {
const [start, end] = range.split('-').map(Number);
if (end) {
return start >= 1 && end <= numberOfPages && start < end;
}
return start >= 1 && start <= numberOfPages;
});
return isWithinRange || (value.length === 0 && allowEmpty) ? null : { invalidRange: true };
} }
return null; return null;
}; };

View File

@ -44,11 +44,15 @@
<div class="iqser-input-group w-300"> <div class="iqser-input-group w-300">
<label translate="edit-dossier-dialog.general-info.form.dossier-state.label"></label> <label translate="edit-dossier-dialog.general-info.form.dossier-state.label"></label>
<mat-form-field> <mat-form-field>
<mat-select [placeholder]="statusPlaceholder" formControlName="dossierStatusId"> <mat-select [placeholder]="statePlaceholder()" formControlName="dossierStatusId">
<mat-option *ngFor="let stateId of states" [value]="stateId"> <mat-option *ngFor="let stateId of states()" [value]="stateId">
<div [matTooltip]="getStateName(stateId)" class="flex-align-items-center" matTooltipPosition="after"> <div
<iqser-small-chip *ngIf="!!stateId" [color]="getStateColor(stateId)"></iqser-small-chip> [matTooltip]="stateNameAndColor()[stateId]?.name"
<div class="clamp-1">{{ getStateName(stateId) }}</div> class="flex-align-items-center"
matTooltipPosition="after"
>
<iqser-small-chip *ngIf="!!stateId" [color]="stateNameAndColor()[stateId]?.color"></iqser-small-chip>
<div class="clamp-1">{{ stateNameAndColor()[stateId]?.name }}</div>
</div> </div>
</mat-option> </mat-option>
</mat-select> </mat-select>
@ -80,7 +84,7 @@
<div class="dialog-actions"> <div class="dialog-actions">
<iqser-icon-button <iqser-icon-button
(action)="deleteDossier()" (action)="deleteDossier()"
*ngIf="permissionsService.canDeleteDossier(dossier)" *ngIf="permissionsService.canDeleteDossier(dossier())"
[attr.help-mode-key]="'edit_dossier_delete_dossier_DIALOG'" [attr.help-mode-key]="'edit_dossier_delete_dossier_DIALOG'"
[buttonId]="'deleteDossier'" [buttonId]="'deleteDossier'"
[icon]="'iqser:trash'" [icon]="'iqser:trash'"
@ -90,7 +94,7 @@
<iqser-icon-button <iqser-icon-button
(action)="archiveDossier()" (action)="archiveDossier()"
*ngIf="permissionsService.canArchiveDossier(dossier)" *ngIf="permissionsService.canArchiveDossier(dossier())"
[attr.help-mode-key]="'edit_dossier_archive_dossier_DIALOG'" [attr.help-mode-key]="'edit_dossier_archive_dossier_DIALOG'"
[icon]="'red:archive'" [icon]="'red:archive'"
[label]="'dossier-listing.archive.action' | translate" [label]="'dossier-listing.archive.action' | translate"

View File

@ -1,12 +1,12 @@
import { NgForOf, NgIf } from '@angular/common'; import { NgForOf, NgIf } from '@angular/common';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, effect, input, OnInit, signal, untracked } from '@angular/core';
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { MatFormField, MatSuffix } from '@angular/material/form-field'; import { MatFormField, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { MatOption, MatSelect } from '@angular/material/select'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -30,12 +30,22 @@ import { DossiersService } from '@services/dossiers/dossiers.service';
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service'; import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
import { TrashService } from '@services/entity-services/trash.service'; import { TrashService } from '@services/entity-services/trash.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { dateWithoutTime } from '@utils/functions'; import { dateWithoutTime, formControlToSignal } from '@utils/functions';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { type EditDossierDialogComponent } from '../edit-dossier-dialog.component'; import { type EditDossierDialogComponent } from '../edit-dossier-dialog.component';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { AsControl, isJustOne } from '@common-ui/utils';
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
interface GeneralInfoForm {
dossierName: string;
dossierTemplateId: string;
dossierStatusId?: string;
description?: string;
dueDate?: string;
}
@Component({ @Component({
selector: 'redaction-edit-dossier-general-info', selector: 'redaction-edit-dossier-general-info',
@ -59,18 +69,36 @@ import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-doss
MatSuffix, MatSuffix,
IconButtonComponent, IconButtonComponent,
NgIf, NgIf,
MatSelectTrigger,
], ],
}) })
export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSectionInterface { export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSectionInterface {
@Input() dossier: Dossier;
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;
readonly dossier = input<Dossier>();
form: UntypedFormGroup;
statusPlaceholder: string;
hasDueDate: boolean; hasDueDate: boolean;
dossierTemplates: IDossierTemplate[]; dossierTemplates: IDossierTemplate[];
states: string[]; form: FormGroup<AsControl<GeneralInfoForm>> = this._formBuilder.group({
dossierName: [null, Validators.required],
dossierTemplateId: [null, Validators.required],
dossierStatusId: [null],
description: [null],
dueDate: [null],
});
initialFormValue: GeneralInfoForm;
readonly dossierStatusIdControl = formControlToSignal(this.form.controls.dossierStatusId);
readonly dossierTemplateIdControl = formControlToSignal<GeneralInfoForm['dossierTemplateId']>(this.form.controls.dossierTemplateId);
readonly states = signal([null]);
readonly stateNameAndColor = computed(() => {
const nameAndColor = {};
this.states().forEach(stateId => {
nameAndColor[stateId] = {
name: this.#getStateName(stateId, untracked(this.dossierTemplateIdControl)),
color: this.#getStateColor(stateId, untracked(this.dossierTemplateIdControl)),
};
});
return nameAndColor;
});
readonly statePlaceholder = computed(() => this.#statePlaceholder);
constructor( constructor(
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
@ -87,18 +115,36 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
private readonly _archivedDossiersService: ArchivedDossiersService, private readonly _archivedDossiersService: ArchivedDossiersService,
) {} private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _dossierStatesService: DossierStatesService,
) {
effect(() => {
if (this.dossierStatusIdControl() !== this.initialFormValue.dossierStatusId && this.dossierStatusIdControl()) {
this.form.controls.dossierTemplateId.disable();
} else {
this.form.controls.dossierTemplateId.enable();
}
});
effect(
() => {
this.states.set(this.#statesForDossierTemplate);
this.#onDossierTemplateChange();
},
{ allowSignalWrites: true },
);
}
get changed(): boolean { get changed(): boolean {
for (const key of Object.keys(this.form.getRawValue())) { for (const key of Object.keys(this.form.getRawValue())) {
if (key === 'dueDate') { if (key === 'dueDate') {
if (this.hasDueDate !== !!this.dossier.dueDate) { if (this.hasDueDate !== !!this.dossier().dueDate) {
return true; return true;
} }
if (this.hasDueDate && !dayjs(this.dossier.dueDate).isSame(dayjs(this.form.get(key).value), 'day')) { if (this.hasDueDate && !dayjs(this.dossier().dueDate).isSame(dayjs(this.form.get(key).value), 'day')) {
return true; return true;
} }
} else if (this.dossier[key] !== this.form.get(key).value) { } else if (this.dossier()[key] !== this.form.get(key).value) {
return true; return true;
} }
} }
@ -114,40 +160,89 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
return this.hasDueDate && this.form.get('dueDate').value === null; return this.hasDueDate && this.form.get('dueDate').value === null;
} }
get #statusPlaceholder(): string { get #statePlaceholder(): string {
return this._translateService.instant( return this._translateService.instant(
this.states.length === 1 isJustOne(this.states())
? 'edit-dossier-dialog.general-info.form.dossier-state.no-state-placeholder' ? 'edit-dossier-dialog.general-info.form.dossier-state.no-state-placeholder'
: 'dossier-state.placeholder', : 'dossier-state.placeholder',
) as string; ) as string;
} }
get #statesForDossierTemplate() {
return [
null,
...this._dossierStatesMapService
.get(this.dossierTemplateIdControl() ?? untracked(this.dossier).dossierTemplateId)
.map(s => s.id),
];
}
get #formValue(): { key: string; value: string; disabled: boolean }[] {
const dossier = untracked(this.dossier);
const formFieldWithArchivedCheck = value => ({ value, disabled: !dossier.isActive });
const dossierStateId = untracked(this.dossierStatusIdControl);
const states = untracked(this.states);
return [
{
key: 'dossierName',
...formFieldWithArchivedCheck(dossier.dossierName),
},
{
key: 'dossierTemplateId',
value: dossier.dossierTemplateId,
disabled: this._dossierStatsService.get(dossier.id).hasFiles || !dossier.isActive || !!dossierStateId,
},
{
key: 'dossierStatusId',
value: dossier.dossierStatusId,
disabled: isJustOne(states) || !dossier.isActive,
},
{
key: 'description',
...formFieldWithArchivedCheck(dossier.description),
},
{
key: 'dueDate',
...formFieldWithArchivedCheck(dossier.dueDate),
},
];
}
ngOnInit() { ngOnInit() {
this.states = [null, ...this._dossierStatesMapService.get(this.dossier.dossierTemplateId).map(s => s.id)]; this.#patchFormValue();
this.statusPlaceholder = this.#statusPlaceholder; if (isJustOne(this._dossierTemplatesService.all)) {
this.#filterInvalidDossierTemplates(); this._loadingService.loadWhile(
this.form = this.#getForm(); firstValueFrom(this._dossierTemplatesService.loadOnlyDossierTemplates()).then(async () => {
if (!this.permissionsService.canEditDossier(this.dossier)) { await firstValueFrom(this._dossierStatesService.loadAllForAllTemplates());
this.#filterInvalidDossierTemplates();
}),
);
} else {
this.#filterInvalidDossierTemplates();
}
if (!this.permissionsService.canEditDossier(this.dossier())) {
this.form.disable(); this.form.disable();
} }
this.hasDueDate = !!this.dossier.dueDate; this.hasDueDate = !!this.dossier().dueDate;
} }
revert() { revert() {
this.form.reset({ this.form.reset({
dossierName: this.dossier.dossierName, dossierName: this.dossier().dossierName,
dossierTemplateId: this.dossier.dossierTemplateId, dossierTemplateId: this.dossier().dossierTemplateId,
dossierStatusId: this.dossier.dossierStatusId, dossierStatusId: this.dossier().dossierStatusId,
description: this.dossier.description, description: this.dossier().description,
dueDate: this.dossier.dueDate, dueDate: this.dossier().dueDate,
}); });
this.hasDueDate = !!this.dossier.dueDate; this.hasDueDate = !!this.dossier().dueDate;
this.initialFormValue = this.form.getRawValue();
} }
async save(): EditDossierSaveResult { async save(): EditDossierSaveResult {
const dueDate = dateWithoutTime(dayjs(this.form.get('dueDate').value)); const dueDate = dateWithoutTime(dayjs(this.form.get('dueDate').value));
const dossier = { const dossier = {
...this.dossier, ...this.dossier(),
dossierName: this.form.get('dossierName').value, dossierName: this.form.get('dossierName').value,
description: this.form.get('description').value, description: this.form.get('description').value,
dueDate: dueDate.isValid() ? dueDate.toISOString() : undefined, dueDate: dueDate.isValid() ? dueDate.toISOString() : undefined,
@ -156,9 +251,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
} as IDossierRequest; } as IDossierRequest;
const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier)); const updatedDossier = await firstValueFrom(this._dossiersService.createOrUpdate(dossier));
if (updatedDossier && updatedDossier.dossierTemplateId !== this.dossier.dossierTemplateId) { if (updatedDossier && updatedDossier.dossierTemplateId !== this.dossier().dossierTemplateId) {
await this._router.navigate([updatedDossier.routerLink]); await this._router.navigate([updatedDossier.routerLink]);
} }
this.initialFormValue = this.form.getRawValue();
return { success: !!updatedDossier }; return { success: !!updatedDossier };
} }
@ -171,14 +267,14 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
requireInput: true, requireInput: true,
denyText: _('confirmation-dialog.delete-dossier.deny-text'), denyText: _('confirmation-dialog.delete-dossier.deny-text'),
translateParams: { translateParams: {
dossierName: this.dossier.dossierName, dossierName: this.dossier().dossierName,
dossiersCount: 1, dossiersCount: 1,
}, },
}; };
this._dialogService.openDialog('confirm', data, async () => { this._dialogService.openDialog('confirm', data, async () => {
this._loadingService.start(); this._loadingService.start();
const successful = await this._trashService.deleteDossier(this.dossier); const successful = await this._trashService.deleteDossier(this.dossier());
if (successful) { if (successful) {
await this.#closeDialogAndRedirectToDossier(); await this.#closeDialogAndRedirectToDossier();
} }
@ -194,7 +290,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
confirmationText: _('confirm-archive-dossier.archive'), confirmationText: _('confirm-archive-dossier.archive'),
denyText: _('confirm-archive-dossier.cancel'), denyText: _('confirm-archive-dossier.cancel'),
titleColor: TitleColors.WARN, titleColor: TitleColors.WARN,
translateParams: { ...this.dossier }, translateParams: { ...this.dossier() },
checkboxes: [{ value: false, label: _('confirm-archive-dossier.checkbox.documents') }], checkboxes: [{ value: false, label: _('confirm-archive-dossier.checkbox.documents') }],
toastMessage: _('confirm-archive-dossier.toast-error'), toastMessage: _('confirm-archive-dossier.toast-error'),
}; };
@ -202,10 +298,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
this._dialogService.openDialog('confirm', data, async result => { this._dialogService.openDialog('confirm', data, async result => {
if (result === ConfirmOptions.CONFIRM) { if (result === ConfirmOptions.CONFIRM) {
this._loadingService.start(); this._loadingService.start();
await firstValueFrom(this._archivedDossiersService.archive([this.dossier])); await firstValueFrom(this._archivedDossiersService.archive([this.dossier()]));
this._toaster.success(_('dossier-listing.archive.archive-succeeded'), { this._toaster.success(_('dossier-listing.archive.archive-succeeded'), {
params: { params: {
dossierName: this.dossier.dossierName, dossierName: this.dossier().dossierName,
}, },
}); });
this._editDossierDialogRef.close(); this._editDossierDialogRef.close();
@ -214,15 +310,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
}); });
} }
getStateName(stateId: string): string {
return (this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId)?.name ||
this._translateService.instant('dossier-state.placeholder')) as string;
}
getStateColor(stateId: string): string {
return this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId).color;
}
toggleDueDateField() { toggleDueDateField() {
this.hasDueDate = !this.hasDueDate; this.hasDueDate = !this.hasDueDate;
if (!this.hasDueDate) { if (!this.hasDueDate) {
@ -230,46 +317,63 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
} }
} }
#getStateName(stateId: string, templateId: string): string {
return (this._dossierStatesMapService.get(templateId, stateId)?.name ||
this._translateService.instant('dossier-state.placeholder')) as string;
}
#getStateColor(stateId: string, templateId: string): string {
return this._dossierStatesMapService.get(templateId, stateId)?.color;
}
#patchFormValue() {
this.#formValue.forEach(formValue => {
this.form.patchValue({ [formValue.key]: formValue.value });
if (formValue.disabled) this.form.get(formValue.key).disable();
});
this.initialFormValue = this.form.getRawValue();
}
async #closeDialogAndRedirectToDossier() { async #closeDialogAndRedirectToDossier() {
this._editDossierDialogRef.close(); this._editDossierDialogRef.close();
await this._router.navigate([this.dossier.dossiersListRouterLink]); await this._router.navigate([this.dossier().dossiersListRouterLink]);
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { this._toaster.success(_('edit-dossier-dialog.delete-successful'), {
params: { params: {
dossierName: this.dossier.dossierName, dossierName: this.dossier().dossierName,
}, },
}); });
} }
#getForm(): UntypedFormGroup {
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
return this._formBuilder.group({
dossierName: [formFieldWithArchivedCheck(this.dossier.dossierName), Validators.required],
dossierTemplateId: [
{
value: this.dossier.dossierTemplateId,
disabled: this._dossierStatsService.get(this.dossier.id).hasFiles || !this.dossier.isActive,
},
Validators.required,
],
dossierStatusId: [
{
value: this.dossier.dossierStatusId,
disabled: this.states.length === 1 || !this.dossier.isActive,
},
],
description: [formFieldWithArchivedCheck(this.dossier.description)],
dueDate: [formFieldWithArchivedCheck(this.dossier.dueDate)],
});
}
#filterInvalidDossierTemplates() { #filterInvalidDossierTemplates() {
const dossier = untracked(this.dossier);
this.dossierTemplates = this._dossierTemplatesService.all.filter(r => { this.dossierTemplates = this._dossierTemplatesService.all.filter(r => {
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) { if (dossier.dossierTemplateId === r.dossierTemplateId) {
return true; return true;
} }
const notYetValid = !!r.validFrom && dayjs(r.validFrom).isAfter(dayjs()); const notYetValid = !!r.validFrom && dayjs(r.validFrom).isAfter(dayjs());
const notValidAnymore = !!r.validTo && dayjs(r.validTo).add(1, 'd').isBefore(dayjs()); const notValidAnymore = !!r.validTo && dayjs(r.validTo).add(1, 'd').isBefore(dayjs());
this._changeDetectorRef.markForCheck();
return !(notYetValid || notValidAnymore) && r.isActive; return !(notYetValid || notValidAnymore) && r.isActive;
}); });
} }
#onDossierTemplateChange() {
const dossierStateId = untracked(this.dossierStatusIdControl);
const dossierTemplateId = untracked(this.dossierTemplateIdControl);
if (!!dossierStateId && dossierTemplateId !== this.initialFormValue.dossierTemplateId) {
this.form.controls.dossierStatusId.setValue(null);
}
const dossier = untracked(this.dossier);
if (dossierTemplateId === this.initialFormValue.dossierTemplateId) {
this.form.controls.dossierStatusId.setValue(dossier.dossierStatusId);
}
const states = untracked(this.states);
if (isJustOne(states) || !dossier.isActive) {
this.form.controls.dossierStatusId.disable();
} else {
this.form.controls.dossierStatusId.enable();
}
this._changeDetectorRef.markForCheck();
}
} }

View File

@ -78,24 +78,6 @@
></textarea> ></textarea>
</div> </div>
<div *ngIf="isIqserDevMode && form.get('aiCreationEnabled')" class="iqser-input-group">
<mat-slide-toggle color="primary" formControlName="aiCreationEnabled">
{{ 'add-edit-entity.form.ai-creation-enabled' | translate }}
</mat-slide-toggle>
</div>
<div *ngIf="isIqserDevMode && form.get('aiCreationEnabled')?.value && form.get('aiDescription')" class="iqser-input-group w-400">
<label translate="add-edit-entity.form.ai-description"></label>
<textarea
[placeholder]="'add-edit-entity.form.ai-description-placeholder' | translate"
formControlName="aiDescription"
iqserHasScrollbar
name="aiDescription"
rows="4"
type="text"
></textarea>
</div>
<div *ngIf="form.get('hasDictionary')" class="iqser-input-group"> <div *ngIf="form.get('hasDictionary')" class="iqser-input-group">
<mat-slide-toggle color="primary" formControlName="hasDictionary"> <mat-slide-toggle color="primary" formControlName="hasDictionary">
{{ 'add-edit-entity.form.has-dictionary' | translate }} {{ 'add-edit-entity.form.has-dictionary' | translate }}

View File

@ -9,7 +9,7 @@ import { MatSelect } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle'; import { MatSlideToggle } from '@angular/material/slide-toggle';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component'; import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component';
import { BaseFormComponent, getConfig, isIqserDevMode, HasScrollbarDirective, LoadingService, Toaster } from '@iqser/common-ui'; import { BaseFormComponent, getConfig, HasScrollbarDirective, LoadingService, Toaster } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { Dictionary, IDictionary } from '@red/domain'; import { Dictionary, IDictionary } from '@red/domain';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@ -62,7 +62,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
colors: Color[]; colors: Color[];
readonly isDocumine = getConfig().IS_DOCUMINE; readonly isDocumine = getConfig().IS_DOCUMINE;
readonly isIqserDevMode = isIqserDevMode();
constructor( constructor(
private readonly _dictionariesMapService: DictionariesMapService, private readonly _dictionariesMapService: DictionariesMapService,
@ -159,8 +158,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
skippedHexColor: [this.entity?.skippedHexColor, [Validators.required, Validators.minLength(7)]], skippedHexColor: [this.entity?.skippedHexColor, [Validators.required, Validators.minLength(7)]],
type: [this.entity?.type], type: [this.entity?.type],
description: [this.entity?.description], description: [this.entity?.description],
aiCreationEnabled: [this.entity?.aiCreationEnabled],
aiDescription: [this.entity?.aiDescription],
rank: [{ value: this.entity?.rank, disabled: this.#isSystemManaged }, Validators.required], rank: [{ value: this.entity?.rank, disabled: this.#isSystemManaged }, Validators.required],
hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }], hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }],
hasDictionary: [ hasDictionary: [
@ -253,8 +250,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
dossierTemplateId: this.dossierTemplateId, dossierTemplateId: this.dossierTemplateId,
type: this.form.get('type').value, type: this.form.get('type').value,
description: this.form.get('description').value, description: this.form.get('description').value,
aiCreationEnabled: this.form.get('aiCreationEnabled').value,
aiDescription: this.form.get('aiDescription').value,
hint: this.#isHint, hint: this.#isHint,
rank: this.form.get('rank').value, rank: this.form.get('rank').value,
caseInsensitive: !this.form.get('caseSensitive').value, caseInsensitive: !this.form.get('caseSensitive').value,

View File

@ -45,6 +45,13 @@ export class DossierTemplatesService extends EntitiesService<IDossierTemplate, D
); );
} }
loadOnlyDossierTemplates(): Observable<DossierTemplate[]> {
return this.getAll().pipe(
mapEach(entity => new DossierTemplate(entity)),
tap(templates => this.setEntities(templates)),
);
}
loadDossierTemplate(dossierTemplateId: string) { loadDossierTemplate(dossierTemplateId: string) {
return this._getOne([dossierTemplateId], this._defaultModelPath).pipe( return this._getOne([dossierTemplateId], this._defaultModelPath).pipe(
map(entity => new DossierTemplate(entity)), map(entity => new DossierTemplate(entity)),

View File

@ -28,6 +28,7 @@ export class ComponentMappingsService extends EntitiesService<IComponentMapping,
{ key: 'name', value: componentMapping.name }, { key: 'name', value: componentMapping.name },
{ key: 'encoding', value: componentMapping.encoding }, { key: 'encoding', value: componentMapping.encoding },
{ key: 'delimiter', value: componentMapping.delimiter }, { key: 'delimiter', value: componentMapping.delimiter },
{ key: 'quoteChar', value: componentMapping.quoteChar },
]; ];
if (componentMapping.id) { if (componentMapping.id) {

View File

@ -57,7 +57,7 @@ export class TranslateChartService {
translateRoles(config: DonutChartConfig[]): DonutChartConfig[] { translateRoles(config: DonutChartConfig[]): DonutChartConfig[] {
return config.map(val => ({ return config.map(val => ({
...val, ...val,
label: this._translateService.instant(rolesTranslations[val.label]).toLowerCase(), label: this._translateService.instant(rolesTranslations[val.label], { length: val.value }),
})); }));
} }
} }

View File

@ -2,7 +2,7 @@ import { ITrackable } from '@iqser/common-ui';
import type { List } from '@iqser/common-ui/lib/utils'; import type { List } from '@iqser/common-ui/lib/utils';
import type { AnnotationWrapper } from '@models/file/annotation.wrapper'; import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dayjs } from 'dayjs'; import { Dayjs } from 'dayjs';
import { FormControl } from '@angular/forms'; import { AbstractControl } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop'; import { toSignal } from '@angular/core/rxjs-interop';
export function hexToRgb(hex: string) { export function hexToRgb(hex: string) {
@ -146,6 +146,6 @@ export function urlFileId() {
return fileId.split('?')[0]; return fileId.split('?')[0];
} }
export function formControlToSignal<T>(control: FormControl<T>) { export function formControlToSignal<T>(control: AbstractControl<T>) {
return toSignal(control.valueChanges, { initialValue: control.value }); return toSignal(control.valueChanges, { initialValue: control.value });
} }

View File

@ -108,10 +108,15 @@
"file": "Mapping-Datei", "file": "Mapping-Datei",
"name": "Mapping-Name", "name": "Mapping-Name",
"name-placeholder": "Mapping-Name", "name-placeholder": "Mapping-Name",
"quote-char": "Quotation marker",
"quote-char-placeholder": "\"",
"version": "Version" "version": "Version"
} }
}, },
"add-edit-dossier-attribute": { "add-edit-dossier-attribute": {
"error": {
"generic": "Speichern des Attributs fehlgeschlagen."
},
"form": { "form": {
"label": "Name des Attributs", "label": "Name des Attributs",
"label-placeholder": "Namen eingeben", "label-placeholder": "Namen eingeben",
@ -135,9 +140,6 @@
}, },
"add-edit-entity": { "add-edit-entity": {
"form": { "form": {
"ai-creation-enabled": "AI Erzeugung aktivieren",
"ai-description": "AI Beschreibung",
"ai-description-placeholder": "AI Beschreibung eingeben",
"case-sensitive": "Groß-/Kleinschreibung beachten", "case-sensitive": "Groß-/Kleinschreibung beachten",
"color": "Farbe {type, select, redaction{Schwärzung} hint{Hinweis} recommendation{Empfehlung} skipped{Ingorierte Schwärzung} ignored{Ignorierter Hinweis} other{}}", "color": "Farbe {type, select, redaction{Schwärzung} hint{Hinweis} recommendation{Empfehlung} skipped{Ingorierte Schwärzung} ignored{Ignorierter Hinweis} other{}}",
"color-placeholder": "#", "color-placeholder": "#",
@ -244,7 +246,7 @@
}, },
"type": "Typ", "type": "Typ",
"type-placeholder": "Typ auswählen...", "type-placeholder": "Typ auswählen...",
"value": "" "value": "Wert"
}, },
"title": "Hinweis hinzufügen" "title": "Hinweis hinzufügen"
} }
@ -276,6 +278,9 @@
"watermarks": "Wasserzeichen" "watermarks": "Wasserzeichen"
}, },
"analysis-disabled": "", "analysis-disabled": "",
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotation-actions": { "annotation-actions": {
"accept-recommendation": { "accept-recommendation": {
"label": "Empfehlung annehmen" "label": "Empfehlung annehmen"
@ -331,14 +336,14 @@
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}", "error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet." "success": "Bild wurde einer neuen Kategorie zugeordnet."
}, },
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"remove": { "remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}", "error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt" "success": "Schwärzung wurde entfernt"
}, },
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"undo": { "undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}", "error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "Rücksetzung erfolgreich" "success": "Rücksetzung erfolgreich"
@ -351,15 +356,15 @@
"remove-highlights": { "remove-highlights": {
"label": "Ausgewählte Markierungen entfernen" "label": "Ausgewählte Markierungen entfernen"
}, },
"resize": {
"label": "Größe ändern"
},
"resize-accept": { "resize-accept": {
"label": "Neue Größe speichern" "label": "Neue Größe speichern"
}, },
"resize-cancel": { "resize-cancel": {
"label": "Größenänderung abbrechen" "label": "Größenänderung abbrechen"
}, },
"resize": {
"label": "Größe ändern"
},
"see-references": { "see-references": {
"label": "Referenzen anzeigen" "label": "Referenzen anzeigen"
}, },
@ -376,7 +381,7 @@
"removed-manual": "Schwärzung/Hinweis wurde entfernt", "removed-manual": "Schwärzung/Hinweis wurde entfernt",
"resized": "Schwärzungsbereich wurde geändert" "resized": "Schwärzungsbereich wurde geändert"
}, },
"annotation-content": "{hasRule, select, true {Regel {matchedRule} trifft zu auf{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Rechstgrundlage: {legalBasis}} other {}} {hasOverride, select, true {Entfernt durch manuelles Überschreiben} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n Abschnitt{sectionSymbol} \"{section}\"} other {}}", "annotation-content": "{hasRule, select, true {Rule {matchedRule} trifft zu:{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 Abschnitt{sectionSymbol} \"{section}\"} other {}}",
"annotation-engines": { "annotation-engines": {
"dictionary": "Basiert auf Wörterbuch", "dictionary": "Basiert auf Wörterbuch",
"dossier-dictionary": "Basiert auf Dossier-Wörterbuch", "dossier-dictionary": "Basiert auf Dossier-Wörterbuch",
@ -394,9 +399,6 @@
"skipped": "Ignorierte Schwärzung", "skipped": "Ignorierte Schwärzung",
"text-highlight": "Markierung" "text-highlight": "Markierung"
}, },
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotations": "Annotationen", "annotations": "Annotationen",
"archived-dossiers-listing": { "archived-dossiers-listing": {
"no-data": { "no-data": {
@ -1020,13 +1022,13 @@
"recent": "Neu ({hours} h)", "recent": "Neu ({hours} h)",
"unassigned": "Keinem Bearbeiter zugewiesen" "unassigned": "Keinem Bearbeiter zugewiesen"
}, },
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": { "reanalyse-dossier": {
"error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.", "error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Dateien für Reanalyse vorgesehen." "success": "Dateien für Reanalyse vorgesehen."
}, },
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "", "report-download": "",
"start-auto-analysis": "Auto-Analyse aktivieren", "start-auto-analysis": "Auto-Analyse aktivieren",
"stop-auto-analysis": "Auto-Analyse anhalten", "stop-auto-analysis": "Auto-Analyse anhalten",
@ -1096,6 +1098,14 @@
"total-documents": "Dokumente", "total-documents": "Dokumente",
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}" "total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
}, },
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-templates-listing": { "dossier-templates-listing": {
"action": { "action": {
"clone": "Vorlage klonen", "clone": "Vorlage klonen",
@ -1130,14 +1140,6 @@
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}" "title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
} }
}, },
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-watermark-selector": { "dossier-watermark-selector": {
"heading": "Wasserzeichen auf Dokumenten", "heading": "Wasserzeichen auf Dokumenten",
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.", "no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
@ -1165,15 +1167,15 @@
"queued": "Ihr Download wurde zur Warteschlange hinzugefügt.<br><br>Hier finden Sie Ihre generierten Downloads: <a href=\"{downloadHref}\">Meine Downloads<a/>." "queued": "Ihr Download wurde zur Warteschlange hinzugefügt.<br><br>Hier finden Sie Ihre generierten Downloads: <a href=\"{downloadHref}\">Meine Downloads<a/>."
}, },
"download-type": { "download-type": {
"annotated": "Annotierte PDF", "annotated": "Annotiertes PDF",
"delta-preview": "Delta-PDF", "delta-preview": "Delta-PDF",
"flatten": "Verflachte PDF", "flatten": "Verflachtes PDF",
"label": "{length} Dokumenten{length, plural, one{typ} other{typen}}", "label": "{length} Dokumenten{length, plural, one{typ} other{typen}}",
"optimized-preview": "Optimierte Vorschau-PDF", "optimized-preview": "Optimiertes Vorschau-PDF",
"original": "Optimierte PDF", "original": "Optimiertes PDF",
"preview": "Vorschau-PDF", "preview": "Vorschau-PDF",
"redacted": "Geschwärzte PDF", "redacted": "Geschwärztes PDF",
"redacted-only": "Geschwärzte PDF (nur freigegebene Dateien)" "redacted-only": "Geschwärztes PDF (nur freigegebene Dateien)"
}, },
"downloads-list": { "downloads-list": {
"actions": { "actions": {
@ -1235,10 +1237,10 @@
"save": "Speichern", "save": "Speichern",
"title": "{label} bearbeiten" "title": "{label} bearbeiten"
}, },
"entries-count": "", "entries-count": "{count} {count, plural, one{Eintrag} other{Einträge}}",
"false-positives": "Falsch-Positive", "false-positives": "Falsch-Positive ({count})",
"false-recommendations": "Falsche Empfehlungen", "false-recommendations": "Falsche Empfehlungen ({count})",
"to-redact": "Schwärzungen" "to-redact": "Schwärzungen ({count})"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1278,15 +1280,15 @@
"content": { "content": {
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "", "description": "Bearbeiten Sie die Schwärzung auf einer Reihe von Seiten",
"extraOptionDescription": "", "extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "", "extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "", "extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "" "label": "Auf mehreren Seiten ändern"
}, },
"only-this-page": { "only-this-page": {
"description": "", "description": "Schwärzung nur an dieser Position in diesem Dokument bearbeiten",
"label": "" "label": "Nur auf dieser Seite ändern"
} }
} }
} }
@ -1309,7 +1311,7 @@
}, },
"only-here": { "only-here": {
"description": "Bearbeiten Sie die Schwärzung nur an dieser Stelle im Dokument.", "description": "Bearbeiten Sie die Schwärzung nur an dieser Stelle im Dokument.",
"label": "Typ nur hier ändern" "label": "Nur hier ändern"
} }
}, },
"reason": "Grund", "reason": "Grund",
@ -1348,6 +1350,15 @@
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}" "title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
} }
}, },
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"entity-rules-screen": { "entity-rules-screen": {
"error": { "error": {
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen." "generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
@ -1361,28 +1372,19 @@
"title": "Entitätsregeln-Editor", "title": "Entitätsregeln-Editor",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden" "warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
}, },
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"error": { "error": {
"deleted-entity": { "deleted-entity": {
"dossier": { "dossier": {
"action": "Zurück zur Übersicht", "action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!" "label": "Dieses Dossier wurde gelöscht!"
}, },
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": { "file": {
"action": "Zurück zum Dossier", "action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!" "label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
} }
}, },
"file-preview": { "file-preview": {
@ -1400,6 +1402,12 @@
}, },
"exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr", "exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr",
"file": "Datei", "file": "Datei",
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attribute-encoding-types": { "file-attribute-encoding-types": {
"ascii": "ASCII", "ascii": "ASCII",
"iso": "ISO-8859-1", "iso": "ISO-8859-1",
@ -1410,12 +1418,6 @@
"number": "Nummer", "number": "Nummer",
"text": "Freier Text" "text": "Freier Text"
}, },
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attributes-configurations": { "file-attributes-configurations": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
"form": { "form": {
@ -1633,15 +1635,6 @@
"csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert." "csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert."
} }
}, },
"filter-menu": {
"filter-options": "Filteroptionen",
"filter-types": "Filter",
"label": "Filter",
"pages-without-annotations": "Nur Seiten ohne Annotationen",
"redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen",
"unseen-pages": "Nur Annotationen auf ungesehenen Seiten",
"with-comments": "Nur Annotationen mit Kommentaren"
},
"filter": { "filter": {
"analysis": "Analyse erforderlich", "analysis": "Analyse erforderlich",
"comment": "Kommentare", "comment": "Kommentare",
@ -1651,6 +1644,15 @@
"redaction": "Schwärzung", "redaction": "Schwärzung",
"updated": "Aktualisiert" "updated": "Aktualisiert"
}, },
"filter-menu": {
"filter-options": "Filteroptionen",
"filter-types": "Filter",
"label": "Filter",
"pages-without-annotations": "Nur Seiten ohne Annotationen",
"redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen",
"unseen-pages": "Nur Annotationen auf ungesehenen Seiten",
"with-comments": "Nur Annotationen mit Kommentaren"
},
"filters": { "filters": {
"assigned-people": "Bearbeiter", "assigned-people": "Bearbeiter",
"documents-status": "Dokumentenstatus", "documents-status": "Dokumentenstatus",
@ -1794,8 +1796,8 @@
}, },
"import-only-for-pages": "Nur für diese Seiten importieren", "import-only-for-pages": "Nur für diese Seiten importieren",
"range": { "range": {
"label": "Minus (-) für Spanne und Komma (,) für Aufzählung.", "label": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"placeholder": "Beispiel: 1-20,22,32" "placeholder": "z. B. 1-20,22,32"
}, },
"title": "Dokument mit Schwärzungen importieren" "title": "Dokument mit Schwärzungen importieren"
}, },
@ -1889,15 +1891,15 @@
"legalBasis": "Rechtsgrundlage", "legalBasis": "Rechtsgrundlage",
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "", "description": "Fügen Sie die Schwärzung auf einer Reihe von Seiten hinzu",
"extraOptionDescription": "", "extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "", "extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "", "extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "" "label": "Auf mehreren Seiten anwenden"
}, },
"only-this-page": { "only-this-page": {
"description": "", "description": "Schwärzung nur an dieser Position in diesem Dokument hinzufügen",
"label": "" "label": "Auf dieser Seite anwenden"
} }
}, },
"reason": "Grund", "reason": "Grund",
@ -1934,6 +1936,13 @@
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!", "user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!" "user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
}, },
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"notifications-screen": { "notifications-screen": {
"category": { "category": {
"email-notifications": "E-Mail-Benachrichtigungen", "email-notifications": "E-Mail-Benachrichtigungen",
@ -1947,7 +1956,6 @@
"dossier": "Benachrichtigungen zu Dossiers", "dossier": "Benachrichtigungen zu Dossiers",
"other": "Andere Benachrichtigungen" "other": "Andere Benachrichtigungen"
}, },
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"options": { "options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde", "ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde",
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde", "ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
@ -1965,6 +1973,7 @@
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde", "USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere" "USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
}, },
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"schedule": { "schedule": {
"daily": "Tägliche Zusammenfassung", "daily": "Tägliche Zusammenfassung",
"instant": "Sofort", "instant": "Sofort",
@ -1972,13 +1981,6 @@
}, },
"title": "Benachrichtigungseinstellungen" "title": "Benachrichtigungseinstellungen"
}, },
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"ocr": { "ocr": {
"confirmation-dialog": { "confirmation-dialog": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
@ -2032,19 +2034,19 @@
"left-panel-button": "Panel", "left-panel-button": "Panel",
"load-all-annotations": "Alle Annotationen laden", "load-all-annotations": "Alle Annotationen laden",
"no-outlines-text": "Keine Gliederung verfügbar", "no-outlines-text": "Keine Gliederung verfügbar",
"no-signatures-text": "In diesem Dokument gibt es keine Unterschriftenfelder", "no-signatures-text": "Dieses Dokument enthält keine Unterschriftenfelder.",
"outline-multi-select": "Bearbeiten", "outline-multi-select": "Bearbeiten",
"outlines-panel-button": "Gliederung", "outlines-panel-button": "Gliederung",
"pan-tool-button": "Verschieben", "pan-tool-button": "Verschieben",
"rectangle-tool-button": "Bereichsschwärzung", "rectangle-tool-button": "Bereich schwärzen",
"rotate-left-button": "Seite nach links drehen", "rotate-left-button": "Seite nach links drehen",
"rotate-right-button": "Seite nach rechts drehen", "rotate-right-button": "Seite nach rechts drehen",
"select-tool-button": "Auswählen", "select-tool-button": "Auswählen",
"signature-panel-button": "Unterschriften", "signature-panel-button": "Unterschriften",
"thumbnails-panel-button": "Miniaturansicht", "thumbnails-panel-button": "Miniaturansicht",
"toggle-layers": "Layout-Raster {active, select, true{deaktivieren} false{aktivieren} other{}}", "toggle-layers": "Layout-Raster {active, select, true{deaktivieren} false{aktivieren} other{}}",
"toggle-readable-redactions": "Schwärzungen {active, select, true{wie im finalen Dokument} false{in Vorschau-Farbe} other{}} anzeigen", "toggle-readable-redactions": "Schwärzungen {active, select, true{wie im finalen Dokument} false{in Preview-Farbe anzeigen} other{}}",
"toggle-tooltips": "Tooltips zu Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}", "toggle-tooltips": "Tooltips für Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}",
"zoom-in-button": "Vergrößern", "zoom-in-button": "Vergrößern",
"zoom-out-button": "Verkleinern" "zoom-out-button": "Verkleinern"
}, },
@ -2090,16 +2092,16 @@
"warnings-label": "Dialoge und Meldungen", "warnings-label": "Dialoge und Meldungen",
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen" "warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
}, },
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Ausstehend", "pending": "Ausstehend",
"processed": "Verarbeitet", "processed": "Verarbeitet",
"processing": "Verarbeitung läuft" "processing": "Verarbeitung läuft"
}, },
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"readonly": "Lesemodus", "readonly": "Lesemodus",
"readonly-archived": "Lesemodus (archiviert)", "readonly-archived": "Lesemodus (archiviert)",
"redact-text": { "redact-text": {
@ -2134,7 +2136,7 @@
"type": "Typ", "type": "Typ",
"type-placeholder": "Typ auswählen...", "type-placeholder": "Typ auswählen...",
"unchanged": "Ungeändert", "unchanged": "Ungeändert",
"value": "" "value": "Wert"
}, },
"title": "Text schwärzen" "title": "Text schwärzen"
} }
@ -2184,15 +2186,15 @@
"content": { "content": {
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "", "description": "Entfernen Sie die Schwärzung auf einer Reihe von Seiten",
"extraOptionDescription": "", "extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "", "extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "", "extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "" "label": "Auf mehreren Seiten entfernen"
}, },
"only-this-page": { "only-this-page": {
"description": "", "description": "Schwärzung nur an dieser Stelle in diesem Dokument entfernen",
"label": "" "label": "Nur auf dieser Seite entfernen"
} }
} }
} }
@ -2217,12 +2219,12 @@
"false-positive": { "false-positive": {
"description": "Markieren Sie die Schwärzung als falsch-positiv. Der Begriff wird in diesem Dossier nicht geschwärzt, wenn er im gleichen Kontext vorkommt.", "description": "Markieren Sie die Schwärzung als falsch-positiv. Der Begriff wird in diesem Dossier nicht geschwärzt, wenn er im gleichen Kontext vorkommt.",
"description-bulk": "Markieren Sie die Schwärzungen als falsch-positiv. Die Begriffe werden in diesem Dossier nicht geschwärzt, wenn sie im gleichen Kontext vorkommen.", "description-bulk": "Markieren Sie die Schwärzungen als falsch-positiv. Die Begriffe werden in diesem Dossier nicht geschwärzt, wenn sie im gleichen Kontext vorkommen.",
"extraOptionDescription": "Um diese Aktion rückgängig machen zu können, benötigen Sie Zugriff auf die Dossier-Vorlage. Als regulärer Benutzer können sie die Aktion nur für dieses Dossier rückgängig machen.", "extraOptionDescription": "Um diese Aktion rückgängig machen zu können, benötigen Sie Zugriff auf die Dossier-Vorlage. Als regulärer Benutzer können Sie die Aktion nur für dieses Dossier rückgängig machen.",
"extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen", "extraOptionLabel": "In alle aktiven und zukünftigen Dossiers übernehmen",
"label": "In diesem Kontext aus Dossier entfernen" "label": "In diesem Kontext aus Dossier entfernen"
}, },
"in-document": { "in-document": {
"description": "{isImage, select, image{Das Bild} other{der Begriff}} wird auf keiner Seite dieses Dokuments automatisch geschwärzt.", "description": "{isImage, select, image{Das Bild} other{Der Begriff}} wird auf keiner Seite dieses Dokuments automatisch geschwärzt.",
"label": "Aus Dokument entfernen" "label": "Aus Dokument entfernen"
}, },
"in-dossier": { "in-dossier": {
@ -2367,6 +2369,12 @@
"red-user-admin": "Benutzeradmin", "red-user-admin": "Benutzeradmin",
"regular": "regulärer Benutzer" "regular": "regulärer Benutzer"
}, },
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"search-screen": { "search-screen": {
"cols": { "cols": {
"assignee": "Bearbeiter", "assignee": "Bearbeiter",
@ -2390,12 +2398,6 @@
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.", "no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}" "table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
}, },
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"seconds": "Sekunden", "seconds": "Sekunden",
"size": "Größe", "size": "Größe",
"smtp-auth-config": { "smtp-auth-config": {
@ -2617,7 +2619,7 @@
}, },
"pagination": { "pagination": {
"landscape": "Querformat", "landscape": "Querformat",
"portrait": "Hoch-" "portrait": "Hochformat"
} }
}, },
"watermarks-listing": { "watermarks-listing": {
@ -2640,7 +2642,7 @@
"table-header": { "table-header": {
"title": "Wasserzeichen" "title": "Wasserzeichen"
}, },
"watermark-is-used": "Dieses Wasserzeichen wird bereits verwendet. Möchten Sie es dennocht löschen?" "watermark-is-used": "Dieses Wasserzeichen wird bereits verwendet. Möchten Sie es dennoch löschen?"
}, },
"workflow": { "workflow": {
"selection": { "selection": {

View File

@ -102,6 +102,8 @@
}, },
"disabled-file-options": "", "disabled-file-options": "",
"form": { "form": {
"quote-char": "Quotation marker",
"quote-char-placeholder": "\"",
"delimiter": "", "delimiter": "",
"delimiter-placeholder": "", "delimiter-placeholder": "",
"encoding-type": "", "encoding-type": "",
@ -112,6 +114,9 @@
} }
}, },
"add-edit-dossier-attribute": { "add-edit-dossier-attribute": {
"error": {
"generic": "Failed to save attribute."
},
"form": { "form": {
"label": "Attribute name", "label": "Attribute name",
"label-placeholder": "Enter name", "label-placeholder": "Enter name",
@ -135,9 +140,6 @@
}, },
"add-edit-entity": { "add-edit-entity": {
"form": { "form": {
"ai-creation-enabled": "Enable AI creation",
"ai-description": "AI Description",
"ai-description-placeholder": "Enter AI description",
"case-sensitive": "Case-sensitive", "case-sensitive": "Case-sensitive",
"color": "{type, select, redaction{Redaction} hint{Hint} recommendation{Recommendation} skipped{Skipped redaction} ignored{Ignored hint} other{}} color", "color": "{type, select, redaction{Redaction} hint{Hint} recommendation{Recommendation} skipped{Skipped redaction} ignored{Ignored hint} other{}} color",
"color-placeholder": "#", "color-placeholder": "#",
@ -1060,7 +1062,7 @@
"dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}" "dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}"
}, },
"error": { "error": {
"conflict": "Dossier state with this name already exists" "conflict": "Dossier state with this name already exists."
}, },
"no-data": { "no-data": {
"title": "There are no dossier states." "title": "There are no dossier states."
@ -1236,9 +1238,9 @@
"title": "Edit {label}" "title": "Edit {label}"
}, },
"entries-count": "{count} {count, plural, one{entry} other{entries}}", "entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives", "false-positives": "False positives ({count})",
"false-recommendations": "False recommendations", "false-recommendations": "False recommendations ({count})",
"to-redact": "To redact" "to-redact": "To redact ({count})"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1278,11 +1280,11 @@
"content": { "content": {
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "Edit redaction on following range of pages", "description": "Edit redaction on a range of pages",
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
"extraOptionLabel": "Range", "extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32", "extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Change on all pages" "label": "Change on multiple pages"
}, },
"only-this-page": { "only-this-page": {
"description": "Edit redaction only at this position in this document", "description": "Edit redaction only at this position in this document",
@ -1304,12 +1306,12 @@
"legal-basis": "Legal basis", "legal-basis": "Legal basis",
"options": { "options": {
"in-document": { "in-document": {
"description": "Edit redaction of all linked occurrences of the term in this document.", "description": "Edit redaction of all instances of the term in this document.",
"label": "Change in document" "label": "Change in document"
}, },
"only-here": { "only-here": {
"description": "Edit redaction only at this position in this document.", "description": "Edit redaction only at this position in this document.",
"label": "Change type only here" "label": "Change only here"
} }
}, },
"reason": "Reason", "reason": "Reason",
@ -1889,9 +1891,9 @@
"legalBasis": "Legal basis", "legalBasis": "Legal basis",
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "Add redaction on following range of pages", "description": "Add redaction on a range of pages",
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
"extraOptionLabel": "Range", "extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32", "extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Apply on multiple pages" "label": "Apply on multiple pages"
}, },
@ -2115,7 +2117,7 @@
"legal-basis": "Legal basis", "legal-basis": "Legal basis",
"options": { "options": {
"in-document": { "in-document": {
"description": "Add redaction for each occurrence of the term in this document.", "description": "Add redaction for each instance of the term in this document.",
"label": "Redact in document" "label": "Redact in document"
}, },
"in-dossier": { "in-dossier": {
@ -2184,11 +2186,11 @@
"content": { "content": {
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "Remove redaction on following range of pages", "description": "Remove redaction on a range of pages",
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
"extraOptionLabel": "Range", "extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32", "extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Remove on all pages" "label": "Remove on multiple pages"
}, },
"only-this-page": { "only-this-page": {
"description": "Remove redaction only at this position in this document", "description": "Remove redaction only at this position in this document",
@ -2222,7 +2224,7 @@
"label": "Remove from dossier in this context" "label": "Remove from dossier in this context"
}, },
"in-document": { "in-document": {
"description": "Do not auto-redact the selected {isImage, select, image{image} other{term}} in any pages of this document.", "description": "Do not auto-redact the selected term on any page of this document.",
"label": "Remove from document" "label": "Remove from document"
}, },
"in-dossier": { "in-dossier": {

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"selected-text": "Selected text:", "selected-text": "Selected text:",
"type": "Type", "type": "Type",
"type-placeholder": "Select type..." "type-placeholder": "Select type..."
@ -100,7 +100,7 @@
"dialog": { "dialog": {
"title": "{type, select, add{Add new} edit{Edit} other{}} component mapping" "title": "{type, select, add{Add new} edit{Edit} other{}} component mapping"
}, },
"disabled-file-options": "Re-upload mapping file to change", "disabled-file-options": "Upload updated mapping file",
"form": { "form": {
"delimiter": "CSV delimiter", "delimiter": "CSV delimiter",
"delimiter-placeholder": "CSV delimiter", "delimiter-placeholder": "CSV delimiter",
@ -112,6 +112,9 @@
} }
}, },
"add-edit-dossier-attribute": { "add-edit-dossier-attribute": {
"error": {
"generic": "Failed to save attribute!"
},
"form": { "form": {
"label": "Attribute name", "label": "Attribute name",
"label-placeholder": "Enter name", "label-placeholder": "Enter name",
@ -135,9 +138,6 @@
}, },
"add-edit-entity": { "add-edit-entity": {
"form": { "form": {
"ai-creation-enabled": "Enable AI creation",
"ai-description": "AI Description",
"ai-description-placeholder": "Enter AI description",
"case-sensitive": "Case-sensitive", "case-sensitive": "Case-sensitive",
"color": "{type, select, redaction{Annotation} hint{Hint} recommendation{Recommendation} skipped{Skipped annotation} ignored{Ignored hint} other{}} Color", "color": "{type, select, redaction{Annotation} hint{Hint} recommendation{Recommendation} skipped{Skipped annotation} ignored{Ignored hint} other{}} Color",
"color-placeholder": "#", "color-placeholder": "#",
@ -230,11 +230,11 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"options": { "options": {
"in-dossier": { "in-dossier": {
"description": "Add hint in every document in {dossierName}.", "description": "Add hint in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Add hint in dossier" "label": "Add hint in dossier"
}, },
"only-here": { "only-here": {
@ -981,7 +981,7 @@
} }
}, },
"download-file": "Download", "download-file": "Download",
"download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be initially processed in order to download.", "download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.",
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Re-processing required", "file-error": "Re-processing required",
@ -1060,13 +1060,13 @@
"dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}" "dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}"
}, },
"error": { "error": {
"conflict": "Dossier state with this name already exists!" "conflict": "Dossier state with this name already exists."
}, },
"no-data": { "no-data": {
"title": "There are no dossier states." "title": "There are no dossier states."
}, },
"no-match": { "no-match": {
"title": "No dossier states match your current filters." "title": "No dossier state matches the currently selected filters."
}, },
"search": "Search...", "search": "Search...",
"table-col-names": { "table-col-names": {
@ -1153,7 +1153,7 @@
"save": "Download" "save": "Download"
}, },
"form": { "form": {
"redaction-preview-color": "Redaction preview color", "redaction-preview-color": "Preview color",
"redaction-preview-color-placeholder": "#000000" "redaction-preview-color-placeholder": "#000000"
}, },
"header": "Download options", "header": "Download options",
@ -1236,9 +1236,9 @@
"title": "" "title": ""
}, },
"entries-count": "{count} {count, plural, one{entry} other{entries}}", "entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives", "false-positives": "False positives ({count})",
"false-recommendations": "False recommendations", "false-recommendations": "False recommendations ({count})",
"to-redact": "To redact" "to-redact": "To redact ({count})"
}, },
"general-info": { "general-info": {
"form": { "form": {
@ -1259,7 +1259,7 @@
} }
}, },
"header": "Edit {dossierName}", "header": "Edit {dossierName}",
"missing-owner": "You cannot edit the dossier because the owner is missing!", "missing-owner": "Editing the dossier not possible: No owner assigned.",
"nav-items": { "nav-items": {
"choose-download": "Choose what is included at download:", "choose-download": "Choose what is included at download:",
"dictionary": "Dictionary", "dictionary": "Dictionary",
@ -1300,12 +1300,12 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"legal-basis": "", "legal-basis": "",
"options": { "options": {
"in-document": { "in-document": {
"description": "", "description": "",
"label": "" "label": "In Dokument ändern"
}, },
"only-here": { "only-here": {
"description": "", "description": "",
@ -1745,7 +1745,7 @@
"label": "Convert on all pages" "label": "Convert on all pages"
}, },
"this-page": { "this-page": {
"description": "The earmarks in the selected HEX color will be converted only on the current page in view.", "description": "The earmarks in the selected HEX color will be converted only on the currently viewed page.",
"label": "Convert only on this page" "label": "Convert only on this page"
} }
}, },
@ -1874,8 +1874,8 @@
} }
}, },
"license-information": "License Information", "license-information": "License Information",
"load-all-annotations-success": "All annotations were loaded and are now visible in the document thumbnails", "load-all-annotations-success": "All annotations were loaded and are now visible in the document thumbnails.",
"load-all-annotations-threshold-exceeded": "Caution, document contains more than {threshold} annotations. Drawing all annotations will affect the performance of the app and could even block it. Do you want to proceed?", "load-all-annotations-threshold-exceeded": "<strong>Alert:</strong> Document contains more than {threshold} annotations. Drawing all annotations may cause the app to slow down or freeze. Do you still want to continue?",
"load-all-annotations-threshold-exceeded-checkbox": "Do not show this warning again", "load-all-annotations-threshold-exceeded-checkbox": "Do not show this warning again",
"loading": "Loading", "loading": "Loading",
"manual-annotation": { "manual-annotation": {
@ -1889,14 +1889,14 @@
"legalBasis": "Legal Basis", "legalBasis": "Legal Basis",
"options": { "options": {
"multiple-pages": { "multiple-pages": {
"description": "Edit redaction the range of pages", "description": "Edit annotation the range of pages",
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
"extraOptionLabel": "Range", "extraOptionLabel": "Range",
"extraOptionPlaceholder": "e.g. 1-20,22,32", "extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Apply on multiple pages" "label": "Apply on multiple pages"
}, },
"only-this-page": { "only-this-page": {
"description": "Edit redaction only at this position in this document", "description": "Edit annotation only at this position in this document",
"label": "Apply only on this page" "label": "Apply only on this page"
} }
}, },
@ -1919,16 +1919,16 @@
"minutes": "minutes", "minutes": "minutes",
"no-active-license": "Invalid or corrupt license Please contact your administrator", "no-active-license": "Invalid or corrupt license Please contact your administrator",
"notification": { "notification": {
"assign-approver": "You have been assigned to <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "assign-approver": "You have been assigned as approver for a document. <br>Document: <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> <br> Dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>",
"assign-reviewer": "You have been assigned to <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "assign-reviewer": "You have been assigned as reviewer for a document. <br>Document: <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> <br>\nDossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>",
"document-approved": " <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> has been moved to Done!", "document-approved": " <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> has been moved to Done!",
"dossier-deleted": "Dossier: <b>{dossierName}</b> has been deleted!", "dossier-deleted": "Dossier: <b>{dossierName}</b> has been deleted!",
"dossier-owner-deleted": "The owner of dossier: <b>{dossierName}</b> has been deleted!", "dossier-owner-deleted": "The owner of dossier: <b>{dossierName}</b> has been deleted!",
"dossier-owner-removed": "You have been removed as dossier owner from <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "dossier-owner-removed": "You have been removed as dossier owner from <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"dossier-owner-set": "You are now the dossier owner of <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "dossier-owner-set": "You are now the dossier owner of <b>{dossierHref, select, null{{dossierName}} other\n{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"download-ready": "Your <b><a href=\"{downloadHref}\", target=\"_self\">download</a></b> is ready!", "download-ready": "Your <b><a href=\"{downloadHref}\", target=\"_self\">download</a></b> is ready!",
"no-data": "You currently have no notifications", "no-data": "You currently have no notifications",
"unassigned-from-file": "You have been unassigned from <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>!", "unassigned-from-file": "You have been unassigned from a document. <br>Document: <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> <br>Dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>\n}}}}</b>",
"user-becomes-dossier-member": "You have been added to dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "user-becomes-dossier-member": "You have been added to dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-demoted-to-reviewer": "You have been demoted to reviewer in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "user-demoted-to-reviewer": "You have been demoted to reviewer in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-promoted-to-approver": "You have been promoted to approver in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!", "user-promoted-to-approver": "You have been promoted to approver in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
@ -1982,12 +1982,12 @@
"ocr": { "ocr": {
"confirmation-dialog": { "confirmation-dialog": {
"cancel": "Cancel", "cancel": "Cancel",
"question": "Manual changes could get lost if OCR makes changes at those positions. Are you sure you want to proceed?", "question": "Manual changes may be overwritten if the OCR makes changes at the respective positions. Do you still want to proceed?",
"title": "Warning: the file has manual adjustments!" "title": "Warning: the file has manual adjustments!"
} }
}, },
"overwrite-files-dialog": { "overwrite-files-dialog": {
"archive-question": "Dossier is not empty, so files might overlap with the contents of the archive you are uploading. Choose how to proceed in case of duplicates:", "archive-question": "Dossier already contains files. Files might overlap with the contents of the folder you are uploading. Select how to handle duplicates:",
"archive-title": "Uploading a ZIP archive", "archive-title": "Uploading a ZIP archive",
"file-question": "<b>{filename}</b> already exists. Choose how to proceed:", "file-question": "<b>{filename}</b> already exists. Choose how to proceed:",
"file-title": "File already exists!", "file-title": "File already exists!",
@ -1996,16 +1996,16 @@
"cancel": "Cancel upload", "cancel": "Cancel upload",
"current-files": "Apply to current file", "current-files": "Apply to current file",
"full-overwrite": { "full-overwrite": {
"description": "Manual changes done to the existing file will be removed and you are able to start over.", "description": "Remove all manual changes made to the file, and start reviewing a freshly processed file.",
"label": "Overwrite and start over" "label": "Overwrite and start over"
}, },
"partial-overwrite": { "partial-overwrite": {
"description": "Manual changes are kept only if the affected annotations are still at the same position in the file. Some annotations could be misplaced if the content of the file changed.", "description": "Keep manual changes if the affected redactions remain in their original positions. Some redactions could be misplaced if the content has changed.",
"label": "Overwrite and keep manual changes" "label": "Overwrite and keep manual changes"
}, },
"proceed": "Proceed", "proceed": "Proceed",
"skip": { "skip": {
"description": "The upload will be skipped and the existing file will not be replaced.", "description": "Skip the file upload and do not replace the existing file.",
"label": "Keep the existing file and do not overwrite" "label": "Keep the existing file and do not overwrite"
} }
}, },
@ -2079,14 +2079,14 @@
}, },
"form": { "form": {
"auto-expand-filters-on-action": "Auto expand filters on my actions", "auto-expand-filters-on-action": "Auto expand filters on my actions",
"help-mode-dialog": "Help Mode Dialog", "help-mode-dialog": "Help mode activation dialog",
"load-all-annotations-warning": "Warning regarding loading all annotations at once in file preview", "load-all-annotations-warning": "Warning regarding simultaneous loading of all annotations in thumbnails",
"overwrite-file-option": "Preferred action when re-uploading an already existing file", "overwrite-file-option": "Preferred action when re-uploading an already existing file",
"table-extraction-type": "Table extraction type" "table-extraction-type": "Table extraction type"
}, },
"label": "Preferences", "label": "Preferences",
"title": "Edit preferences", "title": "Edit preferences",
"warnings-description": "Selecting the 'Do not show this message again' checkbox will skip the warning dialog the next time you trigger it.", "warnings-description": "Selecting the 'Do not show this message again' checkbox will skip the dialog the next time you trigger it.",
"warnings-label": "Prompts and dialogs", "warnings-label": "Prompts and dialogs",
"warnings-subtitle": "Do not show again options" "warnings-subtitle": "Do not show again options"
}, },
@ -2110,7 +2110,7 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"edit-text": "", "edit-text": "",
"legal-basis": "Legal basis", "legal-basis": "Legal basis",
"options": { "options": {
@ -2120,7 +2120,7 @@
}, },
"in-dossier": { "in-dossier": {
"description": "Add redaction in every document in {dossierName}.", "description": "Add redaction in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Redact in dossier" "label": "Redact in dossier"
}, },
"only-here": { "only-here": {
@ -2149,27 +2149,27 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"list-item": "{text}", "list-item": "{text}",
"list-item-false-positive": "''{text}'' in the context: ''{context}''", "list-item-false-positive": "''{text}'' in the context: ''{context}''",
"options": { "options": {
"false-positive": { "false-positive": {
"description": "''{value}'' is not a ''{type}'' in this context: ''{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": "The selected items should not be annotated in their respective contexts.", "description-bulk": "Mark this redaction as a false-positive. The term will not be redacted in this dossier if it occurs in the same context.",
"label": "False positive" "label": "Remove from dossier in this context"
}, },
"in-document": { "in-document": {
"description": "", "description": "",
"label": "" "label": "Aus Dokument entfernen"
}, },
"in-dossier": { "in-dossier": {
"description": "Do not annotate ''{value}'' as ''{type}'' in any dossier.", "description": "Do not annotate the term in any dossier.",
"description-bulk": "Do not annotate the selected terms as their respective types in any dossier.", "description-bulk": "Do not annotate the selected terms as their respective types in any dossier.",
"label": "No longer annotate as ''{type}''", "label": "No longer annotate as ''{type}''",
"label-bulk": "No longer annotate in any dossier" "label-bulk": "Do not annotate as <i>{type}</i>"
}, },
"only-here": { "only-here": {
"description": "Do not annotate ''{value}'' at this position in the current document.", "description": "Do not annotate the term at this position in this document.",
"description-bulk": "Do not annotate the selected terms at this position in the current document.", "description-bulk": "Do not annotate the selected terms at this position in the current document.",
"label": "Remove here" "label": "Remove here"
} }
@ -2206,35 +2206,35 @@
}, },
"content": { "content": {
"comment": "Comment", "comment": "Comment",
"comment-placeholder": "Add remarks or mentions...", "comment-placeholder": "Add remarks or notes...",
"options": { "options": {
"do-not-recommend": { "do-not-recommend": {
"description": "Do not recommend ''{value}'' as {type} in any document of the current dossier.", "description": "Do not recommend the selected term in any document of this dossier.",
"description-bulk": "Do not recommend the selected values as their respective types in any document of the current dossier.", "description-bulk": "Do not recommend the selected values as their respective types in any document of the current dossier.",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier" "label": "Remove from dossier"
}, },
"false-positive": { "false-positive": {
"description": "''{value}'' is not a {type} in this context: ''{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": "", "description-bulk": "",
"extraOptionDescription": "", "extraOptionDescription": "",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "False positive" "label": "Remove from dossier in this context"
}, },
"in-document": { "in-document": {
"description": "", "description": "",
"label": "" "label": "Aus Dokument entfernen"
}, },
"in-dossier": { "in-dossier": {
"description": "Do not {type} \"{value}\" in any document of the current dossier.", "description": "Do not annotate the term in this dossier.",
"description-bulk": "", "description-bulk": "",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier", "label": "Remove from dossier",
"label-bulk": "" "label-bulk": ""
}, },
"only-here": { "only-here": {
"description": "Do not {type} ''{value}'' at this position in the current document.", "description": "Do not{type} '{value}'' at this position in the current document.",
"description-bulk": "", "description-bulk": "Do not{type} ''{value}'' at this position in the current document.",
"label": "Remove here" "label": "Remove here"
} }
} }
@ -2268,7 +2268,7 @@
"display-name": "This placeholder is replaced by the name of the entity the component is based on." "display-name": "This placeholder is replaced by the name of the entity the component is based on."
}, },
"excerpt": "This placeholder is replaced by a text snippet that contains the component.", "excerpt": "This placeholder is replaced by a text snippet that contains the component.",
"is-skipped": "The skipped redaction placeholder indicates whether a redaction is skipped or not. It can be included in a separate column of a template that also contains the '{{redaction.value'}} placeholder. The placeholder is replaced by “true” if the respective redaction is skipped, and by “false” if it is redacted (i. e., not skipped).", "is-skipped": "The skipped redaction placeholder indicates whether a redaction is skipped or not. It can be included in a separate column of a template that also contains the '\n{{'redaction.value'}}' placeholder. The placeholder is replaced by “true” if the respective redaction is skipped, and by “false” if it is redacted (i. e., not skipped).",
"justification": "This placeholder is replaced by the justification of the component. It is a combination of the legal reference (justificationParagraph) and the justification text (justificationReason).", "justification": "This placeholder is replaced by the justification of the component. It is a combination of the legal reference (justificationParagraph) and the justification text (justificationReason).",
"justification-legal-basis": "This placeholder is replaced by the legal basis for the component.", "justification-legal-basis": "This placeholder is replaced by the legal basis for the component.",
"justification-paragraph": "This placeholder is replaced by the legal reference of the justification of the component.", "justification-paragraph": "This placeholder is replaced by the legal reference of the justification of the component.",
@ -2331,7 +2331,7 @@
"options": { "options": {
"in-dossier": { "in-dossier": {
"description": "Resize in every document in {dossierName}.", "description": "Resize in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers", "extraOptionLabel": "Apply to all active and future dossiers",
"label": "Resize in dossier", "label": "Resize in dossier",
"tooltip": "Only available for dictionary-based types" "tooltip": "Only available for dictionary-based types"
}, },
@ -2386,7 +2386,7 @@
}, },
"missing": "Missing", "missing": "Missing",
"must-contain": "Must contain", "must-contain": "Must contain",
"no-data": "Please enter a keyword into the search bar to look for documents or document content.", "no-data": "Enter a keyword into the search bar<br> to look for documents or document content.",
"no-match": "The specified search term was not found in any of the documents.", "no-match": "The specified search term was not found in any of the documents.",
"table-header": "{length} search {length, plural, one{result} other{results}}" "table-header": "{length} search {length, plural, one{result} other{results}}"
}, },
@ -2422,7 +2422,7 @@
"sign-in-previous-domain": "Sign in to a previously used workspace", "sign-in-previous-domain": "Sign in to a previously used workspace",
"youre-logged-out": "You have successfully been logged out." "youre-logged-out": "You have successfully been logged out."
}, },
"input-placeholder": "your workspace" "input-placeholder": "Your workspace"
}, },
"time": { "time": {
"days": "{days} {days, plural, one{day} other{days}}", "days": "{days} {days, plural, one{day} other{days}}",
@ -2515,7 +2515,7 @@
}, },
"error": { "error": {
"file-size": "File too large. Limit is {size}MB.", "file-size": "File too large. Limit is {size}MB.",
"file-type": "This file type is not accepted.", "file-type": "This file type is not supported.",
"generic": "Failed to upload file. {error}" "generic": "Failed to upload file. {error}"
} }
}, },
@ -2570,7 +2570,7 @@
}, },
"title": "Edit profile", "title": "Edit profile",
"update": { "update": {
"success": "Successfully updated profile!" "success": "Successfully updated profile."
} }
}, },
"user-stats": { "user-stats": {
@ -2588,9 +2588,9 @@
}, },
"watermark-screen": { "watermark-screen": {
"action": { "action": {
"change-success": "Watermark has been updated!", "change-success": "Watermark has been updated.",
"created-success": "Watermark has been created!", "created-success": "Watermark has been created.",
"error": "Failed to update watermark", "error": "Failed to update watermark.",
"revert": "Revert", "revert": "Revert",
"save": "Save changes" "save": "Save changes"
}, },

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -14,6 +14,30 @@
"800": "Mplus1pHeavy", "800": "Mplus1pHeavy",
"900": "Mplus1pBlack" "900": "Mplus1pBlack"
} }
},
{
"coverage": "U+1-7F,U+A0-370,U+374-376,U+37A-37F,U+384-38B,U+38C,U+38E-3A2,U+3A3-3CF,U+3D0-514,U+51A-51E,U+591-5C8,U+5D0-5EB,U+5F0-5F5,U+1D00-1DCB,U+1DFE-1E9C,U+1E9E,U+1EA0-1EFA,U+1F00-1F16,U+1F18-1F1E,U+1F20-1F46,U+1F48-1F4E,U+1F50-1F58,U+1F59,U+1F5B,U+1F5D,U+1F5F-1F7E,U+1F80-1FB5,U+1FB6-1FC5,U+1FC6-1FD4,U+1FD6-1FDC,U+1FDD-1FF0,U+1FF2-1FF5,U+1FF6-1FFF,U+2000-2010,U+2012-2023,U+2026,U+202A-2031,U+2032-2035,U+2039-203B,U+203C,U+203E,U+2044,U+205E,U+206A-2070,U+2074-2076,U+2077-2079,U+207F,U+2090-2095,U+20A0-20B6,U+20F0,U+2105,U+2113,U+2116-2118,U+2122,U+2126,U+212E,U+214D-214F,U+2153-2155,U+215B-215F,U+2184,U+2190-2196,U+21A8,U+2202,U+2206,U+220F,U+2211-2213,U+2215,U+2219-221B,U+221E-2220,U+2229,U+222B,U+2248,U+2260-2262,U+2264-2266,U+2302,U+2310,U+2320-2322,U+2500,U+2502,U+250C,U+2510,U+2514,U+2518,U+251C,U+2524,U+252C,U+2534,U+253C,U+2550-256D,U+2580,U+2584,U+2588,U+258C,U+2590-2594,U+25A0-25A2,U+25AA-25AD,U+25B2,U+25BA,U+25BC,U+25C4,U+25CA-25CD,U+25CF,U+25D8-25DA,U+25E6,U+263A-263D,U+2640,U+2642,U+2660,U+2663,U+2665-2667,U+266A-266C,U+266F,U+2C60-2C6E,U+2C71-2C78,U+2E17,U+A717-A722,U+A788-A78D,U+F001-F003,U+F004-F006,U+F00A-F00F,U+FB01-FB03,U+FB1D-FB37,U+FB38-FB3D,U+FB3E,U+FB40-FB42,U+FB43-FB45,U+FB46-FB50,U+FE20-FE24,U+FEFF,U+FFFC",
"ext": ["ttf", "ttf.lzma", "ttf.brotli"],
"family": "Liberation Serif",
"id": "liberationserif1",
"variants": {
"500": "LiberationSerif",
"500I": "LiberationSerifItalic",
"700": "LiberationSerifBold",
"700I": "LiberationSerifBoldItalic"
}
},
{
"coverage": "U+1-7F,U+A0-370,U+374-376,U+37A-37F,U+384-38B,U+38C,U+38E-3A2,U+3A3-3CF,U+3D0-514,U+51A-51E,U+591-5C8,U+5D0-5EB,U+5F0-5F5,U+1D00-1DCB,U+1DFE-1E9C,U+1E9E,U+1EA0-1EFA,U+1F00-1F16,U+1F18-1F1E,U+1F20-1F46,U+1F48-1F4E,U+1F50-1F58,U+1F59,U+1F5B,U+1F5D,U+1F5F-1F7E,U+1F80-1FB5,U+1FB6-1FC5,U+1FC6-1FD4,U+1FD6-1FDC,U+1FDD-1FF0,U+1FF2-1FF5,U+1FF6-1FFF,U+2000-2010,U+2012-2023,U+2026,U+202A-2031,U+2032-2035,U+2039-203B,U+203C,U+203E,U+2044,U+205E,U+206A-2070,U+2074-2076,U+2077-2079,U+207F,U+2090-2095,U+20A0-20B6,U+20F0,U+2105,U+2113,U+2116-2118,U+2122,U+2126,U+212E,U+214D-214F,U+2153-2155,U+215B-215F,U+2184,U+2190-2196,U+21A8,U+2202,U+2206,U+220F,U+2211-2213,U+2215,U+2219-221B,U+221E-2220,U+2229,U+222B,U+2248,U+2260-2262,U+2264-2266,U+2302,U+2310,U+2320-2322,U+2500,U+2502,U+250C,U+2510,U+2514,U+2518,U+251C,U+2524,U+252C,U+2534,U+253C,U+2550-256D,U+2580,U+2584,U+2588,U+258C,U+2590-2594,U+25A0-25A2,U+25AA-25AD,U+25B2,U+25BA,U+25BC,U+25C4,U+25CA-25CD,U+25CF,U+25D8-25DA,U+25E6,U+263A-263D,U+2640,U+2642,U+2660,U+2663,U+2665-2667,U+266A-266C,U+266F,U+2C60-2C6E,U+2C71-2C78,U+2E17,U+A717-A722,U+A788-A78D,U+F001-F003,U+F005,U+F00A-F00F,U+FB01-FB03,U+FB1D-FB37,U+FB38-FB3D,U+FB3E,U+FB40-FB42,U+FB43-FB45,U+FB46-FB50,U+FE20-FE24,U+FEFF,U+FFFC",
"ext": ["ttf", "ttf.lzma", "ttf.brotli"],
"family": "Arimo",
"id": "arimo1",
"variants": {
"500": "Arimo-Regular",
"500I": "Arimo-Italic",
"700": "Arimo-Bold",
"700I": "Arimo-BoldItalic"
}
} }
], ],
"matchPatterns": [ "matchPatterns": [
@ -21,6 +45,16 @@
"match": "M+1p*", "match": "M+1p*",
"target": "m+1p1", "target": "m+1p1",
"type": "Wildcard" "type": "Wildcard"
},
{
"match": "Liberation*Serif*",
"target": "liberationserif1",
"type": "Wildcard"
},
{
"match": "Arimo*",
"target": "arimo1",
"type": "Wildcard"
} }
], ],
"versionMajor": 2, "versionMajor": 2,

@ -1 +1 @@
Subproject commit e88929f0d48e54298d85ef05ea467980be11524b Subproject commit 074f88e66abc765b0ef8654eb56a552d88e1bbda

View File

@ -10,6 +10,7 @@ export interface IComponentMapping extends IListable {
numberOfLines: number; numberOfLines: number;
encoding: string; encoding: string;
delimiter: string; delimiter: string;
quoteChar: string;
} }
export class ComponentMapping implements IComponentMapping, IListable { export class ComponentMapping implements IComponentMapping, IListable {
@ -22,6 +23,7 @@ export class ComponentMapping implements IComponentMapping, IListable {
readonly numberOfLines: number; readonly numberOfLines: number;
readonly encoding: string; readonly encoding: string;
readonly delimiter: string; readonly delimiter: string;
readonly quoteChar: string;
constructor(componentMapping: IComponentMapping) { constructor(componentMapping: IComponentMapping) {
this.id = componentMapping.id; this.id = componentMapping.id;
@ -33,6 +35,7 @@ export class ComponentMapping implements IComponentMapping, IListable {
this.numberOfLines = componentMapping.numberOfLines; this.numberOfLines = componentMapping.numberOfLines;
this.encoding = componentMapping.encoding; this.encoding = componentMapping.encoding;
this.delimiter = componentMapping.delimiter; this.delimiter = componentMapping.delimiter;
this.quoteChar = componentMapping.quoteChar;
} }
get searchKey(): string { get searchKey(): string {

View File

@ -7,8 +7,6 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
readonly addToDictionaryAction: boolean; readonly addToDictionaryAction: boolean;
readonly caseInsensitive: boolean; readonly caseInsensitive: boolean;
readonly description: string; readonly description: string;
readonly aiCreationEnabled: boolean;
readonly aiDescription: string;
readonly dossierTemplateId?: string; readonly dossierTemplateId?: string;
readonly hexColor?: string; readonly hexColor?: string;
readonly recommendationHexColor?: string; readonly recommendationHexColor?: string;
@ -35,8 +33,6 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
this.addToDictionaryAction = !!entity.addToDictionaryAction; this.addToDictionaryAction = !!entity.addToDictionaryAction;
this.caseInsensitive = !!entity.caseInsensitive; this.caseInsensitive = !!entity.caseInsensitive;
this.description = entity.description ?? ''; this.description = entity.description ?? '';
this.aiCreationEnabled = !!entity.aiCreationEnabled;
this.aiDescription = entity.aiDescription ?? '';
this.dossierTemplateId = entity.dossierTemplateId; this.dossierTemplateId = entity.dossierTemplateId;
this.entries = entity.entries ?? []; this.entries = entity.entries ?? [];
this.falsePositiveEntries = entity.falsePositiveEntries ?? []; this.falsePositiveEntries = entity.falsePositiveEntries ?? [];

View File

@ -16,14 +16,6 @@ export interface IDictionary {
* The description of the dictionary type * The description of the dictionary type
*/ */
readonly description?: string; readonly description?: string;
/**
* True if the entries in this type should also be created via AI, default is false.
*/
readonly aiCreationEnabled?: boolean;
/**
* The AI description of the dictionary type
*/
readonly aiDescription?: string;
/** /**
* The DossierTemplate Id for this type * The DossierTemplate Id for this type
*/ */

View File

@ -1,15 +1,18 @@
import { IListable } from '@iqser/common-ui'; import { IListable } from '@iqser/common-ui';
import { ILegalBasis } from './legal-basis'; import { ILegalBasis } from './legal-basis';
import { toSnakeCase } from '@utils/functions';
export class Justification implements ILegalBasis, IListable { export class Justification implements ILegalBasis, IListable {
readonly description?: string; readonly description?: string;
readonly name: string; readonly name: string;
readonly reason?: string; readonly reason?: string;
readonly technicalName?: string;
constructor(justification: ILegalBasis) { constructor(justification: ILegalBasis) {
this.description = justification.description; this.description = justification.description;
this.name = justification.name; this.name = justification.name;
this.reason = justification.reason; this.reason = justification.reason;
this.technicalName = justification.technicalName;
} }
get id(): string { get id(): string {
@ -19,4 +22,13 @@ export class Justification implements ILegalBasis, IListable {
get searchKey(): string { get searchKey(): string {
return this.name; return this.name;
} }
static toTechnicalName(value: string) {
const baseTechnicalName = toSnakeCase(value.trim());
let technicalName = baseTechnicalName.replaceAll(/[^A-Za-z0-9_-]/g, '');
if (!technicalName.length && baseTechnicalName.length) {
technicalName = '_';
}
return technicalName;
}
} }

View File

@ -2,4 +2,5 @@ export interface ILegalBasis {
readonly name: string; readonly name: string;
readonly description?: string; readonly description?: string;
readonly reason?: string; readonly reason?: string;
readonly technicalName?: string;
} }

View File

@ -14,8 +14,8 @@ export interface IBulkRecategorizationRequest {
readonly type: string; readonly type: string;
readonly legalBasis: string; readonly legalBasis: string;
readonly section: string; readonly section: string;
readonly originTypes: string[]; readonly originTypes?: string[];
readonly originLegalBases: string[]; readonly originLegalBases?: string[];
readonly rectangle: boolean; readonly rectangle: boolean;
readonly position?: IEntityLogEntryPosition; readonly position?: IEntityLogEntryPosition;
readonly pageNumbers?: number[]; readonly pageNumbers?: number[];

View File

@ -14,7 +14,7 @@
"installMode": "lazy", "installMode": "lazy",
"updateMode": "prefetch", "updateMode": "prefetch",
"resources": { "resources": {
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani|lzma)"]
} }
} }
], ],