Browse Source

UI: added trendz settings tab

pull/13301/head
yuliaklochai 1 year ago
parent
commit
d6aa53a858
  1. 2
      ui-ngx/src/app/core/auth/auth.models.ts
  2. 4
      ui-ngx/src/app/core/auth/auth.reducer.ts
  3. 39
      ui-ngx/src/app/core/http/trendz-settings.service.ts
  4. 19
      ui-ngx/src/app/core/services/menu.models.ts
  5. 13
      ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts
  6. 4
      ui-ngx/src/app/modules/home/pages/admin/admin.module.ts
  7. 51
      ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html
  8. 36
      ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss
  9. 95
      ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts
  10. 1
      ui-ngx/src/app/shared/models/constants.ts
  11. 14
      ui-ngx/src/app/shared/models/icon.models.ts
  12. 25
      ui-ngx/src/app/shared/models/trendz-settings.models.ts
  13. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
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 {

4
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 = {

39
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<TrendzSettings> {
return this.http.get<TrendzSettings>(`/api/trendz/settings`, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}))
}
public saveTrendzSettings(trendzSettings: TrendzSettings): Observable<TrendzSettings> {
return this.http.post<TrendzSettings>(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}))
}
}

19
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<MenuId, MenuSection>([
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<Authority, MenuReference[]>([
{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<Authority, HomeSectionReference[]>([
},
{
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]
}
]
],

13
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<ScadaSymbolData> =
(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'

4
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,

51
ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html

@ -0,0 +1,51 @@
<!--
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.
-->
<div>
<mat-card appearance="outlined" class="settings-card">
<mat-card-header>
<mat-card-title>
<span class="mat-headline-5" translate>admin.trendz-settings</span>
</mat-card-title>
<span class="flex-1"></span>
<div tb-help="trendzSettings"></div>
</mat-card-header>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<mat-card-content>
<form [formGroup]="trendzSettingsForm" (ngSubmit)="save()">
<fieldset [disabled]="isLoading$ | async">
<section class="tb-trendz-section flex flex-col gt-sm:flex-row">
<mat-form-field class="tb-trendz-url mat-block flex-1" subscriptSizing="dynamic">
<mat-label translate>admin.trendz-url</mat-label>
<input matInput formControlName="trendzUrl">
</mat-form-field>
<mat-checkbox class="flex flex-1" formControlName="isTrendzEnabled">
{{ 'admin.trendz-enable' | translate }}
</mat-checkbox>
</section>
<div class="flex w-full flex-row flex-wrap items-center justify-end">
<button mat-button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || trendzSettingsForm.invalid || !trendzSettingsForm.dirty" type="submit">
{{'action.save' | translate}}
</button>
</div>
</fieldset>
</form>
</mat-card-content>
</mat-card>
</div>

36
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;
}
}
}

95
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<AppState>,
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);
})
}
}

1
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 */

14
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"/></svg>'
'1.9996128,2.004015 z"/></svg>',
'trendz-settings': '<svg viewBox="0 0 25 17"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.04334 0.28949H12.2804V5.7615L11.5894 ' +
'6.4537L10.4605 5.32674L7.04334 8.73801V0.28949ZM7.04334 10.0127V11.0075L7.54073 10.5093L7.04334 10.0127ZM7.04334 ' +
'12.2649V13.2424L7.53209 12.7545L7.04334 12.2649ZM18.3903 13.243V12.2646L17.901 12.7546L18.3903 13.243ZM18.3903 ' +
'11.0079V10.0123L17.8925 10.5093L18.3903 11.0079ZM18.3903 8.73841V3.34443H13.1532V5.76189L13.8438 6.45362L14.9727 ' +
'5.32661L17.0542 7.40453L18.3903 8.73841ZM24.8335 1.16233H19.2631V13.8185H24.8335V1.16233ZM0.833481 5.52653H6.1705V13.8185H0.833481V5.52653Z"/>' +
'<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9729 6.55688L15.819 7.40149L14.6876 8.53099L15.8137 9.65905L16.947 ' +
'8.52767L17.7931 9.37227L16.6583 10.5051L17.7844 11.6331L16.6669 12.7526L17.7931 13.8768L16.947 14.7214L15.8223 13.5986L14.6962 ' +
'14.7267L15.819 15.8476L14.973 16.6921L13.8516 15.5727L12.7169 16.7094L11.5821 15.5727L10.4607 16.6921L9.61465 15.8476L10.7375 ' +
'14.7267L9.61134 13.5985L8.48664 14.7213L7.64059 13.8767L8.76673 12.7525L7.64928 11.6331L8.77533 10.5051L7.64059 9.37227L8.48664 ' +
'8.52767L9.61993 9.65905L10.7461 8.53103L9.61465 7.40158L10.4607 6.55697L11.5907 7.68499L12.7169 6.55688L13.843 7.68494L14.9729 ' +
'6.55688ZM12.7169 8.24609L13.5629 9.0907L10.1787 12.4691L9.33268 11.6245L12.7169 8.24609ZM13.4262 10.0805L14.2723 10.9251L11.4521 ' +
'13.7404L10.6061 12.8958L13.4262 10.0805ZM14.6909 13.0321L13.8449 12.1875L11.8708 14.1583L12.7168 15.0029L14.6909 13.0321Z"/></svg>'
};
export const svgIconsUrl: { [key: string]: string } = {

25
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
}

6
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",

Loading…
Cancel
Save