23 changed files with 1761 additions and 121 deletions
@ -0,0 +1,48 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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. |
||||
|
|
||||
|
--> |
||||
|
<md-dialog aria-label="{{ 'rulenode.add-link' | translate }}" tb-help="'rulechains'" help-container-id="help-container"> |
||||
|
<form name="theForm" ng-submit="vm.add()"> |
||||
|
<md-toolbar> |
||||
|
<div class="md-toolbar-tools"> |
||||
|
<h2 translate>rulenode.add-link</h2> |
||||
|
<span flex></span> |
||||
|
<div id="help-container"></div> |
||||
|
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
||||
|
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</md-toolbar> |
||||
|
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
||||
|
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
||||
|
<md-dialog-content> |
||||
|
<div class="md-dialog-content"> |
||||
|
<tb-rule-node-link link="vm.link" labels="vm.labels" is-edit="true" the-form="theForm"></tb-rule-node-link> |
||||
|
</div> |
||||
|
</md-dialog-content> |
||||
|
<md-dialog-actions layout="row"> |
||||
|
<span flex></span> |
||||
|
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" |
||||
|
class="md-raised md-primary"> |
||||
|
{{ 'action.add' | translate }} |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
||||
|
translate }} |
||||
|
</md-button> |
||||
|
</md-dialog-actions> |
||||
|
</form> |
||||
|
</md-dialog> |
||||
@ -0,0 +1,48 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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. |
||||
|
|
||||
|
--> |
||||
|
<md-dialog aria-label="{{ 'rulenode.add' | translate }}" tb-help="'rulechains'" help-container-id="help-container" style="min-width: 650px;"> |
||||
|
<form name="theForm" ng-submit="vm.add()"> |
||||
|
<md-toolbar> |
||||
|
<div class="md-toolbar-tools"> |
||||
|
<h2 translate>rulenode.add</h2> |
||||
|
<span flex></span> |
||||
|
<div id="help-container"></div> |
||||
|
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
||||
|
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</md-toolbar> |
||||
|
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> |
||||
|
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
||||
|
<md-dialog-content> |
||||
|
<div class="md-dialog-content"> |
||||
|
<tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" is-edit="true" the-form="theForm"></tb-rule-node> |
||||
|
</div> |
||||
|
</md-dialog-content> |
||||
|
<md-dialog-actions layout="row"> |
||||
|
<span flex></span> |
||||
|
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" |
||||
|
class="md-raised md-primary"> |
||||
|
{{ 'action.add' | translate }} |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
||||
|
translate }} |
||||
|
</md-button> |
||||
|
</md-dialog-actions> |
||||
|
</form> |
||||
|
</md-dialog> |
||||
@ -0,0 +1,39 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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. |
||||
|
|
||||
|
--> |
||||
|
<md-content class="md-padding tb-link" layout="column"> |
||||
|
<fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>rulenode.link-label</label> |
||||
|
<md-select ng-model="selectedLabel" ng-change="selectedLabelChanged()"> |
||||
|
<md-option ng-repeat="label in labels" ng-value="label"> |
||||
|
{{label.name}} |
||||
|
</md-option> |
||||
|
</md-select> |
||||
|
<div ng-messages="theForm.linkLabel.$error"> |
||||
|
<div translate ng-message="required">rulenode.link-label-required</div> |
||||
|
</div> |
||||
|
</md-input-container> |
||||
|
<md-input-container ng-if="selectedLabel.custom" class="md-block"> |
||||
|
<label translate>rulenode.link-label</label> |
||||
|
<input required name="customLinkLabel" ng-model="link.label"> |
||||
|
<div ng-messages="theForm.customLinkLabel.$error"> |
||||
|
<div translate ng-message="required">rulenode.custom-link-label-required</div> |
||||
|
</div> |
||||
|
</md-input-container> |
||||
|
</fieldset> |
||||
|
</md-content> |
||||
@ -0,0 +1,71 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2018 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. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import linkFieldsetTemplate from './link-fieldset.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function LinkDirective($compile, $templateCache, $filter) { |
||||
|
var linker = function (scope, element) { |
||||
|
var template = $templateCache.get(linkFieldsetTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.selectedLabel = null; |
||||
|
|
||||
|
scope.$watch('link', function() { |
||||
|
scope.selectedLabel = null; |
||||
|
if (scope.link && scope.labels) { |
||||
|
if (scope.link.label) { |
||||
|
var result = $filter('filter')(scope.labels, {name: scope.link.label}); |
||||
|
if (result && result.length) { |
||||
|
scope.selectedLabel = result[0]; |
||||
|
} else { |
||||
|
result = $filter('filter')(scope.labels, {custom: true}); |
||||
|
if (result && result.length && result[0].custom) { |
||||
|
scope.selectedLabel = result[0]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
scope.selectedLabelChanged = function() { |
||||
|
if (scope.link && scope.selectedLabel) { |
||||
|
if (!scope.selectedLabel.custom) { |
||||
|
scope.link.label = scope.selectedLabel.name; |
||||
|
} else { |
||||
|
scope.link.label = ""; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
} |
||||
|
return { |
||||
|
restrict: "E", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
link: '=', |
||||
|
labels: '=', |
||||
|
isEdit: '=', |
||||
|
isReadOnly: '=', |
||||
|
theForm: '=' |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,262 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2018 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. |
||||
|
*/ |
||||
|
|
||||
|
.tb-rulechain { |
||||
|
.tb-fullscreen-button-style { |
||||
|
z-index: 1; |
||||
|
} |
||||
|
.tb-rulechain-library { |
||||
|
width: 250px; |
||||
|
min-width: 250px; |
||||
|
overflow-y: auto; |
||||
|
overflow-x: hidden; |
||||
|
|
||||
|
.tb-rulechain-library-panel-group { |
||||
|
.tb-panel-title { |
||||
|
-webkit-user-select: none; |
||||
|
-moz-user-select: none; |
||||
|
-ms-user-select: none; |
||||
|
user-select: none; |
||||
|
min-width: 180px; |
||||
|
} |
||||
|
.fc-canvas { |
||||
|
background: none; |
||||
|
} |
||||
|
md-icon.md-expansion-panel-icon { |
||||
|
margin-right: 0px; |
||||
|
} |
||||
|
md-expansion-panel-collapsed, .md-expansion-panel-header-container { |
||||
|
background: #e6e6e6; |
||||
|
border-color: #909090; |
||||
|
position: static; |
||||
|
} |
||||
|
md-expansion-panel { |
||||
|
&.md-open { |
||||
|
margin-top: 0; |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
} |
||||
|
md-expansion-panel-content { |
||||
|
padding: 0px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.tb-rulechain-graph { |
||||
|
overflow: auto; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fc-canvas { |
||||
|
min-width: 100%; |
||||
|
min-height: 100%; |
||||
|
outline: none; |
||||
|
} |
||||
|
|
||||
|
.tb-rule-node { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
min-width: 150px; |
||||
|
max-width: 150px; |
||||
|
min-height: 28px; |
||||
|
max-height: 28px; |
||||
|
padding: 5px 10px; |
||||
|
border-radius: 5px; |
||||
|
background-color: #F15B26; |
||||
|
color: #333; |
||||
|
border: solid 1px #777; |
||||
|
font-size: 12px; |
||||
|
&.tb-input-type { |
||||
|
background-color: #a3eaa9; |
||||
|
user-select: none; |
||||
|
} |
||||
|
&.tb-filter-type { |
||||
|
background-color: #f1e861; |
||||
|
} |
||||
|
&.tb-enrichment-type { |
||||
|
background-color: #cdf14e; |
||||
|
} |
||||
|
&.tb-transformation-type { |
||||
|
background-color: #79cef1; |
||||
|
} |
||||
|
&.tb-action-type { |
||||
|
background-color: #f1928f; |
||||
|
} |
||||
|
&.tb-rule-chain-type { |
||||
|
background-color: #d6c4f1; |
||||
|
} |
||||
|
md-icon { |
||||
|
font-size: 20px; |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
min-height: 20px; |
||||
|
min-width: 20px; |
||||
|
padding-right: 4px; |
||||
|
} |
||||
|
.tb-node-type { |
||||
|
|
||||
|
} |
||||
|
.tb-node-title { |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
.tb-node-type, .tb-node-title { |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fc-node { |
||||
|
z-index: 1; |
||||
|
outline: none; |
||||
|
&.fc-hover, &.fc-selected { |
||||
|
-webkit-filter: brightness(70%); |
||||
|
filter: brightness(70%); |
||||
|
} |
||||
|
&.fc-dragging { |
||||
|
z-index: 10; |
||||
|
} |
||||
|
p { |
||||
|
padding: 0 15px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fc-leftConnectors, .fc-rightConnectors { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
height: 100%; |
||||
|
|
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
z-index: 0; |
||||
|
.fc-magnet { |
||||
|
align-items: center; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fc-leftConnectors { |
||||
|
left: -20px; |
||||
|
} |
||||
|
|
||||
|
.fc-rightConnectors { |
||||
|
right: -20px; |
||||
|
} |
||||
|
|
||||
|
.fc-magnet { |
||||
|
display: flex; |
||||
|
flex-grow: 1; |
||||
|
height: 60px; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.fc-connector { |
||||
|
width: 14px; |
||||
|
height: 14px; |
||||
|
border: 1px solid #333; |
||||
|
margin: 10px; |
||||
|
border-radius: 5px; |
||||
|
background-color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.fc-connector.fc-hover { |
||||
|
background-color: #000; |
||||
|
} |
||||
|
|
||||
|
.fc-edge { |
||||
|
outline: none; |
||||
|
stroke: gray; |
||||
|
stroke-width: 4; |
||||
|
fill: transparent; |
||||
|
&.fc-selected { |
||||
|
stroke: red; |
||||
|
stroke-width: 4; |
||||
|
fill: transparent; |
||||
|
} |
||||
|
&.fc-active { |
||||
|
animation: dash 3s linear infinite; |
||||
|
stroke-dasharray: 20; |
||||
|
} |
||||
|
&.fc-hover { |
||||
|
stroke: gray; |
||||
|
stroke-width: 6; |
||||
|
fill: transparent; |
||||
|
} |
||||
|
&.fc-dragging { |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.edge-endpoint { |
||||
|
fill: gray; |
||||
|
} |
||||
|
|
||||
|
.fc-nodedelete { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.fc-selected .fc-nodedelete { |
||||
|
outline: none; |
||||
|
display: block; |
||||
|
position: absolute; |
||||
|
right: -13px; |
||||
|
top: -16px; |
||||
|
border: solid 2px white; |
||||
|
border-radius: 50%; |
||||
|
font-weight: 600; |
||||
|
font-size: 18px; |
||||
|
line-height: 18px; |
||||
|
height: 20px; |
||||
|
padding-top: 2px; |
||||
|
width: 22px; |
||||
|
background: #494949; |
||||
|
color: #fff; |
||||
|
text-align: center; |
||||
|
vertical-align: bottom; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.fc-edge-label { |
||||
|
position: absolute; |
||||
|
user-select: none; |
||||
|
pointer-events: none; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
|
||||
|
.fc-edge-label-text { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
-webkit-transform: translateX(-50%); |
||||
|
transform: translateX(-50%); |
||||
|
white-space: nowrap; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
top: 5px; |
||||
|
span { |
||||
|
border: solid 2px #003a79; |
||||
|
border-radius: 10px; |
||||
|
color: #003a79; |
||||
|
background-color: #fff; |
||||
|
padding: 3px 5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes dash { |
||||
|
from { |
||||
|
stroke-dashoffset: 500; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,130 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<md-content flex tb-expand-fullscreen |
||||
|
expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"> |
||||
|
<section class="tb-rulechain-container" flex layout="column"> |
||||
|
<div class="tb-rulechain-layout" flex layout="row"> |
||||
|
<div class="tb-rulechain-library"> |
||||
|
<md-expansion-panel-group class="tb-rulechain-library-panel-group" md-component-id="libraryPanelGroup" auto-expand="true" multiple> |
||||
|
<md-expansion-panel md-component-id="{{typeId}}" id="{{typeId}}" ng-repeat="(typeId, typeModel) in vm.ruleNodeTypesModel"> |
||||
|
<md-expansion-panel-collapsed> |
||||
|
<div class="tb-panel-title" translate>{{vm.types.ruleNodeType[typeId].name}}</div> |
||||
|
<md-expansion-panel-icon></md-expansion-panel-icon> |
||||
|
</md-expansion-panel-collapsed> |
||||
|
<md-expansion-panel-expanded> |
||||
|
<md-expansion-panel-header ng-click="vm.$mdExpansionPanel(typeId).collapse()"> |
||||
|
<div class="tb-panel-title" translate>{{vm.types.ruleNodeType[typeId].name}}</div> |
||||
|
<md-expansion-panel-icon></md-expansion-panel-icon> |
||||
|
</md-expansion-panel-header> |
||||
|
<md-expansion-panel-content> |
||||
|
<fc-canvas id="tb-rulechain-{{typeId}}" |
||||
|
model="vm.ruleNodeTypesModel[typeId].model" selected-objects="vm.ruleNodeTypesModel[typeId].selectedObjects" |
||||
|
automatic-resize="false" |
||||
|
node-width="170" |
||||
|
node-height="50" |
||||
|
drop-target-id="'tb-rulchain-canvas'"></fc-canvas> |
||||
|
</md-expansion-panel-content> |
||||
|
</md-expansion-panel-expanded> |
||||
|
</md-expansion-panel> |
||||
|
</md-expansion-panel-group> |
||||
|
</div> |
||||
|
<div flex class="tb-rulechain-graph"> |
||||
|
<fc-canvas id="tb-rulchain-canvas" |
||||
|
ng-keydown="vm.keyDown($event)" |
||||
|
ng-keyup="vm.keyUp($event)" |
||||
|
model="vm.ruleChainModel" |
||||
|
selected-objects="vm.selectedObjects" |
||||
|
edge-style="curved" |
||||
|
node-width="170" |
||||
|
node-height="50" |
||||
|
automatic-resize="true" |
||||
|
control="vm.canvasControl" |
||||
|
callbacks="vm.editCallbacks"> |
||||
|
</fc-canvas> |
||||
|
</div> |
||||
|
</div> |
||||
|
<tb-details-sidenav class="tb-rulenode-details-sidenav" |
||||
|
header-title="{{vm.editingRuleNode.name}}" |
||||
|
header-subtitle="{{'rulenode.rulenode-details' | translate}}" |
||||
|
is-read-only="false" |
||||
|
is-open="vm.isEditingRuleNode" |
||||
|
is-always-edit="true" |
||||
|
on-close-details="vm.onEditRuleNodeClosed()" |
||||
|
on-toggle-details-edit-mode="vm.onRevertRuleNodeEdit(vm.ruleNodeForm)" |
||||
|
on-apply-details="vm.saveRuleNode(vm.ruleNodeForm)" |
||||
|
the-form="vm.ruleNodeForm"> |
||||
|
<details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container"> |
||||
|
<div id="help-container"></div> |
||||
|
</details-buttons> |
||||
|
<form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode"> |
||||
|
<tb-rule-node |
||||
|
rule-node="vm.editingRuleNode" |
||||
|
rule-chain-id="vm.ruleChain.id.id" |
||||
|
is-edit="true" |
||||
|
is-read-only="false" |
||||
|
on-delete-rule-node="vm.deleteRuleNode(event, vm.editingRuleNode)" |
||||
|
the-form="vm.ruleNodeForm"> |
||||
|
</tb-rule-node> |
||||
|
</form> |
||||
|
</tb-details-sidenav> |
||||
|
<tb-details-sidenav class="tb-rulenode-link-details-sidenav" |
||||
|
header-title="{{vm.editingRuleNodeLink.label}}" |
||||
|
header-subtitle="{{'rulenode.link-details' | translate}}" |
||||
|
is-read-only="false" |
||||
|
is-open="vm.isEditingRuleNodeLink" |
||||
|
is-always-edit="true" |
||||
|
on-close-details="vm.onEditRuleNodeLinkClosed()" |
||||
|
on-toggle-details-edit-mode="vm.onRevertRuleNodeLinkEdit(vm.ruleNodeLinkForm)" |
||||
|
on-apply-details="vm.saveRuleNodeLink(vm.ruleNodeLinkForm)" |
||||
|
the-form="vm.ruleNodeLinkForm"> |
||||
|
<details-buttons tb-help="vm.helpLinkIdForRuleNodeLink()" help-container-id="link-help-container"> |
||||
|
<div id="link-help-container"></div> |
||||
|
</details-buttons> |
||||
|
<form name="vm.ruleNodeLinkForm" ng-if="vm.isEditingRuleNodeLink"> |
||||
|
<tb-rule-node-link |
||||
|
link="vm.editingRuleNodeLink" |
||||
|
labels="vm.editingRuleNodeLinkLabels" |
||||
|
is-edit="true" |
||||
|
is-read-only="false" |
||||
|
the-form="vm.ruleNodeLinkForm"> |
||||
|
</tb-rule-node-link> |
||||
|
</form> |
||||
|
</tb-details-sidenav> |
||||
|
</section> |
||||
|
<section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end"> |
||||
|
<md-button ng-disabled="$root.loading || !vm.isDirty" |
||||
|
class="tb-btn-footer md-accent md-hue-2 md-fab" |
||||
|
aria-label="{{ 'action.apply' | translate }}" |
||||
|
ng-click="vm.saveRuleChain()"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'action.apply-changes' | translate }} |
||||
|
</md-tooltip> |
||||
|
<ng-md-icon icon="done"></ng-md-icon> |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="$root.loading || !vm.isDirty" |
||||
|
class="tb-btn-footer md-accent md-hue-2 md-fab" |
||||
|
aria-label="{{ 'action.decline-changes' | translate }}" |
||||
|
ng-click="vm.revertRuleChain()"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'action.decline-changes' | translate }} |
||||
|
</md-tooltip> |
||||
|
<ng-md-icon icon="close"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</section> |
||||
|
</md-content> |
||||
@ -0,0 +1,188 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2018 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. |
||||
|
*/ |
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import addRuleChainTemplate from './add-rulechain.tpl.html'; |
||||
|
import ruleChainCard from './rulechain-card.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function RuleChainsController(ruleChainService, userService, importExport, $state, $stateParams, $filter, $translate, types) { |
||||
|
|
||||
|
var ruleChainActionsList = [ |
||||
|
{ |
||||
|
onAction: function ($event, item) { |
||||
|
vm.grid.openItem($event, item); |
||||
|
}, |
||||
|
name: function() { return $translate.instant('rulechain.details') }, |
||||
|
details: function() { return $translate.instant('rulechain.rulechain-details') }, |
||||
|
icon: "edit" |
||||
|
}, |
||||
|
{ |
||||
|
onAction: function ($event, item) { |
||||
|
exportRuleChain($event, item); |
||||
|
}, |
||||
|
name: function() { $translate.instant('action.export') }, |
||||
|
details: function() { return $translate.instant('rulechain.export') }, |
||||
|
icon: "file_download" |
||||
|
}, |
||||
|
{ |
||||
|
onAction: function ($event, item) { |
||||
|
vm.grid.deleteItem($event, item); |
||||
|
}, |
||||
|
name: function() { return $translate.instant('action.delete') }, |
||||
|
details: function() { return $translate.instant('rulechain.delete') }, |
||||
|
icon: "delete", |
||||
|
isEnabled: isRuleChainEditable |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
var ruleChainAddItemActionsList = [ |
||||
|
{ |
||||
|
onAction: function ($event) { |
||||
|
vm.grid.addItem($event); |
||||
|
}, |
||||
|
name: function() { return $translate.instant('action.create') }, |
||||
|
details: function() { return $translate.instant('rulechain.create-new-rulechain') }, |
||||
|
icon: "insert_drive_file" |
||||
|
}, |
||||
|
{ |
||||
|
onAction: function ($event) { |
||||
|
importExport.importRuleChain($event).then( |
||||
|
function() { |
||||
|
vm.grid.refreshList(); |
||||
|
} |
||||
|
); |
||||
|
}, |
||||
|
name: function() { return $translate.instant('action.import') }, |
||||
|
details: function() { return $translate.instant('rulechain.import') }, |
||||
|
icon: "file_upload" |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
var vm = this; |
||||
|
|
||||
|
vm.types = types; |
||||
|
|
||||
|
vm.ruleChainGridConfig = { |
||||
|
|
||||
|
refreshParamsFunc: null, |
||||
|
|
||||
|
deleteItemTitleFunc: deleteRuleChainTitle, |
||||
|
deleteItemContentFunc: deleteRuleChainText, |
||||
|
deleteItemsTitleFunc: deleteRuleChainsTitle, |
||||
|
deleteItemsActionTitleFunc: deleteRuleChainsActionTitle, |
||||
|
deleteItemsContentFunc: deleteRuleChainsText, |
||||
|
|
||||
|
fetchItemsFunc: fetchRuleChains, |
||||
|
saveItemFunc: saveRuleChain, |
||||
|
clickItemFunc: openRuleChain, |
||||
|
deleteItemFunc: deleteRuleChain, |
||||
|
|
||||
|
getItemTitleFunc: getRuleChainTitle, |
||||
|
itemCardTemplateUrl: ruleChainCard, |
||||
|
parentCtl: vm, |
||||
|
|
||||
|
actionsList: ruleChainActionsList, |
||||
|
addItemActions: ruleChainAddItemActionsList, |
||||
|
|
||||
|
onGridInited: gridInited, |
||||
|
|
||||
|
addItemTemplateUrl: addRuleChainTemplate, |
||||
|
|
||||
|
addItemText: function() { return $translate.instant('rulechain.add-rulechain-text') }, |
||||
|
noItemsText: function() { return $translate.instant('rulechain.no-rulechains-text') }, |
||||
|
itemDetailsText: function() { return $translate.instant('rulechain.rulechain-details') }, |
||||
|
isSelectionEnabled: isRuleChainEditable, |
||||
|
isDetailsReadOnly: function(ruleChain) { |
||||
|
return !isRuleChainEditable(ruleChain); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
if (angular.isDefined($stateParams.items) && $stateParams.items !== null) { |
||||
|
vm.ruleChainGridConfig.items = $stateParams.items; |
||||
|
} |
||||
|
|
||||
|
if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) { |
||||
|
vm.ruleChainGridConfig.topIndex = $stateParams.topIndex; |
||||
|
} |
||||
|
|
||||
|
vm.isRuleChainEditable = isRuleChainEditable; |
||||
|
|
||||
|
vm.exportRuleChain = exportRuleChain; |
||||
|
|
||||
|
function deleteRuleChainTitle(ruleChain) { |
||||
|
return $translate.instant('rulechain.delete-rulechain-title', {ruleChainName: ruleChain.name}); |
||||
|
} |
||||
|
|
||||
|
function deleteRuleChainText() { |
||||
|
return $translate.instant('rulechain.delete-rulechain-text'); |
||||
|
} |
||||
|
|
||||
|
function deleteRuleChainsTitle(selectedCount) { |
||||
|
return $translate.instant('rulechain.delete-rulechains-title', {count: selectedCount}, 'messageformat'); |
||||
|
} |
||||
|
|
||||
|
function deleteRuleChainsActionTitle(selectedCount) { |
||||
|
return $translate.instant('rulechain.delete-rulechains-action-title', {count: selectedCount}, 'messageformat'); |
||||
|
} |
||||
|
|
||||
|
function deleteRuleChainsText() { |
||||
|
return $translate.instant('rulechain.delete-rulechains-text'); |
||||
|
} |
||||
|
|
||||
|
function gridInited(grid) { |
||||
|
vm.grid = grid; |
||||
|
} |
||||
|
|
||||
|
function fetchRuleChains(pageLink) { |
||||
|
return ruleChainService.getRuleChains(pageLink); |
||||
|
} |
||||
|
|
||||
|
function saveRuleChain(ruleChain) { |
||||
|
return ruleChainService.saveRuleChain(ruleChain); |
||||
|
} |
||||
|
|
||||
|
function openRuleChain($event, ruleChain) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
$state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); |
||||
|
} |
||||
|
|
||||
|
function deleteRuleChain(ruleChainId) { |
||||
|
return ruleChainService.deleteRuleChain(ruleChainId); |
||||
|
} |
||||
|
|
||||
|
function getRuleChainTitle(ruleChain) { |
||||
|
return ruleChain ? ruleChain.name : ''; |
||||
|
} |
||||
|
|
||||
|
function isRuleChainEditable(ruleChain) { |
||||
|
if (userService.getAuthority() === 'TENANT_ADMIN') { |
||||
|
return ruleChain && ruleChain.tenantId.id != types.id.nullUid; |
||||
|
} else { |
||||
|
return userService.getAuthority() === 'SYS_ADMIN'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function exportRuleChain($event, ruleChain) { |
||||
|
$event.stopPropagation(); |
||||
|
importExport.exportRuleChain(ruleChain.id.id); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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. |
||||
|
|
||||
|
--> |
||||
|
<md-button ng-click="onDeleteRuleNode({event: $event})" |
||||
|
ng-show="!isEdit && !isReadOnly" |
||||
|
class="md-raised md-primary">{{ 'rulenode.delete' | translate }}</md-button> |
||||
|
|
||||
|
<md-content class="md-padding tb-rulenode" layout="column"> |
||||
|
<fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>rulenode.type</label> |
||||
|
<input readonly name="type" ng-model="ruleNode.type"> |
||||
|
</md-input-container> |
||||
|
<section ng-if="ruleNode.nodeType != types.ruleNodeType.RULE_CHAIN.value"> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>rulenode.name</label> |
||||
|
<input required name="name" ng-model="ruleNode.name"> |
||||
|
<div ng-messages="theForm.name.$error"> |
||||
|
<div translate ng-message="required">rulenode.name-required</div> |
||||
|
</div> |
||||
|
</md-input-container> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>rulenode.description</label> |
||||
|
<textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea> |
||||
|
</md-input-container> |
||||
|
</section> |
||||
|
<section ng-if="ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value"> |
||||
|
<tb-entity-autocomplete the-form="theForm" |
||||
|
ng-disabled="$root.loading || !isEdit || isReadOnly" |
||||
|
tb-required="true" |
||||
|
exclude-entity-ids="[ruleChainId]" |
||||
|
entity-type="types.entityType.rulechain" |
||||
|
ng-model="params.targetRuleChainId"> |
||||
|
</tb-entity-autocomplete> |
||||
|
<md-input-container class="md-block"> |
||||
|
<label translate>rulenode.description</label> |
||||
|
<textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea> |
||||
|
</md-input-container> |
||||
|
</section> |
||||
|
</fieldset> |
||||
|
</md-content> |
||||
@ -0,0 +1,79 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2018 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. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import ruleNodeFieldsetTemplate from './rulenode-fieldset.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function RuleNodeDirective($compile, $templateCache, ruleChainService, types) { |
||||
|
var linker = function (scope, element) { |
||||
|
var template = $templateCache.get(ruleNodeFieldsetTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.types = types; |
||||
|
|
||||
|
scope.params = { |
||||
|
targetRuleChainId: null |
||||
|
}; |
||||
|
|
||||
|
scope.$watch('ruleNode', function() { |
||||
|
if (scope.ruleNode && scope.ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) { |
||||
|
scope.params.targetRuleChainId = scope.ruleNode.targetRuleChainId; |
||||
|
watchTargetRuleChain(); |
||||
|
} else { |
||||
|
if (scope.targetRuleChainWatch) { |
||||
|
scope.targetRuleChainWatch(); |
||||
|
scope.targetRuleChainWatch = null; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function watchTargetRuleChain() { |
||||
|
scope.targetRuleChainWatch = scope.$watch('params.targetRuleChainId', |
||||
|
function(targetRuleChainId) { |
||||
|
if (scope.ruleNode.targetRuleChainId != targetRuleChainId) { |
||||
|
scope.ruleNode.targetRuleChainId = targetRuleChainId; |
||||
|
if (targetRuleChainId) { |
||||
|
ruleChainService.getRuleChain(targetRuleChainId).then( |
||||
|
(ruleChain) => { |
||||
|
scope.ruleNode.name = ruleChain.name; |
||||
|
} |
||||
|
); |
||||
|
} else { |
||||
|
scope.ruleNode.name = ""; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
$compile(element.contents())(scope); |
||||
|
} |
||||
|
return { |
||||
|
restrict: "E", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
ruleChainId: '=', |
||||
|
ruleNode: '=', |
||||
|
isEdit: '=', |
||||
|
isReadOnly: '=', |
||||
|
theForm: '=', |
||||
|
onDeleteRuleNode: '&' |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2018 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 |
||||
|
id="{{node.id}}" |
||||
|
ng-attr-style="position: absolute; top: {{ node.y }}px; left: {{ node.x }}px;" |
||||
|
ng-dblclick="callbacks.doubleClick($event, node)"> |
||||
|
<div class="tb-rule-node {{node.nodeClass}}"> |
||||
|
<md-icon aria-label="node-type-icon" flex="15" |
||||
|
class="material-icons">{{node.icon}}</md-icon> |
||||
|
<div layout="column" flex="85" layout-align="center"> |
||||
|
<span class="tb-node-type">{{ node.type }}</span> |
||||
|
<span class="tb-node-title" ng-if="node.name">{{ node.name }}</span> |
||||
|
</div> |
||||
|
<div class="{{flowchartConstants.leftConnectorClass}}"> |
||||
|
<div fc-magnet |
||||
|
ng-repeat="connector in modelservice.nodes.getConnectorsByType(node, flowchartConstants.leftConnectorType)"> |
||||
|
<div fc-connector></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="{{flowchartConstants.rightConnectorClass}}"> |
||||
|
<div fc-magnet |
||||
|
ng-repeat="connector in modelservice.nodes.getConnectorsByType(node, flowchartConstants.rightConnectorType)"> |
||||
|
<div fc-connector></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div ng-if="modelservice.isEditable() && !node.readonly" class="fc-nodedelete" ng-click="modelservice.nodes.delete(node)"> |
||||
|
× |
||||
|
</div> |
||||
|
</div> |
||||
Loading…
Reference in new issue