diff --git a/ui-ngx/src/app/modules/home/components/tokens.ts b/ui-ngx/src/app/modules/home/components/tokens.ts index 5ddd9fe661..abba6a0636 100644 --- a/ui-ngx/src/app/modules/home/components/tokens.ts +++ b/ui-ngx/src/app/modules/home/components/tokens.ts @@ -23,6 +23,9 @@ export const SHARED_HOME_COMPONENTS_MODULE_TOKEN: InjectionToken> = export const HOME_COMPONENTS_MODULE_TOKEN: InjectionToken> = new InjectionToken>('HOME_COMPONENTS_MODULE_TOKEN'); +export const WIDGET_COMPONENTS_MODULE_TOKEN: InjectionToken> = + new InjectionToken>('WIDGET_COMPONENTS_MODULE_TOKEN'); + export const COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN: InjectionToken> = new InjectionToken>('COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN'); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.html new file mode 100644 index 0000000000..d2807d34dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.html @@ -0,0 +1,77 @@ + + + + + Service id + + + {{ serviceData.serviceId }} + + + + + Service type + + + {{ serviceData.serviceType }} + + + + + CPU + + + {{ serviceData.cpuUsage }}% + + + + + RAM + + + {{ serviceData.memoryUsage }}% + + + + + Disk + + + {{ serviceData.discUsage }}% + + + + +
+No data diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.scss new file mode 100644 index 0000000000..0d76956dfc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.scss @@ -0,0 +1,71 @@ +/** + * 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. + */ +:host { + span.no-data-found { + font-weight: 400; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.76); + } + + .tb-resource-usage { + cursor: default; + &.tb-status-warn, &.tb-status-critical { + position: relative; + overflow: visible; + &:before { + position: absolute; + top: 15px; + left: -24px; + font-family: 'Material Icons Round'; + font-size: 20px; + line-height: 1; + } + } + &.tb-status-warn { + color: #FAA405; + &:before { + content: "warning"; + color: #FAA405; + } + } + &.tb-status-critical { + color: #D12730; + &:before { + content: "warning"; + color: #D12730; + } + } + } +} + +:host ::ng-deep { + .mdc-data-table__header-cell { + font-weight: 500; + font-size: 11px; + line-height: 20px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.54); + } + .mdc-data-table__cell { + font-weight: 400; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.76); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.ts new file mode 100644 index 0000000000..527612ccc0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/cluster-info-table.component.ts @@ -0,0 +1,236 @@ +/// +/// 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 { AfterViewInit, ChangeDetectionStrategy, Component, NgZone, OnInit, ViewChild } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { MAX_SAFE_PAGE_SIZE, PageLink } from '@shared/models/page/page-link'; +import { catchError, map, publishReplay, refCount, share } from 'rxjs/operators'; +import { MatSort } from '@angular/material/sort'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; +import { EntityDataUpdate, TelemetrySubscriber } from '@shared/models/telemetry/telemetry.models'; +import { AliasFilterType } from '@shared/models/alias.models'; +import { EntityKeyType } from '@shared/models/query/query.models'; + +export interface SystemInfoData { + serviceId: string; + serviceType: string; + cpuUsage: number; + cpuCount: number; + memoryUsage: number; + totalMemory: number; + discUsage: number; + totalDiscSpace: number; +} + +@Component({ + selector: 'tb-cluster-info-table', + templateUrl: './cluster-info-table.component.html', + styleUrls: ['./cluster-info-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ClusterInfoTableComponent extends PageComponent implements OnInit, AfterViewInit { + + @ViewChild(MatSort) sort: MatSort; + + displayedColumns = ['serviceId', 'serviceType', 'cpuUsage', 'memoryUsage', 'discUsage']; + + dataSource: SystemInfoDataSource; + + pageLink: PageLink; + + constructor(protected store: Store, + private telemetryWsService: TelemetryWebsocketService, + private zone: NgZone) { + super(store); + } + + ngOnInit() { + this.dataSource = new SystemInfoDataSource(this.telemetryWsService, this.zone); + const sortOrder: SortOrder = { + property: 'serviceId', + direction: Direction.ASC + }; + this.pageLink = new PageLink(MAX_SAFE_PAGE_SIZE, 0, null, sortOrder); + this.dataSource.loadData(this.pageLink); + } + + ngAfterViewInit() { + this.sort.sortChange.subscribe(() => this.updateData()); + } + + updateData() { + this.pageLink.sortOrder.property = this.sort.active; + this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; + this.dataSource.loadData(this.pageLink); + } + + statusClass(value: number): string { + let status = ''; + if (value > 85) { + status = 'tb-status-critical'; + } else if (value > 75) { + status = 'tb-status-warn'; + } + return status; + } + + infoTooltip(serviceData: SystemInfoData, type: 'cpu' | 'ram' | 'disc'): string { + let tooltip = ''; + let value: number; + switch (type) { + case 'cpu': + value = serviceData.cpuUsage; + tooltip += value + ' / 100 %'; + break; + case 'ram': + value = serviceData.memoryUsage; + const memoryUsage = serviceData.memoryUsage; + const totalMemory = serviceData.totalMemory / 1073741824; + const usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0); + tooltip += usedMemory + ' / ' + totalMemory.toFixed(0) + ' Gb'; + break; + case 'disc': + value = serviceData.discUsage; + const discUsage = serviceData.discUsage; + const totalDiscSpace = serviceData.totalDiscSpace / 1073741824; + const usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0); + tooltip += usedDisc + ' / ' + totalDiscSpace.toFixed(0) + ' Gb'; + break; + } + if (value > 85) { + switch (type) { + case 'cpu': + tooltip += '\n\nCritically high CPU usage. To avoid system failure, optimize system performance.'; + break; + case 'ram': + tooltip += '\n\nCritically low reserve of RAM. To avoid system failure, optimize system performance or increase the size of RAM.'; + break; + case 'disc': + tooltip += '\n\nCritically low disk space. To avoid data loss, free up or expand the disk space.'; + break; + } + } else if (value > 75) { + switch (type) { + case 'cpu': + tooltip += '\n\nRunning high on CPU usage. To avoid system failure, optimize system performance.'; + break; + case 'ram': + tooltip += '\n\nRunning low on reserve of RAM. To avoid system failure, optimize system performance or increase the size of RAM.'; + break; + case 'disc': + tooltip += '\n\nRunning low on disk space. To avoid data loss, free up or expand the disk space.'; + break; + } + } + return tooltip; + } +} + +export class SystemInfoDataSource implements DataSource { + + private systemDataSubject = new BehaviorSubject([]); + private pageDataSubject = new BehaviorSubject>(emptyPageData()); + + public pageData$ = this.pageDataSubject.asObservable(); + + private allSystemData: Observable>; + + private telemetrySubscriber: TelemetrySubscriber; + + constructor(private telemetryWsService: TelemetryWebsocketService, + private zone: NgZone) { + } + + connect(collectionViewer: CollectionViewer): Observable> { + return this.systemDataSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.systemDataSubject.complete(); + this.pageDataSubject.complete(); + if (this.telemetrySubscriber) { + this.telemetrySubscriber.unsubscribe(); + this.telemetrySubscriber = null; + } + } + + loadData(pageLink: PageLink): void { + this.getAllSystemData().pipe( + map((data) => pageLink.filterData(data)), + catchError(() => of(emptyPageData())), + ).subscribe( + (pageData) => { + this.onSystemData(pageData.data); + this.pageDataSubject.next(pageData); + } + ); + } + + getAllSystemData(): Observable> { + if (!this.allSystemData) { + this.telemetrySubscriber = TelemetrySubscriber.createEntityFilterLatestSubscription( + this.telemetryWsService, { type: AliasFilterType.apiUsageState }, this.zone, + [ { key: 'clusterSystemData', type: EntityKeyType.TIME_SERIES } ]); + this.telemetrySubscriber.subscribe(); + this.allSystemData = this.telemetrySubscriber.entityData$.pipe( + map((update) => this.toSystemInfoData(update)), + publishReplay(1), + refCount() + ); + } + return this.allSystemData; + } + + private toSystemInfoData(entityDataUpdate: EntityDataUpdate): Array { + const entityData = entityDataUpdate.data?.data ? entityDataUpdate.data?.data : entityDataUpdate.update; + if (entityData && entityData.length) { + const latest = entityData[0].latest; + if (latest && latest[EntityKeyType.TIME_SERIES]) { + const latestTs = latest[EntityKeyType.TIME_SERIES]; + const tsValue = latestTs.clusterSystemData; + if (tsValue && tsValue.value) { + return JSON.parse(tsValue.value); + } + } + } + return []; + } + + protected onSystemData(data: SystemInfoData[]) { + this.systemDataSubject.next(data); + } + + isEmpty(): Observable { + return this.systemDataSubject.pipe( + map((entities) => !entities.length), + share() + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements), + share() + ); + } + +} 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 new file mode 100644 index 0000000000..0e14236720 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widgets.module.ts @@ -0,0 +1,35 @@ +/// +/// 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 { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@app/shared/shared.module'; +import { ClusterInfoTableComponent } from '@home/components/widget/lib/home-page/cluster-info-table.component'; + +@NgModule({ + declarations: + [ + ClusterInfoTableComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + ClusterInfoTableComponent + ] +}) +export class HomePageWidgetsModule { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html index 1c3df367c4..16fb6090e8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -, private utils: UtilsService, @Inject(HOME_COMPONENTS_MODULE_TOKEN) public homeComponentsModule: Type, + @Inject(WIDGET_COMPONENTS_MODULE_TOKEN) public widgetComponentsModule: Type, private cd: ChangeDetectorRef) { super(store); } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 6fd404a31e..61b94a37a6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -42,6 +42,8 @@ import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component'; import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component'; import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component'; +import { HomePageWidgetsModule } from '@home/components/widget/lib/home-page/home-page-widgets.module'; +import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; @NgModule({ declarations: @@ -70,6 +72,7 @@ import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/di CommonModule, SharedModule, RpcWidgetsModule, + HomePageWidgetsModule, SharedHomeComponentsModule ], exports: [ @@ -79,6 +82,7 @@ import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/di EntitiesHierarchyWidgetComponent, EdgesOverviewWidgetComponent, RpcWidgetsModule, + HomePageWidgetsModule, DateRangeNavigatorWidgetComponent, JsonInputWidgetComponent, MultipleInputWidgetComponent, @@ -92,7 +96,8 @@ import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/di ], providers: [ CustomDialogService, - ImportExportService + ImportExportService, + {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule } ] }) export class WidgetComponentsModule { diff --git a/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw b/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw index 9a34cdf51a..4aaa1d7d81 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw +++ b/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw @@ -868,7 +868,13 @@ "label": "clusterMode", "color": "#2196f3", "settings": {}, - "_hash": 0.7272123990942316 + "_hash": 0.7272123990942316, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": false, + "postFuncBody": "" } ] } @@ -903,7 +909,7 @@ "padding": "0px", "settings": { "useMarkdownTextFunction": true, - "markdownTextFunction": "let clusterMode = data[0]['clusterMode'];\nlet targetState = 'monolith_system_info';\nif (clusterMode === 'true') {\n targetState = 'cluster_system_info';\n}\nreturn '
';\n", + "markdownTextFunction": "let html = '
';\nlet clusterMode = data[0]['clusterMode'];\nif (clusterMode === 'true') {\n html += '';\n} else {\n html += '';\n}\nhtml += '
';\nreturn html;", "applyDefaultMarkdownStyle": false }, "title": "Transport messages", @@ -915,7 +921,6 @@ "enableFullscreen": false, "widgetStyle": { "border": "none", - "borderRadius": "0", "boxShadow": "none" }, "titleStyle": { @@ -957,188 +962,42 @@ "color": "#2196f3", "settings": {}, "_hash": 0.659021918423117 - } - ] - } - ], - "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
CPU
\n
\n
${cpuUsage:0}%
\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\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n" - }, - "title": "CPU", - "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": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-right: 6px;\n}", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "76607897-dfd0-8946-7451-3de6a39e4bbf" - }, - "8c2f02bc-ca69-2955-8730-4e945f77da28": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ + }, { - "name": "memoryUsage", + "name": "cpuCount", "type": "timeseries", - "label": "memoryUsage", - "color": "#2196f3", + "label": "cpuCount", + "color": "#4caf50", "settings": {}, - "_hash": 0.659021918423117, + "_hash": 0.5332753234169949 + }, + { + "name": "cpuUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, "aggregationType": "NONE", "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "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
RAM
\n
\n
${memoryUsage:0}%
\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\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n" - }, - "title": "RAM", - "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": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-right: 6px;\n margin-left: 6px;\n}", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "8c2f02bc-ca69-2955-8730-4e945f77da28" - }, - "e3686199-5117-fa5a-d59c-aada6269653e": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, { - "name": "discUsage", + "name": "cpuUsage", "type": "timeseries", - "label": "discUsage", - "color": "#2196f3", + "label": "statusTooltip", + "color": "#ffc107", "settings": {}, - "_hash": 0.659021918423117, + "_hash": 0.8542003006820891, "aggregationType": "NONE", "units": null, "decimals": null, "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = 'Critically high CPU usage. To avoid system failure, optimize system performance.';\n} else if (value > 75) {\n text = 'Running high on CPU usage. To avoid system failure, optimize system performance.';\n}\nreturn text;" } ] } @@ -1173,11 +1032,11 @@ "padding": "16px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
Disk
\n
\n
${discUsage:0}%
\n
", + "markdownTextPattern": "
\n
\n
CPU
\n
\n
\n
\n ${cpuUsage:0}%\n | ${cpuCount:0} cores\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\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n" + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n" }, - "title": "Disk", + "title": "CPU", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", @@ -1191,13 +1050,13 @@ }, "showLegend": false, "useDashboardTimewindow": true, - "widgetCss": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-left: 6px;\n}", + "widgetCss": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-right: 6px;\n}", "pageSize": 1024, "noDataDisplayMessage": "" }, "row": 0, "col": 0, - "id": "e3686199-5117-fa5a-d59c-aada6269653e" + "id": "76607897-dfd0-8946-7451-3de6a39e4bbf" }, "eace9148-b02a-48fe-1a95-acd9928aa8c5": { "isSystemType": true, @@ -1380,7 +1239,7 @@ "tickDecimals": 0, "ticksFormatter": "" }, - "shadowSize": 4, + "shadowSize": 0, "smoothLines": true, "comparisonEnabled": false, "timeForComparison": "previousInterval", @@ -1424,84 +1283,447 @@ "row": 0, "col": 0, "id": "eace9148-b02a-48fe-1a95-acd9928aa8c5" - } - }, - "states": { - "default": { - "name": "System Administrator Home Page", - "root": true, - "layouts": { - "main": { - "widgets": { - "04a9afdf-fbb4-4eaf-0fcc-a190c6202c62": { - "sizeX": 10, - "sizeY": 4, - "row": 0, - "col": 0 - }, - "cf1b7a07-2a16-cab7-2528-ef5caba4ac38": { - "sizeX": 10, - "sizeY": 4, - "row": 0, - "col": 10 - }, - "551a27a0-cd23-a70c-0de2-c97ab6673faa": { - "sizeX": 5, - "sizeY": 3, - "row": 4, - "col": 0 - }, - "a41682b0-2f23-23e9-0807-e8b47c1c1795": { - "sizeX": 5, - "sizeY": 3, - "row": 4, - "col": 5 - }, - "1abf533c-aee7-080d-0274-d047e1d5dc63": { - "sizeX": 5, - "sizeY": 3, - "row": 4, - "col": 10 - }, - "5d02c63b-bcb1-aaf3-f35d-de4dbaca96c2": { - "sizeX": 5, - "sizeY": 3, - "row": 4, - "col": 15 - }, - "e70b51f8-04dd-985f-be81-d0c76c589d4a": { - "sizeX": 16, - "sizeY": 26, - "row": 0, - "col": 38 - }, - "d70cc256-4c7b-ee06-9905-b8c5e546605f": { - "sizeX": 18, - "sizeY": 8, - "row": 11, - "col": 20 - }, - "4b5e47ed-c197-a937-d727-041ba8decec2": { - "sizeX": 18, - "sizeY": 11, - "row": 0, - "col": 20 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 54, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } }, - "transport_messages": { + "d755208a-81cf-edb4-5db8-f4859f5359cc": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "markdown_card", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "memoryUsage", + "type": "timeseries", + "label": "memoryUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "totalMemory", + "type": "timeseries", + "label": "totalMemory", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "statusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = 'Critically low reserve of RAM. To avoid system failure,
optimize system performance or increase the size of RAM.';\n} else if (value > 75) {\n text = 'Running low on reserve of RAM. To avoid system failure,
optimize system performance or increase the size of RAM.';\n}\nreturn text;" + } + ] + } + ], + "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": true, + "markdownTextFunction": "const memoryUsage = data[0]['memoryUsage'];\nconst totalMemory = data[0]['totalMemory'];\nconst usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
RAM
' +\n '
' +\n '
' +\n '
' +\n '${memoryUsage:0}%' +\n '| ${totalMemory:0} Gb' +\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 align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n" + }, + "title": "RAM", + "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": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-right: 6px;\n}", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d755208a-81cf-edb4-5db8-f4859f5359cc" + }, + "6ab892b7-0cb8-cb1e-f5d3-679ef8f25bd7": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "markdown_card", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "discUsage", + "type": "timeseries", + "label": "discUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "totalDiscSpace", + "type": "timeseries", + "label": "totalDiscSpace", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "statusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = 'Critically low disk space. To avoid data loss, free up or expand the disk space.';\n} else if (value > 75) {\n text = 'Running low on disk space. To avoid data loss, free up or expand the disk space.';\n}\nreturn text;" + } + ] + } + ], + "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": true, + "markdownTextFunction": "const discUsage = data[0]['discUsage'];\nconst totalDiscSpace = data[0]['totalDiscSpace'];\nconst usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
Disk
' +\n '
' +\n '
' +\n '
' +\n '${discUsage:0}%' +\n '| ${totalDiscSpace:0} Gb' +\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 align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n" + }, + "title": "Disk", + "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": ".tb-widget-container {\n display: block;\n height: 100%;\n margin-right: 6px;\n}", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "6ab892b7-0cb8-cb1e-f5d3-679ef8f25bd7" + }, + "8acbf5df-f9fc-114d-216f-86f081aa4779": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "markdown_card", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "tenantProfilesCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "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
\n
Functions
\n
\n Switch to PE\n
\n
\n
\n
Alarms
\n
Dashboards
\n
Entities & Relations
\n
Profiles
\n
Advanced features
\n
\n
\n
Notification center
\n
API usage
\n
Customers
\n
Customers hierarchy
\n
Roles & Permissions
\n
\n
\n
Groups
\n
Integrations
\n
Solution templates
\n
Scheduler
\n
White-labeling
\n
\n
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n margin-top: 18px;\n}\n\n.feature {\n position: relative;\n cursor: default;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.feature:before {\n content: \"check\";\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n color: #198038;\n font-weight: 600;\n position: relative;\n background: #F3F6FA;\n border-radius: 4px;\n width: 24px;\n height: 24px;\n margin-right: 8px;\n vertical-align: middle;\n}\n\n.feature.pe:before {\n content: \"close\";\n background: #F4F4F4;\n color: rgba(0, 0, 0, 0.12);\n}\n\n.feature.pe:after {\n position: relative;\n display: inline-block;\n margin-left: 4px;\n vertical-align: baseline;\n top: -0.5em;\n content: \"PE\";\n color: #305680;\n font-weight: 700;\n font-size: 12px;\n line-height: 12px;\n letter-spacing: 0.0015em;\n}\n\n.feature+.feature {\n margin-top: 10px;\n}\n" + }, + "title": "Functions", + "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": "8acbf5df-f9fc-114d-216f-86f081aa4779" + } + }, + "states": { + "default": { + "name": "System Administrator Home Page", + "root": true, + "layouts": { + "main": { + "widgets": { + "04a9afdf-fbb4-4eaf-0fcc-a190c6202c62": { + "sizeX": 10, + "sizeY": 4, + "row": 0, + "col": 0 + }, + "cf1b7a07-2a16-cab7-2528-ef5caba4ac38": { + "sizeX": 10, + "sizeY": 4, + "row": 0, + "col": 10 + }, + "551a27a0-cd23-a70c-0de2-c97ab6673faa": { + "sizeX": 5, + "sizeY": 3, + "row": 4, + "col": 0 + }, + "a41682b0-2f23-23e9-0807-e8b47c1c1795": { + "sizeX": 5, + "sizeY": 3, + "row": 4, + "col": 5 + }, + "1abf533c-aee7-080d-0274-d047e1d5dc63": { + "sizeX": 5, + "sizeY": 3, + "row": 4, + "col": 10 + }, + "5d02c63b-bcb1-aaf3-f35d-de4dbaca96c2": { + "sizeX": 5, + "sizeY": 3, + "row": 4, + "col": 15 + }, + "e70b51f8-04dd-985f-be81-d0c76c589d4a": { + "sizeX": 16, + "sizeY": 26, + "row": 0, + "col": 38 + }, + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "sizeX": 18, + "sizeY": 8, + "row": 11, + "col": 20 + }, + "4b5e47ed-c197-a937-d727-041ba8decec2": { + "sizeX": 18, + "sizeY": 11, + "row": 0, + "col": 20 + }, + "8acbf5df-f9fc-114d-216f-86f081aa4779": { + "sizeX": 27, + "sizeY": 7, + "row": 19, + "col": 11 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 54, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "transport_messages": { "name": "Transport messages", "root": false, "layouts": { @@ -1539,23 +1761,23 @@ "row": 0, "col": 0 }, - "8c2f02bc-ca69-2955-8730-4e945f77da28": { + "eace9148-b02a-48fe-1a95-acd9928aa8c5": { + "sizeX": 24, + "sizeY": 8, + "row": 3, + "col": 0 + }, + "d755208a-81cf-edb4-5db8-f4859f5359cc": { "sizeX": 8, "sizeY": 3, "row": 0, "col": 8 }, - "e3686199-5117-fa5a-d59c-aada6269653e": { + "6ab892b7-0cb8-cb1e-f5d3-679ef8f25bd7": { "sizeX": 8, "sizeY": 3, "row": 0, "col": 16 - }, - "eace9148-b02a-48fe-1a95-acd9928aa8c5": { - "sizeX": 24, - "sizeY": 8, - "row": 3, - "col": 0 } }, "gridSettings": { @@ -1570,21 +1792,6 @@ } } } - }, - "cluster_system_info": { - "name": "Cluster System Info", - "root": false, - "layouts": { - "main": { - "widgets": {}, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%" - } - } - } } }, "entityAliases": { diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index 44af6afc30..17a7031099 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -25,7 +25,7 @@ import { AlarmData, AlarmDataQuery, EntityCountQuery, EntityData, - EntityDataQuery, + EntityDataQuery, EntityFilter, EntityKey, TsValue } from '@shared/models/query/query.models'; @@ -599,6 +599,27 @@ export class TelemetrySubscriber extends WsSubscriber { return subscriber; } + public static createEntityFilterLatestSubscription(telemetryService: TelemetryWebsocketService, + entityFilter: EntityFilter, zone: NgZone, + latestKeys: EntityKey[] = null): TelemetrySubscriber { + const entityDataQuery: EntityDataQuery = { + entityFilter, + pageLink: { + pageSize: 1, + page: 0 + }, + latestValues: latestKeys || [] + }; + const cmd = new EntityDataCmd(); + cmd.query = entityDataQuery; + cmd.latestCmd = { + keys: latestKeys || [] + }; + const subscriber = new TelemetrySubscriber(telemetryService, zone); + subscriber.subscriptionCommands.push(cmd); + return subscriber; + } + constructor(private telemetryService: TelemetryWebsocketService, protected zone?: NgZone) { super(telemetryService, zone); } diff --git a/ui-ngx/src/assets/fonts/MaterialIconsRound-Regular.otf b/ui-ngx/src/assets/fonts/MaterialIconsRound-Regular.otf new file mode 100644 index 0000000000..dacf094b51 Binary files /dev/null and b/ui-ngx/src/assets/fonts/MaterialIconsRound-Regular.otf differ diff --git a/ui-ngx/src/assets/fonts/material-icons.css b/ui-ngx/src/assets/fonts/material-icons.css index 3809ea3696..0e200c245e 100644 --- a/ui-ngx/src/assets/fonts/material-icons.css +++ b/ui-ngx/src/assets/fonts/material-icons.css @@ -22,8 +22,24 @@ url(MaterialIcons-Regular.ttf) format('truetype'); } +@font-face { + font-family: 'Material Icons Round'; + font-style: normal; + font-weight: 400; + src: local('Material Icons Round'), + local('MaterialIconsRound-Regular'), + url(MaterialIconsRound-Regular.otf) format('truetype'); +} + .material-icons { font-family: 'Material Icons'; +} + +.material-icons-round { + font-family: 'Material Icons Round'; +} + +.material-icons, .material-icons-round { font-weight: normal; font-style: normal; font-size: 24px; /* Preferred icon size */ diff --git a/ui-ngx/src/index.html b/ui-ngx/src/index.html index 415dae7fa9..5b0ca17cd6 100644 --- a/ui-ngx/src/index.html +++ b/ui-ngx/src/index.html @@ -25,6 +25,7 @@ +