From 917994eba60d048a4f696ca90e7dd5b1b72c7033 Mon Sep 17 00:00:00 2001 From: rkuffer Date: Mon, 28 Oct 2024 17:13:15 +0100 Subject: [PATCH 1/3] Namespace list management delegated to state service --- .../namespace/namespaces.component.html | 2 +- .../namespace/namespaces.component.ts | 21 ++++--------------- .../synchronization.component.html | 4 ++-- .../synchronization.component.ts | 6 +----- .../src/app/core-nlp/applications.service.ts | 1 + .../web/src/app/core-nlp/state.service.ts | 4 +++- .../components/header/header.component.html | 4 ++-- .../components/header/header.component.ts | 12 +---------- 8 files changed, 15 insertions(+), 39 deletions(-) diff --git a/bot/admin/web/src/app/applications/namespace/namespaces.component.html b/bot/admin/web/src/app/applications/namespace/namespaces.component.html index 5036d0fa35..53accea90e 100644 --- a/bot/admin/web/src/app/applications/namespace/namespaces.component.html +++ b/bot/admin/web/src/app/applications/namespace/namespaces.component.html @@ -34,7 +34,7 @@

Namespaces

diff --git a/bot/admin/web/src/app/applications/namespace/namespaces.component.ts b/bot/admin/web/src/app/applications/namespace/namespaces.component.ts index 564dd167ba..4c09abc7e4 100644 --- a/bot/admin/web/src/app/applications/namespace/namespaces.component.ts +++ b/bot/admin/web/src/app/applications/namespace/namespaces.component.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { StateService } from '../../core-nlp/state.service'; import { ApplicationService } from '../../core-nlp/applications.service'; import { NamespaceConfiguration, NamespaceSharingConfiguration, UserNamespace } from '../../model/application'; @@ -30,9 +30,8 @@ import { Subject, takeUntil } from 'rxjs'; templateUrl: 'namespaces.component.html', styleUrls: ['namespaces.component.scss'] }) -export class NamespacesComponent implements OnInit, OnDestroy { +export class NamespacesComponent implements OnDestroy { destroy = new Subject(); - namespaces: UserNamespace[]; managedNamespace: string; managedUsers: UserNamespace[]; @@ -53,22 +52,10 @@ export class NamespacesComponent implements OnInit, OnDestroy { private nbDialogService: NbDialogService ) {} - ngOnInit(): void { - this.state.currentApplicationEmitter.pipe(takeUntil(this.destroy)).subscribe((arg) => { - this.grabNamespaces(); - }); - this.grabNamespaces(); - } - - grabNamespaces(): void { - this.applicationService.getNamespaces().subscribe((n) => (this.namespaces = n)); - } - selectNamespace(namespace: string): void { this.applicationService.selectNamespace(namespace).subscribe((_) => this.authService.loadUser().subscribe((_) => { - this.applicationService.resetConfiguration; - this.grabNamespaces(); + this.applicationService.resetConfiguration(); }) ); } @@ -85,7 +72,7 @@ export class NamespacesComponent implements OnInit, OnDestroy { const modal = this.nbDialogService.open(CreateNamespaceComponent); const validate = modal.componentRef.instance.validate.pipe(takeUntil(this.destroy)).subscribe((result) => { this.applicationService.createNamespace(result.name.trim()).subscribe((b) => { - this.ngOnInit(); + this.applicationService.resetConfiguration(); }); this.closeEdition(); modal.close(); diff --git a/bot/admin/web/src/app/configuration/synchronization/synchronization.component.html b/bot/admin/web/src/app/configuration/synchronization/synchronization.component.html index 2ff62ca4d5..bab1188fbe 100644 --- a/bot/admin/web/src/app/configuration/synchronization/synchronization.component.html +++ b/bot/admin/web/src/app/configuration/synchronization/synchronization.component.html @@ -25,7 +25,7 @@

Synchronize BOTS contents (configuration)

(selectedChange)="selectSourceNamespace(this.sourceNamespace)" > {{ namespace.namespace }} @@ -51,7 +51,7 @@

Synchronize BOTS contents (configuration)

(selectedChange)="selectTargetNamespace(this.targetNamespace)" > {{ namespace.namespace }} diff --git a/bot/admin/web/src/app/configuration/synchronization/synchronization.component.ts b/bot/admin/web/src/app/configuration/synchronization/synchronization.component.ts index 1544963542..f81371e06d 100644 --- a/bot/admin/web/src/app/configuration/synchronization/synchronization.component.ts +++ b/bot/admin/web/src/app/configuration/synchronization/synchronization.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ApplicationService } from '../../core-nlp/applications.service'; import { Application, UserNamespace } from '../../model/application'; -import { AuthService } from '../../core-nlp/auth/auth.service'; import { StateService } from '../../core-nlp/state.service'; import { BotConfigurationService } from '../../core/bot-configuration.service'; import { DialogService } from '../../core-nlp/dialog.service'; @@ -16,7 +15,6 @@ import { ChoiceDialogComponent } from '../../shared/components'; styleUrls: ['./synchronization.component.css'] }) export class SynchronizationComponent implements OnInit { - namespaces: UserNamespace[]; sourceNamespace: UserNamespace; targetNamespace: UserNamespace; sourceApplications: Application[]; @@ -27,7 +25,6 @@ export class SynchronizationComponent implements OnInit { constructor( private applicationService: ApplicationService, - private authService: AuthService, public state: StateService, private botConfigurationService: BotConfigurationService, private dialog: DialogService, @@ -36,7 +33,6 @@ export class SynchronizationComponent implements OnInit { private router: Router ) {} ngOnInit(): void { - this.applicationService.getNamespaces().subscribe((n) => (this.namespaces = n)); this.sourceApplications = this.state.applications; } @@ -67,7 +63,7 @@ export class SynchronizationComponent implements OnInit { context: { title: 'Overwrite configuration?', subtitle: `During synchronization, configuration will be copied from the source application to the target application (answers, stories, training). - + Please note : ${inboxMessagesCopySubtitle} The synchronization of both applications will be permanent, and there will be no way to reverse it. Do you want to continue? diff --git a/bot/admin/web/src/app/core-nlp/applications.service.ts b/bot/admin/web/src/app/core-nlp/applications.service.ts index 172bd2c6f3..250fb314e5 100644 --- a/bot/admin/web/src/app/core-nlp/applications.service.ts +++ b/bot/admin/web/src/app/core-nlp/applications.service.ts @@ -64,6 +64,7 @@ export class ApplicationService implements OnDestroy { resetConfiguration() { this.locales().subscribe((locales) => (this.state.locales = locales)); this.nlpEngineTypes().subscribe((engines) => (this.state.supportedNlpEngines = engines)); + this.getNamespaces().subscribe((namespaces) => (this.state.namespaces = namespaces)); this.getApplications().subscribe((applications) => { this.state.applications = applications; this.state.currentApplication = null; diff --git a/bot/admin/web/src/app/core-nlp/state.service.ts b/bot/admin/web/src/app/core-nlp/state.service.ts index 2af869deb2..e7f6d76864 100644 --- a/bot/admin/web/src/app/core-nlp/state.service.ts +++ b/bot/admin/web/src/app/core-nlp/state.service.ts @@ -16,7 +16,7 @@ import { map } from 'rxjs/operators'; import { EventEmitter, Injectable } from '@angular/core'; -import { Application } from '../model/application'; +import { Application, UserNamespace } from '../model/application'; import { AuthService } from './auth/auth.service'; import { AuthListener } from './auth/auth.listener'; import { User, UserRole } from '../model/auth'; @@ -44,6 +44,7 @@ export class StateService implements AuthListener { supportedNlpEngines: NlpEngineType[]; user: User; + namespaces: UserNamespace[]; applications: Application[]; dateRange = { start: null, end: null, rangeInDays: null }; @@ -265,6 +266,7 @@ export class StateService implements AuthListener { logout() { this.user = null; this.currentApplication = null; + this.namespaces = null; this.applications = null; } diff --git a/bot/admin/web/src/app/theme/components/header/header.component.html b/bot/admin/web/src/app/theme/components/header/header.component.html index ee20de4758..97fcb8a2b1 100644 --- a/bot/admin/web/src/app/theme/components/header/header.component.html +++ b/bot/admin/web/src/app/theme/components/header/header.component.html @@ -65,7 +65,7 @@ - + {{ namespace.namespace }} diff --git a/bot/admin/web/src/app/theme/components/header/header.component.ts b/bot/admin/web/src/app/theme/components/header/header.component.ts index 1de1356107..89bcd30a00 100644 --- a/bot/admin/web/src/app/theme/components/header/header.component.ts +++ b/bot/admin/web/src/app/theme/components/header/header.component.ts @@ -23,7 +23,6 @@ import { SettingsService } from '../../../core-nlp/settings.service'; import { Subject, take, takeUntil } from 'rxjs'; import { APP_BASE_HREF } from '@angular/common'; import { ApplicationService } from '../../../core-nlp/applications.service'; -import { UserNamespace } from '../../../model/application'; import { BotConfigurationService } from '../../../core/bot-configuration.service'; import { CoreConfig } from '../../../core-nlp/core.config'; import { Router } from '@angular/router'; @@ -40,8 +39,6 @@ export class HeaderComponent implements OnInit, OnDestroy { currentTheme = 'default'; - namespaces: UserNamespace[]; - currentApplicationName: string; constructor( @@ -69,8 +66,6 @@ export class HeaderComponent implements OnInit, OnDestroy { } this.botConfiguration.configurations.pipe(takeUntil(this.destroy)).subscribe((confs) => { - this.grabNamespaces(); - this.currentApplicationName = ''; setTimeout(() => { this.currentApplicationName = this.state?.currentApplication?.name; @@ -78,12 +73,8 @@ export class HeaderComponent implements OnInit, OnDestroy { }); } - grabNamespaces(): void { - this.applicationService.getNamespaces().subscribe((n) => (this.namespaces = n)); - } - get currentNamespaceName() { - return this.namespaces?.find((n) => n.current).namespace; + return this.state.namespaces?.find((n) => n.current).namespace; } changeNamespace(namespace: string) { @@ -93,7 +84,6 @@ export class HeaderComponent implements OnInit, OnDestroy { .subscribe((_) => this.auth.loadUser().subscribe((_) => { this.applicationService.resetConfiguration(); - this.grabNamespaces(); this.applicationService .getApplications() From 03d9607eb30905946f5675117222dd25d73ef3d5 Mon Sep 17 00:00:00 2001 From: rkuffer Date: Mon, 28 Oct 2024 17:42:21 +0100 Subject: [PATCH 2/3] Avoid competing requests --- .../src/app/core-nlp/applications.service.ts | 36 ++++++++++++------- .../src/app/core/bot-configuration.service.ts | 21 ++++++----- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/bot/admin/web/src/app/core-nlp/applications.service.ts b/bot/admin/web/src/app/core-nlp/applications.service.ts index 250fb314e5..6abe15e394 100644 --- a/bot/admin/web/src/app/core-nlp/applications.service.ts +++ b/bot/admin/web/src/app/core-nlp/applications.service.ts @@ -62,8 +62,8 @@ export class ApplicationService implements OnDestroy { } resetConfiguration() { - this.locales().subscribe((locales) => (this.state.locales = locales)); - this.nlpEngineTypes().subscribe((engines) => (this.state.supportedNlpEngines = engines)); + this.getLocales().subscribe((locales) => (this.state.locales = locales)); + this.getNlpEngineTypes().subscribe((engines) => (this.state.supportedNlpEngines = engines)); this.getNamespaces().subscribe((namespaces) => (this.state.namespaces = namespaces)); this.getApplications().subscribe((applications) => { this.state.applications = applications; @@ -80,8 +80,28 @@ export class ApplicationService implements OnDestroy { return this.getApplicationsPending; } - nlpEngineTypes(): Observable { - return this.rest.getArray('/nlp-engines', NlpEngineType.fromJSONArray); + getNamespacesPending: Observable; + getNamespaces(): Observable { + if (!this.getNamespacesPending) { + this.getNamespacesPending = this.rest.get(`/namespaces`, UserNamespace.fromJSONArray).pipe(share()); + } + return this.getNamespacesPending; + } + + getLocalesPending: Observable[]>; + getLocales(): Observable[]> { + if (!this.getLocalesPending) { + this.getLocalesPending = this.rest.get(`/locales`, (m) => Entry.fromJSONArray(m)).pipe(share()); + } + return this.getLocalesPending; + } + + getNlpEngineTypesPending: Observable; + getNlpEngineTypes(): Observable { + if (!this.getNlpEngineTypesPending) { + this.getNlpEngineTypesPending = this.rest.getArray('/nlp-engines', NlpEngineType.fromJSONArray).pipe(share()); + } + return this.getNlpEngineTypesPending; } triggerBuild(application: Application) { @@ -136,10 +156,6 @@ export class ApplicationService implements OnDestroy { } } - locales(): Observable[]> { - return this.rest.get(`/locales`, (m) => Entry.fromJSONArray(m)); - } - getApplicationDump(application: Application): Observable { return this.rest.get(`/application/dump/${application._id}`, (r) => new Blob([JSON.stringify(r)], { type: 'application/json' })); } @@ -168,10 +184,6 @@ export class ApplicationService implements OnDestroy { this.rest.setFileUploaderOptions(uploader, url); } - getNamespaces(): Observable { - return this.rest.get(`/namespaces`, UserNamespace.fromJSONArray); - } - getUsersForNamespace(namespace: string): Observable { return this.rest.get(`/namespaces/${namespace}`, UserNamespace.fromJSONArray); } diff --git a/bot/admin/web/src/app/core/bot-configuration.service.ts b/bot/admin/web/src/app/core/bot-configuration.service.ts index 38d2d4f92a..7db39dd6d1 100644 --- a/bot/admin/web/src/app/core/bot-configuration.service.ts +++ b/bot/admin/web/src/app/core/bot-configuration.service.ts @@ -17,10 +17,10 @@ import { Injectable, OnDestroy } from '@angular/core'; import { RestService } from '../core-nlp/rest/rest.service'; import { StateService } from '../core-nlp/state.service'; -import { BehaviorSubject, Observable, Subscription, forkJoin } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, forkJoin, share } from 'rxjs'; import { ApplicationScopedQuery } from '../model/commons'; import { BotApplicationConfiguration, BotConfiguration, ConnectorType } from './model/configuration'; -import { SynchronizationConfiguration } from "./model/synchronizationConfiguration"; +import { SynchronizationConfiguration } from './model/synchronizationConfiguration'; @Injectable() export class BotConfigurationService implements OnDestroy { @@ -38,8 +38,9 @@ export class BotConfigurationService implements OnDestroy { readonly bots: BehaviorSubject = new BehaviorSubject([]); constructor(private rest: RestService, private state: StateService) { - this.subscription = this.state.configurationChange.subscribe((_) => this.updateConfigurations()); - this.updateConfigurations(); + this.subscription = this.state.configurationChange.subscribe((_) => { + this.updateConfigurations(); + }); } ngOnDestroy(): void { @@ -86,8 +87,12 @@ export class BotConfigurationService implements OnDestroy { return this.rest.post('/configuration/synchronization', conf); } + getBotsPending: Observable; private getBots(botId: string): Observable { - return this.rest.get(`/bots/${botId}`, BotConfiguration.fromJSONArray); + if (!this.getBotsPending) { + this.getBotsPending = this.rest.get(`/bots/${botId}`, BotConfiguration.fromJSONArray).pipe(share()); + } + return this.getBotsPending; } findApplicationConfigurationById(id: string): BotApplicationConfiguration { @@ -114,9 +119,9 @@ export class BotConfigurationService implements OnDestroy { findValidPath(connectorType: ConnectorType): string { const bots = this.bots.getValue(); - const baseTargetPath = `/io/${this.state.user.organization.toLowerCase().replace(/\s/g, '')}/${this.state.currentApplication.name.replace(/\s/g, '_')}/${ - connectorType.id - }`; + const baseTargetPath = `/io/${this.state.user.organization + .toLowerCase() + .replace(/\s/g, '')}/${this.state.currentApplication.name.replace(/\s/g, '_')}/${connectorType.id}`; let targetPath = baseTargetPath; let index = 1; while (bots.findIndex((b) => b.configurations && b.configurations.findIndex((c) => c.path === targetPath) !== -1) !== -1) { From b6ce12b7b7d52790f6cefd5450e74534c36a3c0e Mon Sep 17 00:00:00 2001 From: rkuffer Date: Wed, 30 Oct 2024 12:40:22 +0100 Subject: [PATCH 3/3] Refresh dialogs on configuration change + misc --- .../analytics/activity/activity.component.ts | 13 +++-- .../dialogs-list/dialogs-list.component.html | 29 +++++------ .../dialogs-list/dialogs-list.component.scss | 3 ++ .../dialogs-list/dialogs-list.component.ts | 31 +++++------- .../satisfaction/satisfaction.component.html | 16 +++++-- .../satisfaction/satisfaction.component.ts | 48 +++++++++++-------- .../i18n-filters/i18n-filters.component.ts | 2 +- .../story/edit-story/edit-story.component.ts | 2 +- .../stories-filter.component.ts | 2 +- .../bot-configuration.component.html | 15 +++++- .../bot-configuration.component.ts | 29 ++++++----- .../bot-configurations.component.html | 2 +- .../observability-settings.component.ts | 2 +- .../sentence-generation-settings.component.ts | 2 +- .../vector-db-settings.component.ts | 2 +- .../intents-filters.component.ts | 2 +- .../indicators-filters.component.ts | 2 +- .../count-stats/count-stats.component.ts | 2 +- .../intent-quality.component.ts | 2 +- .../sentence-training-filters.component.ts | 2 +- .../sentence-training.component.ts | 6 ++- .../web/src/app/theme/styles/utilities.scss | 5 ++ 22 files changed, 130 insertions(+), 89 deletions(-) diff --git a/bot/admin/web/src/app/analytics/activity/activity.component.ts b/bot/admin/web/src/app/analytics/activity/activity.component.ts index b629ab5555..a7d705cf6e 100644 --- a/bot/admin/web/src/app/analytics/activity/activity.component.ts +++ b/bot/admin/web/src/app/analytics/activity/activity.component.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; // import * as html2pdf from 'html2pdf.js'; import { StateService } from 'src/app/core-nlp/state.service'; import { BotConfigurationService } from 'src/app/core/bot-configuration.service'; @@ -28,13 +28,15 @@ import { UserAnalyticsQueryResult } from '../users/users'; import { UserFilter } from '../users/users.component'; import { toISOStringWithoutOffset } from '../../shared/utils'; import { SelectBotEvent } from '../../shared/components'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'tock-activity', templateUrl: './activity.component.html', styleUrls: ['./activity.component.css'] }) -export class ActivityComponent implements OnInit { +export class ActivityComponent implements OnInit, OnDestroy { + destroy$ = new Subject(); startDate: Date; endDate: Date; selectedConnectorId: string; @@ -78,7 +80,7 @@ export class ActivityComponent implements OnInit { variationUsersPercentage: number; constructor(private state: StateService, private analytics: AnalyticsService, private botConfiguration: BotConfigurationService) { - this.botConfiguration.configurations.subscribe((configs) => { + this.botConfiguration.configurations.pipe(takeUntil(this.destroy$)).subscribe((configs) => { this.configurations = configs; }); this.userPreferences = this.analytics.getUserPreferences(); @@ -375,4 +377,9 @@ export class ActivityComponent implements OnInit { waitAndRefresh() { setTimeout((_) => this.reload()); } + + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } } diff --git a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.html b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.html index 0c18350f21..75402ae8f9 100644 --- a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.html +++ b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.html @@ -137,17 +137,18 @@
+ + + +
-

- No dialogs found! - -

+
- - - - diff --git a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.scss b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.scss index 824227d493..01dba2674e 100644 --- a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.scss +++ b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.scss @@ -13,3 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +.loader { + min-height: 20vh; +} diff --git a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.ts b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.ts index 53eed324b0..61a8564107 100644 --- a/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.ts +++ b/bot/admin/web/src/app/analytics/dialogs/dialogs-list/dialogs-list.component.ts @@ -69,32 +69,27 @@ export class DialogsListComponent implements OnInit, OnChanges, OnDestroy { private route: ActivatedRoute, public botSharedService: BotSharedService, private router: Router - ) { - this.state = state; - - this.botConfiguration.configurations.pipe(takeUntil(this.destroy$)).subscribe((configs) => { - this.isSatisfactionRoute().subscribe((res) => { - this.botSharedService.getIntentsByApplication(this.state.currentApplication._id).subscribe((intents) => (this.intents = intents)); - - this.configurationNameList = configs.filter((item) => item.targetConfigurationId == null).map((item) => item.applicationId); - - if (res) { - this.ratingFilter = [1, 2, 3, 4, 5]; - } - this.refresh(); - }); - }); + ) {} + ngOnInit() { this.botSharedService .getConnectorTypes() .pipe(take(1)) .subscribe((confConf) => { this.connectorTypes = confConf.map((it) => it.connectorType); }); - } - ngOnInit() { - this.load(); + this.botConfiguration.configurations.pipe(takeUntil(this.destroy$)).subscribe((configs) => { + this.botSharedService.getIntentsByApplication(this.state.currentApplication._id).subscribe((intents) => (this.intents = intents)); + + this.configurationNameList = configs + .filter((item) => item.targetConfigurationId == null) + .map((item) => { + return item.applicationId; + }); + + this.refresh(); + }); } ngOnChanges(changes: SimpleChanges) { diff --git a/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.html b/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.html index d452c40c45..dac0831e90 100644 --- a/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.html +++ b/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.html @@ -1,11 +1,19 @@ -
+ +
+

Satisfaction

+
+ + +
+ +
+
-
- -

{{errorMsg}}

+

{{ errorMsg }}

+
diff --git a/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.ts b/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.ts index d279c186db..313e8852fa 100644 --- a/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.ts +++ b/bot/admin/web/src/app/analytics/satisfaction/satisfaction.component.ts @@ -14,42 +14,50 @@ * limitations under the License. */ -import {Component, OnInit} from '@angular/core'; -import {AnalyticsService} from "../analytics.service"; -import {StateService} from 'src/app/core-nlp/state.service'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { AnalyticsService } from '../analytics.service'; +import { StateService } from 'src/app/core-nlp/state.service'; +import { BotConfigurationService } from '../../core/bot-configuration.service'; +import { Subject, take, takeUntil } from 'rxjs'; +import { BotApplicationConfiguration } from '../../core/model/configuration'; @Component({ selector: 'tock-satisfaction', templateUrl: './satisfaction.component.html', styleUrls: ['./satisfaction.component.css'] }) -export class SatisfactionComponent implements OnInit { +export class SatisfactionComponent implements OnInit, OnDestroy { + destroy = new Subject(); + configurations: BotApplicationConfiguration[]; isStatisfactionActivated: boolean = false; errorMsg: string; - public loaded: boolean = false; + public loading: boolean = true; - constructor( - private analytics: AnalyticsService, - private state: StateService - ) { - } + constructor(private analytics: AnalyticsService, private state: StateService, private botConfiguration: BotConfigurationService) {} ngOnInit(): void { - this.state.currentIntents.subscribe(() => { - this.isActiveSatisfaction() + this.botConfiguration.configurations.pipe(takeUntil(this.destroy)).subscribe((confs) => { + this.configurations = confs; + + if (this.configurations.length) this.isActiveSatisfaction(); + + this.loading = false; }); } isActiveSatisfaction() { this.errorMsg = null; - this.loaded = false; - this.analytics.isActiveSatisfactionByBot() - .subscribe((res: boolean) => - this.isStatisfactionActivated = res, - err => this.errorMsg = err, - () => this.loaded = true); + this.analytics + .isActiveSatisfactionByBot() + .pipe(take(1)) + .subscribe({ + next: (res: boolean) => (this.isStatisfactionActivated = res), + error: (err) => (this.errorMsg = err) + }); } - + ngOnDestroy() { + this.destroy.next(true); + this.destroy.complete(); + } } - diff --git a/bot/admin/web/src/app/bot/i18n/i18n-filters/i18n-filters.component.ts b/bot/admin/web/src/app/bot/i18n/i18n-filters/i18n-filters.component.ts index be27faf240..f2381dd242 100644 --- a/bot/admin/web/src/app/bot/i18n/i18n-filters/i18n-filters.component.ts +++ b/bot/admin/web/src/app/bot/i18n/i18n-filters/i18n-filters.component.ts @@ -38,7 +38,7 @@ export class I18nFiltersComponent implements OnInit { constructor(public state: StateService) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(250)).subscribe(() => this.submitFiltersChange()); + this.form.valueChanges.pipe(debounceTime(250), takeUntil(this.destroy$)).subscribe(() => this.submitFiltersChange()); } form = new FormGroup({ diff --git a/bot/admin/web/src/app/bot/story/edit-story/edit-story.component.ts b/bot/admin/web/src/app/bot/story/edit-story/edit-story.component.ts index 43aa551fcd..b6ff889300 100644 --- a/bot/admin/web/src/app/bot/story/edit-story/edit-story.component.ts +++ b/bot/admin/web/src/app/bot/story/edit-story/edit-story.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subject, take, takeUntil } from 'rxjs'; +import { Subject, take } from 'rxjs'; import { BotService } from '../../bot-service'; import { StoryDefinitionConfiguration } from '../../model/story'; diff --git a/bot/admin/web/src/app/bot/story/search-story/stories-filter/stories-filter.component.ts b/bot/admin/web/src/app/bot/story/search-story/stories-filter/stories-filter.component.ts index 9939e4b34b..cfe1b44e48 100644 --- a/bot/admin/web/src/app/bot/story/search-story/stories-filter/stories-filter.component.ts +++ b/bot/admin/web/src/app/bot/story/search-story/stories-filter/stories-filter.component.ts @@ -49,7 +49,7 @@ export class StoriesFilterComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(300)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe(() => { this.onFilter.emit(this.form.value as StoriesFilters); }); } diff --git a/bot/admin/web/src/app/configuration/bot-configurations/bot-configuration/bot-configuration.component.html b/bot/admin/web/src/app/configuration/bot-configurations/bot-configuration/bot-configuration.component.html index 403b1f2499..0ce16624c1 100644 --- a/bot/admin/web/src/app/configuration/bot-configurations/bot-configuration/bot-configuration.component.html +++ b/bot/admin/web/src/app/configuration/bot-configurations/bot-configuration/bot-configuration.component.html @@ -15,7 +15,15 @@ --> - + + +
- +
diff --git a/bot/admin/web/src/app/configuration/observability-settings/observability-settings.component.ts b/bot/admin/web/src/app/configuration/observability-settings/observability-settings.component.ts index 6b6508ef2a..fa0193162e 100644 --- a/bot/admin/web/src/app/configuration/observability-settings/observability-settings.component.ts +++ b/bot/admin/web/src/app/configuration/observability-settings/observability-settings.component.ts @@ -46,7 +46,7 @@ export class ObservabilitySettingsComponent implements OnInit, OnDestroy { ) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(200)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(200), takeUntil(this.destroy$)).subscribe(() => { this.setActivationDisabledState(); }); diff --git a/bot/admin/web/src/app/configuration/sentence-generation-settings/sentence-generation-settings.component.ts b/bot/admin/web/src/app/configuration/sentence-generation-settings/sentence-generation-settings.component.ts index 70f25c9f07..01d40c8651 100644 --- a/bot/admin/web/src/app/configuration/sentence-generation-settings/sentence-generation-settings.component.ts +++ b/bot/admin/web/src/app/configuration/sentence-generation-settings/sentence-generation-settings.component.ts @@ -50,7 +50,7 @@ export class SentenceGenerationSettingsComponent implements OnInit, OnDestroy { ) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(200)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(200), takeUntil(this.destroy$)).subscribe(() => { this.setActivationDisabledState(); }); diff --git a/bot/admin/web/src/app/configuration/vector-db-settings/vector-db-settings.component.ts b/bot/admin/web/src/app/configuration/vector-db-settings/vector-db-settings.component.ts index 734bade90e..7d8e17da85 100644 --- a/bot/admin/web/src/app/configuration/vector-db-settings/vector-db-settings.component.ts +++ b/bot/admin/web/src/app/configuration/vector-db-settings/vector-db-settings.component.ts @@ -46,7 +46,7 @@ export class VectorDbSettingsComponent implements OnInit { ) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(200)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(200), takeUntil(this.destroy$)).subscribe(() => { this.setActivationDisabledState(); }); diff --git a/bot/admin/web/src/app/language-understanding/intents/intents-filters/intents-filters.component.ts b/bot/admin/web/src/app/language-understanding/intents/intents-filters/intents-filters.component.ts index 0ea5da7188..cba4b41e1a 100644 --- a/bot/admin/web/src/app/language-understanding/intents/intents-filters/intents-filters.component.ts +++ b/bot/admin/web/src/app/language-understanding/intents/intents-filters/intents-filters.component.ts @@ -29,7 +29,7 @@ export class IntentsFiltersComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(300)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe(() => { this.onFilter.emit(this.form.value as IntentsFilter); }); } diff --git a/bot/admin/web/src/app/metrics/indicators/indicators-filters/indicators-filters.component.ts b/bot/admin/web/src/app/metrics/indicators/indicators-filters/indicators-filters.component.ts index 94101984a1..460afc19ad 100644 --- a/bot/admin/web/src/app/metrics/indicators/indicators-filters/indicators-filters.component.ts +++ b/bot/admin/web/src/app/metrics/indicators/indicators-filters/indicators-filters.component.ts @@ -42,7 +42,7 @@ export class IndicatorsFiltersComponent implements OnInit, OnDestroy { } ngOnInit() { - this.form.valueChanges.pipe(takeUntil(this.destroy), debounceTime(500)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(500), takeUntil(this.destroy)).subscribe(() => { this.onFilter.emit(this.form.value as IndicatorsFilter); }); } diff --git a/bot/admin/web/src/app/model-quality/count-stats/count-stats.component.ts b/bot/admin/web/src/app/model-quality/count-stats/count-stats.component.ts index 88b939689f..459401a4a2 100644 --- a/bot/admin/web/src/app/model-quality/count-stats/count-stats.component.ts +++ b/bot/admin/web/src/app/model-quality/count-stats/count-stats.component.ts @@ -33,7 +33,7 @@ export class CountStatsComponent implements OnInit, OnDestroy { constructor(public state: StateService, private qualityService: QualityService) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy), debounceTime(500)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(500), takeUntil(this.destroy)).subscribe(() => { if (isNaN(parseInt(this.minCount.value))) { this.minCount.patchValue(1); } diff --git a/bot/admin/web/src/app/model-quality/intent-quality/intent-quality.component.ts b/bot/admin/web/src/app/model-quality/intent-quality/intent-quality.component.ts index 02d22b1a77..162508e3c1 100644 --- a/bot/admin/web/src/app/model-quality/intent-quality/intent-quality.component.ts +++ b/bot/admin/web/src/app/model-quality/intent-quality/intent-quality.component.ts @@ -24,7 +24,7 @@ export class IntentQualityComponent implements OnInit, OnDestroy { constructor(private state: StateService, private quality: QualityService) {} ngOnInit(): void { - this.form.valueChanges.pipe(takeUntil(this.destroy), debounceTime(500)).subscribe(() => { + this.form.valueChanges.pipe(debounceTime(500), takeUntil(this.destroy)).subscribe(() => { if (isNaN(parseInt(this.minOccurrences.value))) { this.minOccurrences.patchValue(1); } diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-filters/sentence-training-filters.component.ts b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-filters/sentence-training-filters.component.ts index 9030e28c30..0803d69fa7 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-filters/sentence-training-filters.component.ts +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-filters/sentence-training-filters.component.ts @@ -149,7 +149,7 @@ export class SentenceTrainingFiltersComponent implements OnInit, OnDestroy { this.updateEntitiesFilters(); - this.form.valueChanges.pipe(takeUntil(this.destroy$), debounceTime(500)).subscribe(() => this.submitFiltersChange()); + this.form.valueChanges.pipe(debounceTime(500), takeUntil(this.destroy$)).subscribe(() => this.submitFiltersChange()); } submitFiltersChange(): void { diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.ts b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.ts index 159364b2cf..e420c0573e 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.ts +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.ts @@ -57,6 +57,7 @@ export class SentenceTrainingComponent implements OnInit, OnDestroy { @ViewChild('sentenceTrainingFilter') sentenceTrainingFilter: SentenceTrainingFiltersComponent; loading: boolean = false; + unloading: boolean = false; sentences: SentenceExtended[] = []; @@ -187,7 +188,7 @@ export class SentenceTrainingComponent implements OnInit, OnDestroy { ): Observable> { if (showLoadingSpinner) this.loading = true; - let search = this.search(this.state.createPaginatedQuery(start, size)).pipe(takeUntil(this.destroy$), share()); + let search = this.search(this.state.createPaginatedQuery(start, size)).pipe(share(), takeUntil(this.destroy$)); search.subscribe({ next: (data: PaginatedResult) => { @@ -372,6 +373,8 @@ export class SentenceTrainingComponent implements OnInit, OnDestroy { } retrieveSentence(sentence: SentenceExtended, tryCount = 0): Subscription | void { + if (this.unloading) return; + let exists = this.sentences.find((stnce) => { return stnce.text == sentence.text; }); @@ -574,6 +577,7 @@ Would you like to translate all the sentences matching the search criteria above } ngOnDestroy(): void { + this.unloading = true; this.destroy$.next(true); this.destroy$.complete(); } diff --git a/bot/admin/web/src/app/theme/styles/utilities.scss b/bot/admin/web/src/app/theme/styles/utilities.scss index 591c65e9b4..78968307e8 100644 --- a/bot/admin/web/src/app/theme/styles/utilities.scss +++ b/bot/admin/web/src/app/theme/styles/utilities.scss @@ -228,3 +228,8 @@ nb-menu { border-color: var(--input-basic-disabled-border-color); color: var(--input-basic-disabled-text-color); } + +.nb-spinner-container { + // prevent spinners from being displayed over the application header + z-index: 1020; +}