From 1fee9a327302a0f20498267ed62715a22eb76908 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 29 Mar 2023 11:40:32 +0300 Subject: [PATCH 01/42] Revert "Add quarter and half year intervals to time window." --- .../src/app/shared/models/time/time.models.ts | 68 ------------------- .../assets/locale/locale.constant-en_US.json | 6 -- 2 files changed, 74 deletions(-) diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index ae093c71d2..6c04aef946 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -140,8 +140,6 @@ export enum QuickTimeInterval { PREVIOUS_WEEK = 'PREVIOUS_WEEK', PREVIOUS_WEEK_ISO = 'PREVIOUS_WEEK_ISO', PREVIOUS_MONTH = 'PREVIOUS_MONTH', - PREVIOUS_QUARTER = 'PREVIOUS_QUARTER', - PREVIOUS_HALF_YEAR = 'PREVIOUS_HALF_YEAR', PREVIOUS_YEAR = 'PREVIOUS_YEAR', CURRENT_HOUR = 'CURRENT_HOUR', CURRENT_DAY = 'CURRENT_DAY', @@ -152,10 +150,6 @@ export enum QuickTimeInterval { CURRENT_WEEK_ISO_SO_FAR = 'CURRENT_WEEK_ISO_SO_FAR', CURRENT_MONTH = 'CURRENT_MONTH', CURRENT_MONTH_SO_FAR = 'CURRENT_MONTH_SO_FAR', - CURRENT_QUARTER = 'CURRENT_QUARTER', - CURRENT_QUARTER_SO_FAR = 'CURRENT_QUARTER_SO_FAR', - CURRENT_HALF_YEAR = 'CURRENT_HALF_YEAR', - CURRENT_HALF_YEAR_SO_FAR = 'CURRENT_HALF_YEAR_SO_FAR', CURRENT_YEAR = 'CURRENT_YEAR', CURRENT_YEAR_SO_FAR = 'CURRENT_YEAR_SO_FAR' } @@ -167,8 +161,6 @@ export const QuickTimeIntervalTranslationMap = new Map Date: Fri, 31 Mar 2023 12:26:20 +0300 Subject: [PATCH 02/42] Update PR template delete add version in templates --- pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pull_request_template.md b/pull_request_template.md index 21362429dc..d0bf8993f1 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -5,7 +5,7 @@ Put your PR description here instead of this sentence. ## General checklist - [ ] You have reviewed the guidelines [document](https://docs.google.com/document/d/1wqcOafLx5hth8SAg4dqV_LV3un3m5WYR8RdTJ4MbbUM/edit?usp=sharing). -- [ ] PR name contains fix version. For example, "[3.3.4] Hotfix of some UI component" or "[3.4] New Super Feature". +- [ ] [Labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels#about-labels) that classify your pull request have been added. - [ ] The [milestone](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/about-milestones) is specified and corresponds to fix version. - [ ] Description references specific [issue](https://github.com/thingsboard/thingsboard/issues). - [ ] Description contains human-readable scope of changes. From bcc5eba8e6e136eb8fe0ecb0eb14a2afecf8e834 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 14 Apr 2023 19:18:35 +0300 Subject: [PATCH 03/42] UI: Tenant admin home page initial implementation --- .../getting-started-widget.component.html | 112 +++ .../getting-started-widget.component.scss | 117 ++- .../getting-started-widget.component.ts | 4 +- .../lib/home-page/home-page-widgets.module.ts | 7 +- .../home-page/toggle-header.component.html | 20 + .../home-page/toggle-header.component.scss | 87 +++ .../lib/home-page/toggle-header.component.ts | 82 +++ .../home-links/home-links-routing.module.ts | 15 +- .../home-links/tenant_admin_home_page.raw | 667 ++++++++++++++++++ .../shared/components/markdown.component.html | 2 +- .../shared/components/markdown.component.ts | 127 ++-- .../assets/locale/locale.constant-en_US.json | 49 ++ 12 files changed, 1216 insertions(+), 73 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html index b2425a0231..ee84a63ad6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html @@ -91,6 +91,118 @@ description{{ 'widgets.getting-started.sys-admin.step6.how-to-configure-notifications' | translate }} + + + +
+
widgets.getting-started.tenant-admin.step1.title
+ {{ 'device.devices' | translate }} +
+
+
+ + description{{ 'widgets.getting-started.tenant-admin.step1.how-to-create-device' | translate }} +
+ + +
+
widgets.getting-started.tenant-admin.step2.title
+
+
+
+
+ + Ubuntu + MacOS + Windows + + + +

+ +
+ +

+ +
+ +

+
+
+

+ +
+
+ + description{{ 'widgets.getting-started.tenant-admin.step2.how-to-connect-device' | translate }} +
+ + +
+
widgets.getting-started.tenant-admin.step3.title
+ {{ 'dashboard.dashboards' | translate }} +
+
+
+ + description{{ 'widgets.getting-started.tenant-admin.step3.how-to-create-dashboard' | translate }} +
+ + +
+
widgets.getting-started.tenant-admin.step4.title
+ {{ 'widgets.getting-started.tenant-admin.step4.alarm-rules' | translate }} +
+
+
+ + description{{ 'widgets.getting-started.tenant-admin.step4.how-to-configure-alarm-rules' | translate }} +
+ + +
+
widgets.getting-started.tenant-admin.step5.title
+
+
+
+

+ +
+ + description{{ 'widgets.getting-started.tenant-admin.step5.how-to-create-alarm' | translate }} +
+ + +
+
widgets.getting-started.tenant-admin.step6.title
+ {{ 'customer.customers' | translate }} +
+
+
+ + description{{ 'widgets.getting-started.tenant-admin.step6.how-to-create-customer-and-assign-dashboard' | translate }} +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.scss index 193fb5e793..20345a97f4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.scss @@ -83,13 +83,105 @@ color: rgba(0, 0, 0, 0.87); } - .tb-get-started .mat-vertical-content p { - font-style: normal; - font-weight: 400; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.2px; - color: rgba(0, 0, 0, 0.54); + .tb-get-started .mat-vertical-content { + p, li { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.54); + em { + font-style: normal; + font-weight: 500; + font-size: 13px; + color: rgba(0, 0, 0, 0.38); + } + } + ul { + padding-inline-start: 20px; + } + .tb-bordered-content { + display: flex; + flex-direction: column; + align-items: stretch; + border: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 8px; + padding: 16px; + p { + margin-top: 16px; + margin-bottom: 8px; + } + } + .tb-markdown-view { + .tb-getting-started-code { + .code-wrapper { + padding: 0; + pre[class*=language-] { + margin: 0; + padding: 9px 38px 9px 16px; + background: rgba(0, 0, 0, 0.03); + border-radius: 6px; + border: none; + } + code[class*="language-"], pre[class*="language-"] { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.38); + overflow: hidden; + white-space: break-spaces; + word-break: break-all; + & * { + color: rgba(0, 0, 0, 0.38); + cursor: inherit; + background: transparent; + } + } + button.clipboard-btn { + right: 0; + height: 34px; + p, div { + background: transparent; + } + p { + margin: 0; + padding: 7px; + color: #305680; + } + div { + top: 0; + padding: 8px; + height: 34px; + width: 34px; + img { + display: none; + } + &:after { + content: ""; + position: initial; + display: block; + width: 18px; + height: 18px; + background: #305680; + -webkit-mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-repeat: no-repeat; + mask-image: url(/assets/copy-code-icon.svg); + mask-repeat: no-repeat; + } + } + } + } + } + } + @media #{$mat-md-lg} { + .tb-bordered-content { + padding: 4px; + } + } } @media #{$mat-md-lg} { @@ -107,15 +199,16 @@ line-height: 16px; } - .tb-get-started .mat-vertical-content p { - font-size: 12px; - line-height: 16px; - letter-spacing: 0.25px; + .tb-get-started .mat-vertical-content { + p, li { + font-size: 12px; + line-height: 16px; + letter-spacing: 0.25px; + } } .tb-get-started .mat-vertical-content { padding: 0 16px 16px 16px; } - } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.ts index 9c07e62978..7566ae8579 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.ts @@ -27,7 +27,7 @@ import { } from '@home/components/widget/lib/home-page/getting-started-completed-dialog.component'; import { GettingStarted } from '@shared/models/user-settings.models'; import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper'; -import { isUndefined } from '@core/utils'; +import { baseUrl, isUndefined } from '@core/utils'; import { MatStepper } from '@angular/material/stepper'; import { first } from 'rxjs/operators'; import { Authority } from '@shared/models/authority.enum'; @@ -54,6 +54,8 @@ export class GettingStartedWidgetComponent extends PageComponent implements OnIn }; allCompleted = false; + baseUrl = baseUrl(); + constructor(protected store: Store, private cd: ChangeDetectorRef, private userSettingsService: UserSettingsService, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts index 3be30dad30..debd1d6ca0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts @@ -28,6 +28,7 @@ import { GettingStartedWidgetComponent } from '@home/components/widget/lib/home- import { GettingStartedCompletedDialogComponent } from '@home/components/widget/lib/home-page/getting-started-completed-dialog.component'; +import { ToggleHeaderComponent } from '@home/components/widget/lib/home-page/toggle-header.component'; @NgModule({ declarations: @@ -40,7 +41,8 @@ import { AddDocLinkDialogComponent, EditDocLinksDialogComponent, GettingStartedWidgetComponent, - GettingStartedCompletedDialogComponent + GettingStartedCompletedDialogComponent, + ToggleHeaderComponent ], imports: [ CommonModule, @@ -55,7 +57,8 @@ import { AddDocLinkDialogComponent, EditDocLinksDialogComponent, GettingStartedWidgetComponent, - GettingStartedCompletedDialogComponent + GettingStartedCompletedDialogComponent, + ToggleHeaderComponent ] }) export class HomePageWidgetsModule { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html new file mode 100644 index 0000000000..3cbf61fdae --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.html @@ -0,0 +1,20 @@ + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.scss new file mode 100644 index 0000000000..0dce288424 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.scss @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 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 ::ng-deep { + .mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header { + width: 100%; + border-radius: 100px; + height: 32px; + padding: 2px; + border: none; + background: rgba(0, 0, 0, 0.06); + margin-bottom: 8px; + .mat-button-toggle + .mat-button-toggle { + border-left: none; + } + .mat-button-toggle.mat-button-toggle-appearance-standard { + flex: 1; + color: rgba(0, 0, 0, 0.38); + background: transparent; + + .mat-button-toggle-focus-overlay, .mat-button-toggle-ripple { + border-radius: 20px; + } + .mat-button-toggle-button { + height: 28px; + .mat-button-toggle-label-content { + line-height: 26px; + font-weight: 400; + font-size: 14px; + letter-spacing: 0.2px; + } + } + &.mat-button-toggle-checked { + .mat-button-toggle-button { + background: #F3F6FA; + color: #305680; + border: 1px solid #305680; + border-radius: 20px; + + .mat-button-toggle-label-content { + font-weight: 500; + line-height: 24px; + } + } + } + } + } + @media #{$mat-md-lg} { + .mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header { + height: 24px; + margin-bottom: 0; + .mat-button-toggle.mat-button-toggle-appearance-standard { + .mat-button-toggle-button { + height: 20px; + display: grid; + .mat-button-toggle-label-content { + line-height: 20px; + font-size: 10px; + padding: 0 2px; + } + } + &.mat-button-toggle-checked { + .mat-button-toggle-button { + .mat-button-toggle-label-content { + line-height: 18px; + } + } + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.ts new file mode 100644 index 0000000000..1d7876eb36 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/toggle-header.component.ts @@ -0,0 +1,82 @@ +/// +/// Copyright © 2016-2023 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 { + AfterContentInit, + AfterViewInit, + ChangeDetectorRef, + Component, + ContentChildren, + Input, + OnInit, + QueryList, + ViewChild +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { AdminService } from '@core/http/admin.service'; +import { UpdateMessage } from '@shared/models/settings.models'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { Authority } from '@shared/models/authority.enum'; +import { of } from 'rxjs'; +import { MatStepper } from '@angular/material/stepper'; +import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle'; + +@Component({ + selector: 'tb-toggle-header', + templateUrl: './toggle-header.component.html', + styleUrls: ['./toggle-header.component.scss'] +}) +export class ToggleHeaderComponent extends PageComponent implements OnInit, AfterViewInit { + + @ViewChild('toggleGroup') + toggleGroup: MatButtonToggleGroup; + + @ContentChildren(MatButtonToggle) + _buttonToggles: QueryList; + + innerValue: any; + + @Input() + set value(value: any) { + this.innerValue = value; + } + + get value(): any { + return this.toggleGroup?.value; + } + + @Input() + name: string; + + constructor(protected store: Store) { + super(store); + } + + ngOnInit() { + } + + ngAfterViewInit() { + for (const toggle of this._buttonToggles) { + toggle.buttonToggleGroup = this.toggleGroup; + if (this.innerValue === toggle.value) { + toggle.checked = true; + } + } + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts index be408da234..3e0f797be1 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts @@ -27,6 +27,7 @@ import { AppState } from '@core/core.state'; import { map } from 'rxjs/operators'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import sysAdminHomePageDashboardJson from '!raw-loader!./sys_admin_home_page.raw'; +import tenantAdminHomePageDashboardJson from '!raw-loader!./tenant_admin_home_page.raw'; @Injectable() export class HomeDashboardResolver implements Resolve { @@ -39,8 +40,18 @@ export class HomeDashboardResolver implements Resolve { return this.dashboardService.getHomeDashboard().pipe( map((dashboard) => { if (!dashboard) { - if (getCurrentAuthUser(this.store).authority === Authority.SYS_ADMIN) { - dashboard = JSON.parse(sysAdminHomePageDashboardJson); + const authority = getCurrentAuthUser(this.store).authority; + switch (authority) { + case Authority.SYS_ADMIN: + dashboard = JSON.parse(sysAdminHomePageDashboardJson); + break; + case Authority.TENANT_ADMIN: + dashboard = JSON.parse(tenantAdminHomePageDashboardJson); + break; + case Authority.CUSTOMER_USER: + break; + } + if (dashboard) { dashboard.hideDashboardToolbar = true; } } diff --git a/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw b/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw new file mode 100644 index 0000000000..3044499da0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw @@ -0,0 +1,667 @@ +{ + "title": "Tenant Administrator Home Page", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "markdown_card", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'widgets.activity.title' | translate }}
\n \n {{ 'device.devices' | translate }}\n {{ 'widgets.transport-messages.title' | translate }}\n \n
\n \n \n \n \n \n \n \n \n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n" + }, + "title": "Transport messages", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f" + }, + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "isSystemType": true, + "bundleAlias": "charts", + "typeAlias": "timeseries_bars_flot", + "type": "timeseries", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:widgets.transport-messages.title}", + "color": "#305680", + "settings": {}, + "_hash": 0.2880464219129071, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1680443065451, + "endTimeMs": 1680529465451 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 50000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "stack": true, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": 0, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" + }, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [] + }, + "title": "Transport messages", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": { + "padding": "0" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + } + }, + "row": 0, + "col": 0, + "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e" + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "isSystemType": true, + "bundleAlias": "home_page_widgets", + "typeAlias": "documentation_links", + "type": "static", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 2 + }, + "title": "Documentation", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4" + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "isSystemType": true, + "bundleAlias": "home_page_widgets", + "typeAlias": "getting_started", + "type": "static", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 3 + }, + "title": "Getting started", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "a23185ad-dc46-806c-0e50-5b21fb080ace" + }, + "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { + "isSystemType": true, + "bundleAlias": "charts", + "typeAlias": "basic_timeseries", + "type": "timeseries", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "activeDevicesCount", + "type": "timeseries", + "label": "{i18n:device.devices}", + "color": "#305680", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 3, + "fillLines": true, + "showPoints": false, + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showSeparateAxis": false, + "axisPosition": "left", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.9688095820365725, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": true, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 7200000, + "fixedTimewindow": { + "startTimeMs": 1681400576338, + "endTimeMs": 1681486976338 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "stack": false, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": null, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "" + }, + "shadowSize": 0, + "smoothLines": true, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [] + }, + "title": "Devices activity", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": { + "padding": "0" + }, + "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + } + }, + "row": 0, + "col": 0, + "id": "d26e5cd7-75ef-d475-00c7-1a2d1114efe8" + } + }, + "states": { + "default": { + "name": "Tenant Administrator Home Page", + "root": true, + "layouts": { + "main": { + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "sizeX": 41, + "sizeY": 16, + "row": 26, + "col": 44, + "mobileOrder": 7, + "mobileHeight": 8 + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "sizeX": 31, + "sizeY": 16, + "row": 42, + "col": 26, + "mobileHide": true + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "sizeX": 35, + "sizeY": 58, + "row": 0, + "col": 85, + "mobileHide": true + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 120, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 20, + "outerMargin": true + } + } + } + }, + "transport_messages": { + "name": "Transport messages", + "root": false, + "layouts": { + "main": { + "widgets": { + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#ffffff", + "columns": 24, + "margin": 0, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "devices_activity": { + "name": "Devices activity", + "root": false, + "layouts": { + "main": { + "widgets": { + "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#ffffff", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70 + } + } + } + } + }, + "entityAliases": { + "ae870700-071f-b3bc-406c-16ba554c5a55": { + "id": "ae870700-071f-b3bc-406c-16ba554c5a55", + "alias": "Tenants", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "TENANT" + } + }, + "ca4d90e4-f9ae-6fca-6b09-85815a48d52b": { + "id": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", + "alias": "TenantProfiles", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "TENANT" + } + }, + "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { + "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "alias": "Devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "619cdf00-a042-3b55-124e-194c1b28c236": { + "id": "619cdf00-a042-3b55-124e-194c1b28c236", + "alias": "Assets", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "ASSET" + } + }, + "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2": { + "id": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", + "alias": "Users", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "USER" + } + }, + "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31": { + "id": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", + "alias": "Customers", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "CUSTOMER" + } + }, + "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { + "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "alias": "Api Usage State", + "filter": { + "type": "apiUsageState", + "resolveMultiple": true + } + } + }, + "filters": {}, + "timewindow": { + "displayValue": "", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168326072, + "endTimeMs": 1680254726072 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": true, + "showEntitiesSelect": true, + "showDashboardTimewindow": true, + "showDashboardExport": true, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": true, + "showFilters": true, + "showUpdateDashboardImage": true, + "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" + } + }, + "externalId": null, + "name": "Tenant Administrator Home Page" +} \ No newline at end of file diff --git a/ui-ngx/src/app/shared/components/markdown.component.html b/ui-ngx/src/app/shared/components/markdown.component.html index 5b8b9f8af0..67e8cd0da2 100644 --- a/ui-ngx/src/app/shared/components/markdown.component.html +++ b/ui-ngx/src/app/shared/components/markdown.component.html @@ -22,5 +22,5 @@ background: #efefef;"> {{error}}
-
+
diff --git a/ui-ngx/src/app/shared/components/markdown.component.ts b/ui-ngx/src/app/shared/components/markdown.component.ts index 5af7ccce6b..753effc24f 100644 --- a/ui-ngx/src/app/shared/components/markdown.component.ts +++ b/ui-ngx/src/app/shared/components/markdown.component.ts @@ -39,6 +39,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { SHARED_MODULE_TOKEN } from '@shared/components/tokens'; import { deepClone, guid, isDefinedAndNotNull } from '@core/utils'; import { Observable, of, ReplaySubject } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coerce-boolean'; let defaultMarkdownStyle; @@ -76,6 +77,10 @@ export class TbMarkdownComponent implements OnChanges { get fallbackToPlainMarkdown(): boolean { return this.fallbackToPlainMarkdownValue; } set fallbackToPlainMarkdown(value: boolean) { this.fallbackToPlainMarkdownValue = coerceBooleanProperty(value); } + @Input() + @coerceBoolean() + usePlainMarkdown = false; + @Output() ready = new EventEmitter(); private lineNumbersValue = false; @@ -124,13 +129,8 @@ export class TbMarkdownComponent implements OnChanges { } template = this.sanitizeCurlyBraces(template); this.markdownContainer.clear(); - const parent = this; - let readyObservable: Observable; - let compileModules = [this.sharedModule]; - if (this.additionalCompileModules) { - compileModules = compileModules.concat(this.additionalCompileModules); - } let styles: string[] = []; + let readyObservable: Observable; if (this.applyDefaultMarkdownStyle) { if (!defaultMarkdownStyle) { defaultMarkdownStyle = deepClone(TbMarkdownComponent['ɵcmp'].styles)[0].replace(/\[_nghost\-%COMP%\]/g, '') @@ -141,70 +141,87 @@ export class TbMarkdownComponent implements OnChanges { if (this.additionalStyles) { styles = styles.concat(this.additionalStyles); } - this.dynamicComponentFactoryService.createDynamicComponentFactory( - class TbMarkdownInstance { - ngOnDestroy(): void { - parent.destroyMarkdownInstanceResources(); - } - }, - template, - compileModules, - true, 1, styles - ).subscribe((factory) => { - this.tbMarkdownInstanceComponentFactory = factory; - const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector}); - try { - this.tbMarkdownInstanceComponentRef = - this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentFactory, 0, injector); - if (this.context) { - for (const propName of Object.keys(this.context)) { - this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName]; - } - } - this.tbMarkdownInstanceComponentRef.instance.style = this.style; - readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement); - this.cd.detectChanges(); - this.error = null; - } catch (error) { - readyObservable = this.handleError(template, error, styles); - } - readyObservable.subscribe(() => { - this.ready.emit(); - }); - }, - (error) => { - readyObservable = this.handleError(template, error, styles); + if (this.usePlainMarkdown) { + readyObservable = this.plainMarkdown(template, styles); this.cd.detectChanges(); readyObservable.subscribe(() => { this.ready.emit(); }); - }); + } else { + const parent = this; + let compileModules = [this.sharedModule]; + if (this.additionalCompileModules) { + compileModules = compileModules.concat(this.additionalCompileModules); + } + this.dynamicComponentFactoryService.createDynamicComponentFactory( + class TbMarkdownInstance { + ngOnDestroy(): void { + parent.destroyMarkdownInstanceResources(); + } + }, + template, + compileModules, + true, 1, styles + ).subscribe((factory) => { + this.tbMarkdownInstanceComponentFactory = factory; + const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector}); + try { + this.tbMarkdownInstanceComponentRef = + this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentFactory, 0, injector); + if (this.context) { + for (const propName of Object.keys(this.context)) { + this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName]; + } + } + this.tbMarkdownInstanceComponentRef.instance.style = this.style; + readyObservable = this.handleImages(this.tbMarkdownInstanceComponentRef.location.nativeElement); + this.cd.detectChanges(); + this.error = null; + } catch (error) { + readyObservable = this.handleError(template, error, styles); + } + readyObservable.subscribe(() => { + this.ready.emit(); + }); + }, + (error) => { + readyObservable = this.handleError(template, error, styles); + this.cd.detectChanges(); + readyObservable.subscribe(() => { + this.ready.emit(); + }); + }); + } } private handleError(template: string, error, styles?: string[]): Observable { this.error = (error ? error + '' : 'Failed to render markdown!').replace(/\n/g, '
'); this.markdownContainer.clear(); if (this.fallbackToPlainMarkdownValue) { - const element = this.fallbackElement.nativeElement; - let styleElement; - if (styles?.length) { - const markdownClass = 'tb-markdown-view-' + guid(); - let innerStyle = styles.join('\n'); - innerStyle = innerStyle.replace(/\.tb-markdown-view/g, '.' + markdownClass); - template = template.replace(/tb-markdown-view/g, markdownClass); - styleElement = this.renderer.createElement('style'); - styleElement.innerHTML = innerStyle; - } - element.innerHTML = template; - if (styleElement) { - this.renderer.appendChild(element, styleElement); - } - return this.handleImages(element); + return this.plainMarkdown(template, styles); } else { return of(null); } } + private plainMarkdown(template: string, styles?: string[]): Observable { + const element = this.fallbackElement.nativeElement; + let styleElement; + if (styles?.length) { + const markdownClass = 'tb-markdown-view-' + guid(); + let innerStyle = styles.join('\n'); + innerStyle = innerStyle.replace(/\.tb-markdown-view/g, '.' + markdownClass); + template = template.replace(/tb-markdown-view/g, markdownClass); + styleElement = this.renderer.createElement('style'); + styleElement.innerHTML = innerStyle; + } + element.innerHTML = template; + if (styleElement) { + this.renderer.appendChild(element, styleElement); + } + return this.handleImages(element); + } + private handlePlugins(element: HTMLElement): void { if (this.lineNumbers) { this.setPluginClass(element, PrismPlugin.LineNumbers); 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 610cdb209e..0c318d4849 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5114,6 +5114,9 @@ "title": "Transport messages", "info": "All the messages that came from devices" }, + "activity": { + "title": "Activity" + }, "documentation": { "title": "Documentation", "add-link": "Add link", @@ -5201,6 +5204,52 @@ "content": "

Some text

Follow the documentation on how to do it:

", "how-to-configure-notifications": "How to configure Notifications" } + }, + "tenant-admin": { + "step1": { + "title": "Create device", + "content": "

We will manually provision the device using the UI. Follow the documentation on how to do it:

", + "how-to-create-device": "How to create Device" + }, + "step2": { + "title": "Connect device", + "content-before": "

To connect the device you need to get the device credentials. But we recommend using the default auto-generated one.

  • Go to device table
  • Click on the device row to open device details
  • Press the button \"Copy access token\"

Use simple commands to publish data over HTTP:

", + "ubuntu": { + "install-curl": "Install cURL for Ubuntu:" + }, + "macos": { + "install-curl": "Install cURL for MacOS:" + }, + "windows": { + "install-curl": "Starting Windows 10 b17063, cURL is available by default." + }, + "replace-access-token": "Replace $ACCESS_TOKEN with your device's token:", + "content-after": "

You can also use other protocols such as MQTT, CoAP, etc.

Follow the documentation on how to do it:

", + "how-to-connect-device": "How to connect Device" + }, + "step3": { + "title": "Create dashboard", + "content": "

Create a dashboard to visualize data from entities such as assets, devices, etc.

Follow the documentation on how to do it:

", + "how-to-create-dashboard": "How to create Dashboard" + }, + "step4": { + "title": "Configure alarm rules", + "alarm-rules": "Alarm rules", + "content": "

When the temperature reaches 25°C, we will raise an alarm. Follow the documentation on how to do it:

", + "how-to-configure-alarm-rules": "How to configure Alarm rules" + }, + "step5": { + "title": "Create alarm", + "content-before": "

To trigger the alarm, send a new telemetry value of 26°C or higher.

", + "replace-access-token": "Replace $ACCESS_TOKEN with your device's token:", + "content-after": "

Follow the documentation on how to do it:

", + "how-to-create-alarm": "How to create Alarm" + }, + "step6": { + "title": "Create customer and assign dashboard", + "content": "

By creating end-user dashboards, a customer user can only see his own devices, and data from another customer will be hidden.

Follow the documentation on how to do it:

", + "how-to-create-customer-and-assign-dashboard": "How to create Customer and assign Dashboard" + } } } }, From 8b0931e02a7dfd286e7a61bfa34a7b634a019feb Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 17 Apr 2023 19:09:23 +0300 Subject: [PATCH 04/42] UI: Tenant admin home page --- .../widget_bundles/home_page_widgets.json | 48 ++++- ui-ngx/src/app/core/http/public-api.ts | 1 + .../src/app/core/http/usage-info.service.ts | 37 ++++ .../app/core/http/user-settings.service.ts | 12 +- ui-ngx/src/app/core/services/menu.service.ts | 168 +++++++++++------- .../widget/lib/flot-widget.models.ts | 1 + .../home/components/widget/lib/flot-widget.ts | 2 +- .../configured-features.component.scss | 14 -- .../configured-features.component.ts | 2 +- .../home-page/doc-links-widget.component.html | 16 +- .../home-page/doc-links-widget.component.ts | 6 +- .../getting-started-widget.component.html | 19 +- .../lib/home-page/home-page-widget.scss | 70 ++++++++ .../lib/home-page/home-page-widgets.module.ts | 10 +- ...onent.scss => links-widget.component.scss} | 52 +----- .../quick-links-widget.component.html | 51 ++++++ .../home-page/quick-links-widget.component.ts | 162 +++++++++++++++++ .../home-page/toggle-header.component.html | 11 +- .../home-page/toggle-header.component.scss | 51 +++++- .../lib/home-page/toggle-header.component.ts | 57 +++--- .../usage-info-widget.component.html | 89 ++++++++++ .../usage-info-widget.component.scss | 108 +++++++++++ .../home-page/usage-info-widget.component.ts | 111 ++++++++++++ .../lib/home-page/version-info.component.scss | 18 -- .../lib/home-page/version-info.component.ts | 2 +- .../chart/flot-key-settings.component.html | 6 +- .../chart/flot-key-settings.component.ts | 20 ++- ...quick-links-widget-settings.component.html | 23 +++ .../quick-links-widget-settings.component.ts | 52 ++++++ .../lib/settings/widget-settings.module.ts | 12 +- .../home-links/tenant_admin_home_page.raw | 89 +++++++++- ui-ngx/src/app/shared/models/public-api.ts | 1 + ui-ngx/src/app/shared/models/usage.models.ts | 39 ++++ .../app/shared/models/user-settings.models.ts | 4 + .../src/app/shared/pipe/short-number.pipe.ts | 53 ++++++ ui-ngx/src/app/shared/shared.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 11 ++ 37 files changed, 1212 insertions(+), 223 deletions(-) create mode 100644 ui-ngx/src/app/core/http/usage-info.service.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss rename ui-ngx/src/app/modules/home/components/widget/lib/home-page/{doc-links-widget.component.scss => links-widget.component.scss} (73%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/home-page/quick-links-widget-settings.component.ts create mode 100644 ui-ngx/src/app/shared/models/usage.models.ts create mode 100644 ui-ngx/src/app/shared/pipe/short-number.pipe.ts diff --git a/application/src/main/data/json/system/widget_bundles/home_page_widgets.json b/application/src/main/data/json/system/widget_bundles/home_page_widgets.json index fc5b13154c..62eb3d4d6e 100644 --- a/application/src/main/data/json/system/widget_bundles/home_page_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/home_page_widgets.json @@ -8,6 +8,25 @@ "name": "Home page widgets" }, "widgetTypes": [ + { + "alias": "getting_started", + "name": "Getting started", + "image": null, + "description": null, + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Getting started\",\"dropShadow\":true}" + } + }, { "alias": "documentation_links", "name": "Documentation links", @@ -24,12 +43,12 @@ "settingsSchema": "", "dataKeySettingsSchema": "", "settingsDirective": "tb-doc-links-widget-settings", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Documentation links\",\"dropShadow\":true}" + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Documentation links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" } }, { - "alias": "getting_started", - "name": "Getting started", + "alias": "usage_info", + "name": "Usage info", "image": null, "description": null, "descriptor": { @@ -37,13 +56,32 @@ "sizeX": 7.5, "sizeY": 6.5, "resources": [], - "templateHtml": "\n\n", + "templateHtml": "\n\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}", "settingsSchema": "", "dataKeySettingsSchema": "", "settingsDirective": "", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Getting started\",\"dropShadow\":true}" + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Usage info\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + } + }, + { + "alias": "quick_links", + "name": "Quick links", + "image": null, + "description": null, + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-quick-links-widget-settings", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Quick links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" } } ] diff --git a/ui-ngx/src/app/core/http/public-api.ts b/ui-ngx/src/app/core/http/public-api.ts index 5a511b8f60..d96df82055 100644 --- a/ui-ngx/src/app/core/http/public-api.ts +++ b/ui-ngx/src/app/core/http/public-api.ts @@ -42,3 +42,4 @@ export * from './ui-settings.service'; export * from './user.service'; export * from './user-settings.service'; export * from './widget.service'; +export * from './usage-info.service'; diff --git a/ui-ngx/src/app/core/http/usage-info.service.ts b/ui-ngx/src/app/core/http/usage-info.service.ts new file mode 100644 index 0000000000..a0869c5dba --- /dev/null +++ b/ui-ngx/src/app/core/http/usage-info.service.ts @@ -0,0 +1,37 @@ +/// +/// Copyright © 2016-2023 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 { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; +import { UsageInfo } from '@shared/models/usage.models'; + +@Injectable({ + providedIn: 'root' +}) +export class UsageInfoService { + + constructor( + private http: HttpClient + ) {} + + public getUsageInfo(config?: RequestConfig): Observable { + return this.http.get('/api/usage', + defaultHttpOptionsFromConfig(config)); + } + +} diff --git a/ui-ngx/src/app/core/http/user-settings.service.ts b/ui-ngx/src/app/core/http/user-settings.service.ts index 55ce524750..ed6fc42a0c 100644 --- a/ui-ngx/src/app/core/http/user-settings.service.ts +++ b/ui-ngx/src/app/core/http/user-settings.service.ts @@ -19,7 +19,7 @@ import { Observable } from 'rxjs'; import { DocumentationLink, DocumentationLinks, GettingStarted, - initialUserSettings, + initialUserSettings, QuickLinks, UserSettings, UserSettingsType } from '@shared/models/user-settings.models'; @@ -67,6 +67,16 @@ export class UserSettingsService { defaultHttpOptionsFromConfig(config)); } + public getQuickLinks(config?: RequestConfig): Observable { + return this.http.get(`/api/user/settings/${UserSettingsType.QUICK_LINKS}`, + defaultHttpOptionsFromConfig(config)); + } + + public updateQuickLinks(quickLinks: QuickLinks, config?: RequestConfig): Observable { + return this.http.put(`/api/user/settings/${UserSettingsType.QUICK_LINKS}`, quickLinks, + defaultHttpOptionsFromConfig(config)); + } + public getGettingStarted(config?: RequestConfig): Observable { return this.http.get(`/api/user/settings/${UserSettingsType.GETTING_STARTED}`, defaultHttpOptionsFromConfig(config)); diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 82ad8221fa..99f4a557a7 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -18,11 +18,10 @@ import { Injectable } from '@angular/core'; import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; import { getCurrentOpenedMenuSections, selectAuth, selectIsAuthenticated } from '../auth/auth.selectors'; -import { filter, take } from 'rxjs/operators'; +import { filter, map, take } from 'rxjs/operators'; import { HomeSection, MenuSection } from '@core/services/menu.models'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; -import { guid } from '@core/utils'; import { AuthState } from '@core/auth/auth.models'; import { NavigationEnd, Router } from '@angular/router'; @@ -34,6 +33,9 @@ export class MenuService { currentMenuSections: Array; menuSections$: Subject> = new BehaviorSubject>([]); homeSections$: Subject> = new BehaviorSubject>([]); + availableMenuLinks$ = this.menuSections$.pipe( + map((items) => this.allMenuLinks(items)) + ); constructor(private store: Store, private router: Router) { @@ -91,21 +93,21 @@ export class MenuService { const sections: Array = []; sections.push( { - id: guid(), + id: 'home', name: 'home.home', type: 'link', path: '/home', icon: 'home' }, { - id: guid(), + id: 'tenants', name: 'tenant.tenants', type: 'link', path: '/tenants', icon: 'supervisor_account' }, { - id: guid(), + id: 'tenantProfiles', name: 'tenant-profile.tenant-profiles', type: 'link', path: '/tenantProfiles', @@ -113,21 +115,21 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'resources', name: 'admin.resources', type: 'toggle', path: '/resources', icon: 'folder', pages: [ { - id: guid(), + id: 'resourcesWidgetsBundles', name: 'widget.widget-library', type: 'link', path: '/resources/widgets-bundles', icon: 'now_widgets' }, { - id: guid(), + id: 'resourcesResourcesLibrary', name: 'resource.resources-library', type: 'link', path: '/resources/resources-library', @@ -137,7 +139,7 @@ export class MenuService { ] }, { - id: guid(), + id: 'notification', name: 'notification.notification-center', type: 'link', path: '/notification', @@ -145,28 +147,28 @@ export class MenuService { isMdiIcon: true, pages: [ { - id: guid(), + id: 'notificationInbox', name: 'notification.inbox', type: 'link', path: '/notification/inbox', icon: 'inbox' }, { - id: guid(), + id: 'notificationSent', name: 'notification.sent', type: 'link', path: '/notification/sent', icon: 'outbox' }, { - id: guid(), + id: 'notificationRecipients', name: 'notification.recipients', type: 'link', path: '/notification/recipients', icon: 'contacts' }, { - id: guid(), + id: 'notificationTemplates', name: 'notification.templates', type: 'link', path: '/notification/templates', @@ -174,7 +176,7 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'notificationRules', name: 'notification.rules', type: 'link', path: '/notification/rules', @@ -184,28 +186,28 @@ export class MenuService { ] }, { - id: guid(), + id: 'settings', name: 'admin.settings', type: 'link', path: '/settings', icon: 'settings', pages: [ { - id: guid(), + id: 'settingsGeneral', name: 'admin.general', type: 'link', path: '/settings/general', icon: 'settings_applications' }, { - id: guid(), + id: 'settingsOutgoingMail', name: 'admin.outgoing-mail', type: 'link', path: '/settings/outgoing-mail', icon: 'mail' }, { - id: guid(), + id: 'settingsNotifications', name: 'admin.notifications', type: 'link', path: '/settings/notifications', @@ -213,7 +215,7 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'settingsQueues', name: 'admin.queues', type: 'link', path: '/settings/queues', @@ -222,21 +224,21 @@ export class MenuService { ] }, { - id: guid(), + id: 'securitySettings', name: 'security.security', type: 'toggle', path: '/security-settings', icon: 'security', pages: [ { - id: guid(), + id: 'securitySettingsGeneral', name: 'admin.general', type: 'link', path: '/security-settings/general', icon: 'settings_applications' }, { - id: guid(), + id: 'securitySettings2fa', name: 'admin.2fa.2fa', type: 'link', path: '/security-settings/2fa', @@ -244,7 +246,7 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'securitySettingsOauth2', name: 'admin.oauth2.oauth2', type: 'link', path: '/security-settings/oauth2', @@ -340,49 +342,49 @@ export class MenuService { const sections: Array = []; sections.push( { - id: guid(), + id: 'home', name: 'home.home', type: 'link', path: '/home', icon: 'home' }, { - id: guid(), + id: 'alarms', name: 'alarm.alarms', type: 'link', path: '/alarms', icon: 'notifications' }, { - id: guid(), + id: 'dashboards', name: 'dashboard.dashboards', type: 'link', path: '/dashboards', icon: 'dashboards' }, { - id: guid(), + id: 'entities', name: 'entity.entities', type: 'toggle', path: '/entities', icon: 'category', pages: [ { - id: guid(), + id: 'entitiesDevices', name: 'device.devices', type: 'link', path: '/entities/devices', icon: 'devices_other' }, { - id: guid(), + id: 'entitiesAssets', name: 'asset.assets', type: 'link', path: '/entities/assets', icon: 'domain' }, { - id: guid(), + id: 'entitiesEntityViews', name: 'entity-view.entity-views', type: 'link', path: '/entities/entityViews', @@ -391,14 +393,14 @@ export class MenuService { ] }, { - id: guid(), + id: 'profiles', name: 'profiles.profiles', type: 'toggle', path: '/profiles', icon: 'badge', pages: [ { - id: guid(), + id: 'profilesDeviceProfiles', name: 'device-profile.device-profiles', type: 'link', path: '/profiles/deviceProfiles', @@ -406,7 +408,7 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'profilesAssetProfiles', name: 'asset-profile.asset-profiles', type: 'link', path: '/profiles/assetProfiles', @@ -416,14 +418,14 @@ export class MenuService { ] }, { - id: guid(), + id: 'customers', name: 'customer.customers', type: 'link', path: '/customers', icon: 'supervisor_account' }, { - id: guid(), + id: 'ruleChains', name: 'rulechain.rulechains', type: 'link', path: '/ruleChains', @@ -433,21 +435,21 @@ export class MenuService { if (authState.edgesSupportEnabled) { sections.push( { - id: guid(), + id: 'edgeManagement', name: 'edge.management', type: 'toggle', path: '/edgeManagement', icon: 'settings_input_antenna', pages: [ { - id: guid(), + id: 'edgeManagementInstances', name: 'edge.instances', type: 'link', path: '/edgeManagement/instances', icon: 'router' }, { - id: guid(), + id: 'edgeManagementRuleChains', name: 'edge.rulechain-templates', type: 'link', path: '/edgeManagement/ruleChains', @@ -459,21 +461,21 @@ export class MenuService { } sections.push( { - id: guid(), + id: 'features', name: 'feature.advanced-features', type: 'toggle', path: '/features', icon: 'construction', pages: [ { - id: guid(), + id: 'featuresOtaUpdates', name: 'ota-update.ota-updates', type: 'link', path: '/features/otaUpdates', icon: 'memory' }, { - id: guid(), + id: 'featuresVc', name: 'version-control.version-control', type: 'link', path: '/features/vc', @@ -482,21 +484,21 @@ export class MenuService { ] }, { - id: guid(), + id: 'resources', name: 'admin.resources', type: 'toggle', path: '/resources', icon: 'folder', pages: [ { - id: guid(), + id: 'resourcesWidgetsBundles', name: 'widget.widget-library', type: 'link', path: '/resources/widgets-bundles', icon: 'now_widgets' }, { - id: guid(), + id: 'resourcesResourcesLibrary', name: 'resource.resources-library', type: 'link', path: '/resources/resources-library', @@ -506,7 +508,7 @@ export class MenuService { ] }, { - id: guid(), + id: 'notification', name: 'notification.notification-center', type: 'link', path: '/notification', @@ -514,28 +516,28 @@ export class MenuService { isMdiIcon: true, pages: [ { - id: guid(), + id: 'notificationInbox', name: 'notification.inbox', type: 'link', path: '/notification/inbox', icon: 'inbox' }, { - id: guid(), + id: 'notificationSent', name: 'notification.sent', type: 'link', path: '/notification/sent', icon: 'outbox' }, { - id: guid(), + id: 'notificationRecipients', name: 'notification.recipients', type: 'link', path: '/notification/recipients', icon: 'contacts' }, { - id: guid(), + id: 'notificationTemplates', name: 'notification.templates', type: 'link', path: '/notification/templates', @@ -543,7 +545,7 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'notificationRules', name: 'notification.rules', type: 'link', path: '/notification/rules', @@ -553,28 +555,28 @@ export class MenuService { ] }, { - id: guid(), + id: 'usage', name: 'api-usage.api-usage', type: 'link', path: '/usage', icon: 'insert_chart' }, { - id: guid(), + id: 'settings', name: 'admin.settings', type: 'link', path: '/settings', icon: 'settings', pages: [ { - id: guid(), + id: 'settingsHome', name: 'admin.home', type: 'link', path: '/settings/home', icon: 'settings_applications' }, { - id: guid(), + id: 'settingsNotifications', name: 'admin.notifications', type: 'link', path: '/settings/notifications', @@ -582,14 +584,14 @@ export class MenuService { isMdiIcon: true }, { - id: guid(), + id: 'settingsRepository', name: 'admin.repository', type: 'link', path: '/settings/repository', icon: 'manage_history' }, { - id: guid(), + id: 'settingsAutoCommit', name: 'admin.auto-commit', type: 'link', path: '/settings/auto-commit', @@ -598,14 +600,14 @@ export class MenuService { ] }, { - id: guid(), + id: 'securitySettings', name: 'security.security', type: 'toggle', path: '/security-settings', icon: 'security', pages: [ { - id: guid(), + id: 'securitySettingsAuditLogs', name: 'audit-log.audit-logs', type: 'link', path: '/security-settings/auditLogs', @@ -781,49 +783,49 @@ export class MenuService { const sections: Array = []; sections.push( { - id: guid(), + id: 'home', name: 'home.home', type: 'link', path: '/home', icon: 'home' }, { - id: guid(), + id: 'alarms', name: 'alarm.alarms', type: 'link', path: '/alarms', icon: 'notifications' }, { - id: guid(), + id: 'dashboards', name: 'dashboard.dashboards', type: 'link', path: '/dashboards', icon: 'dashboards' }, { - id: guid(), + id: 'entities', name: 'entity.entities', type: 'toggle', path: '/entities', icon: 'category', pages: [ { - id: guid(), + id: 'entitiesDevices', name: 'device.devices', type: 'link', path: '/entities/devices', icon: 'devices_other' }, { - id: guid(), + id: 'entitiesAssets', name: 'asset.assets', type: 'link', path: '/entities/assets', icon: 'domain' }, { - id: guid(), + id: 'entitiesEntityViews', name: 'entity-view.entity-views', type: 'link', path: '/entities/entityViews', @@ -835,7 +837,7 @@ export class MenuService { if (authState.edgesSupportEnabled) { sections.push( { - id: guid(), + id: 'edgeManagementInstances', name: 'edge.edge-instances', type: 'link', path: '/edgeManagement/instances', @@ -845,7 +847,7 @@ export class MenuService { } sections.push( { - id: guid(), + id: 'notification', name: 'notification.notification-center', type: 'link', path: '/notification', @@ -853,7 +855,7 @@ export class MenuService { isMdiIcon: true, pages: [ { - id: guid(), + id: 'notificationInbox', name: 'notification.inbox', type: 'link', path: '/notification/inbox', @@ -928,6 +930,19 @@ export class MenuService { return homeSections; } + private allMenuLinks(sections: Array): Array { + const result: Array = []; + for (const section of sections) { + if (section.type === 'link') { + result.push(section); + } + if (section.pages && section.pages.length) { + result.push(...this.allMenuLinks(section.pages)); + } + } + return result; + } + public menuSections(): Observable> { return this.menuSections$; } @@ -936,5 +951,20 @@ export class MenuService { return this.homeSections$; } -} + public availableMenuLinks(): Observable> { + return this.availableMenuLinks$; + } + public menuLinkById(id: string): Observable { + return this.availableMenuLinks$.pipe( + map((links) => links.find(link => link.id === id)) + ); + } + + public menuLinksByIds(ids: string[]): Observable> { + return this.availableMenuLinks$.pipe( + map((links) => links.filter(link => ids.includes(link.id))) + ); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts index 53d60856dd..6e9e7f85fa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts @@ -217,6 +217,7 @@ export interface TbFlotKeySettings { removeFromLegend: boolean; showLines: boolean; fillLines: boolean; + fillLinesOpacity: number; showPoints: boolean; showPointShape: string; pointShapeFormatter: string; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts index 03e29769ff..08a8c92472 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts @@ -408,7 +408,7 @@ export class TbFlot { } } series.lines = { - fill: keySettings.fillLines === true + fill: keySettings.fillLines === true ? (keySettings.fillLinesOpacity || 0.4) : false }; if (this.settings.stack && !this.comparisonEnabled) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.scss index 427aa8bf3a..12b7bce17d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.scss @@ -17,23 +17,9 @@ @import "../../../../../../../scss/constants"; :host { - .tb-card-content { - width: 100%; - height: 100%; - } .tb-title { margin-top: 8px; - font-style: normal; - font-weight: 500; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - color: rgba(0, 0, 0, 0.54); - @media #{$mat-md-lg} { - font-size: 12px; - line-height: 16px; - } } .tb-info-icon { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.ts index be58092f1f..d9c2609dc7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/configured-features.component.ts @@ -30,7 +30,7 @@ import { MediaBreakpoints } from '@shared/models/constants'; @Component({ selector: 'tb-configured-features', templateUrl: './configured-features.component.html', - styleUrls: ['./configured-features.component.scss'] + styleUrls: ['./home-page-widget.scss', './configured-features.component.scss'] }) export class ConfiguredFeaturesComponent extends PageComponent implements OnInit, OnDestroy { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html index 96c5642feb..1ccc2e8b24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html @@ -26,20 +26,20 @@ edit
- - - + + -
-
+ - -
+