diff --git a/apps/red-ui/src/app/modules/admin/screens/entities/screens/dictionary/dictionary-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/entities/screens/dictionary/dictionary-screen.component.ts index 5ed382572..94063d2b9 100644 --- a/apps/red-ui/src/app/modules/admin/screens/entities/screens/dictionary/dictionary-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/entities/screens/dictionary/dictionary-screen.component.ts @@ -3,10 +3,11 @@ import { ActivatedRoute } from '@angular/router'; import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component'; import { DictionaryService } from '@services/entity-services/dictionary.service'; import { getCurrentUser, getParam, IqserPermissionsService, List, LoadingService } from '@iqser/common-ui'; -import { BehaviorSubject, firstValueFrom, lastValueFrom } from 'rxjs'; +import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { DICTIONARY_TO_ENTRY_TYPE_MAP, DICTIONARY_TYPE_KEY_MAP, DictionaryType, DOSSIER_TEMPLATE_ID, ENTITY_TYPE, User } from '@red/domain'; import { PermissionsService } from '@services/permissions.service'; import { ROLES } from '@users/roles'; +import { NGXLogger } from 'ngx-logger'; @Component({ templateUrl: './dictionary-screen.component.html', @@ -30,6 +31,7 @@ export class DictionaryScreenComponent implements OnInit { readonly iqserPermissionsService: IqserPermissionsService, private readonly _loadingService: LoadingService, private readonly _dictionaryService: DictionaryService, + private readonly _logger: NGXLogger, ) { this.type = route.snapshot.routeConfig.path as DictionaryType; } @@ -43,6 +45,7 @@ export class DictionaryScreenComponent implements OnInit { } async save() { + this._logger.info('[EDITOR] Saving...'); const entries = this._dictionaryManager.editor?.currentEntries; this._loadingService.start(); diff --git a/apps/red-ui/src/app/services/entity-services/dictionary.service.ts b/apps/red-ui/src/app/services/entity-services/dictionary.service.ts index ea65f2479..d761d9643 100644 --- a/apps/red-ui/src/app/services/entity-services/dictionary.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dictionary.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { firstValueFrom, forkJoin, Observable, throwError, zip } from 'rxjs'; +import { firstValueFrom, forkJoin, Observable, throwError } from 'rxjs'; import { EntitiesService, List, QueryParam, Toaster } from '@iqser/common-ui'; import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IDictionary, IUpdateDictionary, SuperTypes } from '@red/domain'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; @@ -94,50 +94,58 @@ export class DictionaryService extends EntitiesService dossierId: string, showToast = true, dictionaryEntryType = DictionaryEntryTypes.ENTRY, - ): Promise { + ) { const entriesToAdd: Array = []; - const deletedEntries: Array = []; + const initialEntriesSet = new Set(initialEntries); + let hasInvalidRows = false; + let hasNewEntries = false; for (let i = 0; i < entries.length; i++) { - if (!entries.at(i).trim()) { + const entry = entries.at(i); + if (!entry.trim()) { continue; } - entriesToAdd.push(entries.at(i)); - } - for (let i = 0; i < initialEntries.length; i++) { - if (entries.includes(initialEntries.at(i))) { - continue; + if (entry.length < MIN_WORD_LENGTH) { + hasInvalidRows = true; } - deletedEntries.push(initialEntries.at(i)); - } - // remove empty lines - const invalidRowsExist = entriesToAdd.filter(e => e.length < MIN_WORD_LENGTH); - if (invalidRowsExist.length === 0) { - // can add at least 1 - block UI - const promises: Promise[] = []; - if (deletedEntries.length) { - promises.push(firstValueFrom(this._deleteEntries(deletedEntries, dossierTemplateId, type, dictionaryEntryType, dossierId))); + if (!hasNewEntries && !initialEntriesSet.has(entry)) { + hasNewEntries = true; } - if (entriesToAdd.filter(e => !initialEntries.includes(e)).length) { - promises.push(firstValueFrom(this._addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId))); - } - try { - await Promise.all(promises); - if (showToast) { - this._toaster.success(_('dictionary-overview.success.generic')); - } - return; - } catch (error) { - if ((error as HttpErrorResponse).status === 400) { - this._toaster.error(_('dictionary-overview.error.400')); - } else { - this._toaster.error(_('dictionary-overview.error.generic')); - } - return; - } - } - this._toaster.error(_('dictionary-overview.error.entries-too-short')); - throw new Error('Entries too short'); + entriesToAdd.push(entry); + } + if (hasInvalidRows) { + this._toaster.error(_('dictionary-overview.error.entries-too-short')); + + throw new Error('Entries too short'); + } + const deletedEntries: Array = []; + const entriesSet = new Set(entries); + for (let i = 0; i < initialEntries.length; i++) { + const entry = initialEntries.at(i); + if (entriesSet.has(entry)) { + continue; + } + deletedEntries.push(entry); + } + + try { + if (deletedEntries.length) { + await this._deleteEntries(deletedEntries, dossierTemplateId, type, dictionaryEntryType, dossierId); + } + if (hasNewEntries) { + await this._addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId); + } + + if (showToast) { + this._toaster.success(_('dictionary-overview.success.generic')); + } + } catch (error) { + if ((error as HttpErrorResponse).status === 400) { + this._toaster.error(_('dictionary-overview.error.400')); + } else { + this._toaster.error(_('dictionary-overview.error.generic')); + } + } } hasManualType(dossierTemplateId: string): boolean { @@ -240,7 +248,7 @@ export class DictionaryService extends EntitiesService { key: 'removeCurrent', value: true }, ]; const url = `${this._defaultModelPath}/${type}/${dossierTemplateId}`; - return this._post(entries, url, queryParams); + return firstValueFrom(this._post(entries, url, queryParams)); } /** @@ -258,7 +266,7 @@ export class DictionaryService extends EntitiesService ? [{ key: 'dossierId', value: dossierId }] : [{ key: 'dictionaryEntryType', value: dictionaryEntryType }]; const url = `${this._defaultModelPath}/delete/${type}/${dossierTemplateId}`; - return this._post(entries, url, queryParams); + return firstValueFrom(this._post(entries, url, queryParams)); } #addUpdateDictionaryErrorToast(error: HttpErrorResponse | unknown): Observable {