Compare commits

...

49 Commits

Author SHA1 Message Date
Nicoleta Panaghiu
1b7ad118d4 RED-10331: fixed missing remove-button from pdf-viewer on multi-select. 2024-10-30 16:09:28 +02:00
Nicoleta Panaghiu
c977f95f1e RED-9663: removed UNASSIGNED option from the status filter. 2024-10-30 16:03:19 +02:00
Valentin Mihai
1da7b41692 RED-10198 - missleading error message - creating user with existing email 2024-10-30 14:36:32 +02:00
Valentin Mihai
6100c59a87 RED-10256 - Bulk-local: Changes should not be filtered + Remove for image-based redactions and hints 2024-10-30 14:24:16 +02:00
Valentin Mihai
860146cd1a RED-10048 - Improve display of error messages for csv files 2024-10-30 13:38:05 +02:00
Nicoleta Panaghiu
0b8a0f08d2 RED-10218: fixed fileNameColumn width overlap. 2024-10-30 12:30:58 +02:00
Nicoleta Panaghiu
e167e94171 RED-10326: fix display of documents in trash on retention capacity chart. 2024-10-29 16:41:00 +02:00
Nicoleta Panaghiu
315bd225af RED-10320: removed general configuration header subtitle. 2024-10-29 15:39:35 +02:00
Nicoleta Panaghiu
6a9f440b8a RED-10275: differentiate available types by applyToAll flag state. 2024-10-29 15:22:48 +02:00
Nicoleta Panaghiu
0ba173b4b9 RED-3800: localazy manual sync. 2024-10-29 14:21:25 +02:00
Nicoleta Panaghiu
6d74ab60cc RED-9733: translated initials in workload icons. 2024-10-29 13:11:04 +02:00
Nicoleta Panaghiu
1f36b0be04 RED-10244: fix dossier with files not having template field disabled. 2024-10-28 18:22:02 +02:00
Valentin Mihai
40d6718e8e RED-7340 - Rectangle redactions: Use bulk-local redactions + New dialog design 2024-10-28 17:58:03 +02:00
Nicoleta Panaghiu
cc22fdf538 RED-3800: localazy manual sync. 2024-10-28 17:45:49 +02:00
Nicoleta Panaghiu
4c6bb84567 RED-10262: fixed cancel resize. 2024-10-28 17:26:50 +02:00
Valentin Mihai
41a3dce600 RED-10256 - Bulk-local: Changes should not be filtered + Remove for image-based redactions 2024-10-28 17:24:32 +02:00
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
74 changed files with 1420 additions and 1106 deletions

View File

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

View File

@ -23,7 +23,7 @@
<div class="mt-44">
<redaction-donut-chart
[config]="chartConfig"
[config]="chartConfig()"
[radius]="63"
[strokeWidth]="15"
[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 { CircleButtonComponent } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
@ -12,6 +12,6 @@ import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.
imports: [CircleButtonComponent, TranslateModule, DonutChartComponent],
})
export class UsersStatsComponent {
@Output() toggleCollapse = new EventEmitter();
@Input() chartConfig: DonutChartConfig[];
readonly chartConfig = input.required<DonutChartConfig[]>();
readonly toggleCollapse = output();
}

View File

@ -101,11 +101,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
this.closeDialog.emit(true);
})
.catch(error => {
if (error.status === HttpStatusCode.Conflict) {
this._toaster.error(_('add-edit-user.error.email-already-used'));
} else {
this._toaster.error(_('add-edit-user.error.generic'));
}
this._toaster.error(null, { error });
this._loadingService.stop();
});
} else {

View File

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

View File

@ -42,6 +42,15 @@
type="text"
/>
</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">
<label translate="add-edit-component-mapping.form.encoding-type"></label>

View File

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

View File

@ -1,6 +1,5 @@
<div class="dialog-header">
<div class="heading-l" translate="general-config-screen.general.title"></div>
<div translate="general-config-screen.general.subtitle"></div>
</div>
<form (submit)="save()" *ngIf="form" [formGroup]="form">
<div class="dialog-content">

View File

@ -17,8 +17,17 @@
type="text"
/>
</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>
<input
[placeholder]="'add-edit-justification.form.reason-placeholder' | translate"
@ -28,7 +37,7 @@
/>
</div>
<div class="iqser-input-group required w-400">
<div class="iqser-input-group w-400">
<label translate="add-edit-justification.form.description"></label>
<textarea
[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 { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Justification } from '@red/domain';
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 { TranslateModule } from '@ngx-translate/core';
import { formControlToSignal } from '@utils/functions';
import { toSignal } from '@angular/core/rxjs-interop';
interface DialogData {
justification?: Justification;
@ -16,9 +18,29 @@ interface DialogData {
templateUrl: './add-edit-justification-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent],
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent, HasScrollbarDirective],
})
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(
private readonly _justificationService: JustificationsService,
protected readonly _dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
@ -26,7 +48,6 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
) {
super(_dialogRef, !!data.justification);
this.form = this._getForm();
this.initialFormValue = this.form.getRawValue();
}
@ -34,7 +55,8 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
const dossierTemplateId = this.data.dossierTemplateId;
this._loadingService.start();
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));
this._dialogRef.close(true);
} catch (error) {
@ -43,11 +65,12 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
this._loadingService.stop();
}
private _getForm(): UntypedFormGroup {
#getForm(): UntypedFormGroup {
return this._formBuilder.group({
name: [{ value: this.data.justification?.name, disabled: !!this.data.justification }, Validators.required],
reason: [this.data.justification?.reason, Validators.required],
description: [this.data.justification?.description, Validators.required],
reason: [this.data.justification?.reason],
description: [this.data.justification?.description],
technicalName: [this.data.justification?.technicalName ?? null],
});
}
}

View File

@ -78,9 +78,9 @@ export class LicenseRetentionCapacityComponent {
return [
{
data: monthlyData.flatMap(d => d.activeFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.retention-capacity-usage.active-documents'),
...getLineConfig(ChartGreen, false, 'origin'),
data: monthlyData.flatMap(d => d.trashFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.retention-capacity-usage.trash-documents'),
...getLineConfig(ChartRed, false, 'origin'),
stack: 'storage',
},
{
@ -90,9 +90,9 @@ export class LicenseRetentionCapacityComponent {
stack: 'storage',
},
{
data: monthlyData.flatMap(d => d.trashFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.retention-capacity-usage.trash-documents'),
...getLineConfig(ChartRed, false, '-1'),
data: monthlyData.flatMap(d => d.activeFilesUploadedBytes),
label: this._translateService.instant('license-info-screen.retention-capacity-usage.active-documents'),
...getLineConfig(ChartGreen, false, 'origin'),
stack: 'storage',
},
{

View File

@ -9,7 +9,7 @@
<mat-icon *ngIf="!fileAttribute.editable" [matTooltip]="'readonly' | translate" svgIcon="red:read-only"></mat-icon>
<span
*ngIf="!isDate; else date"
[style.max-width]="attributeValueWidth"
[style.max-width]="attributeValueWidth()"
[matTooltip]="fileAttributeValue"
[ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }"
>
@ -56,13 +56,12 @@
class="edit-input"
iqserStopPropagation
>
<form [formGroup]="form">
<form [formGroup]="form" [style.width]="inputFormWidth()">
<iqser-dynamic-input
(closedDatepicker)="closedDatepicker = $event"
(keyup.enter)="form.valid && save()"
(keydown.escape)="close()"
[style.max-width]="editFieldWidth"
[style.min-width]="editFieldWidth"
[style.width]="inputFieldWidth()"
[formControlName]="fileAttribute.id"
[id]="fileAttribute.id"
[ngClass]="{ 'workflow-input': mode === 'workflow' || fileNameColumn, 'file-name-input': fileNameColumn }"

View File

@ -97,7 +97,7 @@
iqser-circle-button {
margin-left: 15px;
@media screen and (max-width: 1395px) {
@media screen and (max-width: 1745px) {
margin-left: 6px;
}
}

View File

@ -1,5 +1,5 @@
import { AsyncPipe, NgClass, NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, computed, effect, HostListener, Input, OnDestroy } from '@angular/core';
import { Component, computed, effect, HostListener, input, Input, OnDestroy } from '@angular/core';
import { AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule, UntypedFormGroup, ValidatorFn } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
@ -14,7 +14,7 @@ import {
StopPropagationDirective,
Toaster,
} from '@iqser/common-ui';
import { Debounce, log } from '@iqser/common-ui/lib/utils';
import { Debounce } from '@iqser/common-ui/lib/utils';
import { TranslateModule } from '@ngx-translate/core';
import { Dossier, File, FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
@ -49,7 +49,6 @@ import { ConfigService } from '../../config.service';
})
export class FileAttributeComponent extends BaseFormComponent implements OnDestroy {
readonly #subscriptions = new Subscription();
#widthFactor = window.innerWidth >= 1800 ? 0.85 : 0.7;
isInEditMode = false;
closedDatepicker = true;
@Input({ required: true }) fileAttribute!: IFileAttributeConfig;
@ -66,7 +65,10 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
@Input({ required: true }) dossier!: Dossier;
@Input() fileNameColumn = false;
readonlyAttrs: string[] = [];
@Input() width?: number;
readonly width = input<number>();
readonly inputFormWidth = computed(() => (this.width() ? this.width() + 'px' : 'unset'));
readonly inputFieldWidth = computed(() => (this.width() ? this.width() - 50 + 'px' : 'unset'));
readonly attributeValueWidth = computed(() => (this.width() ? `${this.width() * 0.9}px` : 'unset'));
constructor(
router: Router,
@ -99,14 +101,6 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
);
}
get editFieldWidth(): string {
return this.width ? `${this.width * this.#widthFactor}px` : 'unset';
}
get attributeValueWidth(): string {
return this.width ? `${this.width * 0.9}px` : 'unset';
}
get isDate(): boolean {
return this.fileAttribute.type === FileAttributeConfigTypes.DATE;
}
@ -123,15 +117,6 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
return this.file.fileAttributes.attributeIdToValue[this.fileAttribute.id];
}
@HostListener('window:resize')
onResize() {
if (window.innerWidth >= 1800) {
this.#widthFactor = 0.85;
} else {
this.#widthFactor = 0.7;
}
}
@Debounce(60)
@HostListener('document:click', ['$event'])
clickOutside($event: MouseEvent) {
@ -147,6 +132,7 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
handleClick($event: MouseEvent) {
$event.stopPropagation();
$event.preventDefault();
}
ngOnDestroy() {
@ -154,12 +140,14 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
}
handleFieldClick($event: MouseEvent) {
$event.preventDefault();
if (!this.fileNameColumn) {
this.editFileAttribute($event);
}
}
editFileAttribute($event: MouseEvent) {
$event.preventDefault();
if (
!this.file.isInitialProcessing &&
this.permissionsService.canEditFileAttributes(this.file, this.dossier) &&

View File

@ -2,18 +2,33 @@
<redaction-annotation-icon
*ngIf="file.analysisRequired"
[color]="analysisColor$ | async"
label="A"
[label]="(workloadTranslations['analysis'] | translate)[0]"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="updated"
[color]="updatedColor$ | async"
[label]="(workloadTranslations['updated'] | translate)[0]"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="updated" [color]="updatedColor$ | async" label="U" type="square"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hasRedactions"
[color]="redactionColor$ | async"
[label]="'redaction-abbreviation' | translate"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hasImages" [color]="imageColor$ | async" label="I" type="square"></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hintsOnly" [color]="hintColor$ | async" label="H" type="circle"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hasImages"
[color]="imageColor$ | async"
[label]="(workloadTranslations['image'] | translate)[0]"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hintsOnly"
[color]="hintColor$ | async"
[label]="(workloadTranslations['hint'] | translate)[0]"
type="circle"
></redaction-annotation-icon>
<mat-icon *ngIf="file.hasAnnotationComments" svgIcon="red:comment"></mat-icon>
<ng-container *ngIf="noWorkloadItems"> -</ng-container>
</div>

View File

@ -10,6 +10,7 @@ import { AnnotationIconComponent } from '@shared/components/annotation-icon/anno
import { AsyncPipe, NgIf } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatIcon } from '@angular/material/icon';
import { workloadTranslations } from '@translations/workload-translations';
@Component({
selector: 'redaction-file-workload',
@ -27,6 +28,7 @@ export class FileWorkloadComponent implements OnInit {
analysisColor$: Observable<string>;
hintColor$: Observable<string>;
redactionColor$: Observable<string>;
readonly workloadTranslations = workloadTranslations;
constructor(
private readonly _userService: UserService,

View File

@ -588,4 +588,16 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
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

@ -6,7 +6,7 @@
class="dialog-header heading-l"
></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">
<redaction-selected-annotations-table
[columns]="tableColumns"
@ -90,7 +90,7 @@
</div>
</ng-container>
<div *ngIf="allRectangles" class="iqser-input-group w-400">
<div *ngIf="allRectangles" class="iqser-input-group w-450">
<label [translate]="'change-legal-basis-dialog.content.classification'"></label>
<input
[placeholder]="'edit-redaction.dialog.content.unchanged' | translate"
@ -107,7 +107,7 @@
formControlName="comment"
iqserHasScrollbar
name="comment"
rows="4"
rows="3"
type="text"
></textarea>
</div>

View File

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

View File

@ -33,11 +33,12 @@ import {
LegalBasisOption,
RectangleRedactOption,
RectangleRedactOptions,
RedactOrHintOptions,
} from '../../utils/dialog-types';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
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 {
type: string;
@ -88,7 +89,7 @@ export class EditRedactionDialogComponent
{ label: redaction.value, bold: true },
{ label: redaction.typeLabel },
]);
options = this.allRectangles ? getRectangleRedactOptions('edit') : getEditRedactionOptions();
options = this.allRectangles ? getRectangleRedactOptions('edit') : getEditRedactionOptions(this.isHint);
legalOptions: LegalBasisOption[] = [];
dictionaries: Dictionary[] = [];
typeSelectOptions: TypeSelectOptions[] = [];
@ -103,6 +104,14 @@ export class EditRedactionDialogComponent
private readonly _dictionaryService: DictionaryService,
) {
super();
if (this.allRectangles) {
prefillPageRange(
this.data.annotations[0],
this.data.allFileAnnotations,
this.options as DetailsRadioOption<RectangleRedactOption>[],
);
}
}
get displayedDictionaryLabel() {
@ -206,7 +215,7 @@ export class EditRedactionDialogComponent
const initialReason: LegalBasisOption = this.initialFormValue.reason;
const initialLegalBasis = initialReason?.legalBasis ?? '';
const pageNumbers = parseSelectedPageNumbers(
this.form.get('option').value.additionalInput?.value,
this.form.get('option').value?.additionalInput?.value,
this.data.file,
this.data.annotations[0],
);
@ -218,7 +227,7 @@ export class EditRedactionDialogComponent
comment: value.comment,
type: value.type,
value: this.allRectangles ? value.value : null,
option: value.option.value,
option: value.option?.value ?? RedactOrHintOptions.ONLY_HERE,
position,
pageNumbers,
});
@ -255,7 +264,10 @@ export class EditRedactionDialogComponent
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),
});
}

View File

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

View File

@ -2,7 +2,7 @@
<form (submit)="save()" [formGroup]="form">
<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
[options]="options"
(extraOptionChanged)="extraOptionChanged($event)"
@ -43,7 +43,7 @@
<div class="iqser-input-group w-450">
<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>

View File

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

View File

@ -130,7 +130,7 @@ export class RectangleAnnotationDialog
reason: [null, Validators.required],
comment: [null],
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

@ -162,7 +162,11 @@ export class RedactRecommendationDialogComponent
}
#setDictionaries() {
this.dictionaries = this._dictionaryService.getRedactTextDictionaries(this.#dossier.dossierId, !this.#applyToAllDossiers);
this.dictionaries = this._dictionaryService.getRedactTextDictionaries(
this.#dossier.dossierId,
!this.#applyToAllDossiers,
this.#dossier.dossierTemplateId,
);
}
#selectReason() {

View File

@ -219,7 +219,11 @@ export class RedactTextDialogComponent
}
#setDictionaries() {
this.dictionaries = this._dictionaryService.getRedactTextDictionaries(this.#dossier.dossierId, !this.#applyToAllDossiers);
this.dictionaries = this._dictionaryService.getRedactTextDictionaries(
this.#dossier.dossierId,
!this.#applyToAllDossiers,
this.#dossier.dossierTemplateId,
);
}
#getForm(): FormGroup {

View File

@ -36,7 +36,7 @@ import {
} from '../../utils/dialog-types';
import { isJustOne } from '@common-ui/utils';
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({
templateUrl: './remove-redaction-dialog.component.html',
@ -112,7 +112,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
readonly redactedTexts = this.data.redactions.map(annotation => annotation.value);
form: UntypedFormGroup = this._formBuilder.group({
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)));
@ -140,6 +140,14 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
private readonly _userPreferences: UserPreferenceService,
) {
super();
if (this.#allRectangles) {
prefillPageRange(
this.data.redactions[0],
this.data.allFileRedactions,
this.options as DetailsRadioOption<RectangleRedactOption>[],
);
}
}
get hasFalsePositiveOption() {
@ -177,12 +185,9 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
}
save(): void {
const optionValue = this.form.controls.option.value.value;
const pageNumbers = parseSelectedPageNumbers(
this.form.get('option').value.additionalInput?.value,
this.data.file,
this.data.redactions[0],
);
const optionValue = this.form.controls.option?.value?.value;
const optionInputValue = this.form.controls.option?.value?.additionalInput?.value;
const pageNumbers = parseSelectedPageNumbers(optionInputValue, this.data.file, this.data.redactions[0]);
const position = parseRectanglePosition(this.data.redactions[0]);
this.close({

View File

@ -77,7 +77,7 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
save() {
const formValue = this.form.getRawValue();
const updateDictionary = formValue.option.value === ResizeOptions.IN_DOSSIER;
const updateDictionary = formValue.option?.value === ResizeOptions.IN_DOSSIER;
super.close({
comment: formValue.comment,

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)
super.ngOnDetach();
this.pdf.instance.UI.hotkeys.off('esc');
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
this._changeRef.markForCheck();
}
ngOnDestroy() {
this.pdf.instance.UI.hotkeys.off('esc');
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
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) {
if (!this.state.file().canBeOpened) {
return this.#navigateToDossier();
@ -307,6 +332,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.#restoreOldFilters();
this.pdf.instance.UI.hotkeys.on('esc', this.handleEscInsideViewer);
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) {

View File

@ -7,6 +7,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core } from '@pdftron/webviewer';
import {
DictionaryEntryTypes,
DownloadFileTypes,
EarmarkOperation,
type IBulkLocalRemoveRequest,
IBulkRecategorizationRequest,
@ -51,6 +52,7 @@ import { FilePreviewStateService } from './file-preview-state.service';
import { ManualRedactionService } from './manual-redaction.service';
import { SkippedService } from './skipped.service';
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()
export class AnnotationActionsService {
@ -81,41 +83,49 @@ export class AnnotationActionsService {
this._dialogService.openDialog('highlightAction', data);
}
forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) {
async forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) {
const { dossierId, fileId } = this._state;
const data = { dossier: this._state.dossier(), annotations, hint };
this._dialogService.openDialog('forceAnnotation', data, (request: ILegalBasisChangeRequest) => {
let obs$: Observable<unknown>;
if (request.option === ForceAnnotationOptions.ONLY_HERE) {
obs$ = this._manualRedactionService.bulkForce(
annotations.map(a => ({ ...request, annotationId: a.id })),
dossierId,
fileId,
annotations[0].isIgnoredHint,
);
} else {
const addAnnotationRequest = annotations.map(a => ({
comment: { text: request.comment },
legalBasis: request.legalBasis,
reason: request.reason,
positions: a.positions,
type: a.type,
value: a.value,
}));
obs$ = this._manualRedactionService.addAnnotation(addAnnotationRequest, dossierId, fileId, {
hint,
bulkLocal: true,
});
}
this.#processObsAndEmit(obs$).then();
});
const image = annotations.every(a => a.isImage);
const data = { dossier: this._state.dossier(), annotations, hint, image };
const dialogRef = this._iqserDialog.openDefault(ForceAnnotationDialogComponent, { data });
const result = await dialogRef.result();
if (!result) {
return;
}
let obs$: Observable<unknown>;
if (result.option === ForceAnnotationOptions.ONLY_HERE || hint || image) {
obs$ = this._manualRedactionService.bulkForce(
annotations.map(a => ({ ...result, annotationId: a.id })),
dossierId,
fileId,
annotations[0].isIgnoredHint,
);
} else {
const addAnnotationRequest = annotations.map(a => ({
comment: result.comment,
legalBasis: result.legalBasis,
reason: result.reason,
positions: a.positions,
type: a.type,
value: a.value,
}));
obs$ = this._manualRedactionService.addAnnotation(addAnnotationRequest, dossierId, fileId, {
hint,
bulkLocal: true,
});
}
this.#processObsAndEmit(obs$).then();
}
async editRedaction(annotations: AnnotationWrapper[]) {
const { dossierId, file } = this._state;
const includeUnprocessed = annotations.every(annotation => this.#includeUnprocessed(annotation, true));
const allFileAnnotations = this._fileDataService.annotations();
const data = {
annotations,
allFileAnnotations,
dossierId,
file: file(),
};
@ -145,15 +155,11 @@ export class AnnotationActionsService {
return body;
});
} else {
const originTypes = annotations.map(a => a.type);
const originLegalBases = annotations.map(a => a.legalBasis);
recategorizeBody = {
value: annotations[0].value,
type: result.type,
legalBasis: result.legalBasis,
section: result.section,
originTypes,
originLegalBases,
rectangle: annotations[0].AREA,
pageNumbers: result.pageNumbers,
position: result.position,
@ -168,7 +174,6 @@ export class AnnotationActionsService {
dossierId,
file().id,
this.#getChangedFields(annotations, result),
includeUnprocessed,
result.option === RedactOrHintOptions.IN_DOCUMENT || !!result.pageNumbers.length,
)
.pipe(log()),
@ -184,9 +189,11 @@ export class AnnotationActionsService {
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
const isApprover = this._permissionsService.isApprover(this._state.dossier());
const { file } = this._state;
const allFileRedactions = this._fileDataService.annotations();
const data = {
redactions,
allFileRedactions,
file: file(),
dossier: this._state.dossier(),
falsePositiveContext: redactions.map(r => this.#getFalsePositiveText(r)),
@ -202,8 +209,8 @@ export class AnnotationActionsService {
}
if (
result.option.value === RemoveRedactionOptions.FALSE_POSITIVE ||
result.option.value === RemoveRedactionOptions.DO_NOT_RECOMMEND
result.option?.value === RemoveRedactionOptions.FALSE_POSITIVE ||
result.option?.value === RemoveRedactionOptions.DO_NOT_RECOMMEND
) {
this.#setAsFalsePositive(redactions, result);
} else {
@ -265,7 +272,6 @@ export class AnnotationActionsService {
async acceptResize(annotation: AnnotationWrapper, permissions: AnnotationPermissions): Promise<void> {
const textAndPositions = await this.#extractTextAndPositions(annotation.id);
const includeUnprocessed = this.#includeUnprocessed(annotation);
if (annotation.isRecommendation) {
const recommendation = {
...annotation,
@ -316,16 +322,16 @@ export class AnnotationActionsService {
await this.cancelResize(annotation);
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);
}
async cancelResize(annotationWrapper: AnnotationWrapper) {
this._annotationManager.resizingAnnotationId = undefined;
this._annotationManager.annotationHasBeenResized = false;
this._annotationManager.deselect();
this._annotationManager.delete(annotationWrapper);
await this._annotationDrawService.draw([annotationWrapper], this._skippedService.hideSkipped(), this._state.dossierTemplateId);
this._annotationManager.deselect();
}
#generateRectangle(annotationWrapper: AnnotationWrapper) {
@ -472,8 +478,7 @@ export class AnnotationActionsService {
}
#removeRedaction(redactions: AnnotationWrapper[], dialogResult: RemoveRedactionResult) {
const removeFromDictionary = dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER;
const includeUnprocessed = redactions.every(redaction => this.#includeUnprocessed(redaction, true));
const removeFromDictionary = dialogResult.option?.value === RemoveRedactionOptions.IN_DOSSIER;
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
const isHint = redactions.every(r => r.isHint);
@ -501,7 +506,6 @@ export class AnnotationActionsService {
fileId,
removeFromDictionary,
isHint,
includeUnprocessed,
dialogResult.bulkLocal,
),
),
@ -517,7 +521,6 @@ export class AnnotationActionsService {
fileId,
removeFromDictionary,
isHint,
includeUnprocessed,
dialogResult.bulkLocal || !!dialogResult.pageNumbers.length,
),
).then();
@ -576,20 +579,6 @@ export class AnnotationActionsService {
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(
redactions: AnnotationWrapper[],
dialogResult: RemoveRedactionResult,
@ -599,8 +588,6 @@ export class AnnotationActionsService {
return {
value: redaction.value,
rectangle: redaction.value === NON_READABLE_CONTENT,
originTypes: [redaction.entry.type],
originLegalBases: [redaction.legalBasis],
pageNumbers: dialogResult.pageNumbers,
position: dialogResult.position,
comment: dialogResult.comment,
@ -611,8 +598,8 @@ export class AnnotationActionsService {
annotationId: redaction.id,
value: redaction.value,
comment: dialogResult.comment,
removeFromDictionary: dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER,
removeFromAllDossiers: !!dialogResult.option.additionalCheck?.checked || !!dialogResult.applyToAllDossiers,
removeFromDictionary: dialogResult.option?.value === RemoveRedactionOptions.IN_DOSSIER,
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 { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-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';
type DialogType = 'confirm' | 'documentInfo' | 'changeLegalBasis' | 'forceAnnotation' | 'highlightAction';
type DialogType = 'confirm' | 'documentInfo' | 'changeLegalBasis' | 'highlightAction';
@Injectable()
export class FilePreviewDialogService extends DialogService<DialogType> {
@ -22,9 +21,6 @@ export class FilePreviewDialogService extends DialogService<DialogType> {
changeLegalBasis: {
component: ChangeLegalBasisDialogComponent,
},
forceAnnotation: {
component: ForceAnnotationDialogComponent,
},
highlightAction: {
component: HighlightActionDialogComponent,
},

View File

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

View File

@ -26,7 +26,7 @@ export class PdfAnnotationActionsService {
get(annotations: AnnotationWrapper[], annotationChangesAllowed: boolean): IHeaderElement[] {
const availableActions: IHeaderElement[] = [];
const permissions = this.#getAnnotationsPermissions(annotations);
const sameType = annotations.every(a => a.type === annotations[0].type);
const sameType = annotations.every(a => a.superType === annotations[0].superType);
// you can only resize one annotation at a time
if (permissions.canResizeAnnotation && annotationChangesAllowed) {

View File

@ -30,7 +30,11 @@ const DOCUMENT_ICON = 'iqser:document';
const FOLDER_ICON = 'red:folder';
const REMOVE_FROM_DICT_ICON = 'red:remove-from-dict';
export const getEditRedactionOptions = (): DetailsRadioOption<EditRedactionOption>[] => {
export const getEditRedactionOptions = (hint: boolean): DetailsRadioOption<EditRedactionOption>[] => {
if (hint) {
return [];
}
return [
{
label: editRedactionTranslations.onlyHere.label,
@ -118,6 +122,7 @@ export const getRectangleRedactOptions = (action: 'add' | 'edit' | 'remove' = 'a
description: translations.multiplePages.extraOptionDescription,
placeholder: translations.multiplePages.extraOptionPlaceholder,
value: '',
errorCode: 'invalidRange',
},
},
];
@ -131,22 +136,21 @@ export const getResizeRedactionOptions = (
isApprover: boolean,
canResizeInDictionary: boolean,
): DetailsRadioOption<ResizeRedactionOption>[] => {
if (isRss || !canResizeInDictionary) {
return [];
}
const translations = resizeRedactionTranslations;
const options: DetailsRadioOption<ResizeRedactionOption>[] = [
const dictBasedType = redaction.isModifyDictionary;
return [
{
label: translations.onlyHere.label,
description: translations.onlyHere.description,
icon: PIN_ICON,
value: ResizeOptions.ONLY_HERE,
},
];
if (isRss) {
return options;
}
if (canResizeInDictionary) {
const dictBasedType = redaction.isModifyDictionary;
options.push({
{
label: translations.inDossier.label,
description: translations.inDossier.description,
descriptionParams: { dossierName: dossier.dossierName },
@ -159,9 +163,8 @@ export const getResizeRedactionOptions = (
checked: applyToAllDossiers,
hidden: !isApprover,
},
});
}
return options;
},
];
};
export const getRemoveRedactionOptions = (
@ -172,9 +175,10 @@ export const getRemoveRedactionOptions = (
const translations = isDocumine ? removeAnnotationTranslations : removeRedactionTranslations;
const { permissions, redactions, isApprover, falsePositiveContext } = data;
const isBulk = redactions.length > 1;
const isImage = redactions.reduce((acc, next) => acc && next.isImage, true);
const options: DetailsRadioOption<RemoveRedactionOption>[] = [];
if (permissions.canRemoveOnlyHere) {
if (permissions.canRemoveOnlyHere && !isImage) {
options.push({
label: translations.ONLY_HERE.label,
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
@ -187,15 +191,15 @@ export const getRemoveRedactionOptions = (
value: RemoveRedactionOptions.ONLY_HERE,
});
options.push({
label: removeRedactionTranslations.IN_DOCUMENT.label,
description: removeRedactionTranslations.IN_DOCUMENT.description,
descriptionParams: {
isImage: redactions[0].isImage ? 'image' : redactions[0].typeLabel,
},
icon: DOCUMENT_ICON,
value: RemoveRedactionOptions.IN_DOCUMENT,
});
const isHint = redactions.reduce((acc, next) => acc && next.isHint, true);
if (!isHint) {
options.push({
label: removeRedactionTranslations.IN_DOCUMENT.label,
description: removeRedactionTranslations.IN_DOCUMENT.description,
icon: DOCUMENT_ICON,
value: RemoveRedactionOptions.IN_DOCUMENT,
});
}
}
if (permissions.canRemoveFromDictionary) {
options.push({
@ -264,8 +268,12 @@ export const getRemoveRedactionOptions = (
return options;
};
export const getForceAnnotationOptions = (isDocumine: boolean, isHint: boolean): DetailsRadioOption<ForceAnnotationOption>[] => {
if (isDocumine || isHint) {
export const getForceAnnotationOptions = (
isDocumine: boolean,
isHint: boolean,
isImage: boolean,
): DetailsRadioOption<ForceAnnotationOption>[] => {
if (isDocumine || isHint || isImage) {
return [];
}

View File

@ -64,6 +64,7 @@ export interface RedactTextData {
export interface EditRedactionData {
annotations: AnnotationWrapper[];
allFileAnnotations?: AnnotationWrapper[];
dossierId: string;
file: File;
isApprover?: boolean;
@ -72,6 +73,21 @@ export interface EditRedactionData {
export type AddAnnotationData = 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 {
redaction: IManualRedactionEntry;
dictionary: Dictionary;
@ -135,6 +151,7 @@ export interface RemoveRedactionPermissions {
export interface RemoveRedactionData {
redactions: AnnotationWrapper[];
allFileRedactions?: AnnotationWrapper[];
dossier: Dossier;
file?: File;
falsePositiveContext: string[];

View File

@ -1,7 +1,8 @@
import { Dictionary, File, IAddRedactionRequest, IEntityLogEntryPosition, SuperType } from '@red/domain';
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 { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
export interface EnhanceRequestData {
readonly type: SuperType | null;
@ -79,3 +80,60 @@ export const parseRectanglePosition = (annotation: AnnotationWrapper) => {
pageNumber: position.page,
} 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';
export const validatePageRange = (allowEmpty = false): ValidatorFn => {
export const validatePageRange = (numberOfPages: number, allowEmpty = false): ValidatorFn => {
return (control: AbstractControl): { [key: string]: any } | null => {
const option = control.value;
if (option?.additionalInput) {
const value = option.additionalInput.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;
};

View File

@ -8,7 +8,9 @@
>
<redaction-annotation-icon
[color]="dictionary.hexColor"
[label]="dictionary.hint ? 'H' : 'R'"
[label]="
dictionary.hint ? (workloadTranslations['hint'] | translate)[0] : (workloadTranslations['redaction'] | translate)[0]
"
type="square"
></redaction-annotation-icon>
<div class="details">

View File

@ -20,6 +20,7 @@ import { AnnotationIconComponent } from '@shared/components/annotation-icon/anno
import { MatTooltip } from '@angular/material/tooltip';
import { MatIcon } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
import { workloadTranslations } from '@translations/workload-translations';
@Component({
selector: 'redaction-edit-dossier-dictionary',
@ -47,6 +48,7 @@ export class EditDossierDictionaryComponent implements OnInit {
activeEntryType = DictionaryEntryTypes.ENTRY;
entriesToDisplay: List = [];
readonly entryTypes = DictionaryEntryTypes;
protected readonly workloadTranslations = workloadTranslations;
@ViewChild(DictionaryManagerComponent, { static: false }) private readonly _dictionaryManager: DictionaryManagerComponent;
constructor(

View File

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

View File

@ -1,12 +1,12 @@
import { NgForOf, NgIf } from '@angular/common';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, effect, input, OnInit, signal, untracked } from '@angular/core';
import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogRef } from '@angular/material/dialog';
import { MatFormField, MatSuffix } from '@angular/material/form-field';
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 { Router } from '@angular/router';
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 { TrashService } from '@services/entity-services/trash.service';
import { PermissionsService } from '@services/permissions.service';
import { dateWithoutTime } from '@utils/functions';
import { dateWithoutTime, formControlToSignal } from '@utils/functions';
import dayjs from 'dayjs';
import { firstValueFrom } from 'rxjs';
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
import { type EditDossierDialogComponent } from '../edit-dossier-dialog.component';
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({
selector: 'redaction-edit-dossier-general-info',
@ -59,18 +69,36 @@ import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-doss
MatSuffix,
IconButtonComponent,
NgIf,
MatSelectTrigger,
],
})
export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSectionInterface {
@Input() dossier: Dossier;
readonly iconButtonTypes = IconButtonTypes;
form: UntypedFormGroup;
statusPlaceholder: string;
readonly dossier = input<Dossier>();
hasDueDate: boolean;
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(
readonly permissionsService: PermissionsService,
@ -87,18 +115,40 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {}
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _dossierStatesService: DossierStatesService,
) {
effect(() => {
const shouldBeDisabled = this.#formValue.find(item => item.key === 'dossierTemplateId')?.disabled;
if (
(this.dossierStatusIdControl() !== this.initialFormValue.dossierStatusId && this.dossierStatusIdControl()) ||
shouldBeDisabled
) {
this.form.controls.dossierTemplateId.disable();
} else {
this.form.controls.dossierTemplateId.enable();
}
});
effect(
() => {
this.states.set(this.#statesForDossierTemplate);
this.#onDossierTemplateChange();
},
{ allowSignalWrites: true },
);
}
get changed(): boolean {
for (const key of Object.keys(this.form.getRawValue())) {
if (key === 'dueDate') {
if (this.hasDueDate !== !!this.dossier.dueDate) {
if (this.hasDueDate !== !!this.dossier().dueDate) {
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;
}
} else if (this.dossier[key] !== this.form.get(key).value) {
} else if (this.dossier()[key] !== this.form.get(key).value) {
return true;
}
}
@ -114,40 +164,87 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
return this.hasDueDate && this.form.get('dueDate').value === null;
}
get #statusPlaceholder(): string {
get #statePlaceholder(): string {
return this._translateService.instant(
this.states.length === 1
isJustOne(this.states())
? 'edit-dossier-dialog.general-info.form.dossier-state.no-state-placeholder'
: 'dossier-state.placeholder',
) 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 states = untracked(this.states);
return [
{
key: 'dossierName',
...formFieldWithArchivedCheck(dossier.dossierName),
},
{
key: 'dossierTemplateId',
value: dossier.dossierTemplateId,
disabled: this._dossierStatsService.get(dossier.id).hasFiles || !dossier.isActive,
},
{
key: 'dossierStatusId',
value: dossier.dossierStatusId,
disabled: isJustOne(states) || !dossier.isActive,
},
{
key: 'description',
...formFieldWithArchivedCheck(dossier.description),
},
{
key: 'dueDate',
...formFieldWithArchivedCheck(dossier.dueDate),
},
];
}
ngOnInit() {
this.states = [null, ...this._dossierStatesMapService.get(this.dossier.dossierTemplateId).map(s => s.id)];
this.statusPlaceholder = this.#statusPlaceholder;
this.#filterInvalidDossierTemplates();
this.form = this.#getForm();
if (!this.permissionsService.canEditDossier(this.dossier)) {
if (isJustOne(this._dossierTemplatesService.all)) {
this._loadingService.loadWhile(
firstValueFrom(this._dossierTemplatesService.loadOnlyDossierTemplates()).then(async () => {
await firstValueFrom(this._dossierStatesService.loadAllForAllTemplates());
this.#filterInvalidDossierTemplates();
}),
);
} else {
this.#filterInvalidDossierTemplates();
}
this.#patchFormValue();
if (!this.permissionsService.canEditDossier(this.dossier())) {
this.form.disable();
}
this.hasDueDate = !!this.dossier.dueDate;
this.hasDueDate = !!this.dossier().dueDate;
}
revert() {
this.form.reset({
dossierName: this.dossier.dossierName,
dossierTemplateId: this.dossier.dossierTemplateId,
dossierStatusId: this.dossier.dossierStatusId,
description: this.dossier.description,
dueDate: this.dossier.dueDate,
dossierName: this.dossier().dossierName,
dossierTemplateId: this.dossier().dossierTemplateId,
dossierStatusId: this.dossier().dossierStatusId,
description: this.dossier().description,
dueDate: this.dossier().dueDate,
});
this.hasDueDate = !!this.dossier.dueDate;
this.hasDueDate = !!this.dossier().dueDate;
this.initialFormValue = this.form.getRawValue();
}
async save(): EditDossierSaveResult {
const dueDate = dateWithoutTime(dayjs(this.form.get('dueDate').value));
const dossier = {
...this.dossier,
...this.dossier(),
dossierName: this.form.get('dossierName').value,
description: this.form.get('description').value,
dueDate: dueDate.isValid() ? dueDate.toISOString() : undefined,
@ -156,9 +253,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
} as IDossierRequest;
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]);
}
this.initialFormValue = this.form.getRawValue();
return { success: !!updatedDossier };
}
@ -171,14 +269,14 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
requireInput: true,
denyText: _('confirmation-dialog.delete-dossier.deny-text'),
translateParams: {
dossierName: this.dossier.dossierName,
dossierName: this.dossier().dossierName,
dossiersCount: 1,
},
};
this._dialogService.openDialog('confirm', data, async () => {
this._loadingService.start();
const successful = await this._trashService.deleteDossier(this.dossier);
const successful = await this._trashService.deleteDossier(this.dossier());
if (successful) {
await this.#closeDialogAndRedirectToDossier();
}
@ -194,7 +292,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
confirmationText: _('confirm-archive-dossier.archive'),
denyText: _('confirm-archive-dossier.cancel'),
titleColor: TitleColors.WARN,
translateParams: { ...this.dossier },
translateParams: { ...this.dossier() },
checkboxes: [{ value: false, label: _('confirm-archive-dossier.checkbox.documents') }],
toastMessage: _('confirm-archive-dossier.toast-error'),
};
@ -202,10 +300,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
this._dialogService.openDialog('confirm', data, async result => {
if (result === ConfirmOptions.CONFIRM) {
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'), {
params: {
dossierName: this.dossier.dossierName,
dossierName: this.dossier().dossierName,
},
});
this._editDossierDialogRef.close();
@ -214,15 +312,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() {
this.hasDueDate = !this.hasDueDate;
if (!this.hasDueDate) {
@ -230,46 +319,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() {
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'), {
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() {
const dossier = untracked(this.dossier);
this.dossierTemplates = this._dossierTemplatesService.all.filter(r => {
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) {
if (dossier.dossierTemplateId === r.dossierTemplateId) {
return true;
}
const notYetValid = !!r.validFrom && dayjs(r.validFrom).isAfter(dayjs());
const notValidAnymore = !!r.validTo && dayjs(r.validTo).add(1, 'd').isBefore(dayjs());
this._changeDetectorRef.markForCheck();
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>
</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">
<mat-slide-toggle color="primary" formControlName="hasDictionary">
{{ '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 { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
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 { Dictionary, IDictionary } from '@red/domain';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@ -62,7 +62,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
colors: Color[];
readonly isDocumine = getConfig().IS_DOCUMINE;
readonly isIqserDevMode = isIqserDevMode();
constructor(
private readonly _dictionariesMapService: DictionariesMapService,
@ -159,8 +158,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
skippedHexColor: [this.entity?.skippedHexColor, [Validators.required, Validators.minLength(7)]],
type: [this.entity?.type],
description: [this.entity?.description],
aiCreationEnabled: [this.entity?.aiCreationEnabled],
aiDescription: [this.entity?.aiDescription],
rank: [{ value: this.entity?.rank, disabled: this.#isSystemManaged }, Validators.required],
hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }],
hasDictionary: [
@ -253,8 +250,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
dossierTemplateId: this.dossierTemplateId,
type: this.form.get('type').value,
description: this.form.get('description').value,
aiCreationEnabled: this.form.get('aiCreationEnabled').value,
aiDescription: this.form.get('aiDescription').value,
hint: this.#isHint,
rank: this.form.get('rank').value,
caseInsensitive: !this.form.get('caseSensitive').value,

View File

@ -99,6 +99,8 @@
.error-message {
margin-top: 2px;
color: var(--iqser-primary);
white-space: normal;
word-wrap: break-word;
}
}

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) {
return this._getOne([dossierTemplateId], this._defaultModelPath).pipe(
map(entity => new DossierTemplate(entity)),

View File

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

View File

@ -152,8 +152,9 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
.filter(d => d.model['typeId'] && (d.hasDictionary || d.addToDictionaryAction));
}
getRedactTextDictionaries(dossierId: string, dossierDictionaryOnly: boolean): Dictionary[] {
return this.#extractDossierLevelTypes(dossierId)
getRedactTextDictionaries(dossierId: string, dossierDictionaryOnly: boolean, dossierTemplateId: string): Dictionary[] {
const types = dossierDictionaryOnly ? this.#extractDossierLevelTypes(dossierId) : this.getDictionariesOptions(dossierTemplateId);
return types
.filter(d => d.model['typeId'] && !d.hint && d.addToDictionaryAction && (dossierDictionaryOnly || !d.dossierDictionaryOnly))
.sort((a, b) => a.label.localeCompare(b.label));
}

View File

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

View File

@ -4,7 +4,6 @@ import { ProcessingFileStatus, WorkflowFileStatus } from '@red/domain';
export const workflowFileStatusTranslations: { [key in WorkflowFileStatus]: string } = {
APPROVED: _('file-status.approved'),
NEW: _('file-status.new'),
UNASSIGNED: _('file-status.unassigned'),
UNDER_APPROVAL: _('file-status.under-approval'),
UNDER_REVIEW: _('file-status.under-review'),
};

View File

@ -2,7 +2,7 @@ import { ITrackable } from '@iqser/common-ui';
import type { List } from '@iqser/common-ui/lib/utils';
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dayjs } from 'dayjs';
import { FormControl } from '@angular/forms';
import { AbstractControl } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
export function hexToRgb(hex: string) {
@ -146,6 +146,6 @@ export function urlFileId() {
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 });
}

View File

@ -108,6 +108,8 @@
"file": "Mapping-Datei",
"name": "Mapping-Name",
"name-placeholder": "Mapping-Name",
"quote-char": "Quotation marker",
"quote-char-placeholder": "\"",
"version": "Version"
}
},
@ -135,9 +137,9 @@
},
"add-edit-entity": {
"form": {
"ai-creation-enabled": "AI Erzeugung aktivieren",
"ai-description": "AI Beschreibung",
"ai-description-placeholder": "AI Beschreibung eingeben",
"ai-creation-enabled": "KI-Erstellung aktivieren",
"ai-description": "KI-Beschreibung",
"ai-description-placeholder": "KI-Beschreibung eingeben",
"case-sensitive": "Groß-/Kleinschreibung beachten",
"color": "Farbe {type, select, redaction{Schwärzung} hint{Hinweis} recommendation{Empfehlung} skipped{Ingorierte Schwärzung} ignored{Ignorierter Hinweis} other{}}",
"color-placeholder": "#",
@ -244,7 +246,7 @@
},
"type": "Typ",
"type-placeholder": "Typ auswählen...",
"value": ""
"value": "Wert"
},
"title": "Hinweis hinzufügen"
}
@ -276,6 +278,9 @@
"watermarks": "Wasserzeichen"
},
"analysis-disabled": "",
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotation-actions": {
"accept-recommendation": {
"label": "Empfehlung annehmen"
@ -331,14 +336,14 @@
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
},
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"remove": {
"error": "Entfernen der Schwärzung fehlgeschlagen: {error}",
"success": "Schwärzung wurde entfernt"
},
"remove-hint": {
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
"success": "Hinweis wurde entfernt"
},
"undo": {
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
"success": "Rücksetzung erfolgreich"
@ -351,15 +356,15 @@
"remove-highlights": {
"label": "Ausgewählte Markierungen entfernen"
},
"resize": {
"label": "Größe ändern"
},
"resize-accept": {
"label": "Neue Größe speichern"
},
"resize-cancel": {
"label": "Größenänderung abbrechen"
},
"resize": {
"label": "Größe ändern"
},
"see-references": {
"label": "Referenzen anzeigen"
},
@ -376,7 +381,7 @@
"removed-manual": "Schwärzung/Hinweis wurde entfernt",
"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": {
"dictionary": "Basiert auf Wörterbuch",
"dossier-dictionary": "Basiert auf Dossier-Wörterbuch",
@ -394,9 +399,6 @@
"skipped": "Ignorierte Schwärzung",
"text-highlight": "Markierung"
},
"annotation": {
"pending": "(Analyse steht aus)"
},
"annotations": "Annotationen",
"archived-dossiers-listing": {
"no-data": {
@ -775,7 +777,7 @@
"revert-changes": "Zurücksetzen",
"save-changes": "Änderungen speichern",
"search": "Suche...",
"select-dictionary": "Wählen Sie oben ein Wörterbuch für den Vergleich aus.",
"select-dictionary": "Wählen Sie aus dem Drop-down oben ein Wörterbuch für den Vergleich aus.",
"success": {
"generic": "Wörterbuch wurde aktualisiert"
}
@ -1020,13 +1022,13 @@
"recent": "Neu ({hours} h)",
"unassigned": "Keinem Bearbeiter zugewiesen"
},
"reanalyse": {
"action": "Datei analysieren"
},
"reanalyse-dossier": {
"error": "Einplanung der Dateien für die Reanalyse fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Dateien für Reanalyse vorgesehen."
},
"reanalyse": {
"action": "Datei analysieren"
},
"report-download": "",
"start-auto-analysis": "Auto-Analyse aktivieren",
"stop-auto-analysis": "Auto-Analyse anhalten",
@ -1096,6 +1098,14 @@
"total-documents": "Dokumente",
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-templates-listing": {
"action": {
"clone": "Vorlage klonen",
@ -1130,14 +1140,6 @@
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
}
},
"dossier-templates": {
"label": "Dossier-Vorlagen",
"status": {
"active": "Aktiv",
"inactive": "Inaktiv",
"incomplete": "Unvollständig"
}
},
"dossier-watermark-selector": {
"heading": "Wasserzeichen auf Dokumenten",
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
@ -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/>."
},
"download-type": {
"annotated": "Annotierte PDF",
"annotated": "Annotiertes PDF",
"delta-preview": "Delta-PDF",
"flatten": "Verflachte PDF",
"flatten": "Verflachtes PDF",
"label": "{length} Dokumenten{length, plural, one{typ} other{typen}}",
"optimized-preview": "Optimierte Vorschau-PDF",
"original": "Optimierte PDF",
"optimized-preview": "Optimiertes Vorschau-PDF",
"original": "Optimiertes PDF",
"preview": "Vorschau-PDF",
"redacted": "Geschwärzte PDF",
"redacted-only": "Geschwärzte PDF (nur freigegebene Dateien)"
"redacted": "Geschwärztes PDF",
"redacted-only": "Geschwärztes PDF (nur freigegebene Dateien)"
},
"downloads-list": {
"actions": {
@ -1235,10 +1237,10 @@
"save": "Speichern",
"title": "{label} bearbeiten"
},
"entries-count": "",
"false-positives": "Falsch-Positive",
"false-recommendations": "Falsche Empfehlungen",
"to-redact": "Schwärzungen"
"entries-count": "{count} {count, plural, one{Eintrag} other{Einträge}}",
"false-positives": "Falsch-Positive ({count})",
"false-recommendations": "Falsche Empfehlungen ({count})",
"to-redact": "Einträge ({count})"
},
"general-info": {
"form": {
@ -1250,7 +1252,7 @@
"label": "Dossier-Status",
"no-state-placeholder": "Für dieses Dossier ist noch kein Status festgelegt"
},
"due-date": "Enddatum",
"due-date": "Termin",
"name": {
"label": "Dossier-Name",
"placeholder": "Namen eingeben"
@ -1262,7 +1264,7 @@
"missing-owner": "Bearbeiten des Dossiers nicht möglich: Kein Besitzer zugewiesen.",
"nav-items": {
"choose-download": "Stellen Sie Ihr Download-Paket zusammen:",
"dictionary": "Wörterbücher",
"dictionary": "Dossier-Einträge",
"dossier-attributes": "Dossier-Attribute",
"dossier-dictionary": "Dossier-Einträge",
"dossier-info": "Dossier-Info",
@ -1278,15 +1280,15 @@
"content": {
"options": {
"multiple-pages": {
"description": "",
"extraOptionDescription": "",
"extraOptionLabel": "",
"extraOptionPlaceholder": "",
"label": ""
"description": "Bearbeiten Sie die Schwärzung auf einer Reihe von Seiten",
"extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "Auf mehreren Seiten ändern"
},
"only-this-page": {
"description": "",
"label": ""
"description": "Schwärzung nur an dieser Position in diesem Dokument bearbeiten",
"label": "Nur auf dieser Seite ändern"
}
}
}
@ -1309,7 +1311,7 @@
},
"only-here": {
"description": "Bearbeiten Sie die Schwärzung nur an dieser Stelle im Dokument.",
"label": "Typ nur hier ändern"
"label": "Nur hier ändern"
}
},
"reason": "Grund",
@ -1348,6 +1350,15 @@
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
}
},
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"entity-rules-screen": {
"error": {
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
@ -1361,28 +1372,19 @@
"title": "Entitätsregeln-Editor",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
},
"entity": {
"info": {
"actions": {
"revert": "Zurücksetzen",
"save": "Änderungen speichern"
},
"heading": "Entität bearbeiten"
}
},
"error": {
"deleted-entity": {
"dossier": {
"action": "Zurück zur Übersicht",
"label": "Dieses Dossier wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
},
"file": {
"action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!"
},
"file-dossier": {
"action": "Zurück zur Übersicht",
"label": "Das Dossier dieser Datei wurde gelöscht!"
}
},
"file-preview": {
@ -1400,6 +1402,12 @@
},
"exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr",
"file": "Datei",
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attribute-encoding-types": {
"ascii": "ASCII",
"iso": "ISO-8859-1",
@ -1410,12 +1418,6 @@
"number": "Nummer",
"text": "Freier Text"
},
"file-attribute": {
"update": {
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
}
},
"file-attributes-configurations": {
"cancel": "Abbrechen",
"form": {
@ -1633,15 +1635,6 @@
"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": {
"analysis": "Analyse erforderlich",
"comment": "Kommentare",
@ -1651,6 +1644,15 @@
"redaction": "Schwärzung",
"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": {
"assigned-people": "Bearbeiter",
"documents-status": "Dokumentenstatus",
@ -1695,7 +1697,6 @@
"form": {
"forgot-password": "„Passwort vergessen?“-Link auf Login-Seite anzeigen"
},
"subtitle": "",
"title": "Allgemeine Einstellungen"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) ermöglicht es Ihnen, Ihre E-Mails über die angegebenen Servereinstellungen zu versenden.",
@ -1794,8 +1795,8 @@
},
"import-only-for-pages": "Nur für diese Seiten importieren",
"range": {
"label": "Minus (-) für Spanne und Komma (,) für Aufzählung.",
"placeholder": "Beispiel: 1-20,22,32"
"label": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"placeholder": "z. B. 1-20,22,32"
},
"title": "Dokument mit Schwärzungen importieren"
},
@ -1889,15 +1890,15 @@
"legalBasis": "Rechtsgrundlage",
"options": {
"multiple-pages": {
"description": "",
"extraOptionDescription": "",
"extraOptionLabel": "",
"extraOptionPlaceholder": "",
"label": ""
"description": "Fügen Sie die Schwärzung auf einer Reihe von Seiten hinzu",
"extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "Auf mehreren Seiten anwenden"
},
"only-this-page": {
"description": "",
"label": ""
"description": "Schwärzung nur an dieser Position in diesem Dokument hinzufügen",
"label": "Auf dieser Seite anwenden"
}
},
"reason": "Grund",
@ -1934,6 +1935,13 @@
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
},
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"notifications-screen": {
"category": {
"email-notifications": "E-Mail-Benachrichtigungen",
@ -1947,7 +1955,6 @@
"dossier": "Benachrichtigungen zu Dossiers",
"other": "Andere Benachrichtigungen"
},
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"options": {
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde",
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
@ -1965,6 +1972,7 @@
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
},
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
"schedule": {
"daily": "Tägliche Zusammenfassung",
"instant": "Sofort",
@ -1972,13 +1980,6 @@
},
"title": "Benachrichtigungseinstellungen"
},
"notifications": {
"button-text": "Benachrichtigungen",
"deleted-dossier": "Gelöschtes Dossier",
"label": "Benachrichtigungen",
"mark-all-as-read": "Alle als gelesen markieren",
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
},
"ocr": {
"confirmation-dialog": {
"cancel": "Abbrechen",
@ -2032,19 +2033,19 @@
"left-panel-button": "Panel",
"load-all-annotations": "Alle Annotationen laden",
"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",
"outlines-panel-button": "Gliederung",
"pan-tool-button": "Verschieben",
"rectangle-tool-button": "Bereichsschwärzung",
"rectangle-tool-button": "Bereich schwärzen",
"rotate-left-button": "Seite nach links drehen",
"rotate-right-button": "Seite nach rechts drehen",
"select-tool-button": "Auswählen",
"signature-panel-button": "Unterschriften",
"thumbnails-panel-button": "Miniaturansicht",
"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-tooltips": "Tooltips zu Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}",
"toggle-readable-redactions": "Schwärzungen {active, select, true{wie im finalen Dokument} false{in Preview-Farbe anzeigen} other{}}",
"toggle-tooltips": "Tooltips für Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}",
"zoom-in-button": "Vergrößern",
"zoom-out-button": "Verkleinern"
},
@ -2090,16 +2091,16 @@
"warnings-label": "Dialoge und Meldungen",
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"processing-status": {
"ocr": "OCR",
"pending": "Ausstehend",
"processed": "Verarbeitet",
"processing": "Verarbeitung läuft"
},
"processing": {
"basic": "Verarbeitung läuft",
"ocr": "OCR"
},
"readonly": "Lesemodus",
"readonly-archived": "Lesemodus (archiviert)",
"redact-text": {
@ -2134,7 +2135,7 @@
"type": "Typ",
"type-placeholder": "Typ auswählen...",
"unchanged": "Ungeändert",
"value": ""
"value": "Wert"
},
"title": "Text schwärzen"
}
@ -2184,15 +2185,15 @@
"content": {
"options": {
"multiple-pages": {
"description": "",
"extraOptionDescription": "",
"extraOptionLabel": "",
"extraOptionPlaceholder": "",
"label": ""
"description": "Entfernen Sie die Schwärzung auf einer Reihe von Seiten",
"extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
"extraOptionLabel": "Seiten",
"extraOptionPlaceholder": "z. B. 1-20,22,32",
"label": "Auf mehreren Seiten entfernen"
},
"only-this-page": {
"description": "",
"label": ""
"description": "Schwärzung nur an dieser Stelle in diesem Dokument entfernen",
"label": "Nur auf dieser Seite entfernen"
}
}
}
@ -2217,12 +2218,12 @@
"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-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",
"label": "In diesem Kontext aus Dossier entfernen"
},
"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"
},
"in-dossier": {
@ -2367,6 +2368,12 @@
"red-user-admin": "Benutzeradmin",
"regular": "regulärer Benutzer"
},
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"search-screen": {
"cols": {
"assignee": "Bearbeiter",
@ -2390,12 +2397,6 @@
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
},
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",
"all-dossiers": "Alle Dokumente",
"placeholder": "Dokumente durchsuchen...",
"this-dossier": "In diesem Dossier"
},
"seconds": "Sekunden",
"size": "Größe",
"smtp-auth-config": {
@ -2482,7 +2483,7 @@
"time-to-restore": "Verbleibende Zeit für Wiederherstellung"
},
"table-header": {
"title": "{length} {length, plural, one{gelöschtes Dossier} other{gelöschte Dossiers}}"
"title": "{length} {length, plural, one{gelöschtes Element} other{gelöschte Elemente}}"
}
},
"type": "Typ",
@ -2617,7 +2618,7 @@
},
"pagination": {
"landscape": "Querformat",
"portrait": "Hoch-"
"portrait": "Hochformat"
}
},
"watermarks-listing": {
@ -2640,7 +2641,7 @@
"table-header": {
"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": {
"selection": {

View File

@ -102,6 +102,8 @@
},
"disabled-file-options": "",
"form": {
"quote-char": "Quotation marker",
"quote-char-placeholder": "\"",
"delimiter": "",
"delimiter-placeholder": "",
"encoding-type": "",
@ -112,6 +114,9 @@
}
},
"add-edit-dossier-attribute": {
"error": {
"generic": "Failed to save attribute."
},
"form": {
"label": "Attribute name",
"label-placeholder": "Enter name",
@ -135,9 +140,6 @@
},
"add-edit-entity": {
"form": {
"ai-creation-enabled": "Enable AI creation",
"ai-description": "AI Description",
"ai-description-placeholder": "Enter AI description",
"case-sensitive": "Case-sensitive",
"color": "{type, select, redaction{Redaction} hint{Hint} recommendation{Recommendation} skipped{Skipped redaction} ignored{Ignored hint} other{}} color",
"color-placeholder": "#",
@ -775,7 +777,7 @@
"revert-changes": "Revert",
"save-changes": "Save changes",
"search": "Search entries...",
"select-dictionary": "Select a dictionary for comparison above.",
"select-dictionary": "Select a dictionary for comparison from the drop-down.",
"success": {
"generic": "Dictionary updated"
}
@ -1060,7 +1062,7 @@
"dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}"
},
"error": {
"conflict": "Dossier state with this name already exists"
"conflict": "Dossier state with this name already exists."
},
"no-data": {
"title": "There are no dossier states."
@ -1236,9 +1238,9 @@
"title": "Edit {label}"
},
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
"false-positives": "False positives ({count})",
"false-recommendations": "False recommendations ({count})",
"to-redact": "Entries ({count})"
},
"general-info": {
"form": {
@ -1262,7 +1264,7 @@
"missing-owner": "Editing the dossier not possible: No owner assigned.",
"nav-items": {
"choose-download": "Select the documents for your download:",
"dictionary": "Dictionaries",
"dictionary": "Dossier entries",
"dossier-attributes": "Dossier attributes",
"dossier-dictionary": "Dossier entries",
"dossier-info": "Dossier info",
@ -1278,11 +1280,11 @@
"content": {
"options": {
"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",
"extraOptionLabel": "Range",
"extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Change on all pages"
"label": "Change on multiple pages"
},
"only-this-page": {
"description": "Edit redaction only at this position in this document",
@ -1304,12 +1306,12 @@
"legal-basis": "Legal basis",
"options": {
"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"
},
"only-here": {
"description": "Edit redaction only at this position in this document.",
"label": "Change type only here"
"label": "Change only here"
}
},
"reason": "Reason",
@ -1695,7 +1697,6 @@
"form": {
"forgot-password": "Show 'Forgot password' link on login screen"
},
"subtitle": " ",
"title": "General configurations"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) enables you to send your e-mails through the specified server settings.",
@ -1889,9 +1890,9 @@
"legalBasis": "Legal basis",
"options": {
"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",
"extraOptionLabel": "Range",
"extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Apply on multiple pages"
},
@ -2115,7 +2116,7 @@
"legal-basis": "Legal basis",
"options": {
"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"
},
"in-dossier": {
@ -2184,11 +2185,11 @@
"content": {
"options": {
"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",
"extraOptionLabel": "Range",
"extraOptionLabel": "Pages",
"extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Remove on all pages"
"label": "Remove on multiple pages"
},
"only-this-page": {
"description": "Remove redaction only at this position in this document",
@ -2222,7 +2223,7 @@
"label": "Remove from dossier in this context"
},
"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 {isImage, select, image{image} other{term}} on any page of this document.",
"label": "Remove from document"
},
"in-dossier": {

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"selected-text": "Selected text:",
"type": "Type",
"type-placeholder": "Select type..."
@ -100,7 +100,7 @@
"dialog": {
"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": {
"delimiter": "CSV delimiter",
"delimiter-placeholder": "CSV delimiter",
@ -112,6 +112,9 @@
}
},
"add-edit-dossier-attribute": {
"error": {
"generic": "Failed to save attribute!"
},
"form": {
"label": "Attribute name",
"label-placeholder": "Enter name",
@ -135,9 +138,6 @@
},
"add-edit-entity": {
"form": {
"ai-creation-enabled": "Enable AI creation",
"ai-description": "AI Description",
"ai-description-placeholder": "Enter AI description",
"case-sensitive": "Case-sensitive",
"color": "{type, select, redaction{Annotation} hint{Hint} recommendation{Recommendation} skipped{Skipped annotation} ignored{Ignored hint} other{}} Color",
"color-placeholder": "#",
@ -230,11 +230,11 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"options": {
"in-dossier": {
"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"
},
"only-here": {
@ -981,7 +981,7 @@
}
},
"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-entry": {
"file-error": "Re-processing required",
@ -1060,13 +1060,13 @@
"dossier-states": "{count, plural, one{Dossier state} other{Dossier states}}"
},
"error": {
"conflict": "Dossier state with this name already exists!"
"conflict": "Dossier state with this name already exists."
},
"no-data": {
"title": "There are no dossier states."
},
"no-match": {
"title": "No dossier states match your current filters."
"title": "No dossier state matches the currently selected filters."
},
"search": "Search...",
"table-col-names": {
@ -1141,7 +1141,7 @@
"dossier-watermark-selector": {
"heading": "Watermarks on documents",
"no-watermark": "There is no watermark defined for the dossier template.<br>Contact your app admin to define one.",
"preview": "Watermark application on preview documents",
"preview": "Watermark on preview documents",
"watermark": "Watermark application on documents"
},
"dossiers-type-switch": {
@ -1153,7 +1153,7 @@
"save": "Download"
},
"form": {
"redaction-preview-color": "Redaction preview color",
"redaction-preview-color": "Preview color",
"redaction-preview-color-placeholder": "#000000"
},
"header": "Download options",
@ -1161,7 +1161,7 @@
},
"download-includes": "Choose what is included at download:",
"download-status": {
"error": "The download preparation failed, please recheck the selected files and download option settings.",
"error": "<strong>Download generation failed</strong><br><br>Please check the selected files and download option settings.",
"queued": "Your download has been queued, you can find all your requested downloads here: <a href=\"{downloadHref}\">My downloads<a/>."
},
"download-type": {
@ -1236,9 +1236,9 @@
"title": ""
},
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
"false-positives": "False positives",
"false-recommendations": "False recommendations",
"to-redact": "To redact"
"false-positives": "False positives ({count})",
"false-recommendations": "False recommendations ({count})",
"to-redact": "To redact ({count})"
},
"general-info": {
"form": {
@ -1259,7 +1259,7 @@
}
},
"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": {
"choose-download": "Choose what is included at download:",
"dictionary": "Dictionary",
@ -1300,12 +1300,12 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"legal-basis": "",
"options": {
"in-document": {
"description": "",
"label": ""
"label": "In Dokument ändern"
},
"only-here": {
"description": "",
@ -1350,13 +1350,13 @@
},
"entity-rules-screen": {
"error": {
"generic": "Something went wrong... Entity rules update failed!"
"generic": "Error: Entity rules update failed."
},
"errors-found": "{errors, plural, one{An error} other{{errors} errors}} found in rules",
"revert-changes": "Revert",
"save-changes": "Save changes",
"success": {
"generic": "Entity rules updated!"
"generic": "Entity rules updated."
},
"title": "Entity rule editor",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} found in rules"
@ -1458,7 +1458,7 @@
"none": "None"
},
"save": {
"error": "Failed to create file attributes!",
"error": "Failed to create file attributes.",
"label": "Save attributes",
"success": "{count} file {count, plural, one{attribute} other{attributes}} created successfully!"
},
@ -1551,7 +1551,7 @@
"redacted": "Preview",
"redacted-tooltip": "Component preview shows only annotations. Consider this a preview for the final version. This view is only available if the file has no pending changes & doesn't require a reanalysis",
"standard": "Standard",
"standard-tooltip": "Standard workload view shows all hints, annotations & recommendations. This view allows editing.",
"standard-tooltip": "Standard shows all annotation types and allows for editing.",
"tabs": {
"annotations": {
"hide-skipped": "",
@ -1695,7 +1695,6 @@
"form": {
"forgot-password": "Show 'Forgot password' link on login screen"
},
"subtitle": " ",
"title": "General configurations"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) enables you to send your e-mails through the specified server settings.",
@ -1745,7 +1744,7 @@
"label": "Convert on all pages"
},
"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"
}
},
@ -1874,8 +1873,8 @@
}
},
"license-information": "License Information",
"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-success": "All annotations were loaded and are now visible in the document thumbnails.",
"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",
"loading": "Loading",
"manual-annotation": {
@ -1889,14 +1888,14 @@
"legalBasis": "Legal Basis",
"options": {
"multiple-pages": {
"description": "Edit redaction the range of pages",
"description": "Edit annotation the range of pages",
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
"extraOptionLabel": "Range",
"extraOptionPlaceholder": "e.g. 1-20,22,32",
"label": "Apply on multiple pages"
},
"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"
}
},
@ -1919,16 +1918,16 @@
"minutes": "minutes",
"no-active-license": "Invalid or corrupt license Please contact your administrator",
"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-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-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 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!",
"dossier-deleted": "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-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!",
"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-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>!",
@ -1982,12 +1981,12 @@
"ocr": {
"confirmation-dialog": {
"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!"
}
},
"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",
"file-question": "<b>{filename}</b> already exists. Choose how to proceed:",
"file-title": "File already exists!",
@ -1996,16 +1995,16 @@
"cancel": "Cancel upload",
"current-files": "Apply to current file",
"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"
},
"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"
},
"proceed": "Proceed",
"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"
}
},
@ -2079,14 +2078,14 @@
},
"form": {
"auto-expand-filters-on-action": "Auto expand filters on my actions",
"help-mode-dialog": "Help Mode Dialog",
"load-all-annotations-warning": "Warning regarding loading all annotations at once in file preview",
"help-mode-dialog": "Help mode activation dialog",
"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",
"table-extraction-type": "Table extraction type"
},
"label": "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-subtitle": "Do not show again options"
},
@ -2110,7 +2109,7 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"edit-text": "",
"legal-basis": "Legal basis",
"options": {
@ -2120,7 +2119,7 @@
},
"in-dossier": {
"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"
},
"only-here": {
@ -2149,27 +2148,27 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"list-item": "{text}",
"list-item-false-positive": "''{text}'' in the context: ''{context}''",
"options": {
"false-positive": {
"description": "''{value}'' is not a ''{type}'' in this context: ''{context}''.",
"description-bulk": "The selected items should not be annotated in their respective contexts.",
"label": "False positive"
"description": "Mark this redaction as a false-positive. The term will not be redacted in this dossier if it occurs in the same context.",
"description-bulk": "Mark this redaction as a false-positive. The term will not be redacted in this dossier if it occurs in the same context.",
"label": "Remove from dossier in this context"
},
"in-document": {
"description": "",
"label": ""
"label": "Aus Dokument entfernen"
},
"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.",
"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": {
"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.",
"label": "Remove here"
}
@ -2206,35 +2205,35 @@
},
"content": {
"comment": "Comment",
"comment-placeholder": "Add remarks or mentions...",
"comment-placeholder": "Add remarks or notes...",
"options": {
"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.",
"extraOptionLabel": "Apply to all dossiers",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier"
},
"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": "",
"extraOptionDescription": "",
"extraOptionLabel": "Apply to all dossiers",
"label": "False positive"
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier in this context"
},
"in-document": {
"description": "",
"label": ""
"label": "Aus Dokument entfernen"
},
"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": "",
"extraOptionLabel": "Apply to all dossiers",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier",
"label-bulk": ""
},
"only-here": {
"description": "Do not {type} ''{value}'' at this position in the current document.",
"description-bulk": "",
"description": "Do not{type} '{value}'' at this position in the current document.",
"description-bulk": "Do not{type} ''{value}'' at this position in the current document.",
"label": "Remove here"
}
}
@ -2268,7 +2267,7 @@
"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.",
"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-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.",
@ -2331,7 +2330,7 @@
"options": {
"in-dossier": {
"description": "Resize in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Resize in dossier",
"tooltip": "Only available for dictionary-based types"
},
@ -2386,7 +2385,7 @@
},
"missing": "Missing",
"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.",
"table-header": "{length} search {length, plural, one{result} other{results}}"
},
@ -2422,7 +2421,7 @@
"sign-in-previous-domain": "Sign in to a previously used workspace",
"youre-logged-out": "You have successfully been logged out."
},
"input-placeholder": "your workspace"
"input-placeholder": "Your workspace"
},
"time": {
"days": "{days} {days, plural, one{day} other{days}}",
@ -2515,7 +2514,7 @@
},
"error": {
"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}"
}
},
@ -2570,7 +2569,7 @@
},
"title": "Edit profile",
"update": {
"success": "Successfully updated profile!"
"success": "Successfully updated profile."
}
},
"user-stats": {
@ -2588,9 +2587,9 @@
},
"watermark-screen": {
"action": {
"change-success": "Watermark has been updated!",
"created-success": "Watermark has been created!",
"error": "Failed to update watermark",
"change-success": "Watermark has been updated.",
"created-success": "Watermark has been created.",
"error": "Failed to update watermark.",
"revert": "Revert",
"save": "Save changes"
},

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -14,6 +14,30 @@
"800": "Mplus1pHeavy",
"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": [
@ -21,6 +45,16 @@
"match": "M+1p*",
"target": "m+1p1",
"type": "Wildcard"
},
{
"match": "Liberation*Serif*",
"target": "liberationserif1",
"type": "Wildcard"
},
{
"match": "Arimo*",
"target": "arimo1",
"type": "Wildcard"
}
],
"versionMajor": 2,

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

View File

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

View File

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

View File

@ -16,14 +16,6 @@ export interface IDictionary {
* The description of the dictionary type
*/
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
*/

View File

@ -5,7 +5,6 @@ export const FILE_ID = 'fileId';
export const WorkflowFileStatuses = {
APPROVED: 'APPROVED',
NEW: 'NEW',
UNASSIGNED: 'UNASSIGNED',
UNDER_APPROVAL: 'UNDER_APPROVAL',
UNDER_REVIEW: 'UNDER_REVIEW',
} as const;

View File

@ -1,15 +1,18 @@
import { IListable } from '@iqser/common-ui';
import { ILegalBasis } from './legal-basis';
import { toSnakeCase } from '@utils/functions';
export class Justification implements ILegalBasis, IListable {
readonly description?: string;
readonly name: string;
readonly reason?: string;
readonly technicalName?: string;
constructor(justification: ILegalBasis) {
this.description = justification.description;
this.name = justification.name;
this.reason = justification.reason;
this.technicalName = justification.technicalName;
}
get id(): string {
@ -19,4 +22,13 @@ export class Justification implements ILegalBasis, IListable {
get searchKey(): string {
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 description?: string;
readonly reason?: string;
readonly technicalName?: string;
}

View File

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

View File

@ -1,4 +1,4 @@
import { WorkflowFileStatus } from '../../files/types';
import { WorkflowFileStatus } from '../../files';
type StatusSorterItem = { key: WorkflowFileStatus } | WorkflowFileStatus | string;
type Sorter = Record<WorkflowFileStatus, number> & {
@ -7,10 +7,9 @@ type Sorter = Record<WorkflowFileStatus, number> & {
export const StatusSorter: Sorter = {
NEW: 0,
UNASSIGNED: 1,
UNDER_REVIEW: 2,
UNDER_APPROVAL: 3,
APPROVED: 4,
UNDER_REVIEW: 1,
UNDER_APPROVAL: 2,
APPROVED: 3,
byStatus: (a: StatusSorterItem, b: StatusSorterItem): number => {
if (typeof a !== typeof b) {
throw TypeError('Used different types when calling StatusSorter.byStatus1');

View File

@ -14,7 +14,7 @@
"installMode": "lazy",
"updateMode": "prefetch",
"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)"]
}
}
],