Merge remote-tracking branch 'origin/master' into RED-9381
This commit is contained in:
commit
b2091d4f83
@ -8,7 +8,7 @@ import {
|
||||
IconButtonTypes,
|
||||
IqserDialogComponent,
|
||||
} from '@iqser/common-ui';
|
||||
import { Dictionary, Dossier, SuperTypes } from '@red/domain';
|
||||
import { Dictionary, Dossier } from '@red/domain';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { Roles } from '@users/roles';
|
||||
@ -47,12 +47,12 @@ export class EditAnnotationDialogComponent
|
||||
extends IqserDialogComponent<EditAnnotationDialogComponent, EditRedactionData, EditRedactResult>
|
||||
implements OnInit
|
||||
{
|
||||
readonly #dossier: Dossier;
|
||||
readonly roles = Roles;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly redactedTexts: string[];
|
||||
dictionaries: Dictionary[] = [];
|
||||
form: UntypedFormGroup;
|
||||
readonly #dossier: Dossier;
|
||||
|
||||
constructor(
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
@ -60,7 +60,7 @@ export class EditAnnotationDialogComponent
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
) {
|
||||
super();
|
||||
this.#dossier = _activeDossiersService.find(this.data.dossierId);
|
||||
this.#dossier = this._activeDossiersService.find(this.data.dossierId);
|
||||
const annotations = this.data.annotations;
|
||||
this.redactedTexts = annotations.map(annotation => annotation.value);
|
||||
this.form = this.#getForm();
|
||||
@ -83,10 +83,6 @@ export class EditAnnotationDialogComponent
|
||||
this.#setTypes();
|
||||
}
|
||||
|
||||
reasonChanged() {
|
||||
this.form.patchValue({ reason: this.dictionaries.find(d => d.type === SuperTypes.ManualRedaction) });
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const value = this.form.value;
|
||||
this.dialogRef.close({
|
||||
@ -106,8 +102,4 @@ export class EditAnnotationDialogComponent
|
||||
type: [sameType ? this.data.annotations[0].type : null],
|
||||
});
|
||||
}
|
||||
|
||||
#allRectangles() {
|
||||
return this.data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div *ngIf="dictionaries && selectedDictionary" class="dictionary-content">
|
||||
<div class="dictionaries">
|
||||
<div
|
||||
(click)="selectDictionary(dictionary)"
|
||||
(click)="selectDictionary(dictionary, undefined, true)"
|
||||
*ngFor="let dictionary of dictionaries"
|
||||
[class.active]="dictionary.label === selectedDictionary.label"
|
||||
class="dictionary"
|
||||
@ -33,44 +33,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="entries">
|
||||
<div class="header-wrapper">
|
||||
<div class="header-left">
|
||||
<div class="heading">
|
||||
<div class="flex-align-items-center">
|
||||
{{ selectedDictionary?.label }}
|
||||
<iqser-circle-button
|
||||
(action)="openEditDictionaryModal()"
|
||||
*ngIf="selectedDictionary.dossierDictionaryOnly && selectedDictionary.hasDictionary"
|
||||
[size]="20"
|
||||
[tooltip]="'edit-dossier-dialog.dictionary.edit-button-tooltip' | translate"
|
||||
class="p-left-8"
|
||||
icon="iqser:edit"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:entries"></mat-icon>
|
||||
<ng-container *ngIf="activeEntryType === entryTypes.ENTRY || selectedDictionary.hint">
|
||||
{{
|
||||
'edit-dossier-dialog.dictionary.entries'
|
||||
| translate: { length: entriesToDisplay.length, hint: selectedDictionary.hint }
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="activeEntryType === entryTypes.FALSE_POSITIVE && !selectedDictionary.hint">
|
||||
{{
|
||||
'edit-dossier-dialog.dictionary.false-positive-entries' | translate: { length: entriesToDisplay.length }
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="activeEntryType === entryTypes.FALSE_RECOMMENDATION && !selectedDictionary.hint">
|
||||
{{
|
||||
'edit-dossier-dialog.dictionary.false-recommendation-entries'
|
||||
| translate: { length: entriesToDisplay.length }
|
||||
}}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="heading flex-align-items-center">
|
||||
{{ selectedDictionary?.label }}
|
||||
<iqser-circle-button
|
||||
(action)="openEditDictionaryModal()"
|
||||
*ngIf="selectedDictionary.dossierDictionaryOnly && selectedDictionary.hasDictionary"
|
||||
[size]="20"
|
||||
[tooltip]="'edit-dossier-dialog.dictionary.edit-button-tooltip' | translate"
|
||||
class="p-left-8"
|
||||
icon="iqser:edit"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
|
||||
<redaction-dictionary-manager
|
||||
@ -85,14 +57,14 @@
|
||||
[withFloatingActions]="false"
|
||||
>
|
||||
<ng-container slot="typeSwitch">
|
||||
<div *ngIf="!selectedDictionary.hint" [class.read-only]="!canEdit" class="header-right flex">
|
||||
<div class="flex">
|
||||
<iqser-icon-button
|
||||
(click)="selectEntryType(entryTypes.ENTRY)"
|
||||
(click)="selectEntryType(entryTypes.ENTRY, true)"
|
||||
[active]="activeEntryType === entryTypes.ENTRY"
|
||||
[label]="'edit-dossier-dialog.dictionary.to-redact' | translate: { count: selectedDictionary.entries.length }"
|
||||
></iqser-icon-button>
|
||||
<iqser-icon-button
|
||||
(click)="selectEntryType(entryTypes.FALSE_POSITIVE)"
|
||||
(click)="selectEntryType(entryTypes.FALSE_POSITIVE, true)"
|
||||
[active]="activeEntryType === entryTypes.FALSE_POSITIVE"
|
||||
[label]="
|
||||
'edit-dossier-dialog.dictionary.false-positives'
|
||||
@ -100,7 +72,7 @@
|
||||
"
|
||||
></iqser-icon-button>
|
||||
<iqser-icon-button
|
||||
(click)="selectEntryType(entryTypes.FALSE_RECOMMENDATION)"
|
||||
(click)="selectEntryType(entryTypes.FALSE_RECOMMENDATION, true)"
|
||||
[active]="activeEntryType === entryTypes.FALSE_RECOMMENDATION"
|
||||
[label]="
|
||||
'edit-dossier-dialog.dictionary.false-recommendations'
|
||||
|
||||
@ -11,13 +11,14 @@
|
||||
|
||||
.dictionaries {
|
||||
border-right: 1px solid var(--iqser-separator);
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
width: 200px;
|
||||
@include common-mixins.scroll-bar;
|
||||
|
||||
.dictionary {
|
||||
height: 40px;
|
||||
padding: 15px;
|
||||
border: 1px solid var(--iqser-separator);
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
@ -43,33 +44,7 @@
|
||||
.entries {
|
||||
flex-grow: 1;
|
||||
padding: 16px 0 16px 12px;
|
||||
|
||||
.header-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
|
||||
.iqser-input-group {
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.read-only {
|
||||
padding-right: 100px;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
> div {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { CircleButtonComponent, IconButtonComponent, IqserDialog, LoadingService } from '@iqser/common-ui';
|
||||
import {
|
||||
CircleButtonComponent,
|
||||
ConfirmationDialogService,
|
||||
ConfirmOptions,
|
||||
IconButtonComponent,
|
||||
IqserDialog,
|
||||
LoadingService,
|
||||
} from '@iqser/common-ui';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, Dossier } from '@red/domain';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
@ -42,6 +49,14 @@ export class EditDossierDictionaryComponent implements OnInit {
|
||||
readonly entryTypes = DictionaryEntryTypes;
|
||||
@ViewChild(DictionaryManagerComponent, { static: false }) private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
|
||||
constructor(
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
private readonly _confirmationDialogService: ConfirmationDialogService,
|
||||
) {}
|
||||
|
||||
get changed(): boolean {
|
||||
return this._dictionaryManager?.editor.hasChanges;
|
||||
}
|
||||
@ -54,13 +69,6 @@ export class EditDossierDictionaryComponent implements OnInit {
|
||||
return this._dictionaryManager?.editor.hasChanges;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this._loadingService.start();
|
||||
this.canEdit = this._permissionsService.canEditDossierDictionary(this.dossier);
|
||||
@ -95,16 +103,23 @@ export class EditDossierDictionaryComponent implements OnInit {
|
||||
this._dictionaryManager.revert();
|
||||
}
|
||||
|
||||
selectDictionary(dictionary: Dictionary, entryType?: DictionaryEntryType) {
|
||||
async selectDictionary(dictionary: Dictionary, entryType?: DictionaryEntryType, checkForChanges = false) {
|
||||
if (checkForChanges && !(await this._checkForChanges()).continue) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedDictionary = dictionary;
|
||||
this.selectEntryType(entryType);
|
||||
await this.selectEntryType(entryType);
|
||||
}
|
||||
|
||||
selectEntryType(selectedEntryType: DictionaryEntryType) {
|
||||
this.activeEntryType = selectedEntryType ?? this.activeEntryType;
|
||||
const entryType = this.selectedDictionary.hint ? DictionaryEntryTypes.ENTRY : this.activeEntryType;
|
||||
async selectEntryType(selectedEntryType: DictionaryEntryType, checkForChanges = false) {
|
||||
if (checkForChanges && !(await this._checkForChanges()).continue) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (entryType) {
|
||||
this.activeEntryType = selectedEntryType ?? this.activeEntryType;
|
||||
|
||||
switch (this.activeEntryType) {
|
||||
case DictionaryEntryTypes.ENTRY: {
|
||||
this.entriesToDisplay = this.selectedDictionary.entries;
|
||||
break;
|
||||
@ -141,13 +156,32 @@ export class EditDossierDictionaryComponent implements OnInit {
|
||||
await this.#retrieveDictionaries();
|
||||
}
|
||||
|
||||
private async _checkForChanges(): Promise<{ continue: boolean }> {
|
||||
if (this.changed) {
|
||||
const dialogRef = this._confirmationDialogService.open({ disableConfirm: !this.valid });
|
||||
const result = await firstValueFrom(dialogRef.afterClosed());
|
||||
if (result === ConfirmOptions.CONFIRM) {
|
||||
this._loadingService.start();
|
||||
const { success } = await this.save();
|
||||
this._loadingService.stop();
|
||||
if (!success) {
|
||||
return { continue: false };
|
||||
}
|
||||
} else if (!result) {
|
||||
return { continue: false };
|
||||
}
|
||||
}
|
||||
|
||||
return { continue: true };
|
||||
}
|
||||
|
||||
async #updateDossierDictionary() {
|
||||
await this.#retrieveDictionaries();
|
||||
let dictionaryToSelect = this.dictionaries[0];
|
||||
if (this.selectedDictionary) {
|
||||
dictionaryToSelect = this.dictionaries.find(d => d.type === this.selectedDictionary.type);
|
||||
}
|
||||
this.selectDictionary(dictionaryToSelect, this.activeEntryType);
|
||||
await this.selectDictionary(dictionaryToSelect, this.activeEntryType);
|
||||
}
|
||||
|
||||
async #retrieveDictionaries() {
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="editor.openFindPanel()"
|
||||
(action)="editor.toggleFindPanel()"
|
||||
[attr.aria-expanded]="_isSearchOpen()"
|
||||
[matTooltip]="'dictionary-overview.search' | translate"
|
||||
class="ml-8"
|
||||
icon="iqser:search"
|
||||
@ -92,6 +93,7 @@
|
||||
|
||||
<div class="editor-container">
|
||||
<redaction-editor
|
||||
[(isSearchOpen)]="_isSearchOpen"
|
||||
[canEdit]="canEdit"
|
||||
[diffEditorText]="diffEditorText"
|
||||
[initialEntries]="initialEntries"
|
||||
|
||||
@ -1,4 +1,15 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
signal,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { CircleButtonComponent, IconButtonComponent, IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, DictionaryType, Dossier, DossierTemplate, IDictionary } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -77,6 +88,7 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
selectDossierTemplate = { name: _('dictionary-overview.compare.select-dossier-template') } as DossierTemplate;
|
||||
compare = false;
|
||||
dictionaries: List<Dictionary> = this.#dictionaries;
|
||||
protected readonly _isSearchOpen = signal(false);
|
||||
protected initialDossierTemplateId: string;
|
||||
readonly #currentTab = window.location.href.split('/').pop();
|
||||
#dossierTemplate = this.dossierTemplatesService.all[0];
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { Component, Input, model, OnChanges, OnInit, SimpleChanges, untracked } from '@angular/core';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { EditorThemeService } from '@services/editor-theme.service';
|
||||
import { Subject } from 'rxjs';
|
||||
@ -39,6 +39,7 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
@Input() diffEditorText: string;
|
||||
@Input() @OnChange<List, EditorComponent>('revert') initialEntries: List;
|
||||
@Input() canEdit = false;
|
||||
readonly isSearchOpen = model.required<boolean>();
|
||||
/**
|
||||
* Used as [modified] input on diff editor
|
||||
* Shouldn't be updated when editing in diff editor.
|
||||
@ -84,9 +85,14 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
return this.currentEntries.length;
|
||||
}
|
||||
|
||||
async openFindPanel(): Promise<void> {
|
||||
async toggleFindPanel(): Promise<void> {
|
||||
const isFindPanelOpen = untracked(this.isSearchOpen);
|
||||
const editor = this.showDiffEditor ? this._diffEditor.getOriginalEditor() : this.codeEditor;
|
||||
await editor.getAction('actions.find').run();
|
||||
if (isFindPanelOpen) {
|
||||
await (editor.getContribution('editor.contrib.findController') as any).closeFindWidget();
|
||||
} else {
|
||||
await editor.getAction('actions.find').run();
|
||||
}
|
||||
}
|
||||
|
||||
onPaste(event: ClipboardEvent) {
|
||||
@ -127,11 +133,13 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
this._diffEditor.getModifiedEditor().onDidChangeModelContent(() => {
|
||||
this.value = this._diffEditor.getModel().modified.getValue();
|
||||
});
|
||||
this._initializeFindWidget(editor.getOriginalEditor());
|
||||
this.#setTheme();
|
||||
}
|
||||
|
||||
onCodeEditorInit(editor: MonacoStandaloneCodeEditor): void {
|
||||
this.codeEditor = editor;
|
||||
this._initializeFindWidget(editor);
|
||||
this.#setTheme();
|
||||
}
|
||||
|
||||
@ -143,6 +151,15 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
this._editorTextChanged$.next(this.value);
|
||||
}
|
||||
|
||||
private _initializeFindWidget(editor: MonacoStandaloneCodeEditor): void {
|
||||
this.isSearchOpen.set(false);
|
||||
(editor.getContribution('editor.contrib.findController') as any).getState().onFindReplaceStateChange(event => {
|
||||
if (event.isRevealed) {
|
||||
this.isSearchOpen.update(v => !v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#getDecorations(newText: string) {
|
||||
const currentEntries = newText.split('\n');
|
||||
const newDecorations: IModelDeltaDecoration[] = [];
|
||||
|
||||
@ -1234,11 +1234,8 @@
|
||||
"save": "Speichern",
|
||||
"title": "{label} bearbeiten"
|
||||
},
|
||||
"entries": "{length} {length, plural, one{Eintrag} other{Einträge}}",
|
||||
"entries-count": "",
|
||||
"false-positive-entries": "{length} {length, plural, one{Falsch-Positiver} other{Falsch-Positive}}",
|
||||
"false-positives": "Falsch-Positive ({count})",
|
||||
"false-recommendation-entries": "{length} {length, plural, one{falsche Empfehlung} other{falsche Empfehlungen}}",
|
||||
"false-recommendations": "Falsche Empfehlungen ({count})",
|
||||
"to-redact": "Schwärzungen ({count})"
|
||||
},
|
||||
|
||||
@ -1234,11 +1234,8 @@
|
||||
"save": "Save",
|
||||
"title": "Edit {label}"
|
||||
},
|
||||
"entries": "{length} {length, plural, one{entry} other{entries}} to redact",
|
||||
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
|
||||
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
|
||||
"false-positives": "False positives ({count})",
|
||||
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
|
||||
"false-recommendations": "False recommendations ({count})",
|
||||
"to-redact": "Entries ({count})"
|
||||
},
|
||||
|
||||
@ -1234,11 +1234,8 @@
|
||||
"save": "",
|
||||
"title": ""
|
||||
},
|
||||
"entries": "{length} {length, plural, one{entry} other{entries}} to {hint, select, true{annotate} other{redact}}",
|
||||
"entries-count": "",
|
||||
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
|
||||
"false-positives": "False positives ({count})",
|
||||
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
|
||||
"false-recommendations": "False recommendations ({count})",
|
||||
"to-redact": "To redact ({count})"
|
||||
},
|
||||
|
||||
@ -1234,11 +1234,8 @@
|
||||
"save": "",
|
||||
"title": ""
|
||||
},
|
||||
"entries": "{length} {length, plural, one{entry} other{entries}} to {hint, select, true{annotate} other{redact}}",
|
||||
"entries-count": "{count} {count, plural, one{entry} other{entries}}",
|
||||
"false-positive-entries": "{length} false positive {length, plural, one{entry} other{entries}}",
|
||||
"false-positives": "False positives ({count})",
|
||||
"false-recommendation-entries": "{length} false recommendation {length, plural, one{entry} other{entries}}",
|
||||
"false-recommendations": "False recommendations ({count})",
|
||||
"to-redact": "To redact ({count})"
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user