Merge branch 'master' into RED-7550

# Conflicts:
#	apps/red-ui/src/app/modules/file-preview/dialogs/edit-redaction-dialog/edit-redaction-dialog.component.html
This commit is contained in:
Nicoleta Panaghiu 2023-09-18 17:05:01 +03:00
commit 5d90277680
31 changed files with 187 additions and 207 deletions

View File

@ -49,7 +49,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
],
},
{
path: 'rules',
path: 'entity-rules',
component: BaseDossierTemplateScreenComponent,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
data: {
@ -59,7 +59,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
redirectTo: 'info',
},
},
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
loadChildren: () => import('./screens/rules/entity-rules.module').then(m => m.EntityRulesModule),
},
{
path: 'component-rules',

View File

@ -57,7 +57,6 @@ import { DossierTemplateActionsComponent } from './shared/components/dossier-tem
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
import { SelectComponent } from '@shared/components/select/select.component';
import { ComponentRulesService } from './services/component-rules.service';
const dialogs = [
AddEditCloneDossierTemplateDialogComponent,
@ -98,7 +97,7 @@ const components = [
@NgModule({
declarations: [...components],
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, ComponentRulesService, SmtpConfigService],
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService],
imports: [
CommonModule,
SharedModule,

View File

@ -8,7 +8,7 @@ import { DOSSIER_TEMPLATE_ID } from '@red/domain';
import { EditorThemeService } from '@services/editor-theme.service';
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import { Debounce, getParam } from '@iqser/common-ui/lib/utils';
import { ComponentRulesService } from '../../../services/component-rules.service';
import { RulesService } from '../../../services/rules.service';
import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
@ -46,7 +46,7 @@ export class ComponentRulesScreenComponent implements OnInit, ComponentCanDeacti
constructor(
readonly permissionsService: PermissionsService,
private readonly _componentRulesService: ComponentRulesService,
private readonly _rulesService: RulesService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _toaster: Toaster,
private readonly _loadingService: LoadingService,
@ -99,9 +99,10 @@ export class ComponentRulesScreenComponent implements OnInit, ComponentCanDeacti
this._loadingService.start();
this._removeErrorMarkers();
await firstValueFrom(
this._componentRulesService.uploadRules({
this._rulesService.uploadRules({
rules: this._codeEditor.getModel().getValue(),
dossierTemplateId: this.#dossierTemplateId,
ruleFileType: 'COMPONENT',
}),
).then(
async () => {
@ -205,7 +206,7 @@ export class ComponentRulesScreenComponent implements OnInit, ComponentCanDeacti
private async _initialize() {
this._loadingService.start();
await firstValueFrom(this._componentRulesService.download(this.#dossierTemplateId)).then(
await firstValueFrom(this._rulesService.download(this.#dossierTemplateId, 'COMPONENT')).then(
rules => {
this.currentLines = this.initialLines = rules.rules.split('\n');
this.revert();

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { ComponentRulesScreenComponent } from './rules-screen/component-rules-screen.component';
import { ComponentRulesScreenComponent } from './component-rules-screen/component-rules-screen.component';
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
import { TranslateModule } from '@ngx-translate/core';

View File

@ -1,6 +1,6 @@
<div class="header-container">
<div [translate]="'rules-screen.title'" class="heading-l"></div>
<div [translate]="'rules-screen.warning-text'" class="error"></div>
<div [translate]="'entity-rules-screen.title'" class="heading-l"></div>
<div [translate]="'entity-rules-screen.warning-text'" class="error"></div>
</div>
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
@ -9,18 +9,18 @@
<div (click)="goToErrors()" *ngIf="numberOfErrors()" class="errors">
<span>
<mat-icon [svgIcon]="'iqser:alert-circle'" class="icon"></mat-icon>
<span [translateParams]="{ errors: numberOfErrors() }" [translate]="'rules-screen.errors-found'"></span>
<span [translateParams]="{ errors: numberOfErrors() }" [translate]="'entity-rules-screen.errors-found'"></span>
</span>
<mat-icon [svgIcon]="'iqser:expand'" class="icon face-up"></mat-icon>
</div>
<div class="actions">
<iqser-icon-button
(action)="save()"
[label]="'rules-screen.save-changes' | translate"
[label]="'entity-rules-screen.save-changes' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"
></iqser-icon-button>
<div (click)="revert()" [translate]="'rules-screen.revert-changes'" class="all-caps-label cancel"></div>
<div (click)="revert()" [translate]="'entity-rules-screen.revert-changes'" class="all-caps-label cancel"></div>
</div>
</div>

View File

@ -20,11 +20,11 @@ interface SyntaxError {
}
@Component({
templateUrl: './rules-screen.component.html',
styleUrls: ['./rules-screen.component.scss'],
templateUrl: './entity-rules-screen.component.html',
styleUrls: ['./entity-rules-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
export class EntityRulesScreenComponent implements OnInit, ComponentCanDeactivate {
readonly iconButtonTypes = IconButtonTypes;
readonly editorOptions: IStandaloneEditorConstructionOptions = {
theme: 'vs',
@ -106,13 +106,13 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
).then(
async () => {
await this._initialize();
this._toaster.success(_('rules-screen.success.generic'));
this._toaster.success(_('entity-rules-screen.success.generic'));
},
error => {
const errors = error.error as SyntaxError[] | undefined;
this._drawErrorMarkers(errors);
this._loadingService.stop();
this._toaster.error(_('rules-screen.error.generic'));
this._toaster.error(_('entity-rules-screen.error.generic'));
},
);
}

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { RulesScreenComponent } from './rules-screen/rules-screen.component';
import { EntityRulesScreenComponent } from './entity-rules-screen/entity-rules-screen.component';
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
import { TranslateModule } from '@ngx-translate/core';
@ -9,10 +9,10 @@ import { IconButtonComponent } from '@iqser/common-ui';
import { FormsModule } from '@angular/forms';
import { MatIconModule } from '@angular/material/icon';
const routes = [{ path: '', component: RulesScreenComponent, canDeactivate: [PendingChangesGuard] }];
const routes = [{ path: '', component: EntityRulesScreenComponent, canDeactivate: [PendingChangesGuard] }];
@NgModule({
declarations: [RulesScreenComponent],
declarations: [EntityRulesScreenComponent],
imports: [
RouterModule.forChild(routes),
CommonModule,
@ -23,4 +23,4 @@ const routes = [{ path: '', component: RulesScreenComponent, canDeactivate: [Pen
MatIconModule,
],
})
export class RulesModule {}
export class EntityRulesModule {}

View File

@ -1,16 +0,0 @@
import { Injectable } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
import { IComponentRules } from '@red/domain';
@Injectable()
export class ComponentRulesService extends GenericService<IComponentRules> {
protected readonly _defaultModelPath = 'rules';
download(dossierTemplateId: string) {
return this._getOne([dossierTemplateId]);
}
uploadRules(body: IComponentRules) {
return this._post<unknown>(body);
}
}

View File

@ -6,11 +6,11 @@ import { IRules } from '@red/domain';
export class RulesService extends GenericService<IRules> {
protected readonly _defaultModelPath = 'rules';
download(dossierTemplateId: string) {
return this._getOne([dossierTemplateId]);
download(dossierTemplateId: string, ruleFileType: IRules['ruleFileType'] = 'ENTITY') {
return this._getOne([dossierTemplateId, ruleFileType]);
}
uploadRules(body: IRules) {
return this._post<unknown>(body);
return this._post<unknown>({ ...body, ruleFileType: body.ruleFileType ?? 'ENTITY' });
}
}

View File

@ -95,17 +95,14 @@ export class AdminSideNavComponent implements OnInit {
show: true,
},
{
screen: 'rules',
label: _('admin-side-nav.rule-editor'),
screen: 'entity-rules',
label: _('admin-side-nav.entity-rule-editor'),
show: (this.isIqserDevMode || this.canAccessRulesInDocumine) && this._permissionsService.has(Roles.rules.read),
},
{
screen: 'component-rules',
label: _('admin-side-nav.component-rule-editor'),
show:
this.isDocumine &&
(this.isIqserDevMode || this.canAccessRulesInDocumine) &&
this._permissionsService.has(Roles.rules.read),
show: this.isDocumine && this.isIqserDevMode && this._permissionsService.has(Roles.rules.read),
},
{
screen: 'default-colors',

View File

@ -1,10 +1,10 @@
import { Component, HostBinding, inject, Input, OnChanges } from '@angular/core';
import { getConfig } from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ActionsHelpModeKeys } from '../../utils/constants';
import { ListItem } from '@models/file/list-item';
import { MultiSelectService } from '../../services/multi-select.service';
import { getConfig } from '@iqser/common-ui';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ActionsHelpModeKeys } from '../../utils/constants';
@Component({
selector: 'redaction-annotation-wrapper',
@ -12,14 +12,13 @@ import { getConfig } from '@iqser/common-ui';
styleUrls: ['./annotation-wrapper.component.scss'],
})
export class AnnotationWrapperComponent implements OnChanges {
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey?: string;
readonly #isDocumine = getConfig().IS_DOCUMINE;
protected readonly _pdfProxyService = inject(PdfProxyService);
protected readonly _multiSelectService = inject(MultiSelectService);
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey?: string;
ngOnChanges() {
this.annotationId = this.annotation.item.id;

View File

@ -24,7 +24,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
}
return [] as EarmarkGroup[];
});
@Input() annotations: ListItem<AnnotationWrapper>[];
@Input({ required: true }) annotations: ListItem<AnnotationWrapper>[];
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
constructor(

View File

@ -37,7 +37,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly #devMode = this._userPreferenceService.isIqserDevMode;
readonly #isIqserDevMode = this._userPreferenceService.isIqserDevMode;
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
displayedAnnotations = new Map<number, AnnotationWrapper[]>();
@ -96,14 +96,14 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
});
effect(() => {
const __ = this.viewModeService.viewMode();
this.viewModeService.viewMode();
this._scrollViews();
});
effect(
() => {
if (this.excludedPagesService.shown()) {
this._disableMultiSelectAndDocumentInfo();
this.#disableMultiSelectAndDocumentInfo();
}
},
{ allowSignalWrites: true },
@ -130,8 +130,8 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this._pageRotationService.rotations$,
]).pipe(
delay(0),
map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary)),
map(annotations => this._mapListItemsFromAnnotationWrapperArray(annotations)),
map(([annotations, primary, secondary]) => this.#filterAnnotations(annotations, primary, secondary)),
map(annotations => this.#mapListItemsFromAnnotationWrapperArray(annotations)),
);
}
@ -196,7 +196,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
// selected annotation on this page and not in multi select mode
if (!this.pagesPanelActive && this.multiSelectService.inactive()) {
this._documentViewer.clearSelection();
this._selectFirstAnnotationOnCurrentPageIfNecessary();
this.#selectFirstAnnotationOnCurrentPageIfNecessary();
}
this._changeDetectorRef.markForCheck();
return;
@ -209,7 +209,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this.navigateAnnotations($event);
}
} else {
this._navigatePages($event);
this.#navigatePages($event);
}
this._changeDetectorRef.markForCheck();
@ -247,7 +247,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== currentPage) {
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
}
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
this.#scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
}
scrollQuickNavFirst(): void {
@ -265,11 +265,11 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
jumpToPreviousWithAnnotations(): void {
this.pdf.navigateTo(this._prevPageWithAnnotations());
this.pdf.navigateTo(this.#prevPageWithAnnotations());
}
jumpToNextWithAnnotations(): void {
this.pdf.navigateTo(this._nextPageWithAnnotations());
this.pdf.navigateTo(this.#nextPageWithAnnotations());
}
navigateAnnotations($event: KeyboardEvent) {
@ -281,12 +281,12 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
// Displayed page doesn't have annotations
if ($event.key === 'ArrowDown') {
const nextPage = this._nextPageWithAnnotations();
const nextPage = this.#nextPageWithAnnotations();
return this.listingService.selectAnnotations(this.displayedAnnotations.get(nextPage)[0]);
}
const prevPage = this._prevPageWithAnnotations();
const prevPage = this.#prevPageWithAnnotations();
const prevPageAnnotations = this.displayedAnnotations.get(prevPage);
return this.listingService.selectAnnotations(prevPageAnnotations[prevPageAnnotations.length - 1]);
@ -339,12 +339,12 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this.scrollAnnotations();
}
private _disableMultiSelectAndDocumentInfo(): void {
#disableMultiSelectAndDocumentInfo(): void {
this.multiSelectService.deactivate();
this.documentInfoService.hide();
}
private _filterAnnotations(
#filterAnnotations(
annotations: AnnotationWrapper[],
primary: INestedFilter[],
secondary: INestedFilter[] = [],
@ -359,7 +359,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
annotations = annotations.filter(a => !bool(a.isChangeLogRemoved));
}
if (this.#isDocumine && !this.#devMode) {
if (this.#isDocumine && !this.#isIqserDevMode) {
annotations = annotations.filter(a => !a.isOCR);
}
@ -383,7 +383,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
return this.displayedAnnotations;
}
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
#selectFirstAnnotationOnCurrentPageIfNecessary() {
const currentPage = this.pdf.currentPage();
if (
(!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) &&
@ -394,13 +394,13 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
}
private _navigatePages($event: KeyboardEvent) {
#navigatePages($event: KeyboardEvent) {
const pageIdx = this.displayedPages.indexOf(this.pdf.currentPage());
if ($event.key !== 'ArrowDown') {
if (pageIdx === -1) {
// If active page doesn't have annotations
const prevPage = this._prevPageWithAnnotations();
const prevPage = this.#prevPageWithAnnotations();
if (prevPage) {
this.pdf.navigateTo(prevPage);
}
@ -414,7 +414,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
if (pageIdx === -1) {
// If active page doesn't have annotations
const nextPage = this._nextPageWithAnnotations();
const nextPage = this.#nextPageWithAnnotations();
if (nextPage) {
this.pdf.navigateTo(nextPage);
}
@ -426,7 +426,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
}
private _nextPageWithAnnotations() {
#nextPageWithAnnotations() {
let idx = 0;
for (const page of this.displayedPages) {
if (page > this.pdf.currentPage() && this.displayedAnnotations.get(page)) {
@ -437,7 +437,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
return idx < this.displayedPages.length ? this.displayedPages[idx] : this.displayedPages[this.displayedPages.length - 1];
}
private _prevPageWithAnnotations() {
#prevPageWithAnnotations() {
let idx = this.displayedPages.length - 1;
const reverseDisplayedPages = [...this.displayedPages].reverse();
for (const page of reverseDisplayedPages) {
@ -449,14 +449,14 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
return idx >= 0 ? this.displayedPages[idx] : this.displayedPages[0];
}
private _scrollQuickNavigationToPage(page: number) {
#scrollQuickNavigationToPage(page: number) {
if (this._quickNavigationElement) {
const elements: HTMLElement[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
FileWorkloadComponent._scrollToFirstElement(elements);
}
}
private _mapListItemsFromAnnotationWrapperArray(annotations: Map<number, AnnotationWrapper[]>) {
#mapListItemsFromAnnotationWrapperArray(annotations: Map<number, AnnotationWrapper[]>) {
const listItemsMap = new Map<number, ListItem<AnnotationWrapper>[]>();
if (!annotations) {
return listItemsMap;

View File

@ -5,7 +5,7 @@
<div class="dialog-content redaction">
<div class="iqser-input-group w-450">
<label [translate]="'add-annotation.dialog.content.selected-text'" class="selected-text"></label>
{{ form.get('selectedText').value }}
<span [innerHTML]="form.controls.selectedText.value"></span>
</div>
<div class="iqser-input-group required w-450">

View File

@ -1,10 +1,9 @@
import { Component, inject, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { IconButtonTypes, IqserDialogComponent, IqserPermissionsService } from '@iqser/common-ui';
import { IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import { Dictionary, IAddRedactionRequest } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { Roles } from '@users/roles';
import { AddAnnotationData, AddAnnotationResult } from '../../../utils/dialog-types';
@Component({
@ -15,17 +14,19 @@ export class AddAnnotationDialogComponent
implements OnInit
{
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
readonly #isRss = inject(IqserPermissionsService).has(Roles.getRss);
readonly iconButtonTypes = IconButtonTypes;
dictionaries: Dictionary[] = [];
readonly form = this.#getForm();
constructor(private readonly _dictionaryService: DictionaryService, private readonly _formBuilder: FormBuilder) {
constructor(
private readonly _dictionaryService: DictionaryService,
private readonly _formBuilder: FormBuilder,
) {
super();
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
const dictType = this.form.controls.dictionary.value;
if (dictType) {
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
}
@ -33,7 +34,7 @@ export class AddAnnotationDialogComponent
}
get disabled() {
return !this.#isRss || !this.form.get('dictionary').value;
return !this.form.controls.dictionary.value;
}
ngOnInit() {
@ -45,7 +46,7 @@ export class AddAnnotationDialogComponent
const redaction = this.data.manualRedactionEntryWrapper.manualRedactionEntry;
this.close({
redaction,
dictionary: this.dictionaries.find(d => d.type === this.form.get('dictionary').value),
dictionary: this.dictionaries.find(d => d.type === this.form.controls.dictionary.value),
});
}
@ -58,16 +59,16 @@ export class AddAnnotationDialogComponent
}
#enhanceManualRedaction(addRedactionRequest: IAddRedactionRequest) {
addRedactionRequest.type = this.form.get('dictionary').value;
addRedactionRequest.type = this.form.controls.dictionary.value;
const selectedType = this.dictionaries.find(d => d.type === addRedactionRequest.type);
addRedactionRequest.addToDictionary = !!selectedType?.hasDictionary;
addRedactionRequest.reason ??= 'Dictionary Request';
const commentValue = this.form.get('comment').value;
const commentValue = this.form.controls.comment.value;
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
addRedactionRequest.section = null;
addRedactionRequest.value = this.form.get('selectedText').value;
addRedactionRequest.value = this.form.controls.selectedText.value;
addRedactionRequest.addToAllDossiers = addRedactionRequest.addToDictionary || !!this.data.applyToAllDossiers;
}
}

View File

@ -7,6 +7,13 @@
></div>
<div class="dialog-content redaction">
<!-- <div *ngIf="!allRectangles && redactedText" class="iqser-input-group w-450">-->
<!-- <label-->
<!-- [translateParams]="{ type: isImage ? 'image' : isHint ? 'hint' : 'redaction' }"-->
<!-- [translate]="'edit-redaction.dialog.content.redacted-text'"-->
<!-- class="selected-text"-->
<!-- ></label>-->
<!-- {{ redactedText }}-->
<div *ngIf="!allRectangles && redactedTexts" class="iqser-input-group w-450">
<label
[translate]="'edit-redaction.dialog.content.redacted-text'"

View File

@ -6,14 +6,16 @@
<div class="iqser-input-group w-450 selected-text-group">
<label
[translate]="
initialText === form.get('selectedText').value
initialText === form.controls.selectedText.value
? 'redact-text.dialog.content.selected-text'
: 'redact-text.dialog.content.text'
"
class="selected-text"
></label>
<div [ngClass]="isEditingSelectedText ? 'flex relative' : 'flex-align-items-center'" class="fixed-height">
<span *ngIf="!isEditingSelectedText">{{ form.get('selectedText').value }}</span>
<span *ngIf="!isEditingSelectedText" [innerHTML]="form.controls.selectedText.value"></span>
<textarea
*ngIf="isEditingSelectedText"
[rows]="selectedTextRows"
@ -23,6 +25,7 @@
name="comment"
type="text"
></textarea>
<iqser-circle-button
(action)="toggleEditingSelectedText()"
*ngIf="dictionaryRequest"
@ -34,6 +37,7 @@
icon="iqser:edit"
tooltipPosition="below"
></iqser-circle-button>
<iqser-circle-button
(action)="undoTextChange()"
*ngIf="isEditingSelectedText"
@ -74,7 +78,7 @@
<div *ngIf="!dictionaryRequest" class="iqser-input-group w-450">
<label [translate]="'redact-text.dialog.content.legal-basis'"></label>
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
<input [value]="form.controls.reason.value?.legalBasis" disabled type="text" />
</div>
<div *ngIf="dictionaryRequest" class="iqser-input-group required w-450">

View File

@ -1,18 +1,18 @@
import { Component, inject, OnInit } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
import { FormBuilder } from '@angular/forms';
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import { Dictionary, IAddRedactionRequest, SuperTypes } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Roles } from '@users/roles';
import { calcTextWidthInPixels } from '@utils/functions';
import { firstValueFrom } from 'rxjs';
import { tap } from 'rxjs/operators';
import { getRedactOrHintOptions, RedactOrHintOption, RedactOrHintOptions } from '../../utils/dialog-options';
import { RedactTextData, RedactTextResult } from '../../utils/dialog-types';
import { LegalBasisOption } from '../manual-redaction-dialog/manual-annotation-dialog.component';
import { calcTextWidthInPixels } from '@utils/functions';
const MAXIMUM_SELECTED_TEXT_WIDTH = 421;
@ -24,19 +24,19 @@ export class RedactTextDialogComponent
extends IqserDialogComponent<RedactTextDialogComponent, RedactTextData, RedactTextResult>
implements OnInit
{
readonly #manualRedactionTypeExists: boolean;
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
readonly #manualRedactionTypeExists = inject(DictionaryService).hasManualType(this.#dossier.dossierTemplateId);
#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
readonly roles = Roles;
readonly iconButtonTypes = IconButtonTypes;
readonly options: DetailsRadioOption<RedactOrHintOption>[];
readonly form: UntypedFormGroup;
readonly initialText: string;
readonly options = getRedactOrHintOptions(this.#dossier, this.#applyToAllDossiers, this.data.isApprover);
readonly initialText = this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value;
readonly form = this.#getForm();
dictionaryRequest = false;
legalOptions: LegalBasisOption[] = [];
dictionaries: Dictionary[] = [];
isEditingSelectedText = false;
modifiedText: string;
modifiedText = this.initialText;
selectedTextRows = 1;
constructor(
@ -45,16 +45,8 @@ export class RedactTextDialogComponent
private readonly _formBuilder: FormBuilder,
) {
super();
this.#manualRedactionTypeExists = this._dictionaryService.hasManualType(this.#dossier.dossierTemplateId);
this.options = getRedactOrHintOptions(this.#dossier, this.#applyToAllDossiers, this.data.isApprover);
this.form = this.#getForm();
this.initialText = this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value;
this.modifiedText = this.initialText;
this.form
.get('option')
.valueChanges.pipe(
this.form.controls.option.valueChanges
.pipe(
tap((option: DetailsRadioOption<RedactOrHintOption>) => {
this.dictionaryRequest = option.value === RedactOrHintOptions.IN_DOSSIER;
if (this.dictionaryRequest) {
@ -62,7 +54,7 @@ export class RedactTextDialogComponent
this.form.patchValue({ selectedText: this.modifiedText });
} else {
this.isEditingSelectedText = false;
this.modifiedText = this.form.get('selectedText').value;
this.modifiedText = this.form.controls.selectedText.value;
this.form.patchValue({ selectedText: this.initialText });
}
this.#resetValues();
@ -73,7 +65,7 @@ export class RedactTextDialogComponent
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
const dictType = this.form.controls.dictionary.value;
if (dictType) {
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
}
@ -82,9 +74,9 @@ export class RedactTextDialogComponent
get disabled() {
if (this.dictionaryRequest) {
return !this.form.get('dictionary').value;
return !this.form.controls.dictionary.value;
}
return !this.form.get('reason').value;
return !this.form.controls.dictionary.value;
}
async ngOnInit(): Promise<void> {
@ -108,18 +100,18 @@ export class RedactTextDialogComponent
this.#applyToAllDossiers = option.extraOption.checked;
this.#setDictionaries();
if (this.#applyToAllDossiers && this.form.get('dictionary').value) {
const selectedDictionaryLabel = this.form.get('dictionary').value;
if (this.#applyToAllDossiers && this.form.controls.dictionary.value) {
const selectedDictionaryLabel = this.form.controls.dictionary.value;
const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryLabel);
if (!selectedDictionary) {
this.form.get('dictionary').setValue(null);
this.form.controls.dictionary.setValue(null);
}
}
}
typeChanged() {
if (!this.#applyToAllDossiers) {
const selectedDictionaryType = this.form.get('dictionary').value;
const selectedDictionaryType = this.form.controls.dictionary.value;
const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryType);
this.options[1].extraOption.disabled = selectedDictionary.dossierDictionaryOnly;
}
@ -130,14 +122,14 @@ export class RedactTextDialogComponent
const redaction = this.data.manualRedactionEntryWrapper.manualRedactionEntry;
this.close({
redaction,
dictionary: this.dictionaries.find(d => d.type === this.form.get('dictionary').value),
dictionary: this.dictionaries.find(d => d.type === this.form.controls.dictionary.value),
});
}
toggleEditingSelectedText() {
this.isEditingSelectedText = !this.isEditingSelectedText;
if (this.isEditingSelectedText) {
const width = calcTextWidthInPixels(this.form.get('selectedText').value);
const width = calcTextWidthInPixels(this.form.controls.selectedText.value);
this.selectedTextRows = Math.ceil(width / MAXIMUM_SELECTED_TEXT_WIDTH);
}
}
@ -149,25 +141,25 @@ export class RedactTextDialogComponent
#getForm() {
return this._formBuilder.group({
selectedText: this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value,
reason: [null],
reason: null as LegalBasisOption,
comment: [null],
dictionary: [this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null],
option: [this.options[0]],
option: this.options[0],
});
}
#selectReason() {
if (this.legalOptions.length === 1) {
this.form.get('reason').setValue(this.legalOptions[0]);
this.form.controls.reason.setValue(this.legalOptions[0]);
}
}
#enhanceManualRedaction(addRedactionRequest: IAddRedactionRequest) {
addRedactionRequest.type = this.form.get('dictionary').value;
addRedactionRequest.type = this.form.controls.dictionary.value;
addRedactionRequest.section = null;
addRedactionRequest.value = this.form.get('selectedText').value;
addRedactionRequest.value = this.form.controls.selectedText.value;
const legalOption: LegalBasisOption = this.form.get('reason').value;
const legalOption: LegalBasisOption = this.form.controls.reason.value;
if (legalOption) {
addRedactionRequest.reason = legalOption.description;
addRedactionRequest.legalBasis = legalOption.legalBasis;
@ -184,7 +176,7 @@ export class RedactTextDialogComponent
if (!addRedactionRequest.reason) {
addRedactionRequest.reason = 'Dictionary Request';
}
const commentValue = this.form.get('comment').value;
const commentValue = this.form.controls.comment.value;
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
addRedactionRequest.addToAllDossiers = this.data.isApprover && this.dictionaryRequest && this.#applyToAllDossiers;
}
@ -193,10 +185,10 @@ export class RedactTextDialogComponent
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
this.options[1].extraOption.checked = this.#applyToAllDossiers;
if (this.dictionaryRequest) {
this.form.get('reason').setValue(null);
this.form.get('dictionary').setValue(null);
this.form.controls.reason.setValue(null);
this.form.controls.dictionary.setValue(null);
return;
}
this.form.get('dictionary').setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null);
this.form.controls.dictionary.setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null);
}
}

View File

@ -266,19 +266,11 @@ export class PdfProxyService {
}
}
entry.value = this.#formatSelectedText(text);
entry.value = text;
entry.rectangle = !text;
return entry;
}
#formatSelectedText(text: string): string {
return text?.replace(
// eslint-disable-next-line no-control-regex,max-len
/([^\s\d-]{2,})[-\u00AD]\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]/gi,
'$1',
);
}
#deactivateMultiSelect() {
this._logger.info('[PDF] Deactivating multi-select');
this._multiSelectService.deactivate();

View File

@ -159,6 +159,7 @@ export const getRemoveRedactionOptions = (
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
descriptionParams: {
value: redactions[0].value,
type: redactions[0].hint ? 'hint' : redactions[0].type,
},
icon: PIN_ICON,
value: RemoveRedactionOptions.ONLY_HERE,
@ -168,7 +169,7 @@ export const getRemoveRedactionOptions = (
options.push({
label: isBulk ? translations.IN_DOSSIER.labelBulk : translations.IN_DOSSIER.label,
description: isBulk ? translations.IN_DOSSIER.descriptionBulk : translations.IN_DOSSIER.description,
descriptionParams: { value: redactions[0].value, type: redactions[0].type },
descriptionParams: { value: redactions[0].value, type: redactions[0].hint ? 'hint' : redactions[0].type },
icon: FOLDER_ICON,
value: RemoveRedactionOptions.IN_DOSSIER,
extraOption: !isDocumine

View File

@ -154,7 +154,6 @@ export class REDAnnotationManager {
this.#manager.selectAnnotation(annotation);
annotation.disableRotationControl();
}
this.#annotationSelected$.next([annotations, action]);
});
}

View File

@ -1,5 +1,5 @@
import type { List } from '@iqser/common-ui/lib/utils';
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';

View File

@ -245,13 +245,13 @@
"dossier-templates": "Dossier-Vorlage",
"entities": "Entities",
"entity-info": "Info",
"entity-rule-editor": "Entity Rule Editor",
"false-positive": "False Positive",
"false-recommendations": "False Recommendations",
"file-attributes": "File Attributes",
"justifications": "Justifications",
"license-information": "License Information",
"reports": "Reports",
"rule-editor": "Rule Editor",
"settings": "Einstellungen",
"user-management": "User Management",
"watermarks": "Watermarks"
@ -1249,7 +1249,7 @@
}
},
"reason": "Reason",
"redacted-text": "Redacted text",
"redacted-text": "{type, select, hint{Hint} other{Redacted}} text",
"section": "Paragraph / Location",
"type": "Type",
"unchanged": "Unchanged"
@ -1293,6 +1293,19 @@
"heading": "Edit Entity"
}
},
"entity-rules-screen": {
"error": {
"generic": "Something went wrong... 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!"
},
"title": "Entity Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"error": {
"deleted-entity": {
"dossier": {
@ -2086,14 +2099,14 @@
"label": "False positive"
},
"in-dossier": {
"description": "Do not {type} \"{value}\" in any document of the current dossier.",
"description": "Do not {type, select, hint{annotate} other{redact}} \"{value}\" in any document of the current dossier.",
"description-bulk": "Do not redact the selected terms as their respective types in any dossier.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Remove from dossier",
"label-bulk": "No longer redact in any dossier"
},
"only-here": {
"description": "Do not {type, select, undefined{redact} other{type}} \"{value}\" at this position in the current document.",
"description": "Do not {type, select, hint{annotate} other{redact}} \"{value}\" at this position in the current document.",
"description-bulk": "Do not redact the selected terms at this position in the current document.",
"label": "Remove here"
}
@ -2239,19 +2252,6 @@
},
"title": "Structured Component Management"
},
"rules-screen": {
"error": {
"generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!"
},
"errors-found": "{errors, plural, one{An error} other{{errors} errors}} found in rules",
"revert-changes": "Anmeldedaten speichern",
"save-changes": "Änderungen speichern",
"success": {
"generic": "Die Regeln wurden aktualisiert!"
},
"title": "Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"search": {
"active-dossiers": "ganze Plattform",
"all-dossiers": "all documents",

View File

@ -245,13 +245,13 @@
"dossier-templates": "Dossier Templates",
"entities": "Entities",
"entity-info": "Info",
"entity-rule-editor": "Entity Rule Editor",
"false-positive": "False Positive",
"false-recommendations": "False Recommendations",
"file-attributes": "File Attributes",
"justifications": "Justifications",
"license-information": "License Information",
"reports": "Reports",
"rule-editor": "Rule Editor",
"settings": "Settings",
"user-management": "User Management",
"watermarks": "Watermarks"
@ -1249,7 +1249,7 @@
}
},
"reason": "Reason",
"redacted-text": "Redacted text",
"redacted-text": "{type, select, hint{Hint} other{Redacted}} text",
"section": "Paragraph / Location",
"type": "Type",
"unchanged": "Unchanged"
@ -1284,6 +1284,19 @@
"title": "{length} {length, plural, one{entity} other{entities}}"
}
},
"entity-rules-screen": {
"error": {
"generic": "Something went wrong... 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!"
},
"title": "Entity Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"entity": {
"info": {
"actions": {
@ -2086,14 +2099,14 @@
"label": "False positive"
},
"in-dossier": {
"description": "Do not {type} \"{value}\" in any document of the current dossier.",
"description": "Do not {type, select, hint{annotate} other{redact}} \"{value}\" in any document of the current dossier.",
"description-bulk": "Do not redact the selected terms as their respective types in any dossier.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Remove from dossier",
"label-bulk": "No longer redact in any dossier"
},
"only-here": {
"description": "Do not {type, select, undefined{redact} other{type}} \"{value}\" at this position in the current document.",
"description": "Do not {type, select, hint{annotate} other{redact}} \"{value}\" at this position in the current document.",
"description-bulk": "Do not redact the selected terms at this position in the current document.",
"label": "Remove here"
}
@ -2239,19 +2252,6 @@
},
"title": "Structured Component Management"
},
"rules-screen": {
"error": {
"generic": "Something went wrong... 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": "Rules updated!"
},
"title": "Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"search-screen": {
"cols": {
"assignee": "Assignee",

View File

@ -245,13 +245,13 @@
"dossier-templates": "Dossier-Vorlage",
"entities": "",
"entity-info": "",
"entity-rule-editor": "",
"false-positive": "",
"false-recommendations": "",
"file-attributes": "",
"justifications": "",
"license-information": "",
"reports": "",
"rule-editor": "",
"settings": "Einstellungen",
"user-management": "",
"watermarks": ""
@ -1284,6 +1284,19 @@
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
}
},
"entity-rules-screen": {
"error": {
"generic": ""
},
"errors-found": "",
"revert-changes": "",
"save-changes": "",
"success": {
"generic": ""
},
"title": "",
"warning-text": ""
},
"entity": {
"info": {
"actions": {
@ -2239,19 +2252,6 @@
},
"title": ""
},
"rules-screen": {
"error": {
"generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!"
},
"errors-found": "",
"revert-changes": "Anmeldedaten speichern",
"save-changes": "Änderungen speichern",
"success": {
"generic": "Die Regeln wurden aktualisiert!"
},
"title": "",
"warning-text": ""
},
"search-screen": {
"cols": {
"assignee": "Bevollmächtigter",

View File

@ -245,13 +245,13 @@
"dossier-templates": "Dossier Templates",
"entities": "Entities",
"entity-info": "Info",
"entity-rule-editor": "Entity Rule Editor",
"false-positive": "False Positive",
"false-recommendations": "False Recommendations",
"file-attributes": "File Attributes",
"justifications": "Justifications",
"license-information": "License Information",
"reports": "Reports",
"rule-editor": "Rule Editor",
"settings": "Settings",
"user-management": "User Management",
"watermarks": "Watermarks"
@ -1284,6 +1284,19 @@
"title": "{length} {length, plural, one{entity} other{entities}}"
}
},
"entity-rules-screen": {
"error": {
"generic": "Something went wrong... 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!"
},
"title": "Entity Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"entity": {
"info": {
"actions": {
@ -2239,19 +2252,6 @@
},
"title": "Component View"
},
"rules-screen": {
"error": {
"generic": "Something went wrong... 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": "Rules updated!"
},
"title": "Rule Editor",
"warning-text": "Warning: experimental feature!"
},
"search-screen": {
"cols": {
"assignee": "Assignee",

View File

@ -1,4 +1,4 @@
FROM node:20.5-buster as builder
FROM node:20.6-buster as builder
WORKDIR /ng-app

View File

@ -3,9 +3,13 @@
*/
export interface IRules {
/**
* The DossierTemplate Id for these rules
* The DossierTemplate ID for these rules
*/
dossierTemplateId?: string;
/**
* The file type to be retrieved/saved under, defaults to ENTITY
*/
ruleFileType?: 'ENTITY' | 'COMPONENT';
/**
* The actual string of rules.
*/