Edge widgets with empty datasource
This commit is contained in:
parent
19c04f3387
commit
58c580ecaf
File diff suppressed because one or more lines are too long
@ -439,6 +439,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
this.deleteSystemWidgetBundle("input_widgets");
|
this.deleteSystemWidgetBundle("input_widgets");
|
||||||
this.deleteSystemWidgetBundle("date");
|
this.deleteSystemWidgetBundle("date");
|
||||||
this.deleteSystemWidgetBundle("entity_admin_widgets");
|
this.deleteSystemWidgetBundle("entity_admin_widgets");
|
||||||
|
this.deleteSystemWidgetBundle("edge_widgets");
|
||||||
installScripts.loadSystemWidgets();
|
installScripts.loadSystemWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -126,7 +126,7 @@ export class WidgetService {
|
|||||||
return this.getBundleWidgetTypes(bundleAlias, isSystem, config).pipe(
|
return this.getBundleWidgetTypes(bundleAlias, isSystem, config).pipe(
|
||||||
map((types) => {
|
map((types) => {
|
||||||
if (!getCurrentAuthState(this.store).edgesSupportEnabled) {
|
if (!getCurrentAuthState(this.store).edgesSupportEnabled) {
|
||||||
types = types.filter(type => type.alias !== 'edges_hierarchy')
|
types = types.filter(type => type.alias !== 'edges_overview')
|
||||||
}
|
}
|
||||||
types = types.sort((a, b) => {
|
types = types.sort((a, b) => {
|
||||||
let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
|
let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]);
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
Copyright © 2016-2020 The Thingsboard Authors
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<div class="tb-edge-instances-overview tb-absolute-fill" tb-toast toastTarget="{{ toastTargetId }}">
|
|
||||||
<div fxFlex fxLayout="column" class="tb-absolute-fill">
|
|
||||||
<mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
|
|
||||||
<div class="mat-toolbar-tools">
|
|
||||||
<button mat-button mat-icon-button
|
|
||||||
matTooltip="{{ 'action.search' | translate }}"
|
|
||||||
matTooltipPosition="above">
|
|
||||||
<mat-icon>search</mat-icon>
|
|
||||||
</button>
|
|
||||||
<mat-form-field fxFlex>
|
|
||||||
<mat-label> </mat-label>
|
|
||||||
<input #searchInput matInput
|
|
||||||
[(ngModel)]="textSearch"
|
|
||||||
placeholder="{{ 'entity.search' | translate }}"/>
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-button mat-icon-button (click)="exitFilterMode()"
|
|
||||||
matTooltip="{{ 'action.close' | translate }}"
|
|
||||||
matTooltipPosition="above">
|
|
||||||
<mat-icon>close</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</mat-toolbar>
|
|
||||||
<div fxFlex class="tb-edges-nav-tree-panel">
|
|
||||||
<tb-nav-tree
|
|
||||||
[loadNodes]="loadNodes"
|
|
||||||
[onNodeSelected]="onNodeSelected"
|
|
||||||
[onNodesInserted]="onNodesInserted"
|
|
||||||
[editCallbacks]="nodeEditCallbacks"
|
|
||||||
enableSearch="true"
|
|
||||||
[searchCallback]="searchCallback"
|
|
||||||
></tb-nav-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,554 +0,0 @@
|
|||||||
///
|
|
||||||
/// Copyright © 2016-2020 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, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
|
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { AppState } from '@core/core.state';
|
|
||||||
import { WidgetAction, WidgetContext } from '@home/models/widget-component.models';
|
|
||||||
import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models';
|
|
||||||
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
|
|
||||||
import { UtilsService } from '@core/services/utils.service';
|
|
||||||
import cssjs from '@core/css/css';
|
|
||||||
import { fromEvent } from 'rxjs';
|
|
||||||
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
|
|
||||||
import { constructTableCssString } from '@home/components/widget/lib/table-widget.models';
|
|
||||||
import { Overlay } from '@angular/cdk/overlay';
|
|
||||||
import {
|
|
||||||
LoadNodesCallback,
|
|
||||||
NavTreeEditCallbacks, NodesCallback,
|
|
||||||
NodeSearchCallback,
|
|
||||||
NodeSelectedCallback,
|
|
||||||
NodesInsertedCallback
|
|
||||||
} from '@shared/components/nav-tree.component';
|
|
||||||
import { EntityType } from '@shared/models/entity-type.models';
|
|
||||||
import { deepClone, hashCode } from '@core/utils';
|
|
||||||
import {
|
|
||||||
defaultNodeIconFunction,
|
|
||||||
defaultNodeOpenedFunction,
|
|
||||||
defaultNodeRelationQueryFunction,
|
|
||||||
defaultNodesSortFunction,
|
|
||||||
EdgeGroupsNodeData,
|
|
||||||
edgeGroupsNodeText,
|
|
||||||
edgeGroupsTypes,
|
|
||||||
EdgeNodeData,
|
|
||||||
edgeNodeText,
|
|
||||||
EdgeInstancesOverviewNode,
|
|
||||||
EdgeInstancesOverviewWidgetSettings,
|
|
||||||
HierarchyNavTreeNode,
|
|
||||||
HierarchyNodeContext,
|
|
||||||
HierarchyNodeDatasource,
|
|
||||||
iconUrlHtml,
|
|
||||||
loadNodeCtxFunction,
|
|
||||||
materialIconHtml,
|
|
||||||
NodeDisabledFunction,
|
|
||||||
NodeHasChildrenFunction,
|
|
||||||
NodeIconFunction,
|
|
||||||
NodeOpenedFunction,
|
|
||||||
NodeRelationQueryFunction,
|
|
||||||
NodesSortFunction,
|
|
||||||
NodeTextFunction
|
|
||||||
} from '@home/components/widget/lib/edge-instances-overview-widget.models';
|
|
||||||
import { EdgeService } from "@core/http/edge.service";
|
|
||||||
import { PageLink } from "@shared/models/page/page-link";
|
|
||||||
import { Edge, EdgeInfo } from "@shared/models/edge.models";
|
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
|
||||||
import { EntityService } from "@core/http/entity.service";
|
|
||||||
import { Direction, SortOrder } from "@shared/models/page/sort-order";
|
|
||||||
import { EntityRelationsQuery } from "@shared/models/relation.models";
|
|
||||||
import { EntityFilter } from "@shared/models/query/query.models";
|
|
||||||
import { AliasFilterType, RelationsQueryFilter } from "@shared/models/alias.models";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'tb-edge-instances-overview-widget',
|
|
||||||
templateUrl: './edge-instances-overview-widget.component.html',
|
|
||||||
styleUrls: ['./edge-instances-overview-widget.component.scss']
|
|
||||||
})
|
|
||||||
export class EdgeInstancesOverviewWidgetComponent extends PageComponent implements OnInit, AfterViewInit {
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
ctx: WidgetContext;
|
|
||||||
|
|
||||||
@ViewChild('searchInput') searchInputField: ElementRef;
|
|
||||||
|
|
||||||
public toastTargetId = 'edge-instances-overview-' + this.utils.guid();
|
|
||||||
|
|
||||||
public textSearchMode = false;
|
|
||||||
public textSearch = null;
|
|
||||||
|
|
||||||
public nodeEditCallbacks: NavTreeEditCallbacks = {};
|
|
||||||
|
|
||||||
private settings: EdgeInstancesOverviewWidgetSettings;
|
|
||||||
private widgetConfig: WidgetConfig;
|
|
||||||
private subscription: IWidgetSubscription;
|
|
||||||
private datasources: Array<HierarchyNodeDatasource>;
|
|
||||||
private data: Array<Array<DatasourceData>>;
|
|
||||||
|
|
||||||
private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {};
|
|
||||||
private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {};
|
|
||||||
private nodeIdCounter = 0;
|
|
||||||
|
|
||||||
private nodeRelationQueryFunction: NodeRelationQueryFunction;
|
|
||||||
private nodeIconFunction: NodeIconFunction;
|
|
||||||
private nodeTextFunction: NodeTextFunction;
|
|
||||||
private nodeDisabledFunction: NodeDisabledFunction;
|
|
||||||
private nodeOpenedFunction: NodeOpenedFunction;
|
|
||||||
private nodeHasChildrenFunction: NodeHasChildrenFunction;
|
|
||||||
private nodesSortFunction: NodesSortFunction;
|
|
||||||
|
|
||||||
private edgeNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {};
|
|
||||||
private edgeGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {};
|
|
||||||
|
|
||||||
|
|
||||||
private searchAction: WidgetAction = {
|
|
||||||
name: 'action.search',
|
|
||||||
show: false,
|
|
||||||
icon: 'search',
|
|
||||||
onAction: () => {
|
|
||||||
this.enterFilterMode();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
|
||||||
private elementRef: ElementRef,
|
|
||||||
private edgeService: EdgeService,
|
|
||||||
private entityService: EntityService,
|
|
||||||
private translateService: TranslateService,
|
|
||||||
private overlay: Overlay,
|
|
||||||
private viewContainerRef: ViewContainerRef,
|
|
||||||
private utils: UtilsService) {
|
|
||||||
super(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.ctx.$scope.edgeInstancesOverviewWidget = this;
|
|
||||||
this.settings = this.ctx.settings;
|
|
||||||
this.widgetConfig = this.ctx.widgetConfig;
|
|
||||||
this.subscription = this.ctx.defaultSubscription;
|
|
||||||
this.datasources = this.subscription.datasources as Array<HierarchyNodeDatasource>;
|
|
||||||
this.data = this.subscription.dataPages[0].data;
|
|
||||||
this.initializeConfig();
|
|
||||||
this.ctx.updateWidgetParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
|
||||||
fromEvent(this.searchInputField.nativeElement, 'keyup')
|
|
||||||
.pipe(
|
|
||||||
debounceTime(150),
|
|
||||||
distinctUntilChanged(),
|
|
||||||
tap(() => {
|
|
||||||
this.updateSearchNodes();
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDataUpdated() {
|
|
||||||
this.updateNodeData(this.subscription.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeConfig() {
|
|
||||||
this.ctx.widgetActions = [this.searchAction];
|
|
||||||
|
|
||||||
const testNodeCtx: HierarchyNodeContext = {
|
|
||||||
entity: {
|
|
||||||
id: {
|
|
||||||
entityType: EntityType.DEVICE,
|
|
||||||
id: '123'
|
|
||||||
},
|
|
||||||
name: 'TEST DEV1'
|
|
||||||
},
|
|
||||||
data: {},
|
|
||||||
level: 2
|
|
||||||
};
|
|
||||||
const parentNodeCtx = deepClone(testNodeCtx);
|
|
||||||
parentNodeCtx.level = 1;
|
|
||||||
testNodeCtx.parentNodeCtx = parentNodeCtx;
|
|
||||||
|
|
||||||
this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx);
|
|
||||||
|
|
||||||
const testNodeCtx2 = deepClone(testNodeCtx);
|
|
||||||
testNodeCtx2.entity.name = 'TEST DEV2';
|
|
||||||
|
|
||||||
this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2);
|
|
||||||
|
|
||||||
this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction;
|
|
||||||
this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction;
|
|
||||||
this.nodeTextFunction = this.nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name);
|
|
||||||
this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false);
|
|
||||||
this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction;
|
|
||||||
this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true);
|
|
||||||
this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction;
|
|
||||||
|
|
||||||
const cssString = constructTableCssString(this.widgetConfig);
|
|
||||||
const cssParser = new cssjs();
|
|
||||||
cssParser.testMode = false;
|
|
||||||
const namespace = 'edges-instances-overview-' + hashCode(cssString);
|
|
||||||
cssParser.cssPreviewNamespace = namespace;
|
|
||||||
cssParser.createStyleElement(namespace, cssString);
|
|
||||||
$(this.elementRef.nativeElement).addClass(namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
private enterFilterMode() {
|
|
||||||
this.textSearchMode = true;
|
|
||||||
this.textSearch = '';
|
|
||||||
this.ctx.hideTitlePanel = true;
|
|
||||||
this.ctx.detectChanges(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
this.searchInputField.nativeElement.focus();
|
|
||||||
this.searchInputField.nativeElement.setSelectionRange(0, 0);
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
exitFilterMode() {
|
|
||||||
this.textSearchMode = false;
|
|
||||||
this.textSearch = null;
|
|
||||||
this.updateSearchNodes();
|
|
||||||
this.ctx.hideTitlePanel = false;
|
|
||||||
this.ctx.detectChanges(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateSearchNodes() {
|
|
||||||
if (this.textSearch != null) {
|
|
||||||
this.nodeEditCallbacks.search(this.textSearch);
|
|
||||||
} else {
|
|
||||||
this.nodeEditCallbacks.clearSearch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateNodeData(subscriptionData: Array<DatasourceData>) {
|
|
||||||
const affectedNodes: string[] = [];
|
|
||||||
if (subscriptionData) {
|
|
||||||
subscriptionData.forEach((datasourceData) => {
|
|
||||||
const datasource = datasourceData.datasource as HierarchyNodeDatasource;
|
|
||||||
if (datasource.nodeId) {
|
|
||||||
const node = this.nodesMap[datasource.nodeId];
|
|
||||||
const key = datasourceData.dataKey.label;
|
|
||||||
let value;
|
|
||||||
if (datasourceData.data && datasourceData.data.length) {
|
|
||||||
value = datasourceData.data[0][1];
|
|
||||||
}
|
|
||||||
if (node.data.nodeCtx.data[key] !== value) {
|
|
||||||
if (affectedNodes.indexOf(datasource.nodeId) === -1) {
|
|
||||||
affectedNodes.push(datasource.nodeId);
|
|
||||||
}
|
|
||||||
node.data.nodeCtx.data[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
affectedNodes.forEach((nodeId) => {
|
|
||||||
const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId);
|
|
||||||
if (node) {
|
|
||||||
this.updateNodeStyle(this.nodesMap[nodeId]);
|
|
||||||
} else {
|
|
||||||
this.pendingUpdateNodeTasks[nodeId] = () => {
|
|
||||||
this.updateNodeStyle(this.nodesMap[nodeId]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadNodes: LoadNodesCallback = (node, cb) => {
|
|
||||||
if (node.id === '#') {
|
|
||||||
const sortOrder: SortOrder = { property: 'name', direction: Direction.ASC };
|
|
||||||
const pageLink = new PageLink(100, 0, null, sortOrder);
|
|
||||||
this.edgeService.getTenantEdgeInfos(pageLink).subscribe(
|
|
||||||
(edges) => {
|
|
||||||
cb(this.edgesToNodes(node.id, edges.data))
|
|
||||||
});
|
|
||||||
} else if (node.data.type === 'edge') {
|
|
||||||
const edge = node.data.entity;
|
|
||||||
cb(this.loadNodesForEdge(node.id, edge));
|
|
||||||
} else if (node.data.type === 'edgeGroups') {
|
|
||||||
const pageLink = new PageLink(100);
|
|
||||||
this.entityService.getAssignedToEdgeEntitiesByType(node, pageLink).subscribe(
|
|
||||||
(entities) => {
|
|
||||||
if (entities.data.length > 0) {
|
|
||||||
cb(this.edgesToNodes(node.id, entities.data));
|
|
||||||
} else {
|
|
||||||
cb([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadNodesForEdge(parentNodeId: string, edge: EdgeInfo): EdgeInstancesOverviewNode[] {
|
|
||||||
const nodes: EdgeInstancesOverviewNode[] = [];
|
|
||||||
const nodesMap = {};
|
|
||||||
this.edgeGroupsNodesMap[parentNodeId] = nodesMap;
|
|
||||||
edgeGroupsTypes.forEach((entityType) => {
|
|
||||||
const node: EdgeInstancesOverviewNode = {
|
|
||||||
id: (++this.nodeIdCounter)+'',
|
|
||||||
icon: false,
|
|
||||||
text: edgeGroupsNodeText(this.translateService, entityType),
|
|
||||||
children: true,
|
|
||||||
data: {
|
|
||||||
type: 'edgeGroups',
|
|
||||||
entityType,
|
|
||||||
edge,
|
|
||||||
internalId: edge.id.id + '_' + entityType
|
|
||||||
} as EdgeGroupsNodeData
|
|
||||||
};
|
|
||||||
nodes.push(node);
|
|
||||||
nodesMap[entityType] = node.id;
|
|
||||||
});
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createEdgeNode(parentNodeId: string, edge: Edge): EdgeInstancesOverviewNode {
|
|
||||||
let nodesMap = this.edgeNodesMap[parentNodeId];
|
|
||||||
if (!nodesMap) {
|
|
||||||
nodesMap = {};
|
|
||||||
this.edgeNodesMap[parentNodeId] = nodesMap;
|
|
||||||
}
|
|
||||||
const node: EdgeInstancesOverviewNode = {
|
|
||||||
id: (++this.nodeIdCounter)+'',
|
|
||||||
icon: false,
|
|
||||||
text: edgeNodeText(edge),
|
|
||||||
children: parentNodeId === '#',
|
|
||||||
state: {
|
|
||||||
disabled: false
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: 'edge',
|
|
||||||
entity: edge,
|
|
||||||
internalId: edge.id.id
|
|
||||||
} as EdgeNodeData
|
|
||||||
};
|
|
||||||
nodesMap[edge.id.id] = node.id;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private edgesToNodes(parentNodeId: string, edges: Array<Edge>): EdgeInstancesOverviewNode[] {
|
|
||||||
const nodes: EdgeInstancesOverviewNode[] = [];
|
|
||||||
this.edgeNodesMap[parentNodeId] = {};
|
|
||||||
if (edges) {
|
|
||||||
edges.forEach((edge) => {
|
|
||||||
const node = this.createEdgeNode(parentNodeId, edge);
|
|
||||||
nodes.push(node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNodeSelected: NodeSelectedCallback = (node, event) => {
|
|
||||||
let nodeId;
|
|
||||||
if (!node) {
|
|
||||||
nodeId = -1;
|
|
||||||
} else {
|
|
||||||
nodeId = node.id;
|
|
||||||
}
|
|
||||||
if (nodeId !== -1) {
|
|
||||||
const selectedNode = this.nodesMap[nodeId];
|
|
||||||
if (selectedNode) {
|
|
||||||
const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected');
|
|
||||||
if (descriptors.length) {
|
|
||||||
const entity = selectedNode.data.nodeCtx.entity;
|
|
||||||
this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNodesInserted: NodesInsertedCallback = (nodes) => {
|
|
||||||
if (nodes) {
|
|
||||||
nodes.forEach((nodeId) => {
|
|
||||||
const task = this.pendingUpdateNodeTasks[nodeId];
|
|
||||||
if (task) {
|
|
||||||
task();
|
|
||||||
delete this.pendingUpdateNodeTasks[nodeId];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public searchCallback: NodeSearchCallback = (searchText, node) => {
|
|
||||||
const theNode = this.nodesMap[node.id];
|
|
||||||
if (theNode && theNode.data.searchText) {
|
|
||||||
return theNode.data.searchText.includes(searchText.toLowerCase());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateNodeStyle(node: HierarchyNavTreeNode) {
|
|
||||||
const newText = this.prepareNodeText(node);
|
|
||||||
if (node.text !== newText) {
|
|
||||||
node.text = newText;
|
|
||||||
this.nodeEditCallbacks.updateNode(node.id, node.text);
|
|
||||||
}
|
|
||||||
const newDisabled = this.nodeDisabledFunction(node.data.nodeCtx);
|
|
||||||
if (node.state.disabled !== newDisabled) {
|
|
||||||
node.state.disabled = newDisabled;
|
|
||||||
if (node.state.disabled) {
|
|
||||||
this.nodeEditCallbacks.disableNode(node.id);
|
|
||||||
} else {
|
|
||||||
this.nodeEditCallbacks.enableNode(node.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const newHasChildren = this.nodeHasChildrenFunction(node.data.nodeCtx);
|
|
||||||
if (node.children !== newHasChildren) {
|
|
||||||
node.children = newHasChildren;
|
|
||||||
this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private prepareNodes(nodes: HierarchyNavTreeNode[]): HierarchyNavTreeNode[] {
|
|
||||||
nodes = nodes.filter((node) => node !== null);
|
|
||||||
nodes.sort((node1, node2) => this.nodesSortFunction(node1.data.nodeCtx, node2.data.nodeCtx));
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private prepareNodeText(node: HierarchyNavTreeNode): string {
|
|
||||||
const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx);
|
|
||||||
const nodeText = this.nodeTextFunction(node.data.nodeCtx);
|
|
||||||
node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : '';
|
|
||||||
return nodeIcon + nodeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string {
|
|
||||||
let iconInfo = this.nodeIconFunction(nodeCtx);
|
|
||||||
if (iconInfo) {
|
|
||||||
if (iconInfo === 'default') {
|
|
||||||
iconInfo = defaultNodeIconFunction(nodeCtx);
|
|
||||||
}
|
|
||||||
if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) {
|
|
||||||
if (iconInfo.materialIcon) {
|
|
||||||
return materialIconHtml(iconInfo.materialIcon);
|
|
||||||
} else {
|
|
||||||
return iconUrlHtml(iconInfo.iconUrl);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private datasourceToNode(datasource: HierarchyNodeDatasource,
|
|
||||||
data: DatasourceData[],
|
|
||||||
parentNodeCtx?: HierarchyNodeContext): HierarchyNavTreeNode {
|
|
||||||
const node: HierarchyNavTreeNode = {
|
|
||||||
id: (++this.nodeIdCounter) + ''
|
|
||||||
};
|
|
||||||
this.nodesMap[node.id] = node;
|
|
||||||
datasource.nodeId = node.id;
|
|
||||||
node.icon = false;
|
|
||||||
const nodeCtx: HierarchyNodeContext = {
|
|
||||||
parentNodeCtx,
|
|
||||||
entity: {
|
|
||||||
id: {
|
|
||||||
id: datasource.entityId,
|
|
||||||
entityType: datasource.entityType
|
|
||||||
},
|
|
||||||
name: datasource.entityName,
|
|
||||||
label: datasource.entityLabel ? datasource.entityLabel : datasource.entityName
|
|
||||||
},
|
|
||||||
data: {}
|
|
||||||
};
|
|
||||||
datasource.dataKeys.forEach((dataKey, index) => {
|
|
||||||
const keyData = data[index].data;
|
|
||||||
if (keyData && keyData.length && keyData[0].length > 1) {
|
|
||||||
nodeCtx.data[dataKey.label] = keyData[0][1];
|
|
||||||
} else {
|
|
||||||
nodeCtx.data[dataKey.label] = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1;
|
|
||||||
node.data = {
|
|
||||||
datasource,
|
|
||||||
nodeCtx
|
|
||||||
};
|
|
||||||
node.state = {
|
|
||||||
disabled: this.nodeDisabledFunction(node.data.nodeCtx),
|
|
||||||
opened: this.nodeOpenedFunction(node.data.nodeCtx)
|
|
||||||
};
|
|
||||||
node.text = this.prepareNodeText(node);
|
|
||||||
node.children = this.nodeHasChildrenFunction(node.data.nodeCtx);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadChildren(parentNode: HierarchyNavTreeNode, datasource: HierarchyNodeDatasource, childrenNodesLoadCb: NodesCallback) {
|
|
||||||
const nodeCtx = parentNode.data.nodeCtx;
|
|
||||||
nodeCtx.childrenNodesLoaded = false;
|
|
||||||
const entityFilter = this.prepareNodeRelationsQueryFilter(nodeCtx);
|
|
||||||
const childrenDatasource = {
|
|
||||||
dataKeys: datasource.dataKeys,
|
|
||||||
type: DatasourceType.entity,
|
|
||||||
filterId: datasource.filterId,
|
|
||||||
entityFilter
|
|
||||||
} as HierarchyNodeDatasource;
|
|
||||||
const subscriptionOptions: WidgetSubscriptionOptions = {
|
|
||||||
type: widgetType.latest,
|
|
||||||
datasources: [childrenDatasource],
|
|
||||||
callbacks: {
|
|
||||||
onSubscriptionMessage: (subscription, message) => {
|
|
||||||
this.ctx.showToast(message.severity, message.message, undefined,
|
|
||||||
'bottom', 'left', this.toastTargetId);
|
|
||||||
},
|
|
||||||
onInitialPageDataChanged: (subscription) => {
|
|
||||||
this.ctx.subscriptionApi.removeSubscription(subscription.id);
|
|
||||||
this.nodeEditCallbacks.refreshNode(parentNode.id);
|
|
||||||
},
|
|
||||||
onDataUpdated: subscription => {
|
|
||||||
if (nodeCtx.childrenNodesLoaded) {
|
|
||||||
this.updateNodeData(subscription.data);
|
|
||||||
} else {
|
|
||||||
const datasourcesPageData = subscription.datasourcePages[0];
|
|
||||||
const dataPageData = subscription.dataPages[0];
|
|
||||||
const childNodes: HierarchyNavTreeNode[] = [];
|
|
||||||
datasourcesPageData.data.forEach((childDatasource, index) => {
|
|
||||||
childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, dataPageData.data[index]));
|
|
||||||
});
|
|
||||||
nodeCtx.childrenNodesLoaded = true;
|
|
||||||
childrenNodesLoadCb(this.prepareNodes(childNodes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.ctx.subscriptionApi.createSubscription(subscriptionOptions, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private prepareNodeRelationQuery(nodeCtx: HierarchyNodeContext): EntityRelationsQuery {
|
|
||||||
let relationQuery = this.nodeRelationQueryFunction(nodeCtx);
|
|
||||||
if (relationQuery && relationQuery === 'default') {
|
|
||||||
relationQuery = defaultNodeRelationQueryFunction(nodeCtx);
|
|
||||||
}
|
|
||||||
return relationQuery as EntityRelationsQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
private prepareNodeRelationsQueryFilter(nodeCtx: HierarchyNodeContext): EntityFilter {
|
|
||||||
const relationQuery = this.prepareNodeRelationQuery(nodeCtx);
|
|
||||||
return {
|
|
||||||
rootEntity: {
|
|
||||||
id: relationQuery.parameters.rootId,
|
|
||||||
entityType: relationQuery.parameters.rootType
|
|
||||||
},
|
|
||||||
direction: relationQuery.parameters.direction,
|
|
||||||
filters: relationQuery.filters,
|
|
||||||
maxLevel: relationQuery.parameters.maxLevel,
|
|
||||||
fetchLastLevelOnly: relationQuery.parameters.fetchLastLevelOnly,
|
|
||||||
type: AliasFilterType.relationsQuery
|
|
||||||
} as RelationsQueryFilter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,260 +0,0 @@
|
|||||||
///
|
|
||||||
/// Copyright © 2016-2020 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 { BaseData } from '@shared/models/base-data';
|
|
||||||
import { EntityId } from '@shared/models/id/entity-id';
|
|
||||||
import { NavTreeNode } from '@shared/components/nav-tree.component';
|
|
||||||
import { Datasource } from '@shared/models/widget.models';
|
|
||||||
import { isDefined, isUndefined } from '@core/utils';
|
|
||||||
import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models';
|
|
||||||
import { EntityType } from '@shared/models/entity-type.models';
|
|
||||||
import { Edge } from "@shared/models/edge.models";
|
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
|
||||||
|
|
||||||
export interface EdgeInstancesOverviewWidgetSettings {
|
|
||||||
nodeRelationQueryFunction: string;
|
|
||||||
nodeHasChildrenFunction: string;
|
|
||||||
nodeOpenedFunction: string;
|
|
||||||
nodeDisabledFunction: string;
|
|
||||||
nodeIconFunction: string;
|
|
||||||
nodeTextFunction: string;
|
|
||||||
nodesSortFunction: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HierarchyNodeContext {
|
|
||||||
parentNodeCtx?: HierarchyNodeContext;
|
|
||||||
entity: BaseData<EntityId>;
|
|
||||||
childrenNodesLoaded?: boolean;
|
|
||||||
level?: number;
|
|
||||||
data: {[key: string]: any};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HierarchyNavTreeNode extends NavTreeNode {
|
|
||||||
data?: {
|
|
||||||
datasource: HierarchyNodeDatasource;
|
|
||||||
nodeCtx: HierarchyNodeContext;
|
|
||||||
searchText?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HierarchyNodeDatasource extends Datasource {
|
|
||||||
nodeId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HierarchyNodeIconInfo {
|
|
||||||
iconUrl?: string;
|
|
||||||
materialIcon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NodeRelationQueryFunction = (nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default';
|
|
||||||
export type NodeTextFunction = (nodeCtx: HierarchyNodeContext) => string;
|
|
||||||
export type NodeDisabledFunction = (nodeCtx: HierarchyNodeContext) => boolean;
|
|
||||||
export type NodeIconFunction = (nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default';
|
|
||||||
export type NodeOpenedFunction = (nodeCtx: HierarchyNodeContext) => boolean;
|
|
||||||
export type NodeHasChildrenFunction = (nodeCtx: HierarchyNodeContext) => boolean;
|
|
||||||
export type NodesSortFunction = (nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number;
|
|
||||||
|
|
||||||
export function loadNodeCtxFunction<F extends (...args: any[]) => any>(functionBody: string, argNames: string, ...args: any[]): F {
|
|
||||||
let nodeCtxFunction: F = null;
|
|
||||||
if (isDefined(functionBody) && functionBody.length) {
|
|
||||||
try {
|
|
||||||
nodeCtxFunction = new Function(argNames, functionBody) as F;
|
|
||||||
const res = nodeCtxFunction.apply(null, args);
|
|
||||||
if (isUndefined(res)) {
|
|
||||||
nodeCtxFunction = null;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
nodeCtxFunction = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeCtxFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function materialIconHtml(materialIcon: string): string {
|
|
||||||
return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function iconUrlHtml(iconUrl: string): string {
|
|
||||||
return '<div class="node-icon" style="background-image: url(' + iconUrl + ');"> </div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string {
|
|
||||||
const nodeIcon = materialIconByEntityType(entityType);
|
|
||||||
const nodeText = textForEdgeGroupsType(translate, entityType);
|
|
||||||
return nodeIcon + nodeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function edgeNodeText(edge: Edge): string {
|
|
||||||
const nodeIcon = materialIconByEntityType(edge.id.entityType);
|
|
||||||
const nodeText = edge.name;
|
|
||||||
return nodeIcon + nodeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function materialIconByEntityType(entityType: EntityType): string {
|
|
||||||
let materialIcon = 'insert_drive_file';
|
|
||||||
switch (entityType) {
|
|
||||||
case EntityType.DEVICE:
|
|
||||||
materialIcon = 'devices_other';
|
|
||||||
break;
|
|
||||||
case EntityType.ASSET:
|
|
||||||
materialIcon = 'domain';
|
|
||||||
break;
|
|
||||||
case EntityType.CUSTOMER:
|
|
||||||
materialIcon = 'supervisor_account';
|
|
||||||
break;
|
|
||||||
case EntityType.USER:
|
|
||||||
materialIcon = 'account_circle';
|
|
||||||
break;
|
|
||||||
case EntityType.DASHBOARD:
|
|
||||||
materialIcon = 'dashboards';
|
|
||||||
break;
|
|
||||||
case EntityType.ENTITY_VIEW:
|
|
||||||
materialIcon = 'view_quilt';
|
|
||||||
break;
|
|
||||||
case EntityType.RULE_CHAIN:
|
|
||||||
materialIcon = 'settings_ethernet';
|
|
||||||
break;
|
|
||||||
case EntityType.EDGE:
|
|
||||||
materialIcon = 'router';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function textForEdgeGroupsType(translate: TranslateService, entityType: EntityType): string {
|
|
||||||
let textForEdgeGroupsType: string = '';
|
|
||||||
switch (entityType) {
|
|
||||||
case EntityType.DEVICE:
|
|
||||||
textForEdgeGroupsType = 'device.devices';
|
|
||||||
break;
|
|
||||||
case EntityType.ASSET:
|
|
||||||
textForEdgeGroupsType = 'asset.assets';
|
|
||||||
break;
|
|
||||||
case EntityType.DASHBOARD:
|
|
||||||
textForEdgeGroupsType = 'dashboard.dashboards';
|
|
||||||
break;
|
|
||||||
case EntityType.ENTITY_VIEW:
|
|
||||||
textForEdgeGroupsType = 'entity-view.entity-views';
|
|
||||||
break;
|
|
||||||
case EntityType.RULE_CHAIN:
|
|
||||||
textForEdgeGroupsType = 'rulechain.rulechains';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return translate.instant(textForEdgeGroupsType);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = nodeCtx => {
|
|
||||||
const entity = nodeCtx.entity;
|
|
||||||
const query: EntityRelationsQuery = {
|
|
||||||
parameters: {
|
|
||||||
rootId: entity.id.id,
|
|
||||||
rootType: entity.id.entityType as EntityType,
|
|
||||||
direction: EntitySearchDirection.FROM,
|
|
||||||
relationTypeGroup: RelationTypeGroup.COMMON,
|
|
||||||
maxLevel: 1
|
|
||||||
},
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
relationType: 'Contains',
|
|
||||||
entityTypes: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
return query;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const edgeGroupsTypes: EntityType[] = [
|
|
||||||
EntityType.ASSET,
|
|
||||||
EntityType.DEVICE,
|
|
||||||
EntityType.ENTITY_VIEW,
|
|
||||||
EntityType.DASHBOARD,
|
|
||||||
EntityType.RULE_CHAIN
|
|
||||||
]
|
|
||||||
|
|
||||||
export const defaultNodeIconFunction: NodeIconFunction = nodeCtx => {
|
|
||||||
let materialIcon = 'insert_drive_file';
|
|
||||||
const entity = nodeCtx.entity;
|
|
||||||
if (entity && entity.id && entity.id.entityType) {
|
|
||||||
switch (entity.id.entityType as EntityType | string) {
|
|
||||||
case 'function':
|
|
||||||
materialIcon = 'functions';
|
|
||||||
break;
|
|
||||||
case EntityType.DEVICE:
|
|
||||||
materialIcon = 'devices_other';
|
|
||||||
break;
|
|
||||||
case EntityType.ASSET:
|
|
||||||
materialIcon = 'domain';
|
|
||||||
break;
|
|
||||||
case EntityType.TENANT:
|
|
||||||
materialIcon = 'supervisor_account';
|
|
||||||
break;
|
|
||||||
case EntityType.CUSTOMER:
|
|
||||||
materialIcon = 'supervisor_account';
|
|
||||||
break;
|
|
||||||
case EntityType.USER:
|
|
||||||
materialIcon = 'account_circle';
|
|
||||||
break;
|
|
||||||
case EntityType.DASHBOARD:
|
|
||||||
materialIcon = 'dashboards';
|
|
||||||
break;
|
|
||||||
case EntityType.ALARM:
|
|
||||||
materialIcon = 'notifications_active';
|
|
||||||
break;
|
|
||||||
case EntityType.ENTITY_VIEW:
|
|
||||||
materialIcon = 'view_quilt';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
materialIcon
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultNodeOpenedFunction: NodeOpenedFunction = nodeCtx => {
|
|
||||||
return nodeCtx.level <= 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultNodesSortFunction: NodesSortFunction = (nodeCtx1, nodeCtx2) => {
|
|
||||||
let result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);
|
|
||||||
if (result === 0) {
|
|
||||||
result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface EdgeInstancesOverviewNode extends NavTreeNode {
|
|
||||||
data?: EdgeInstancesOverviewNodeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EdgeInstancesOverviewNodeData = EdgeGroupsNodeData | EdgeNodeData;
|
|
||||||
|
|
||||||
export interface EdgeGroupsNodeData extends BaseEdgeInstancesOverviewNodeData {
|
|
||||||
type: 'edgeGroups';
|
|
||||||
entityType: EntityType;
|
|
||||||
edge: Edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EdgeNodeData extends BaseEdgeInstancesOverviewNodeData {
|
|
||||||
type: 'edge';
|
|
||||||
entity: Edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BaseEdgeInstancesOverviewNodeData {
|
|
||||||
type: EdgeInstancesOverviewNodeType;
|
|
||||||
internalId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EdgeInstancesOverviewNodeType = 'edge' | 'edgeGroups';
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
:host-context(.tb-has-timewindow) {
|
:host-context(.tb-has-timewindow) {
|
||||||
.tb-edge-instances-overview {
|
.tb-edges-overview {
|
||||||
mat-toolbar {
|
mat-toolbar {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
.tb-edge-instances-overview {
|
.tb-edges-overview {
|
||||||
mat-toolbar.mat-table-toolbar:not([color="primary"]) {
|
mat-toolbar.mat-table-toolbar:not([color="primary"]) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tb-edges-nav-tree-panel {
|
.tb-entities-nav-tree-panel {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ import { TripAnimationComponent } from './trip-animation/trip-animation.componen
|
|||||||
import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component';
|
import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component';
|
||||||
import { GatewayFormComponent } from './lib/gateway/gateway-form.component';
|
import { GatewayFormComponent } from './lib/gateway/gateway-form.component';
|
||||||
import { ImportExportService } from '@home/components/import-export/import-export.service';
|
import { ImportExportService } from '@home/components/import-export/import-export.service';
|
||||||
import { EdgeInstancesOverviewWidgetComponent } from "@home/components/widget/lib/edge-instances-overview-widget.component";
|
import { EdgesOverviewWidgetComponent } from "@home/components/widget/lib/edges-overview-widget.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations:
|
declarations:
|
||||||
@ -46,7 +46,7 @@ import { EdgeInstancesOverviewWidgetComponent } from "@home/components/widget/li
|
|||||||
AlarmsTableWidgetComponent,
|
AlarmsTableWidgetComponent,
|
||||||
TimeseriesTableWidgetComponent,
|
TimeseriesTableWidgetComponent,
|
||||||
EntitiesHierarchyWidgetComponent,
|
EntitiesHierarchyWidgetComponent,
|
||||||
EdgeInstancesOverviewWidgetComponent,
|
EdgesOverviewWidgetComponent,
|
||||||
DateRangeNavigatorWidgetComponent,
|
DateRangeNavigatorWidgetComponent,
|
||||||
DateRangeNavigatorPanelComponent,
|
DateRangeNavigatorPanelComponent,
|
||||||
MultipleInputWidgetComponent,
|
MultipleInputWidgetComponent,
|
||||||
@ -65,7 +65,7 @@ import { EdgeInstancesOverviewWidgetComponent } from "@home/components/widget/li
|
|||||||
AlarmsTableWidgetComponent,
|
AlarmsTableWidgetComponent,
|
||||||
TimeseriesTableWidgetComponent,
|
TimeseriesTableWidgetComponent,
|
||||||
EntitiesHierarchyWidgetComponent,
|
EntitiesHierarchyWidgetComponent,
|
||||||
EdgeInstancesOverviewWidgetComponent,
|
EdgesOverviewWidgetComponent,
|
||||||
RpcWidgetsModule,
|
RpcWidgetsModule,
|
||||||
DateRangeNavigatorWidgetComponent,
|
DateRangeNavigatorWidgetComponent,
|
||||||
MultipleInputWidgetComponent,
|
MultipleInputWidgetComponent,
|
||||||
|
|||||||
@ -117,7 +117,7 @@ export class DashboardWidgetSelectComponent implements OnInit, OnChanges {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!getCurrentAuthState(this.store).edgesSupportEnabled) {
|
if (!getCurrentAuthState(this.store).edgesSupportEnabled) {
|
||||||
this.latestWidgetTypes = this.latestWidgetTypes.filter(type => type.typeAlias !== 'edges_instances_overview')
|
this.staticWidgetTypes = this.staticWidgetTypes.filter(type => type.typeAlias !== 'edges_instances_overview')
|
||||||
}
|
}
|
||||||
top += widget.sizeY;
|
top += widget.sizeY;
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user