thingsboard-developers
Changes
ui/src/app/api/rule-chain.service.js 17(+16 -1)
ui/src/app/locale/locale.constant.js 5(+3 -2)
ui/src/app/rulechain/rulechain.controller.js 288(+214 -74)
ui/src/app/rulechain/rulechain.routes.js 41(+41 -0)
ui/src/app/rulechain/rulechain.scss 17(+12 -5)
Details
ui/src/app/api/rule-chain.service.js 17(+16 -1)
diff --git a/ui/src/app/api/rule-chain.service.js b/ui/src/app/api/rule-chain.service.js
index af14a3f..b930cf1 100644
--- a/ui/src/app/api/rule-chain.service.js
+++ b/ui/src/app/api/rule-chain.service.js
@@ -253,7 +253,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
if (ruleChainConnections && ruleChainConnections.length) {
var tasks = [];
for (var i = 0; i < ruleChainConnections.length; i++) {
- tasks.push(getRuleChain(ruleChainConnections[i].targetRuleChainId.id));
+ tasks.push(resolveRuleChain(ruleChainConnections[i].targetRuleChainId.id));
}
$q.all(tasks).then(
(ruleChains) => {
@@ -273,6 +273,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
return deferred.promise;
}
+ function resolveRuleChain(ruleChainId) {
+ var deferred = $q.defer();
+ getRuleChain(ruleChainId, {ignoreErrors: true}).then(
+ (ruleChain) => {
+ deferred.resolve(ruleChain);
+ },
+ () => {
+ deferred.resolve({
+ id: {id: ruleChainId, entityType: types.entityType.rulechain}
+ });
+ }
+ );
+ return deferred.promise;
+ }
+
function loadRuleNodeComponents() {
return componentDescriptorService.getComponentDescriptorsByTypes(types.ruleNodeTypeComponentTypes);
}
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index 19c249f..88c6353 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -281,39 +281,63 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
function exportRuleChain(ruleChainId) {
ruleChainService.getRuleChain(ruleChainId).then(
- function success(ruleChain) {
- var name = ruleChain.name;
- name = name.toLowerCase().replace(/\W/g,"_");
- exportToPc(prepareExport(ruleChain), name + '.json');
- //TODO: metadata
+ (ruleChain) => {
+ ruleChainService.getRuleChainMetaData(ruleChainId).then(
+ (ruleChainMetaData) => {
+ var ruleChainExport = {
+ ruleChain: prepareRuleChain(ruleChain),
+ metadata: prepareRuleChainMetaData(ruleChainMetaData)
+ };
+ var name = ruleChain.name;
+ name = name.toLowerCase().replace(/\W/g,"_");
+ exportToPc(ruleChainExport, name + '.json');
+ },
+ (rejection) => {
+ processExportRuleChainRejection(rejection);
+ }
+ );
},
- function fail(rejection) {
- var message = rejection;
- if (!message) {
- message = $translate.instant('error.unknown-error');
- }
- toast.showError($translate.instant('rulechain.export-failed-error', {error: message}));
+ (rejection) => {
+ processExportRuleChainRejection(rejection);
}
);
}
+ function prepareRuleChain(ruleChain) {
+ ruleChain = prepareExport(ruleChain);
+ if (ruleChain.firstRuleNodeId) {
+ ruleChain.firstRuleNodeId = null;
+ }
+ ruleChain.root = false;
+ return ruleChain;
+ }
+
+ function prepareRuleChainMetaData(ruleChainMetaData) {
+ delete ruleChainMetaData.ruleChainId;
+ for (var i=0;i<ruleChainMetaData.nodes.length;i++) {
+ var node = ruleChainMetaData.nodes[i];
+ ruleChainMetaData.nodes[i] = prepareExport(node);
+ }
+ return ruleChainMetaData;
+ }
+
+ function processExportRuleChainRejection(rejection) {
+ var message = rejection;
+ if (!message) {
+ message = $translate.instant('error.unknown-error');
+ }
+ toast.showError($translate.instant('rulechain.export-failed-error', {error: message}));
+ }
+
function importRuleChain($event) {
var deferred = $q.defer();
openImportDialog($event, 'rulechain.import', 'rulechain.rulechain-file').then(
- function success(ruleChain) {
- if (!validateImportedRuleChain(ruleChain)) {
+ function success(ruleChainImport) {
+ if (!validateImportedRuleChain(ruleChainImport)) {
toast.showError($translate.instant('rulechain.invalid-rulechain-file-error'));
deferred.reject();
} else {
- //TODO: rulechain metadata
- ruleChainService.saveRuleChain(ruleChain).then(
- function success() {
- deferred.resolve();
- },
- function fail() {
- deferred.reject();
- }
- );
+ deferred.resolve(ruleChainImport);
}
},
function fail() {
@@ -323,10 +347,14 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
return deferred.promise;
}
- function validateImportedRuleChain(ruleChain) {
- //TODO: rulechain metadata
- if (angular.isUndefined(ruleChain.name))
- {
+ function validateImportedRuleChain(ruleChainImport) {
+ if (angular.isUndefined(ruleChainImport.ruleChain)) {
+ return false;
+ }
+ if (angular.isUndefined(ruleChainImport.metadata)) {
+ return false;
+ }
+ if (angular.isUndefined(ruleChainImport.ruleChain.name)) {
return false;
}
return true;
ui/src/app/locale/locale.constant.js 5(+3 -2)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 9c0cd12..f366644 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -1175,7 +1175,7 @@ export default angular.module('thingsboard.locale', [])
"export": "Export rule chain",
"export-failed-error": "Unable to export rule chain: {{error}}",
"create-new-rulechain": "Create new rule chain",
- "rule-file": "Rule chain file",
+ "rulechain-file": "Rule chain file",
"invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.",
"copyId": "Copy rule chain Id",
"idCopiedMessage": "Rule chain Id has been copied to clipboard",
@@ -1219,7 +1219,8 @@ export default angular.module('thingsboard.locale', [])
"type-rule-chain": "Rule Chain",
"type-rule-chain-details": "Forwards incoming messages to specified Rule Chain",
"directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.",
- "ui-resources-load-error": "Failed to load configuration ui resources."
+ "ui-resources-load-error": "Failed to load configuration ui resources.",
+ "invalid-target-rulechain": "Unable to resolve target rule chain!"
},
"rule-plugin": {
"management": "Rules and plugins management"
ui/src/app/rulechain/rulechain.controller.js 288(+214 -74)
diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js
index 14cf798..c20fa44 100644
--- a/ui/src/app/rulechain/rulechain.controller.js
+++ b/ui/src/app/rulechain/rulechain.controller.js
@@ -28,7 +28,7 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
-export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $window, $document, $mdDialog,
+export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $window, $document, $mdDialog,
$filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants,
ruleChain, ruleChainMetaData, ruleNodeComponents) {
@@ -37,6 +37,22 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
vm.$mdExpansionPanel = $mdExpansionPanel;
vm.types = types;
+ if ($state.current.data.import && !ruleChain) {
+ $state.go('home.ruleChains');
+ return;
+ }
+
+ vm.isImport = $state.current.data.import;
+ vm.isConfirmOnExit = false;
+
+ $scope.$watch(function() {
+ return vm.isDirty || vm.isImport;
+ }, (val) => {
+ vm.isConfirmOnExit = val;
+ });
+
+ vm.errorTooltips = {};
+
vm.isFullscreen = false;
vm.editingRuleNode = null;
@@ -151,6 +167,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
$scope.$broadcast('form-submit');
if (theForm.$valid) {
theForm.$setPristine();
+ if (vm.editingRuleNode.error) {
+ delete vm.editingRuleNode.error;
+ }
vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode;
vm.editingRuleNode = angular.copy(vm.editingRuleNode);
updateRuleNodesHighlight();
@@ -210,7 +229,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
}
var instances = angular.element.tooltipster.instances();
instances.forEach((instance) => {
- instance.destroy();
+ if (!instance.isErrorTooltip) {
+ instance.destroy();
+ }
});
}
@@ -256,6 +277,71 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
}, 500);
}
+ function updateNodeErrorTooltip(node) {
+ if (node.error) {
+ var element = angular.element('#' + node.id);
+ var tooltip = vm.errorTooltips[node.id];
+ if (!tooltip || !element.hasClass("tooltipstered")) {
+ element.tooltipster(
+ {
+ theme: 'tooltipster-shadow',
+ delay: 0,
+ animationDuration: 0,
+ trigger: 'custom',
+ triggerOpen: {
+ click: false,
+ tap: false
+ },
+ triggerClose: {
+ click: false,
+ tap: false,
+ scroll: false
+ },
+ side: 'top',
+ trackOrigin: true
+ }
+ );
+ var content = '<div class="tb-rule-node-error-tooltip">' +
+ '<div id="tooltip-content" layout="column">' +
+ '<div class="tb-node-details">' + node.error + '</div>' +
+ '</div>' +
+ '</div>';
+ var contentElement = angular.element(content);
+ $compile(contentElement)($scope);
+ tooltip = element.tooltipster('instance');
+ tooltip.isErrorTooltip = true;
+ tooltip.content(contentElement);
+ vm.errorTooltips[node.id] = tooltip;
+ }
+ $mdUtil.nextTick(() => {
+ tooltip.open();
+ });
+ } else {
+ if (vm.errorTooltips[node.id]) {
+ tooltip = vm.errorTooltips[node.id];
+ tooltip.destroy();
+ delete vm.errorTooltips[node.id];
+ }
+ }
+ }
+
+ function updateErrorTooltips(hide) {
+ for (var nodeId in vm.errorTooltips) {
+ var tooltip = vm.errorTooltips[nodeId];
+ if (hide) {
+ tooltip.close();
+ } else {
+ tooltip.open();
+ }
+ }
+ }
+
+ $scope.$watch(function() {
+ return vm.isEditingRuleNode || vm.isEditingRuleNodeLink;
+ }, (val) => {
+ updateErrorTooltips(val);
+ });
+
vm.editCallbacks = {
edgeDoubleClick: function (event, edge) {
var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
@@ -381,6 +467,16 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize(true);
}
}
+ for (componentType in vm.ruleNodeTypesModel) {
+ var panel = vm.$mdExpansionPanel(componentType);
+ if (panel) {
+ if (!vm.ruleNodeTypesModel[componentType].model.nodes.length) {
+ panel.collapse();
+ } else {
+ panel.expand();
+ }
+ }
+ }
});
}
@@ -512,11 +608,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
ruleChainNode = {
id: 'rule-chain-node-' + vm.nextNodeID++,
additionalInfo: ruleChainConnection.additionalInfo,
- targetRuleChainId: ruleChainConnection.targetRuleChainId.id,
x: ruleChainConnection.additionalInfo.layoutX,
y: ruleChainConnection.additionalInfo.layoutY,
component: types.ruleChainNodeComponent,
- name: ruleChain.name,
nodeClass: vm.types.ruleNodeType.RULE_CHAIN.nodeClass,
icon: vm.types.ruleNodeType.RULE_CHAIN.icon,
connectors: [
@@ -526,6 +620,14 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
}
]
};
+ if (ruleChain.name) {
+ ruleChainNode.name = ruleChain.name;
+ ruleChainNode.targetRuleChainId = ruleChainConnection.targetRuleChainId.id;
+ } else {
+ ruleChainNode.name = "Unresolved";
+ ruleChainNode.targetRuleChainId = null;
+ ruleChainNode.error = $translate.instant('rulenode.invalid-target-rulechain');
+ }
ruleChainNodesMap[ruleChainConnection.additionalInfo.ruleChainNodeId] = ruleChainNode;
vm.ruleChainModel.nodes.push(ruleChainNode);
}
@@ -553,11 +655,16 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
updateRuleNodesHighlight();
+ validate();
+
$mdUtil.nextTick(() => {
vm.ruleChainWatch = $scope.$watch('vm.ruleChainModel',
function (newVal, oldVal) {
- if (!vm.isDirty && !angular.equals(newVal, oldVal)) {
- vm.isDirty = true;
+ if (!angular.equals(newVal, oldVal)) {
+ validate();
+ if (!vm.isDirty) {
+ vm.isDirty = true;
+ }
}
}, true
);
@@ -565,91 +672,122 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
}
function updateRuleNodesHighlight() {
- for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
+ for (var i = 0; i < vm.ruleChainModel.nodes.length; i++) {
vm.ruleChainModel.nodes[i].highlighted = false;
}
if ($scope.searchConfig.searchText) {
var res = $filter('filter')(vm.ruleChainModel.nodes, {name: $scope.searchConfig.searchText});
if (res) {
- for (i=0;i<res.length;i++) {
+ for (i = 0; i < res.length; i++) {
res[i].highlighted = true;
}
}
}
}
+ function validate() {
+ $mdUtil.nextTick(() => {
+ vm.isInvalid = false;
+ for (var i = 0; i < vm.ruleChainModel.nodes.length; i++) {
+ if (vm.ruleChainModel.nodes[i].error) {
+ vm.isInvalid = true;
+ }
+ updateNodeErrorTooltip(vm.ruleChainModel.nodes[i]);
+ }
+ });
+ }
+
function saveRuleChain() {
- var ruleChainMetaData = {
- ruleChainId: vm.ruleChain.id,
- nodes: [],
- connections: [],
- ruleChainConnections: []
- };
+ var saveRuleChainPromise;
+ if (vm.isImport) {
+ saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain);
+ } else {
+ saveRuleChainPromise = $q.when(vm.ruleChain);
+ }
+ saveRuleChainPromise.then(
+ (ruleChain) => {
+ vm.ruleChain = ruleChain;
+ var ruleChainMetaData = {
+ ruleChainId: vm.ruleChain.id,
+ nodes: [],
+ connections: [],
+ ruleChainConnections: []
+ };
- var nodes = [];
+ var nodes = [];
- for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
- var node = vm.ruleChainModel.nodes[i];
- if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) {
- var ruleNode = {};
- if (node.ruleNodeId) {
- ruleNode.id = node.ruleNodeId;
+ for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
+ var node = vm.ruleChainModel.nodes[i];
+ if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) {
+ var ruleNode = {};
+ if (node.ruleNodeId) {
+ ruleNode.id = node.ruleNodeId;
+ }
+ ruleNode.type = node.component.clazz;
+ ruleNode.name = node.name;
+ ruleNode.configuration = node.configuration;
+ ruleNode.additionalInfo = node.additionalInfo;
+ ruleNode.debugMode = node.debugMode;
+ if (!ruleNode.additionalInfo) {
+ ruleNode.additionalInfo = {};
+ }
+ ruleNode.additionalInfo.layoutX = node.x;
+ ruleNode.additionalInfo.layoutY = node.y;
+ ruleChainMetaData.nodes.push(ruleNode);
+ nodes.push(node);
+ }
}
- ruleNode.type = node.component.clazz;
- ruleNode.name = node.name;
- ruleNode.configuration = node.configuration;
- ruleNode.additionalInfo = node.additionalInfo;
- ruleNode.debugMode = node.debugMode;
- if (!ruleNode.additionalInfo) {
- ruleNode.additionalInfo = {};
+ var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId});
+ if (res && res.length) {
+ var firstNodeEdge = res[0];
+ var firstNode = vm.modelservice.nodes.getNodeByConnectorId(firstNodeEdge.destination);
+ ruleChainMetaData.firstNodeIndex = nodes.indexOf(firstNode);
}
- ruleNode.additionalInfo.layoutX = node.x;
- ruleNode.additionalInfo.layoutY = node.y;
- ruleChainMetaData.nodes.push(ruleNode);
- nodes.push(node);
- }
- }
- var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId});
- if (res && res.length) {
- var firstNodeEdge = res[0];
- var firstNode = vm.modelservice.nodes.getNodeByConnectorId(firstNodeEdge.destination);
- ruleChainMetaData.firstNodeIndex = nodes.indexOf(firstNode);
- }
- for (i=0;i<vm.ruleChainModel.edges.length;i++) {
- var edge = vm.ruleChainModel.edges[i];
- var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
- var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
- if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
- var fromIndex = nodes.indexOf(sourceNode);
- if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
- var ruleChainConnection = {
- fromIndex: fromIndex,
- targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId},
- additionalInfo: destNode.additionalInfo,
- type: edge.label
- };
- if (!ruleChainConnection.additionalInfo) {
- ruleChainConnection.additionalInfo = {};
+ for (i=0;i<vm.ruleChainModel.edges.length;i++) {
+ var edge = vm.ruleChainModel.edges[i];
+ var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
+ var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
+ if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
+ var fromIndex = nodes.indexOf(sourceNode);
+ if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
+ var ruleChainConnection = {
+ fromIndex: fromIndex,
+ targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId},
+ additionalInfo: destNode.additionalInfo,
+ type: edge.label
+ };
+ if (!ruleChainConnection.additionalInfo) {
+ ruleChainConnection.additionalInfo = {};
+ }
+ ruleChainConnection.additionalInfo.layoutX = destNode.x;
+ ruleChainConnection.additionalInfo.layoutY = destNode.y;
+ ruleChainConnection.additionalInfo.ruleChainNodeId = destNode.id;
+ ruleChainMetaData.ruleChainConnections.push(ruleChainConnection);
+ } else {
+ var toIndex = nodes.indexOf(destNode);
+ var nodeConnection = {
+ fromIndex: fromIndex,
+ toIndex: toIndex,
+ type: edge.label
+ };
+ ruleChainMetaData.connections.push(nodeConnection);
+ }
}
- ruleChainConnection.additionalInfo.layoutX = destNode.x;
- ruleChainConnection.additionalInfo.layoutY = destNode.y;
- ruleChainConnection.additionalInfo.ruleChainNodeId = destNode.id;
- ruleChainMetaData.ruleChainConnections.push(ruleChainConnection);
- } else {
- var toIndex = nodes.indexOf(destNode);
- var nodeConnection = {
- fromIndex: fromIndex,
- toIndex: toIndex,
- type: edge.label
- };
- ruleChainMetaData.connections.push(nodeConnection);
}
- }
- }
- ruleChainService.saveRuleChainMetaData(ruleChainMetaData).then(
- (ruleChainMetaData) => {
- vm.ruleChainMetaData = ruleChainMetaData;
- prepareRuleChain();
+ ruleChainService.saveRuleChainMetaData(ruleChainMetaData).then(
+ (ruleChainMetaData) => {
+ vm.ruleChainMetaData = ruleChainMetaData;
+ if (vm.isImport) {
+ vm.isDirty = false;
+ vm.isImport = false;
+ $mdUtil.nextTick(() => {
+ $state.go('home.ruleChains.ruleChain', {ruleChainId: vm.ruleChain.id.id});
+ });
+ } else {
+ prepareRuleChain();
+ }
+ }
+ );
}
);
}
@@ -662,12 +800,14 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration);
+ var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null;
+
$mdDialog.show({
controller: 'AddRuleNodeController',
controllerAs: 'vm',
templateUrl: addRuleNodeTemplate,
parent: angular.element($document[0].body),
- locals: {ruleNode: ruleNode, ruleChainId: vm.ruleChain.id.id},
+ locals: {ruleNode: ruleNode, ruleChainId: ruleChainId},
fullscreen: true,
targetEvent: $event
}).then(function (ruleNode) {
ui/src/app/rulechain/rulechain.routes.js 41(+41 -0)
diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js
index 48559d9..2aefd82 100644
--- a/ui/src/app/rulechain/rulechain.routes.js
+++ b/ui/src/app/rulechain/rulechain.routes.js
@@ -76,11 +76,52 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
}
},
data: {
+ import: false,
searchEnabled: true,
pageTitle: 'rulechain.rulechain'
},
ncyBreadcrumb: {
label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
}
+ }).state('home.ruleChains.importRuleChain', {
+ url: '/ruleChain/import',
+ reloadOnSearch: false,
+ module: 'private',
+ auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
+ views: {
+ "content@home": {
+ templateUrl: ruleChainTemplate,
+ controller: 'RuleChainController',
+ controllerAs: 'vm'
+ }
+ },
+ params: {
+ ruleChainImport: {}
+ },
+ resolve: {
+ ruleChain:
+ /*@ngInject*/
+ function($stateParams) {
+ return $stateParams.ruleChainImport.ruleChain;
+ },
+ ruleChainMetaData:
+ /*@ngInject*/
+ function($stateParams) {
+ return $stateParams.ruleChainImport.metadata;
+ },
+ ruleNodeComponents:
+ /*@ngInject*/
+ function($stateParams, ruleChainService) {
+ return ruleChainService.getRuleNodeComponents();
+ }
+ },
+ data: {
+ import: true,
+ searchEnabled: true,
+ pageTitle: 'rulechain.rulechain'
+ },
+ ncyBreadcrumb: {
+ label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
+ }
});
}
ui/src/app/rulechain/rulechain.scss 17(+12 -5)
diff --git a/ui/src/app/rulechain/rulechain.scss b/ui/src/app/rulechain/rulechain.scss
index 850de3c..187a722 100644
--- a/ui/src/app/rulechain/rulechain.scss
+++ b/ui/src/app/rulechain/rulechain.scss
@@ -125,13 +125,16 @@
color: #333;
border: solid 1px #777;
font-size: 12px;
- &.tb-rule-node-highlighted {
+ &.tb-rule-node-highlighted:not(.tb-rule-node-invalid) {
box-shadow: 0 0 10px 6px #51cbee;
.tb-node-title {
text-decoration: underline;
font-weight: bold;
}
}
+ &.tb-rule-node-invalid {
+ box-shadow: 0 0 10px 6px #ff5c50;
+ }
&.tb-input-type {
background-color: #a3eaa9;
user-select: none;
@@ -191,10 +194,6 @@
bottom: 0;
background-color: #000;
opacity: 0;
-/* &.tb-rule-node-highlighted {
- background-color: green;
- opacity: 0.15;
- }*/
}
&.fc-hover {
.fc-node-overlay {
@@ -391,6 +390,14 @@
font-size: 14px;
width: 300px;
color: #333;
+}
+
+.tb-rule-node-error-tooltip {
+ font-size: 16px;
+ color: #ea0d0d;
+}
+
+.tb-rule-node-tooltip, .tb-rule-node-error-tooltip {
#tooltip-content {
.tb-node-title {
font-weight: 600;
diff --git a/ui/src/app/rulechain/rulechain.tpl.html b/ui/src/app/rulechain/rulechain.tpl.html
index a843b03..37fcd9b 100644
--- a/ui/src/app/rulechain/rulechain.tpl.html
+++ b/ui/src/app/rulechain/rulechain.tpl.html
@@ -16,7 +16,7 @@
-->
-<md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isDirty"
+<md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isConfirmOnExit"
expand-tooltip-direction="bottom" layout="column" class="tb-rulechain"
ng-keydown="vm.keyDown($event)"
ng-keyup="vm.keyUp($event)" on-fullscreen-changed="vm.isFullscreen = expanded">
@@ -185,7 +185,7 @@
</md-tooltip>
<ng-md-icon icon="delete"></ng-md-icon>
</md-button>
- <md-button ng-disabled="$root.loading || !vm.isDirty"
+ <md-button ng-disabled="$root.loading || vm.isInvalid || (!vm.isDirty && !vm.isImport)"
class="tb-btn-footer md-accent md-hue-2 md-fab"
aria-label="{{ 'action.apply' | translate }}"
ng-click="vm.saveRuleChain()">
diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js
index 2a30b0c..7c857f2 100644
--- a/ui/src/app/rulechain/rulechains.controller.js
+++ b/ui/src/app/rulechain/rulechains.controller.js
@@ -63,8 +63,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
{
onAction: function ($event) {
importExport.importRuleChain($event).then(
- function() {
- vm.grid.refreshList();
+ function(ruleChainImport) {
+ $state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport});
}
);
},
diff --git a/ui/src/app/rulechain/rulenode.tpl.html b/ui/src/app/rulechain/rulenode.tpl.html
index 83ce988..973ea1f 100644
--- a/ui/src/app/rulechain/rulenode.tpl.html
+++ b/ui/src/app/rulechain/rulenode.tpl.html
@@ -22,8 +22,8 @@
ng-mousedown="callbacks.mouseDown($event, node)"
ng-mouseenter="callbacks.mouseEnter($event, node)"
ng-mouseleave="callbacks.mouseLeave($event, node)">
- <div class="{{flowchartConstants.nodeOverlayClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}"></div>
- <div class="tb-rule-node {{node.nodeClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}">
+ <div class="{{flowchartConstants.nodeOverlayClass}}"></div>
+ <div class="tb-rule-node {{node.nodeClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted, 'tb-rule-node-invalid': node.error }">
<md-icon aria-label="node-type-icon" flex="15"
class="material-icons">{{node.icon}}</md-icon>
<div layout="column" flex="85" layout-align="center">