From d6aa53a858e90b66b2f5c9a050308f8a67a0cdc3 Mon Sep 17 00:00:00 2001 From: yuliaklochai Date: Fri, 2 May 2025 12:42:14 +0300 Subject: [PATCH] UI: added trendz settings tab --- ui-ngx/src/app/core/auth/auth.models.ts | 2 + ui-ngx/src/app/core/auth/auth.reducer.ts | 4 +- .../app/core/http/trendz-settings.service.ts | 39 ++++++++ ui-ngx/src/app/core/services/menu.models.ts | 19 +++- .../home/pages/admin/admin-routing.module.ts | 13 +++ .../modules/home/pages/admin/admin.module.ts | 4 +- .../admin/trendz-settings.component.html | 51 ++++++++++ .../admin/trendz-settings.component.scss | 36 +++++++ .../pages/admin/trendz-settings.component.ts | 95 +++++++++++++++++++ ui-ngx/src/app/shared/models/constants.ts | 1 + ui-ngx/src/app/shared/models/icon.models.ts | 14 ++- .../shared/models/trendz-settings.models.ts | 25 +++++ .../assets/locale/locale.constant-en_US.json | 6 +- 13 files changed, 302 insertions(+), 7 deletions(-) create mode 100644 ui-ngx/src/app/core/http/trendz-settings.service.ts create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts create mode 100644 ui-ngx/src/app/shared/models/trendz-settings.models.ts diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index e5cc1424ab..142d845cf4 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -16,6 +16,7 @@ import { AuthUser, User } from '@shared/models/user.model'; import { UserSettings } from '@shared/models/user-settings.models'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; export interface SysParamsState { userTokenAccessEnabled: boolean; @@ -32,6 +33,7 @@ export interface SysParamsState { maxArgumentsPerCF: number; ruleChainDebugPerTenantLimitsConfiguration?: string; calculatedFieldDebugPerTenantLimitsConfiguration?: string; + trendzSettings: TrendzSettings; } export interface SysParams extends SysParamsState { diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 3ecf70074c..fde778284d 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -17,6 +17,7 @@ import { AuthPayload, AuthState } from './auth.models'; import { AuthActions, AuthActionTypes } from './auth.actions'; import { initialUserSettings, UserSettings } from '@shared/models/user-settings.models'; +import { initialTrendzSettings } from '@shared/models/trendz-settings.models'; import { unset } from '@core/utils'; const emptyUserAuthState: AuthPayload = { @@ -34,7 +35,8 @@ const emptyUserAuthState: AuthPayload = { maxArgumentsPerCF: 0, maxDataPointsPerRollingArg: 0, maxDebugModeDurationMinutes: 0, - userSettings: initialUserSettings + userSettings: initialUserSettings, + trendzSettings: initialTrendzSettings }; export const initialState: AuthState = { diff --git a/ui-ngx/src/app/core/http/trendz-settings.service.ts b/ui-ngx/src/app/core/http/trendz-settings.service.ts new file mode 100644 index 0000000000..4d965f920e --- /dev/null +++ b/ui-ngx/src/app/core/http/trendz-settings.service.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; +import { defaultHttpOptionsFromConfig } from '@core/http/http-utils'; + +@Injectable({ + providedIn: 'root' +}) +export class TrendzSettingsService { + + constructor( + private http: HttpClient + ) {} + + public getTrendzSettings(): Observable { + return this.http.get(`/api/trendz/settings`, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + } + + public saveTrendzSettings(trendzSettings: TrendzSettings): Observable { + return this.http.post(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + } +} diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 4775a3a771..607c5c6dff 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -104,7 +104,8 @@ export enum MenuId { features = 'features', otaUpdates = 'otaUpdates', version_control = 'version_control', - api_usage = 'api_usage' + api_usage = 'api_usage', + trendz_settings = 'trendz_settings' } declare type MenuFilter = (authState: AuthState) => boolean; @@ -684,6 +685,17 @@ export const menuSectionMap = new Map([ path: '/usage', icon: 'insert_chart' } + ], + [ + MenuId.trendz_settings, + { + id: MenuId.trendz_settings, + name: 'admin.trendz', + fullName: 'admin.trendz-settings', + type: 'link', + path: '/settings/trendz', + icon: 'trendz-settings' + } ] ]); @@ -843,7 +855,8 @@ const defaultUserMenuMap = new Map([ {id: MenuId.home_settings}, {id: MenuId.notification_settings}, {id: MenuId.repository_settings}, - {id: MenuId.auto_commit_settings} + {id: MenuId.auto_commit_settings}, + {id: MenuId.trendz_settings} ] }, { @@ -946,7 +959,7 @@ const defaultHomeSectionMap = new Map([ }, { name: 'admin.system-settings', - places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings] + places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings, MenuId.trendz_settings] } ] ], diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 8bd724de4a..2836224a9a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -46,6 +46,7 @@ import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol-editor.mo import { MenuId } from '@core/services/menu.models'; import { catchError } from 'rxjs/operators'; import { JsLibraryTableConfigResolver } from '@home/pages/admin/resource/js-library-table-config.resolver'; +import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component'; export const scadaSymbolResolver: ResolveFn = (route: ActivatedRouteSnapshot, @@ -349,6 +350,18 @@ const routes: Routes = [ } } }, + { + path: 'trendz', + component: TrendzSettingsComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.TENANT_ADMIN], + title: 'admin.trendz-settings', + breadcrumb: { + menuId: MenuId.trendz_settings + } + } + }, { path: 'security-settings', redirectTo: '/security-settings/general' diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts index 63878ebac9..a5f18122fd 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts @@ -37,6 +37,7 @@ import { OAuth2Module } from '@home/pages/admin/oauth2/oauth2.module'; import { JsLibraryTableHeaderComponent } from '@home/pages/admin/resource/js-library-table-header.component'; import { JsResourceComponent } from '@home/pages/admin/resource/js-resource.component'; import { NgxFlowModule } from '@flowjs/ngx-flow'; +import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component'; @NgModule({ declarations: @@ -55,7 +56,8 @@ import { NgxFlowModule } from '@flowjs/ngx-flow'; QueueComponent, RepositoryAdminSettingsComponent, AutoCommitAdminSettingsComponent, - TwoFactorAuthSettingsComponent + TwoFactorAuthSettingsComponent, + TrendzSettingsComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html new file mode 100644 index 0000000000..45ba62dace --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html @@ -0,0 +1,51 @@ + +
+ + + + admin.trendz-settings + + +
+
+ + +
+ +
+
+
+ + admin.trendz-url + + + + {{ 'admin.trendz-enable' | translate }} + +
+
+ +
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss new file mode 100644 index 0000000000..cbb8e698bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + @import "../../../../../scss/constants"; + +:host { + .mat-mdc-card-header { + min-height: 64px; + } + + .tb-trendz-section { + margin: 16px 0; + } + + .tb-trendz-url { + @media #{$mat-gt-sm} { + padding-right: 12px; + } + + @media #{$mat-lt-md} { + padding-bottom: 12px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts new file mode 100644 index 0000000000..ffd2898d2a --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts @@ -0,0 +1,95 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, OnInit } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; +import { select, Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TrendzSettingsService } from '@core/http/trendz-settings.service'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-trendz-settings', + templateUrl: './trendz-settings.component.html', + styleUrls: ['./trendz-settings.component.scss', './settings-card.scss'] +}) +export class TrendzSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { + + trendzSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder, + private trendzSettingsService: TrendzSettingsService) { + super(store); + } + + ngOnInit() { + this.trendzSettingsForm = this.fb.group({ + trendzUrl: [null, [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]], + isTrendzEnabled: [false] + }); + + this.trendzSettingsService.getTrendzSettings().subscribe((trendzSettings) => { + this.setTrendzSettings(trendzSettings); + }); + + this.trendzSettingsForm.get('isTrendzEnabled').valueChanges + .subscribe((enabled: boolean) => this.toggleUrlRequired(enabled)); + } + + toggleUrlRequired(enabled: boolean) { + const trendzUrlControl = this.trendzSettingsForm.get('trendzUrl')!; + const validators = [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]; + + if (enabled) { + validators.push(Validators.required); + } + + trendzUrlControl.setValidators(validators); + trendzUrlControl.updateValueAndValidity(); + } + + setTrendzSettings(trendzSettings: TrendzSettings) { + this.trendzSettingsForm.reset({ + trendzUrl: trendzSettings?.baseUrl, + isTrendzEnabled: isDefinedAndNotNull(trendzSettings?.enabled) ? + trendzSettings?.enabled : false + }); + + this.toggleUrlRequired(this.trendzSettingsForm.get('isTrendzEnabled').value); + } + + confirmForm(): FormGroup { + return this.trendzSettingsForm; + } + + save(): void { + const trendzUrl = this.trendzSettingsForm.get('trendzUrl').value; + const isTrendzEnabled = this.trendzSettingsForm.get('isTrendzEnabled').value; + + const trendzSettings: TrendzSettings = { + baseUrl: trendzUrl, + enabled: isTrendzEnabled + }; + + this.trendzSettingsService.saveTrendzSettings(trendzSettings).subscribe(() => { + this.setTrendzSettings(trendzSettings); + }) + } +} diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index e1306e577c..b2f96409dc 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -200,6 +200,7 @@ export const HelpLinks = { mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, calculatedField: `${helpBaseUrl}/docs${docPlatformPrefix}/`, timewindowSettings: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/dashboards/#time-window`, + trendzSettings: `${helpBaseUrl}/docs/trendz/` } }; /* eslint-enable max-len */ diff --git a/ui-ngx/src/app/shared/models/icon.models.ts b/ui-ngx/src/app/shared/models/icon.models.ts index c9de8ec3da..6643bd9c7f 100644 --- a/ui-ngx/src/app/shared/models/icon.models.ts +++ b/ui-ngx/src/app/shared/models/icon.models.ts @@ -62,7 +62,19 @@ export const svgIcons: {[key: string]: string} = { '4.6760606 4.678212,7.3604329 7.3397982,4.6839955 4.6657413,2.0041717 6.6653477,2.2309572e-4 9.3360035,2.6766286 11.997681,' + '0 14.659287,2.6765011 Z m -5.332255,4.0079963 1.999613,2.003945 -7.99844,8.0158157 -1.9996133,-2.004017 z m 1.676684,4.3522483 ' + '1.999613,2.0039454 -6.6654242,6.679793 -1.9996133,-2.003874 z m 2.988987,7.0033574 -1.999544,-2.003945 -4.6658108,4.675848 ' + - '1.9996128,2.004015 z"/>' + '1.9996128,2.004015 z"/>', + 'trendz-settings': '' + + '' }; export const svgIconsUrl: { [key: string]: string } = { diff --git a/ui-ngx/src/app/shared/models/trendz-settings.models.ts b/ui-ngx/src/app/shared/models/trendz-settings.models.ts new file mode 100644 index 0000000000..e09797bd7e --- /dev/null +++ b/ui-ngx/src/app/shared/models/trendz-settings.models.ts @@ -0,0 +1,25 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export interface TrendzSettings { + baseUrl: string, + enabled: boolean +} + +export const initialTrendzSettings: TrendzSettings = { + baseUrl: null, + enabled: false +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b160a44b0e..8255d4e10b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -545,7 +545,11 @@ "slack-settings": "Slack settings", "mobile-settings": "Mobile settings", "firebase-service-account-file": "Firebase service account credentials JSON file", - "select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or " + "select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or ", + "trendz": "Trendz", + "trendz-settings": "Trendz settings", + "trendz-url": "Trendz URL", + "trendz-enable": "Enable Trendz" }, "alarm": { "alarm": "Alarm",