2019-11-15 16:12:24 +02:00
|
|
|
<!--
|
|
|
|
|
|
2023-01-31 10:43:56 +02:00
|
|
|
Copyright © 2016-2023 The Thingsboard Authors
|
2019-11-15 16:12:24 +02:00
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
-->
|
2020-01-20 15:14:08 +02:00
|
|
|
<div class="mat-content" fxFlex tb-fullscreen [fullscreen]="isFullscreen" tb-hotkeys [hotkeys]="hotKeys"
|
|
|
|
|
[cheatSheet]="cheatSheetComponent"
|
2019-12-10 11:04:14 +02:00
|
|
|
fxLayout="column" class="tb-rulechain">
|
2020-01-20 15:14:08 +02:00
|
|
|
<tb-hotkeys-cheatsheet #cheatSheetComponent></tb-hotkeys-cheatsheet>
|
2019-12-10 11:04:14 +02:00
|
|
|
<section class="tb-rulechain-container" fxFlex fxLayout="column">
|
|
|
|
|
<div class="tb-rulechain-layout" fxFlex fxLayout="row">
|
2019-12-17 20:16:40 +02:00
|
|
|
<mat-drawer-container style="width: 100%; height: 100%;">
|
2019-12-12 19:55:17 +02:00
|
|
|
<mat-drawer class="tb-rulechain-library mat-elevation-z4"
|
2019-12-10 11:04:14 +02:00
|
|
|
disableClose="true"
|
|
|
|
|
mode="side"
|
2023-05-25 13:35:58 +03:00
|
|
|
#drawer
|
|
|
|
|
[opened]="true"
|
2019-12-10 11:04:14 +02:00
|
|
|
position="start"
|
|
|
|
|
fxLayout="column">
|
|
|
|
|
<mat-toolbar color="primary" class="tb-dark">
|
2023-05-25 13:35:58 +03:00
|
|
|
<tb-rule-chain-select
|
|
|
|
|
fxFlex
|
|
|
|
|
*ngIf="!isImport"
|
|
|
|
|
[disabled]="isDirtyValue"
|
|
|
|
|
[(ngModel)]="ruleChain.id.id"
|
|
|
|
|
(ngModelChange)="currentRuleChainIdChanged(ruleChain.id?.id)">
|
|
|
|
|
</tb-rule-chain-select>
|
|
|
|
|
</mat-toolbar>
|
|
|
|
|
<mat-toolbar>
|
2019-12-10 11:04:14 +02:00
|
|
|
<div class="mat-toolbar-tools">
|
2023-02-21 19:18:04 +02:00
|
|
|
<mat-form-field fxFlex class="tb-appearance-transparent">
|
2023-05-25 13:35:58 +03:00
|
|
|
<button mat-icon-button matPrefix class="tb-small"
|
|
|
|
|
matTooltip="{{'rulenode.search' | translate}}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>search</mat-icon>
|
|
|
|
|
</button>
|
2019-12-12 19:55:17 +02:00
|
|
|
<input #ruleNodeSearchInput matInput
|
|
|
|
|
[(ngModel)]="ruleNodeTypeSearch"
|
2019-12-10 11:04:14 +02:00
|
|
|
placeholder="{{'rulenode.search' | translate}}"/>
|
2023-05-25 13:35:58 +03:00
|
|
|
<button mat-icon-button matSuffix class="tb-small"
|
|
|
|
|
[fxShow]="ruleNodeTypeSearch !== ''"
|
|
|
|
|
(click)="ruleNodeTypeSearch = ''; updateRuleChainLibrary()"
|
|
|
|
|
matTooltip="{{'action.clear-search' | translate}}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>close</mat-icon>
|
|
|
|
|
</button>
|
2019-12-10 11:04:14 +02:00
|
|
|
</mat-form-field>
|
|
|
|
|
</div>
|
|
|
|
|
</mat-toolbar>
|
2019-12-12 19:55:17 +02:00
|
|
|
<div class="tb-rulechain-library-panel-group">
|
|
|
|
|
<mat-expansion-panel #ruleNodeTypeExpansionPanels
|
|
|
|
|
class="mat-elevation-z2"
|
|
|
|
|
[expanded]="true" *ngFor="let ruleNodeType of ruleNodeTypesLibraryArray">
|
|
|
|
|
<mat-expansion-panel-header expandedHeight="48px"
|
|
|
|
|
(mouseenter)="typeHeaderMouseEnter($event, ruleNodeType)"
|
|
|
|
|
(mouseleave)="destroyTooltips()">
|
|
|
|
|
<mat-panel-title>
|
2020-02-06 19:56:10 +02:00
|
|
|
<mat-icon>{{ ruleNodeTypeDescriptorsMap.get(ruleNodeType).icon }}</mat-icon>
|
2019-12-12 19:55:17 +02:00
|
|
|
<div class="tb-panel-title" translate>{{ ruleNodeTypeDescriptorsMap.get(ruleNodeType).name }}</div>
|
|
|
|
|
</mat-panel-title>
|
|
|
|
|
</mat-expansion-panel-header>
|
|
|
|
|
<fc-canvas id="tb-rulchain-{{ruleNodeType}}"
|
|
|
|
|
[model]="ruleNodeTypesModel[ruleNodeType].model"
|
|
|
|
|
[selectedObjects]="ruleNodeTypesModel[ruleNodeType].selectedObjects"
|
|
|
|
|
[automaticResize]="false"
|
2020-01-20 15:14:08 +02:00
|
|
|
fitModelSizeByDefault
|
2019-12-12 19:55:17 +02:00
|
|
|
[userCallbacks]="nodeLibCallbacks"
|
|
|
|
|
[nodeWidth]="170"
|
|
|
|
|
[nodeHeight]="50"
|
|
|
|
|
[dropTargetId]="'tb-rulchain-canvas'">
|
|
|
|
|
</fc-canvas>
|
|
|
|
|
</mat-expansion-panel>
|
|
|
|
|
</div>
|
2019-12-10 11:04:14 +02:00
|
|
|
</mat-drawer>
|
2019-12-17 20:16:40 +02:00
|
|
|
<mat-drawer class="tb-details-drawer"
|
|
|
|
|
[opened]="isEditingRuleNode || isEditingRuleNodeLink"
|
|
|
|
|
(closed)="onDetailsDrawerClosed()"
|
|
|
|
|
mode="over"
|
|
|
|
|
position="end">
|
|
|
|
|
<tb-details-panel *ngIf="editingRuleNode" fxFlex
|
|
|
|
|
headerTitle="{{editingRuleNode.name}}"
|
2020-01-14 16:57:42 +02:00
|
|
|
headerSubtitle="{{
|
|
|
|
|
(ruleNodeTypeDescriptorsMap.get(editingRuleNode.component.type).name | translate)
|
|
|
|
|
+ ' - ' + editingRuleNode.component.name
|
|
|
|
|
}}"
|
2019-12-17 20:16:40 +02:00
|
|
|
[isReadOnly]="selectedRuleNodeTabIndex > 0"
|
|
|
|
|
[isAlwaysEdit]="true"
|
|
|
|
|
(closeDetails)="onEditRuleNodeClosed()"
|
|
|
|
|
(toggleDetailsEditMode)="onRevertRuleNodeEdit()"
|
|
|
|
|
(applyDetails)="saveRuleNode()"
|
2020-04-27 10:39:18 +03:00
|
|
|
[theForm]="tbRuleNode.ruleNodeFormGroup">
|
2019-12-17 20:16:40 +02:00
|
|
|
<div class="details-buttons">
|
|
|
|
|
<div [tb-help]="helpLinkIdForRuleNodeType()"></div>
|
|
|
|
|
</div>
|
2023-02-21 19:18:04 +02:00
|
|
|
<mat-tab-group fxFlex mat-stretch-tabs="false" class="tb-absolute-fill tb-rulenode-details" [(selectedIndex)]="selectedRuleNodeTabIndex">
|
2019-12-17 20:16:40 +02:00
|
|
|
<mat-tab label="{{ 'rulenode.details' | translate }}">
|
|
|
|
|
<tb-rule-node #tbRuleNode
|
|
|
|
|
[ruleNode]="editingRuleNode"
|
2020-01-22 20:05:30 +02:00
|
|
|
[ruleChainId]="ruleChain.id?.id"
|
2021-01-08 21:39:34 +02:00
|
|
|
[ruleChainType]="ruleChainType"
|
2019-12-17 20:16:40 +02:00
|
|
|
[isEdit]="true"
|
|
|
|
|
[isReadOnly]="false">
|
|
|
|
|
</tb-rule-node>
|
|
|
|
|
</mat-tab>
|
2020-01-22 20:05:30 +02:00
|
|
|
<mat-tab *ngIf="editingRuleNode.ruleNodeId" label="{{ 'rulenode.events' | translate }}" #eventsTab="matTab">
|
2020-02-13 18:38:11 +02:00
|
|
|
<tb-event-table [debugEventTypes]="[debugEventTypes.DEBUG_RULE_NODE]"
|
2020-01-22 20:05:30 +02:00
|
|
|
[defaultEventType]="debugEventTypes.DEBUG_RULE_NODE"
|
2020-02-13 18:38:11 +02:00
|
|
|
[active]="eventsTab.isActive"
|
2020-01-22 20:05:30 +02:00
|
|
|
[tenantId]="ruleChain.tenantId.id"
|
|
|
|
|
[entityId]="editingRuleNode.ruleNodeId"></tb-event-table>
|
2019-12-17 20:16:40 +02:00
|
|
|
</mat-tab>
|
|
|
|
|
<mat-tab label="{{ 'rulenode.help' | translate }}">
|
|
|
|
|
<div class="tb-rule-node-help">
|
|
|
|
|
<div id="tb-node-content" class="mat-padding" fxLayout="column">
|
|
|
|
|
<div class="tb-node-title">{{editingRuleNode.component.name}}</div>
|
|
|
|
|
<div> </div>
|
|
|
|
|
<div class="tb-node-description">{{editingRuleNode.component.configurationDescriptor.nodeDefinition.description}}</div>
|
|
|
|
|
<div> </div>
|
|
|
|
|
<div class="tb-node-details" [innerHtml]="editingRuleNode.component.configurationDescriptor.nodeDefinition.details"></div>
|
|
|
|
|
</div>
|
2019-12-13 15:49:46 +02:00
|
|
|
</div>
|
2019-12-17 20:16:40 +02:00
|
|
|
</mat-tab>
|
|
|
|
|
</mat-tab-group>
|
|
|
|
|
</tb-details-panel>
|
|
|
|
|
<tb-details-panel *ngIf="editingRuleNodeLink" fxFlex
|
|
|
|
|
headerTitle="{{editingRuleNodeLink.label}}"
|
|
|
|
|
headerSubtitle="{{'rulenode.link-details' | translate}}"
|
|
|
|
|
[isReadOnly]="false"
|
|
|
|
|
[isAlwaysEdit]="true"
|
|
|
|
|
(closeDetails)="onEditRuleNodeLinkClosed()"
|
|
|
|
|
(toggleDetailsEditMode)="onRevertRuleNodeLinkEdit()"
|
|
|
|
|
(applyDetails)="saveRuleNodeLink()"
|
2020-04-27 10:39:18 +03:00
|
|
|
[theForm]="tbRuleNodeLink.ruleNodeLinkFormGroup">
|
2019-12-17 20:16:40 +02:00
|
|
|
<div class="details-buttons">
|
|
|
|
|
<div [tb-help]="'ruleEngine'"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<tb-rule-node-link
|
|
|
|
|
fxFlex
|
|
|
|
|
#tbRuleNodeLink
|
|
|
|
|
[(ngModel)]="editingRuleNodeLink"
|
|
|
|
|
[allowedLabels]="editingRuleNodeLinkLabels"
|
2021-12-08 15:35:59 +02:00
|
|
|
[allowCustom]="editingRuleNodeAllowCustomLabels"
|
|
|
|
|
[sourceRuleChainId]="editingRuleNodeSourceRuleChainId">
|
2019-12-17 20:16:40 +02:00
|
|
|
</tb-rule-node-link>
|
|
|
|
|
</tb-details-panel>
|
|
|
|
|
</mat-drawer>
|
|
|
|
|
<mat-drawer-content class="tb-rulechain-graph-content">
|
2023-05-25 13:35:58 +03:00
|
|
|
<button color="primary"
|
|
|
|
|
mat-mini-fab
|
|
|
|
|
class="tb-library-node-btn"
|
|
|
|
|
(click)="drawer.toggle();"
|
|
|
|
|
matTooltip="{{ (drawer.opened ? 'rulenode.close-node-library' : 'rulenode.open-node-library') | translate }}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon style="transition: transform 0.4s" [ngClass]="{'tb-library-node-btn-toggled' : !drawer.opened}">chevron_right</mat-icon>
|
|
|
|
|
</button>
|
2022-06-01 18:02:52 +03:00
|
|
|
<button #versionControlButton
|
|
|
|
|
*ngIf="!isImport"
|
|
|
|
|
color="primary"
|
|
|
|
|
type="button"
|
2023-02-21 19:18:04 +02:00
|
|
|
mat-mini-fab class="version-control-button"
|
2022-06-01 18:02:52 +03:00
|
|
|
(click)="toggleVersionControl($event, versionControlButton)"
|
|
|
|
|
matTooltip="{{'version-control.version-control' | translate}}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>history</mat-icon>
|
|
|
|
|
</button>
|
2023-02-21 19:18:04 +02:00
|
|
|
<button type="button"
|
|
|
|
|
mat-icon-button class="tb-fullscreen-button tb-mat-40"
|
2019-12-17 20:16:40 +02:00
|
|
|
(click)="isFullscreen = !isFullscreen"
|
|
|
|
|
matTooltip="{{(isFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>{{ isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
|
|
|
|
|
</button>
|
2020-01-20 15:14:08 +02:00
|
|
|
<div class="tb-absolute-fill tb-rulechain-graph" (contextmenu)="openRuleChainContextMenu($event)">
|
|
|
|
|
<div #ruleChainMenuTrigger="matMenuTrigger" style="visibility: hidden; position: fixed"
|
|
|
|
|
[style.left]="ruleChainMenuPosition.x"
|
|
|
|
|
[style.top]="ruleChainMenuPosition.y"
|
|
|
|
|
[matMenuTriggerFor]="ruleChainMenu">
|
|
|
|
|
</div>
|
|
|
|
|
<mat-menu #ruleChainMenu="matMenu" class="tb-rule-chain-context-menu"
|
|
|
|
|
[overlapTrigger]="true">
|
|
|
|
|
<ng-template matMenuContent let-contextInfo="contextInfo">
|
|
|
|
|
<div class="tb-rule-chain-context-menu-container" (mouseleave)="onRuleChainContextMenuMouseLeave()">
|
|
|
|
|
<div class="tb-context-menu-header {{contextInfo.headerClass}}">
|
|
|
|
|
<mat-icon *ngIf="!contextInfo.iconUrl">{{contextInfo.icon}}</mat-icon>
|
|
|
|
|
<img *ngIf="contextInfo.iconUrl" [src]="contextInfo.iconUrl"/>
|
|
|
|
|
<div fxFlex>
|
|
|
|
|
<div class="tb-context-menu-title">{{contextInfo.title}}</div>
|
|
|
|
|
<div class="tb-context-menu-subtitle">{{contextInfo.subtitle}}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div *ngFor="let menuItem of contextInfo.menuItems">
|
|
|
|
|
<mat-divider *ngIf="menuItem.divider"></mat-divider>
|
|
|
|
|
<button *ngIf="!menuItem.divider"
|
|
|
|
|
mat-menu-item
|
|
|
|
|
[disabled]="!menuItem.enabled"
|
|
|
|
|
(click)="menuItem.action(contextMenuEvent)">
|
|
|
|
|
<span *ngIf="menuItem.shortcut" class="tb-alt-text"> {{ menuItem.shortcut | keyboardShortcut }}</span>
|
|
|
|
|
<mat-icon *ngIf="menuItem.icon">{{menuItem.icon}}</mat-icon>
|
|
|
|
|
<span translate>{{menuItem.value}}</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ng-template>
|
|
|
|
|
</mat-menu>
|
2019-12-17 20:16:40 +02:00
|
|
|
<fc-canvas #ruleChainCanvas
|
|
|
|
|
id="tb-rulchain-canvas"
|
|
|
|
|
[model]="ruleChainModel"
|
|
|
|
|
(modelChanged)="onModelChanged()"
|
|
|
|
|
[selectedObjects]="selectedObjects"
|
|
|
|
|
[edgeStyle]="flowchartConstants.curvedStyle"
|
|
|
|
|
[automaticResize]="true"
|
2020-01-20 15:14:08 +02:00
|
|
|
fitModelSizeByDefault="false"
|
2019-12-17 20:16:40 +02:00
|
|
|
[nodeWidth]="170"
|
|
|
|
|
[nodeHeight]="50"
|
|
|
|
|
[dragAnimation]="flowchartConstants.dragAnimationRepaint"
|
|
|
|
|
[userCallbacks]="editCallbacks">
|
|
|
|
|
</fc-canvas>
|
|
|
|
|
</div>
|
|
|
|
|
<section fxLayout="row" class="layout-wrap tb-footer-buttons" fxLayoutAlign="start end">
|
|
|
|
|
<button [disabled]="isLoading$ | async"
|
|
|
|
|
mat-fab color="accent" class="tb-btn-footer"
|
|
|
|
|
[ngClass]="{'tb-hide': !objectsSelected()}"
|
|
|
|
|
(click)="deleteSelected()"
|
|
|
|
|
matTooltip="{{ 'rulenode.delete-selected-objects' | translate }}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>delete</mat-icon>
|
|
|
|
|
</button>
|
|
|
|
|
<button [disabled]="(isLoading$ | async) || !isDebugModeEnabled()"
|
|
|
|
|
mat-fab color="accent" class="tb-btn-footer"
|
|
|
|
|
(click)="resetDebugModeInAllNodes()"
|
|
|
|
|
matTooltip="{{ 'rulenode.reset-debug-mode' | translate }}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>bug_report</mat-icon>
|
|
|
|
|
</button>
|
|
|
|
|
<button [disabled]="(isLoading$ | async) || isInvalid || (!isDirtyValue && !isImport)"
|
|
|
|
|
mat-fab color="accent" class="tb-btn-footer"
|
|
|
|
|
(click)="saveRuleChain()"
|
|
|
|
|
matTooltip="{{ 'action.apply-changes' | translate }}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>done</mat-icon>
|
|
|
|
|
</button>
|
|
|
|
|
<button [disabled]="(isLoading$ | async) || !isDirtyValue"
|
|
|
|
|
mat-fab color="accent" class="tb-btn-footer"
|
|
|
|
|
(click)="revertRuleChain()"
|
|
|
|
|
matTooltip="{{ 'action.decline-changes' | translate }}"
|
|
|
|
|
matTooltipPosition="above">
|
|
|
|
|
<mat-icon>close</mat-icon>
|
|
|
|
|
</button>
|
|
|
|
|
</section>
|
2019-12-10 11:04:14 +02:00
|
|
|
</mat-drawer-content>
|
|
|
|
|
</mat-drawer-container>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|