diff --git a/apps/red-ui/src/app/models/dictionary.ts b/apps/red-ui/src/app/models/dictionary.ts new file mode 100644 index 000000000..97ae1eb65 --- /dev/null +++ b/apps/red-ui/src/app/models/dictionary.ts @@ -0,0 +1,36 @@ +import { IDictionary } from '@redaction/red-ui-http'; +import { IListable } from '@iqser/common-ui'; + +export class Dictionary implements IDictionary, IListable { + addToDictionaryAction?: boolean; + caseInsensitive?: boolean; + description?: string; + dossierTemplateId?: string; + entries?: Array; + hexColor?: string; + hint?: boolean; + label?: string; + rank?: number; + recommendation?: boolean; + + constructor(dictionary: IDictionary) { + this.addToDictionaryAction = dictionary.addToDictionaryAction; + this.caseInsensitive = dictionary.caseInsensitive; + this.description = dictionary.description; + this.dossierTemplateId = dictionary.dossierTemplateId; + this.entries = dictionary.entries; + this.hexColor = dictionary.hexColor; + this.hint = dictionary.hint; + this.label = dictionary.label; + this.rank = dictionary.rank; + this.recommendation = dictionary.recommendation; + } + + get id(): string { + return this.label; + } + + get searchKey(): string { + return this.label; + } +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts index c8054ee1b..7814a4b95 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { DictionaryControllerService, ITypeValue } from '@redaction/red-ui-http'; +import { ITypeValue } from '@redaction/red-ui-http'; import { Observable } from 'rxjs'; import { Toaster } from '@iqser/common-ui'; import { TranslateService } from '@ngx-translate/core'; @@ -9,6 +9,7 @@ import { TypeValue } from '@models/file/type-value'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { AppStateService } from '@state/app-state.service'; import { toKebabCase } from '@utils/functions'; +import { DictionaryService } from '@shared/services/dictionary.service'; @Component({ selector: 'redaction-add-edit-dictionary-dialog', @@ -22,7 +23,7 @@ export class AddEditDictionaryDialogComponent { private readonly _dossierTemplateId: string; constructor( - private readonly _dictionaryControllerService: DictionaryControllerService, + private readonly _dictionaryService: DictionaryService, private readonly _appStateService: AppStateService, private readonly _formBuilder: FormBuilder, private readonly _toaster: Toaster, @@ -87,10 +88,10 @@ export class AddEditDictionaryDialogComponent { if (this.dictionary) { // edit mode - observable = this._dictionaryControllerService.updateType(typeValue, this._dossierTemplateId, typeValue.type); + observable = this._dictionaryService.updateType(typeValue, this._dossierTemplateId, typeValue.type); } else { // create mode - observable = this._dictionaryControllerService.addType({ ...typeValue, dossierTemplateId: this._dossierTemplateId }); + observable = this._dictionaryService.addType({ ...typeValue, dossierTemplateId: this._dossierTemplateId }); } observable.subscribe( diff --git a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts index 6846684b7..6f4711c25 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts @@ -1,12 +1,13 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Colors, DictionaryControllerService } from '@redaction/red-ui-http'; +import { Colors } from '@redaction/red-ui-http'; import { Toaster } from '@iqser/common-ui'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { DefaultColorType } from '@models/default-color-key.model'; import { defaultColorsTranslations } from '../../translations/default-colors-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { DictionaryService } from '@shared/services/dictionary.service'; @Component({ selector: 'redaction-edit-color-dialog', @@ -23,7 +24,7 @@ export class EditColorDialogComponent { constructor( private readonly _formBuilder: FormBuilder, - private readonly _dictionaryControllerService: DictionaryControllerService, + private readonly _dictionaryService: DictionaryService, private readonly _toaster: Toaster, private readonly _translateService: TranslateService, private readonly _dialogRef: MatDialogRef, @@ -51,7 +52,7 @@ export class EditColorDialogComponent { }; try { - await this._dictionaryControllerService.setColors(colors, this._dossierTemplateId).toPromise(); + await this._dictionaryService.setColors(colors, this._dossierTemplateId).toPromise(); this._dialogRef.close(true); const color = this._translateService.instant(defaultColorsTranslations[this.colorKey]); this._toaster.info(_('edit-color-dialog.success'), { params: { color: color } }); diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts index 5a8197992..26ed04f75 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; -import { Colors, DictionaryControllerService } from '@redaction/red-ui-http'; +import { Colors } from '@redaction/red-ui-http'; import { ActivatedRoute } from '@angular/router'; import { AdminDialogService } from '../../services/admin-dialog.service'; import { @@ -15,6 +15,7 @@ import { DefaultColorType } from '@models/default-color-key.model'; import { defaultColorsTranslations } from '../../translations/default-colors-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserService } from '@services/user.service'; +import { DictionaryService } from '@shared/services/dictionary.service'; interface ListItem extends IListable { readonly key: string; @@ -44,7 +45,7 @@ export class DefaultColorsScreenComponent extends ListingComponent imp private readonly _activatedRoute: ActivatedRoute, private readonly _appStateService: AppStateService, private readonly _dialogService: AdminDialogService, - private readonly _dictionaryControllerService: DictionaryControllerService + private readonly _dictionaryService: DictionaryService ) { super(_injector); _appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId); @@ -89,7 +90,7 @@ export class DefaultColorsScreenComponent extends ListingComponent imp private async _loadColors() { this._loadingService.start(); - const data = await this._dictionaryControllerService.getColors(this._appStateService.activeDossierTemplateId).toPromise(); + const data = await this._dictionaryService.getColors(this._appStateService.activeDossierTemplateId).toPromise(); this._colorsObj = data; const entities: ListItem[] = Object.keys(data).map(key => ({ id: key, diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts index 258dc62ce..d4feffbc7 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts @@ -1,6 +1,5 @@ import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { DictionaryControllerService } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { catchError, defaultIfEmpty, tap } from 'rxjs/operators'; import { forkJoin, of } from 'rxjs'; @@ -18,6 +17,7 @@ import { import { AdminDialogService } from '../../services/admin-dialog.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserService } from '@services/user.service'; +import { DictionaryService } from '@shared/services/dictionary.service'; const toChartConfig = (dict: TypeValue): DoughnutChartConfig => ({ value: dict.entries?.length ?? 0, @@ -50,7 +50,7 @@ export class DictionaryListingScreenComponent extends ListingComponent - this._dictionaryControllerService.getDictionaryForType(this._appStateService.activeDossierTemplateId, dict.type).pipe( + this._dictionaryControllerService.getFor(this._appStateService.activeDossierTemplateId, dict.type).pipe( tap(values => (dict.entries = values.entries ?? [])), catchError(() => { dict.entries = []; diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts index 5a2199c4a..79c21e88f 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts @@ -1,5 +1,4 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { DictionaryControllerService } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; @@ -7,7 +6,7 @@ import { saveAs } from 'file-saver'; import { ComponentHasChanges } from '@guards/can-deactivate.guard'; import { AdminDialogService } from '../../services/admin-dialog.service'; import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component'; -import { DictionarySaveService } from '@shared/services/dictionary-save.service'; +import { DictionaryService } from '@shared/services/dictionary.service'; import { TypeValue } from '@models/file/type-value'; import { CircleButtonTypes, LoadingService } from '@iqser/common-ui'; import { UserService } from '@services/user.service'; @@ -35,8 +34,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple private readonly _appStateService: AppStateService, private readonly _dialogService: AdminDialogService, protected readonly _translateService: TranslateService, - private readonly _dictionarySaveService: DictionarySaveService, - private readonly _dictionaryControllerService: DictionaryControllerService + private readonly _dictionaryService: DictionaryService ) { super(_translateService); } @@ -51,7 +49,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple this._activatedRoute.snapshot.params.dossierTemplateId ); this.dictionary = this._appStateService.activeDictionary; - this._loadEntries(); + await this._loadEntries(); } openEditDictionaryDialog($event: any) { @@ -75,7 +73,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple $event?.stopPropagation(); this._dialogService.openDialog('confirm', $event, null, async () => { - await this._dictionaryControllerService.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise(); + await this._dictionaryService.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise(); await this._appStateService.loadDictionaryData(); await this._router.navigate([ '/main', @@ -110,30 +108,34 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple saveEntries(entries: string[]) { this._loadingService.start(); - this._dictionarySaveService - .saveEntries(entries, this.entries, this.dictionary.dossierTemplateId, this.dictionary.type, null) - .subscribe( - () => { - this._loadingService.stop(); - this._loadEntries(); - }, - () => { - this._loadingService.stop(); - } - ); - } - - private _loadEntries() { - this._loadingService.start(); - this._dictionaryControllerService.getDictionaryForType(this.dictionary.dossierTemplateId, this.dictionary.type).subscribe( - data => { - this._loadingService.stop(); - this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' })); + this._dictionaryService.saveEntries(entries, this.entries, this.dictionary.dossierTemplateId, this.dictionary.type, null).subscribe( + async () => { + await this._loadEntries(); }, () => { this._loadingService.stop(); - this.entries = []; } ); } + + private async _loadEntries() { + this._loadingService.start(); + await this._dictionaryService + .getFor(this.dictionary.dossierTemplateId, this.dictionary.type) + .toPromise() + .then( + data => { + this._loadingService.stop(); + this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' })); + }, + () => { + this._loadingService.stop(); + this.entries = []; + } + ) + .catch(() => { + this._loadingService.stop(); + this.entries = []; + }); + } } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts index 3c8dcdff6..39508e09a 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts @@ -4,10 +4,10 @@ import { Dossier } from '@state/model/dossier'; import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { PermissionsService } from '@services/permissions.service'; import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component'; -import { DictionarySaveService } from '@shared/services/dictionary-save.service'; +import { DictionaryService } from '@shared/services/dictionary.service'; import { FormBuilder } from '@angular/forms'; import { CircleButtonTypes, LoadingService } from '@iqser/common-ui'; -import { Dictionary, DictionaryControllerService } from '@redaction/red-ui-http'; +import { IDictionary } from '@redaction/red-ui-http'; @Component({ selector: 'redaction-edit-dossier-dictionary', @@ -24,9 +24,8 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa constructor( private readonly _appStateService: AppStateService, - private readonly _dictionarySaveService: DictionarySaveService, + private readonly _dictionaryService: DictionaryService, private readonly _permissionsService: PermissionsService, - private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _loadingService: LoadingService, private readonly _formBuilder: FormBuilder ) { @@ -48,8 +47,8 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa } async updateDisplayName(label: string) { - const typeValue: Dictionary = { ...this.dossier.type, label }; - await this._dictionaryControllerService + const typeValue: IDictionary = { ...this.dossier.type, label }; + await this._dictionaryService .updateType(typeValue, this.dossier.dossierTemplateId, 'dossier_redaction', this.dossier.id) .toPromise(); await this._appStateService.updateDossierDictionary(this.dossier.dossierTemplateId, this.dossier.id); @@ -57,7 +56,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa } async save() { - await this._dictionarySaveService + await this._dictionaryService .saveEntries( this._dictionaryManager.currentEntries, this._dictionaryManager.initialEntries, diff --git a/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts b/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts index f0d4113db..46a87f29b 100644 --- a/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/dossiers.service.ts @@ -2,23 +2,52 @@ import { Injectable, Injector } from '@angular/core'; import { IDossier } from '@redaction/red-ui-http'; import { EntitiesService, List, QueryParam } from '@iqser/common-ui'; import { Dossier } from '@state/model/dossier'; -import { map } from 'rxjs/operators'; +import { filter, map } from 'rxjs/operators'; import { TEMPORARY_INJECTOR } from './injector'; -import { Observable } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { ActivationEnd, Router } from '@angular/router'; +import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; export interface IDossiersStats { totalPeople: number; totalAnalyzedPages: number; } +const getRelatedEvents = filter(event => event instanceof ActivationEnd && event.snapshot.component !== BaseScreenComponent); + @Injectable({ providedIn: 'root' }) export class DossiersService extends EntitiesService { readonly stats$ = this.all$.pipe(map(entities => this._computeStats(entities))); + readonly activeDossierId$: Observable; + readonly activeDossier$: Observable; + private readonly _activeDossierId$ = new BehaviorSubject(null); - constructor(protected readonly _injector: Injector) { + constructor(protected readonly _injector: Injector, private readonly _router: Router) { super(TEMPORARY_INJECTOR(_injector), 'dossier'); + this.activeDossierId$ = this._activeDossierId$.asObservable(); + this.activeDossier$ = this.activeDossierId$.pipe(map(id => this.all.find(dossier => dossier.id === id))); + + _router.events.pipe(getRelatedEvents).subscribe((event: ActivationEnd) => { + const dossierId = event.snapshot.paramMap.get('dossierId'); + const sameIdAsCurrentActive = dossierId === this._activeDossierId$.getValue(); + + if (sameIdAsCurrentActive) { + return; + } + + if (dossierId === null || dossierId === undefined) { + return this._activeDossierId$.next(null); + } + + // const notFound = !this.all.some(dossier => dossier.id === dossierId); + // if (notFound) { + // return this._router.navigate(['/main/dossiers']).then(); + // } + + this._activeDossierId$.next(dossierId); + }); } get(): Observable; @@ -41,7 +70,7 @@ export class DossiersService extends EntitiesService { hardDelete(dossierIds: List): Promise { const body = dossierIds.map(id => ({ key: 'dossierId', value: id })); - return this.delete(body, 'deleted-dossiers/hard-delete').toPromise(); + return this.delete(body, 'deleted-dossiers/hard-delete', body).toPromise(); } private _computeStats(entities: List): IDossiersStats { diff --git a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts index db88b7afa..f38d0bdc5 100644 --- a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts @@ -1,11 +1,6 @@ import { Injectable } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; -import { - AddRedactionRequest, - DictionaryControllerService, - ForceRedactionRequest, - ManualRedactionControllerService -} from '@redaction/red-ui-http'; +import { AddRedactionRequest, ForceRedactionRequest, ManualRedactionControllerService } from '@redaction/red-ui-http'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { Toaster } from '@iqser/common-ui'; import { TranslateService } from '@ngx-translate/core'; @@ -28,7 +23,6 @@ export class ManualAnnotationService { private readonly _translateService: TranslateService, private readonly _toaster: Toaster, private readonly _manualRedactionControllerService: ManualRedactionControllerService, - private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _permissionsService: PermissionsService ) { this.CONFIG = { diff --git a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.ts b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.ts index 909fc063d..0555b8f0d 100644 --- a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.ts @@ -1,11 +1,11 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { DictionaryControllerService } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { Debounce, IconButtonTypes } from '@iqser/common-ui'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; -import { Dossier } from '../../../../state/model/dossier'; +import { Dossier } from '@state/model/dossier'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { DictionaryService } from '@shared/services/dictionary.service'; import ICodeEditor = monaco.editor.ICodeEditor; import IDiffEditor = monaco.editor.IDiffEditor; import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration; @@ -48,10 +48,7 @@ export class DictionaryManagerComponent implements OnChanges, OnInit { private _decorations: string[] = []; private _searchDecorations: string[] = []; - constructor( - private readonly _dictionaryControllerService: DictionaryControllerService, - private readonly _appStateService: AppStateService - ) { + constructor(private readonly _dictionaryService: DictionaryService, private readonly _appStateService: AppStateService) { this.currentEntries = this.initialEntries; } @@ -205,7 +202,7 @@ export class DictionaryManagerComponent implements OnChanges, OnInit { } private _onDossierChanged({ id, dossierTemplateId }: Dossier): Observable { - const dictionary$ = this._dictionaryControllerService.getDictionaryForType(dossierTemplateId, 'dossier_redaction', id); + const dictionary$ = this._dictionaryService.getFor(dossierTemplateId, 'dossier_redaction', id); return dictionary$.pipe(map(data => this._toString(data.entries))); } diff --git a/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts b/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts deleted file mode 100644 index dd4382b47..000000000 --- a/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; -import { Toaster } from '@iqser/common-ui'; -import { DictionaryControllerService } from '@redaction/red-ui-http'; -import { tap } from 'rxjs/operators'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; - -const MIN_WORD_LENGTH = 2; - -@Injectable({ - providedIn: 'root' -}) -export class DictionarySaveService { - constructor(private readonly _toaster: Toaster, private readonly _dictionaryControllerService: DictionaryControllerService) {} - - saveEntries( - entries: string[], - initialEntries: string[], - dossierTemplateId: string, - type: string, - dossierId: string, - showToast = true - ): Observable { - let entriesToAdd = []; - entries.forEach(currentEntry => { - entriesToAdd.push(currentEntry); - }); - // remove empty lines - entriesToAdd = entriesToAdd.filter(e => e && e.trim().length > 0).map(e => e.trim()); - const invalidRowsExist = entriesToAdd.filter(e => e.length < MIN_WORD_LENGTH); - if (invalidRowsExist.length === 0) { - // can add at least 1 - block UI - let obs: Observable; - if (entriesToAdd.length > 0) { - obs = this._dictionaryControllerService.addEntry(entriesToAdd, dossierTemplateId, type, dossierId, true); - } else { - obs = this._dictionaryControllerService.deleteEntries(initialEntries, dossierTemplateId, type, dossierId); - } - - return obs.pipe( - tap( - () => { - if (showToast) { - this._toaster.success(_('dictionary-overview.success.generic')); - } - }, - () => this._toaster.error(_('dictionary-overview.error.generic')) - ) - ); - } else { - this._toaster.error(_('dictionary-overview.error.entries-too-short')); - - return throwError('Entries too short'); - } - } -} diff --git a/apps/red-ui/src/app/modules/shared/services/dictionary.service.ts b/apps/red-ui/src/app/modules/shared/services/dictionary.service.ts new file mode 100644 index 000000000..825878b36 --- /dev/null +++ b/apps/red-ui/src/app/modules/shared/services/dictionary.service.ts @@ -0,0 +1,162 @@ +import { Injectable, Injector } from '@angular/core'; +import { Observable, throwError } from 'rxjs'; +import { EntitiesService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; +import { Colors, IDictionary, ITypeValue, UpdateTypeValue } from '@redaction/red-ui-http'; +import { tap } from 'rxjs/operators'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { TEMPORARY_INJECTOR } from '../../dossier/services/injector'; +import { Dictionary } from '@models/dictionary'; + +const MIN_WORD_LENGTH = 2; + +@Injectable({ + providedIn: 'root' +}) +export class DictionaryService extends EntitiesService { + constructor(private readonly _toaster: Toaster, protected readonly _injector: Injector) { + super(TEMPORARY_INJECTOR(_injector), 'dictionary'); + } + + /** + * Retrieves all dictionary entries of an entry type + */ + @Validate() + getFor(@RequiredParam() dossierTemplateId: string, @RequiredParam() type: string, dossierId?: string) { + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + return this._getOne([type, dossierTemplateId], this._defaultModelPath, queryParams); + } + + /** + * Deletes entry types + */ + @Validate() + deleteTypes(@RequiredParam() body: List, @RequiredParam() dossierTemplateId: string, dossierId?: string) { + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + const url = `${this._defaultModelPath}/type/${dossierTemplateId}/delete`; + return this._post(body, url, queryParams); + } + + /** + * Retrieve all entry types + */ + @Validate() + getAllTypes(@RequiredParam() dossierTemplateId: string, dossierId?: string) { + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + return this._getOne<{ types: ITypeValue[] }>(['type', dossierTemplateId], this._defaultModelPath, queryParams); + } + + /** + * Retrieves system colors for redaction. + */ + @Validate() + getColors(@RequiredParam() dossierTemplateId: string) { + return this._getOne([dossierTemplateId], 'color'); + } + + /** + * Updates colors, hint and caseInsensitive of an entry type. + */ + @Validate() + updateType( + @RequiredParam() body: UpdateTypeValue, + @RequiredParam() dossierTemplateId: string, + @RequiredParam() type: string, + dossierId?: string + ) { + const url = `${this._defaultModelPath}/type/${type}/${dossierTemplateId}`; + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + return this._post(body, url, queryParams); + } + + /** + * Set system colors for redaction + */ + @Validate() + setColors(@RequiredParam() body: Colors, @RequiredParam() dossierTemplateId: string) { + return this._post(body, `color/${dossierTemplateId}`); + } + + /** + * Creates entry type with colors, hint and caseInsensitive + */ + @Validate() + addType(@RequiredParam() body: ITypeValue, @RequiredParam() dossierId?: string) { + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + return this._post(body, `${this._defaultModelPath}/type`, queryParams); + } + + /** + * Add dictionary entries with entry type. + */ + @Validate() + addEntry( + @RequiredParam() body: List, + @RequiredParam() dossierTemplateId: string, + @RequiredParam() type: string, + dossierId?: string, + removeCurrent?: boolean + ) { + const queryParams: List = [ + { key: 'dossierId', value: dossierId }, + { key: 'removeCurrent', value: removeCurrent } + ]; + const url = `${this._defaultModelPath}/${type}/${dossierTemplateId}`; + return this._post(body, url, queryParams); + } + + /** + * Delete dictionary entries with entry type. + */ + @Validate() + deleteEntries( + @RequiredParam() body: List, + @RequiredParam() dossierTemplateId: string, + @RequiredParam() type: string, + @RequiredParam() dossierId?: string + ) { + const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined; + const url = `${this._defaultModelPath}/delete/${type}/${dossierTemplateId}`; + return this._post(body, url, queryParams); + } + + saveEntries( + entries: string[], + initialEntries: string[], + dossierTemplateId: string, + type: string, + dossierId: string, + showToast = true + ): Observable { + let entriesToAdd = []; + entries.forEach(currentEntry => { + entriesToAdd.push(currentEntry); + }); + // remove empty lines + entriesToAdd = entriesToAdd.filter(e => e && e.trim().length > 0).map(e => e.trim()); + const invalidRowsExist = entriesToAdd.filter(e => e.length < MIN_WORD_LENGTH); + if (invalidRowsExist.length === 0) { + // can add at least 1 - block UI + let obs: Observable; + if (entriesToAdd.length > 0) { + obs = this.addEntry(entriesToAdd, dossierTemplateId, type, dossierId, true); + } else { + obs = this.deleteEntries(initialEntries, dossierTemplateId, type, dossierId); + } + + return obs.pipe( + tap( + () => { + if (showToast) { + this._toaster.success(_('dictionary-overview.success.generic')); + } + }, + () => this._toaster.error(_('dictionary-overview.error.generic')) + ) + ); + } else { + this._toaster.error(_('dictionary-overview.error.entries-too-short')); + + return throwError('Entries too short'); + } + } +} diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index ae4cf7b6f..c32e29691 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core'; import { - DictionaryControllerService, DossierTemplateControllerService, FileAttributesConfig, FileAttributesControllerService, @@ -23,6 +22,7 @@ import { DossiersService } from '../modules/dossier/services/dossiers.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserPreferenceService } from '@services/user-preference.service'; import { FilesService } from '../modules/dossier/services/files.service'; +import { DictionaryService } from '@shared/services/dictionary.service'; export interface AppState { dossiers: Dossier[]; @@ -54,7 +54,7 @@ export class AppStateService { private readonly _toaster: Toaster, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _translateService: TranslateService, - private readonly _dictionaryControllerService: DictionaryControllerService, + private readonly _dictionaryService: DictionaryService, private readonly _dossierTemplateControllerService: DossierTemplateControllerService, private readonly _fileAttributesService: FileAttributesControllerService, private readonly _userPreferenceService: UserPreferenceService @@ -282,9 +282,7 @@ export class AppStateService { // dossier exists, load its dictionary const dossier = this.getDossierById(dossierId); try { - dossier.type = await this._dictionaryControllerService - .getDictionaryForType(dossierTemplateId, 'dossier_redaction', dossierId) - .toPromise(); + dossier.type = await this._dictionaryService.getFor(dossierTemplateId, 'dossier_redaction', dossierId).toPromise(); } catch (e) { dossier.type = null; } @@ -443,7 +441,7 @@ export class AppStateService { private _getDictionaryDataForDossierTemplateObservables(dossierTemplateId: string): Observable<{ [key: string]: any }> { const dictionaryData: { [key: string]: any } = {}; - const typeObs = this._dictionaryControllerService.getAllTypes(dossierTemplateId).pipe( + const typeObs = this._dictionaryService.getAllTypes(dossierTemplateId).pipe( tap(typesResponse => { for (const type of typesResponse.types) { dictionaryData[type.type] = new TypeValue(type); @@ -451,7 +449,7 @@ export class AppStateService { }) ); - const colorsObs = this._dictionaryControllerService.getColors(dossierTemplateId).pipe( + const colorsObs = this._dictionaryService.getColors(dossierTemplateId).pipe( tap(colors => { for (const key of Object.keys(colors)) { const color = colors[key]; diff --git a/apps/red-ui/src/app/state/model/dossier.ts b/apps/red-ui/src/app/state/model/dossier.ts index 1dfff06b8..6e2ec7389 100644 --- a/apps/red-ui/src/app/state/model/dossier.ts +++ b/apps/red-ui/src/app/state/model/dossier.ts @@ -1,5 +1,5 @@ import { File } from '@models/file/file'; -import { Dictionary, DossierStatus, DownloadFileType, IDossier, List } from '@redaction/red-ui-http'; +import { DossierStatus, DownloadFileType, IDictionary, IDossier, List } from '@redaction/red-ui-http'; import { IListable } from '@iqser/common-ui'; export class Dossier implements IDossier, IListable { @@ -33,7 +33,7 @@ export class Dossier implements IDossier, IListable { hasPendingOrProcessing?: boolean; allFilesApproved?: boolean; - type?: Dictionary; + type?: IDictionary; constructor(dossier: IDossier, private _files: File[] = []) { this.dossierId = dossier.dossierId; diff --git a/libs/red-ui-http/src/lib/api.module.ts b/libs/red-ui-http/src/lib/api.module.ts index 5e558f413..93f794458 100644 --- a/libs/red-ui-http/src/lib/api.module.ts +++ b/libs/red-ui-http/src/lib/api.module.ts @@ -3,7 +3,6 @@ import { Configuration } from './configuration'; import { HttpClient } from '@angular/common/http'; import { AuditControllerService } from './api/auditController.service'; -import { DictionaryControllerService } from './api/dictionaryController.service'; import { DigitalSignatureControllerService } from './api/digitalSignatureController.service'; import { DossierAttributesControllerService } from './api/dossierAttributesController.service'; import { DossierTemplateControllerService } from './api/dossierTemplateController.service'; @@ -35,7 +34,6 @@ import { NotificationControllerService } from './api/notificationController.serv exports: [], providers: [ AuditControllerService, - DictionaryControllerService, DigitalSignatureControllerService, DossierAttributesControllerService, DossierTemplateControllerService, diff --git a/libs/red-ui-http/src/lib/api/api.ts b/libs/red-ui-http/src/lib/api/api.ts index 61cf9c815..3c6557617 100644 --- a/libs/red-ui-http/src/lib/api/api.ts +++ b/libs/red-ui-http/src/lib/api/api.ts @@ -1,6 +1,5 @@ import { AuditControllerService } from './auditController.service'; import { DebugControllerService } from './debugController.service'; -import { DictionaryControllerService } from './dictionaryController.service'; import { FileManagementControllerService } from './fileManagementController.service'; import { InfoControllerService } from './infoController.service'; import { LegalBasisMappingControllerService } from './legalBasisMappingController.service'; @@ -87,7 +86,6 @@ export * from './notificationController.service'; export const APIS = [ AuditControllerService, DebugControllerService, - DictionaryControllerService, FileManagementControllerService, InfoControllerService, LegalBasisMappingControllerService, diff --git a/libs/red-ui-http/src/lib/api/dictionaryController.service.ts b/libs/red-ui-http/src/lib/api/dictionaryController.service.ts index 7cf6bece3..8c50fab00 100644 --- a/libs/red-ui-http/src/lib/api/dictionaryController.service.ts +++ b/libs/red-ui-http/src/lib/api/dictionaryController.service.ts @@ -17,7 +17,7 @@ import { CustomHttpUrlEncodingCodec } from "../encoder"; import { Observable } from "rxjs"; import { Colors } from "../model/colors"; -import { Dictionary } from "../model/dictionary"; +import { IDictionary } from "../model/dictionary"; import { TypeResponse } from "../model/typeResponse"; import { ITypeValue } from "../model/typeValue"; import { UpdateTypeValue } from "../model/updateTypeValue"; @@ -780,7 +780,7 @@ export class DictionaryControllerService { dossierId?: string, observe?: 'body', reportProgress?: boolean - ): Observable; + ): Observable; public getDictionaryForType( dossierTemplateId: string, @@ -788,7 +788,7 @@ export class DictionaryControllerService { dossierId?: string, observe?: 'response', reportProgress?: boolean - ): Observable>; + ): Observable>; public getDictionaryForType( dossierTemplateId: string, @@ -796,7 +796,7 @@ export class DictionaryControllerService { dossierId?: string, observe?: 'events', reportProgress?: boolean - ): Observable>; + ): Observable>; public getDictionaryForType( dossierTemplateId: string, @@ -834,7 +834,7 @@ export class DictionaryControllerService { headers = headers.set('Accept', httpHeaderAcceptSelected); } - return this.httpClient.request( + return this.httpClient.request( 'get', `${this.basePath}/dictionary/${encodeURIComponent(String(type))}/${encodeURIComponent(String(dossierTemplateId))}`, { diff --git a/libs/red-ui-http/src/lib/model/dictionary.ts b/libs/red-ui-http/src/lib/model/dictionary.ts index 7b32c04be..e182acc4c 100644 --- a/libs/red-ui-http/src/lib/model/dictionary.ts +++ b/libs/red-ui-http/src/lib/model/dictionary.ts @@ -13,7 +13,7 @@ /** * Object containing a list of dictionary entries and colors of an entry type. */ -export interface Dictionary { +export interface IDictionary { /** * If true the ui will add a action to add values to dictionary */