compare dictionary to other dossiers
This commit is contained in:
parent
5cf7ccb81c
commit
a495c55b3d
@ -16,7 +16,7 @@
|
||||
</div>
|
||||
<div *ngIf="searchText.length > 0" class="with-input">
|
||||
<div class="search-match-text">
|
||||
{{ currentMatch + '/' + searchMatches.length }}
|
||||
{{ currentMatch + '/' + findMatches.length }}
|
||||
</div>
|
||||
<mat-icon
|
||||
(click)="previousSearchMatch()"
|
||||
@ -36,60 +36,40 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form [formGroup]="form">
|
||||
<div class="red-input-group mr-16">
|
||||
<mat-checkbox color="primary" formControlName="active">
|
||||
{{ 'dictionary-overview.compare.compare' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="red-input-group w-200 mr-8">
|
||||
<mat-select formControlName="dossierTemplate">
|
||||
<mat-option
|
||||
*ngFor="let dossierTemplate of dossierTemplates"
|
||||
[value]="dossierTemplate"
|
||||
>
|
||||
{{
|
||||
dossierTemplate === selectDossierTemplate
|
||||
? (dossierTemplate.name | translate)
|
||||
: dossierTemplate.name
|
||||
}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
<div class="red-input-group w-200">
|
||||
<mat-select formControlName="dictionary">
|
||||
<mat-option *ngFor="let dictionary of dictionaries" [value]="dictionary">
|
||||
{{
|
||||
dictionary === selectDictionary
|
||||
? (dictionary.label | translate)
|
||||
: dictionary.label
|
||||
}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
</form>
|
||||
<div class="red-input-group mr-16">
|
||||
<mat-checkbox [(ngModel)]="compare" color="primary">
|
||||
{{ 'dictionary-overview.compare.compare' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="red-input-group w-200 mr-8">
|
||||
<mat-select [(ngModel)]="dossier" [disabled]="!compare">
|
||||
<mat-option [value]="selectDossier">{{
|
||||
selectDossier.name | translate
|
||||
}}</mat-option>
|
||||
<mat-option *ngFor="let dossier of dossiers" [value]="dossier">
|
||||
{{ dossier.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-container">
|
||||
<ngx-monaco-editor
|
||||
*ngIf="!showDiffEditor"
|
||||
[options]="editorOptions"
|
||||
[(ngModel)]="codeEditorText"
|
||||
(init)="onCodeEditorInit($event)"
|
||||
*ngIf="!compare || !showDiffEditor"
|
||||
[(ngModel)]="editorValue"
|
||||
[options]="editorOptions"
|
||||
></ngx-monaco-editor>
|
||||
|
||||
<ngx-monaco-diff-editor
|
||||
*ngIf="showDiffEditor"
|
||||
(init)="onDiffEditorInit($event)"
|
||||
*ngIf="compare && showDiffEditor"
|
||||
[modified]="editorValue"
|
||||
[options]="editorOptions"
|
||||
[original]="diffEditorText"
|
||||
[modified]="codeEditorText"
|
||||
(init)="onDiffEditorInit($event)"
|
||||
></ngx-monaco-diff-editor>
|
||||
|
||||
<div
|
||||
*ngIf="form.get('active').value && form.get('dictionary').value === selectDictionary"
|
||||
class="no-dictionary-selected"
|
||||
>
|
||||
<div *ngIf="compare && dossier === selectDossier" class="no-dictionary-selected">
|
||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||
<span class="heading-l" translate="dictionary-overview.select-dictionary"></span>
|
||||
</div>
|
||||
@ -97,11 +77,11 @@
|
||||
|
||||
<div
|
||||
*ngIf="withFloatingActions && hasChanges && canEdit"
|
||||
[class.offset]="form.get('active').value"
|
||||
[class.offset]="compare"
|
||||
class="changes-box"
|
||||
>
|
||||
<redaction-icon-button
|
||||
(action)="saveEntries()"
|
||||
(action)="saveDictionary.emit(currentEntries)"
|
||||
icon="red:check"
|
||||
text="dictionary-overview.save-changes"
|
||||
type="primary"
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { DictionaryControllerService, DossierTemplateModel } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { debounce } from '@utils/debounce';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import ICodeEditor = monaco.editor.ICodeEditor;
|
||||
import IDiffEditor = monaco.editor.IDiffEditor;
|
||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||
@ -31,7 +31,7 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
saveDictionary = new EventEmitter<string[]>();
|
||||
|
||||
currentMatch = 0;
|
||||
searchMatches: FindMatch[] = [];
|
||||
findMatches: FindMatch[] = [];
|
||||
currentEntries: string[] = [];
|
||||
editorOptions: IStandaloneEditorConstructionOptions = {
|
||||
theme: 'vs',
|
||||
@ -43,11 +43,8 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
showDiffEditor = false;
|
||||
searchText = '';
|
||||
|
||||
selectDossierTemplate = { name: 'dictionary-overview.compare.select-dossier-template' };
|
||||
selectDictionary = new TypeValueWrapper(null, 'dictionary-overview.compare.select-dictionary');
|
||||
dossierTemplates: DossierTemplateModel[];
|
||||
dictionaries: TypeValueWrapper[] = [this.selectDictionary];
|
||||
form: FormGroup;
|
||||
selectDossier = { name: 'dictionary-overview.compare.select-dossier' };
|
||||
compare: false;
|
||||
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _diffEditor: IDiffEditor;
|
||||
@ -60,43 +57,48 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
private readonly _formBuilder: FormBuilder
|
||||
) {
|
||||
this.currentEntries = this.initialEntries;
|
||||
this.form = this._formBuilder.group({
|
||||
active: [false],
|
||||
dossierTemplate: [{ value: this.selectDossierTemplate, disabled: true }],
|
||||
dictionary: [{ value: this.selectDictionary, disabled: true }]
|
||||
});
|
||||
}
|
||||
|
||||
this.form.valueChanges.subscribe((value) => {
|
||||
this._setFieldStatus('dossierTemplate', value.active);
|
||||
this._setFieldStatus(
|
||||
'dictionary',
|
||||
value.active &&
|
||||
this.form.get('dossierTemplate').value !== this.selectDossierTemplate
|
||||
);
|
||||
this._loadDictionaries();
|
||||
});
|
||||
private _dossier: DossierWrapper = this.selectDossier as DossierWrapper;
|
||||
|
||||
this.dossierTemplates = [
|
||||
this.selectDossierTemplate,
|
||||
...this._appStateService.dossierTemplates
|
||||
];
|
||||
get dossier() {
|
||||
return this._dossier;
|
||||
}
|
||||
|
||||
this.form.controls.dossierTemplate.valueChanges.subscribe(() => {
|
||||
this._onDossierTemplateChanged();
|
||||
});
|
||||
set dossier(dossier: DossierWrapper) {
|
||||
this._dossier = dossier;
|
||||
|
||||
this.form.controls.active.valueChanges.subscribe((value) => {
|
||||
this.showDiffEditor =
|
||||
value && this.form.get('dictionary').value !== this.selectDictionary;
|
||||
});
|
||||
if (dossier === this.selectDossier) {
|
||||
this.showDiffEditor = false;
|
||||
this.diffEditorText = '';
|
||||
return;
|
||||
}
|
||||
|
||||
this.form.controls.dictionary.valueChanges.subscribe((dictionary) => {
|
||||
this._onDictionaryChanged(dictionary).subscribe((data) => {
|
||||
this.diffEditorText = data;
|
||||
this.showDiffEditor = dictionary !== this.selectDictionary;
|
||||
if (this.showDiffEditor) this._diffEditor?.getOriginalEditor().setValue(data);
|
||||
this._onDossierChanged(dossier)
|
||||
.pipe(take(1))
|
||||
.subscribe(entries => {
|
||||
this.diffEditorText = entries;
|
||||
this.showDiffEditor = true;
|
||||
if (this.showDiffEditor)
|
||||
this._diffEditor?.getOriginalEditor().setValue(this.diffEditorText);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get dossiers() {
|
||||
return this._appStateService.allDossiers;
|
||||
}
|
||||
|
||||
get editorValue(): string {
|
||||
return this.currentEntries.join('\n');
|
||||
}
|
||||
|
||||
set editorValue(text: string) {
|
||||
this.currentEntries = text.split('\n');
|
||||
this.codeEditorTextChanged();
|
||||
}
|
||||
|
||||
get hasChanges(): boolean {
|
||||
return this.currentEntries.toString() !== this.initialEntries.toString();
|
||||
}
|
||||
|
||||
onDiffEditorInit(editor: IDiffEditor): void {
|
||||
@ -116,14 +118,6 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
(window as any).monaco.editor.setTheme('redaction');
|
||||
}
|
||||
|
||||
get editorValue(): string {
|
||||
return this._codeEditor?.getModel()?.getValue();
|
||||
}
|
||||
|
||||
set editorValue(value: string) {
|
||||
this._codeEditor?.getModel()?.setValue(value);
|
||||
}
|
||||
|
||||
revert() {
|
||||
this.currentEntries = this.initialEntries;
|
||||
this.searchChanged('');
|
||||
@ -132,25 +126,46 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
@debounce()
|
||||
searchChanged(text: string) {
|
||||
this.searchText = text.toLowerCase();
|
||||
this.searchMatches = this._getMatches(this.searchText);
|
||||
this.findMatches = this._getMatches(this.searchText);
|
||||
this._applySearchDecorations();
|
||||
|
||||
this.currentMatch = 0;
|
||||
this.nextSearchMatch();
|
||||
}
|
||||
|
||||
@debounce()
|
||||
codeEditorTextChanged() {
|
||||
const newDecorations = this.currentEntries
|
||||
.filter(entry => this._isNew(entry))
|
||||
.map(entry => this._getDecoration(entry));
|
||||
|
||||
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
|
||||
}
|
||||
|
||||
nextSearchMatch(): void {
|
||||
if (this.findMatches.length <= 0) return;
|
||||
|
||||
this.currentMatch = this.currentMatch < this.findMatches.length ? this.currentMatch + 1 : 1;
|
||||
this._scrollToCurrentMatch();
|
||||
}
|
||||
|
||||
previousSearchMatch(): void {
|
||||
if (this.findMatches.length <= 0) return;
|
||||
|
||||
this.currentMatch = this.currentMatch > 1 ? this.currentMatch - 1 : this.findMatches.length;
|
||||
this._scrollToCurrentMatch();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.revert();
|
||||
}
|
||||
|
||||
private _applySearchDecorations() {
|
||||
this._searchDecorations = this._codeEditor.deltaDecorations(this._searchDecorations, []);
|
||||
this._searchDecorations = this._codeEditor?.deltaDecorations(this._searchDecorations, []);
|
||||
|
||||
const decorations = this.searchMatches.map(
|
||||
(match) =>
|
||||
({
|
||||
range: match.range,
|
||||
options: { inlineClassName: 'search-marker' }
|
||||
} as IModelDeltaDecoration)
|
||||
);
|
||||
const decorations = this.findMatches.map(match => this._getSearchDecoration(match));
|
||||
|
||||
this._searchDecorations = this._codeEditor.deltaDecorations(
|
||||
this._searchDecorations = this._codeEditor?.deltaDecorations(
|
||||
this._searchDecorations,
|
||||
decorations
|
||||
);
|
||||
@ -161,113 +176,44 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
return model?.findMatches(text, false, false, false, null, false) || [];
|
||||
}
|
||||
|
||||
get codeEditorText(): string {
|
||||
return this.currentEntries.join('\n');
|
||||
}
|
||||
|
||||
set codeEditorText(text: string) {
|
||||
this.currentEntries = text.split('\n');
|
||||
this.codeEditorTextChanged();
|
||||
}
|
||||
|
||||
@debounce()
|
||||
codeEditorTextChanged() {
|
||||
const newDecorations = this.currentEntries
|
||||
.filter((entry) => this._isNew(entry))
|
||||
.map((entry) => this._makeDecorationFor(entry));
|
||||
|
||||
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
|
||||
}
|
||||
|
||||
private _isNew(entry: string): boolean {
|
||||
return this.initialEntries.indexOf(entry) < 0 && entry?.trim().length > 0;
|
||||
}
|
||||
|
||||
private _makeDecorationFor(entry: string): IModelDeltaDecoration {
|
||||
private _getDecoration(entry: string): IModelDeltaDecoration {
|
||||
const line = this.currentEntries.indexOf(entry) + 1;
|
||||
const cssClass = entry.length < MIN_WORD_LENGTH ? 'too-short-marker' : 'changed-row-marker';
|
||||
const range = new monaco.Range(line, 1, line, 1);
|
||||
|
||||
return {
|
||||
range: new monaco.Range(line, 1, line, 1),
|
||||
options: { isWholeLine: true, className: cssClass }
|
||||
} as IModelDeltaDecoration;
|
||||
return { range: range, options: { isWholeLine: true, className: cssClass } };
|
||||
}
|
||||
|
||||
get hasChanges(): boolean {
|
||||
return (
|
||||
this.currentEntries.length &&
|
||||
this.currentEntries.filter((e) => e.trim().length > 0).length <
|
||||
this.initialEntries.length
|
||||
);
|
||||
}
|
||||
|
||||
nextSearchMatch(): void {
|
||||
if (this.searchMatches?.length > 0) {
|
||||
this.currentMatch =
|
||||
this.currentMatch < this.searchMatches.length ? this.currentMatch + 1 : 1;
|
||||
this._scrollToCurrentMatch();
|
||||
}
|
||||
}
|
||||
|
||||
previousSearchMatch(): void {
|
||||
if (this.searchMatches.length > 0) {
|
||||
this.currentMatch =
|
||||
this.currentMatch > 1 ? this.currentMatch - 1 : this.searchMatches.length;
|
||||
this._scrollToCurrentMatch();
|
||||
}
|
||||
}
|
||||
|
||||
private _setFieldStatus(field: 'dossierTemplate' | 'dictionary', enabled: boolean) {
|
||||
this.form.get(field)[enabled ? 'enable' : 'disable']({ emitEvent: false });
|
||||
}
|
||||
|
||||
private _loadDictionaries() {
|
||||
const dossierTemplateId = this.form.get('dossierTemplate').value.dossierTemplateId;
|
||||
if (!dossierTemplateId) {
|
||||
this.dictionaries = [this.selectDictionary];
|
||||
return;
|
||||
}
|
||||
const appStateDictionaryData = this._appStateService.dictionaryData[dossierTemplateId];
|
||||
this.dictionaries = [
|
||||
this.selectDictionary,
|
||||
...Object.values(appStateDictionaryData).filter(
|
||||
(d) => !d.virtual || d.type === 'false_positive'
|
||||
)
|
||||
];
|
||||
private _getSearchDecoration(match: FindMatch): IModelDeltaDecoration {
|
||||
return { range: match.range, options: { inlineClassName: 'search-marker' } };
|
||||
}
|
||||
|
||||
private _scrollToCurrentMatch(): void {
|
||||
const range = this.searchMatches[this.currentMatch - 1].range;
|
||||
const range = this.findMatches[this.currentMatch - 1].range;
|
||||
|
||||
this._codeEditor.setSelection(range);
|
||||
this._codeEditor.revealLineInCenter(range.startLineNumber, SMOOTH_SCROLL);
|
||||
}
|
||||
|
||||
private _onDossierTemplateChanged() {
|
||||
this._loadDictionaries();
|
||||
this.form.patchValue({ dictionary: this.selectDictionary });
|
||||
}
|
||||
private _onDossierChanged({
|
||||
dossierId,
|
||||
dossierTemplateId
|
||||
}: DossierWrapper): Observable<string> {
|
||||
const dictionary$ = this._dictionaryControllerService.getDictionaryForType(
|
||||
dossierTemplateId,
|
||||
'dossier_redaction',
|
||||
dossierId
|
||||
);
|
||||
|
||||
private _onDictionaryChanged(dictionary: TypeValueWrapper): Observable<string> {
|
||||
if (dictionary === this.selectDictionary) {
|
||||
return of('');
|
||||
}
|
||||
return this._dictionaryControllerService
|
||||
.getDictionaryForType(dictionary.dossierTemplateId, dictionary.type)
|
||||
.pipe(map((data) => this._toString(data.entries)));
|
||||
return dictionary$.pipe(map(data => this._toString(data.entries)));
|
||||
}
|
||||
|
||||
private _toString(entries: string[]) {
|
||||
return entries
|
||||
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'accent' }))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
saveEntries() {
|
||||
this.saveDictionary.emit(this.currentEntries);
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.revert();
|
||||
const compareFn = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'accent' });
|
||||
return entries.sort(compareFn).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user