Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into develop/3.0

pull/2486/head
Igor Kulikov 7 years ago
parent
commit
25ba81394d
  1. 4
      ui/src/app/api/widget.service.js
  2. 5
      ui/src/app/common/types.constant.js
  3. 32
      ui/src/app/components/json-content.directive.js
  4. 2
      ui/src/app/components/json-content.scss
  5. 8
      ui/src/app/components/json-content.tpl.html
  6. 10
      ui/src/app/components/json-object-edit.directive.js
  7. 13
      ui/src/app/components/json-object-edit.scss
  8. 8
      ui/src/app/components/json-object-edit.tpl.html
  9. 45
      ui/src/app/components/tb-json-to-string.directive.js
  10. 38
      ui/src/app/entity/attribute/add-attribute-dialog.controller.js
  11. 34
      ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
  12. 1
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
  13. 20
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
  14. 35
      ui/src/app/entity/attribute/attribute-dialog-edit-json.controller.js
  15. 24
      ui/src/app/entity/attribute/attribute-dialog-edit-json.scss
  16. 54
      ui/src/app/entity/attribute/attribute-dialog-edit-json.tpl.html
  17. 143
      ui/src/app/entity/attribute/attribute-table.directive.js
  18. 8
      ui/src/app/entity/attribute/attribute-table.scss
  19. 4
      ui/src/app/entity/attribute/attribute-table.tpl.html
  20. 58
      ui/src/app/entity/attribute/edit-attribute-value.controller.js
  21. 30
      ui/src/app/entity/attribute/edit-attribute-value.tpl.html
  22. 2
      ui/src/app/locale/locale.constant-el_GR.json
  23. 8
      ui/src/app/locale/locale.constant-en_US.json
  24. 4
      ui/src/app/locale/locale.constant-ru_RU.json
  25. 4
      ui/src/app/locale/locale.constant-uk_UA.json

4
ui/src/app/api/widget.service.js

@ -29,6 +29,8 @@ import thingsboardWebCameraInputWidget from '../widget/lib/web-camera-input-widg
import thingsboardRpcWidgets from '../widget/lib/rpc'; import thingsboardRpcWidgets from '../widget/lib/rpc';
import thingsboardJsonToString from '../components/tb-json-to-string.directive';
import TbFlot from '../widget/lib/flot-widget'; import TbFlot from '../widget/lib/flot-widget';
import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge'; import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge';
import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge'; import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge';
@ -52,7 +54,7 @@ export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsbo
thingsboardTimeseriesTableWidget, thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardTimeseriesTableWidget, thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget,
thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget, thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget,
thingsboardMultipleInputWidget, thingsboardWebCameraInputWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardMultipleInputWidget, thingsboardWebCameraInputWidget, thingsboardRpcWidgets, thingsboardTypes,
thingsboardUtils, TripAnimationWidget]) thingsboardUtils, thingsboardJsonToString, TripAnimationWidget])
.factory('widgetService', WidgetService) .factory('widgetService', WidgetService)
.name; .name;

5
ui/src/app/common/types.constant.js

@ -881,6 +881,11 @@ export default angular.module('thingsboard.types', [])
value: "boolean", value: "boolean",
name: "value.boolean", name: "value.boolean",
icon: "mdi:checkbox-marked-outline" icon: "mdi:checkbox-marked-outline"
},
json: {
value: "json",
name: "value.json",
icon: "mdi:json"
} }
}, },
widgetType: { widgetType: {

32
ui/src/app/components/json-content.directive.js

@ -57,9 +57,12 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
updateEditorSize(); updateEditorSize();
}; };
scope.beautifyJson = function () { scope.beautifyJSON = function () {
var res = js_beautify(scope.contentBody, {indent_size: 4, wrap_line_length: 60}); scope.contentBody = js_beautify(scope.contentBody, {indent_size: 4, wrap_line_length: 60});
scope.contentBody = res; };
scope.minifyJSON = function () {
scope.contentBody = angular.toJson(angular.fromJson(scope.contentBody));
}; };
function updateEditorSize() { function updateEditorSize() {
@ -116,7 +119,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
scope.$watch('contentBody', function (newContent, oldContent) { scope.$watch('contentBody', function (newContent, oldContent) {
ngModelCtrl.$setViewValue(scope.contentBody); ngModelCtrl.$setViewValue(scope.contentBody);
if (!angular.equals(newContent, oldContent)) { if (!angular.equals(newContent, oldContent)) {
scope.contentValid = true; scope.contentValid = scope.validate();
} }
scope.updateValidity(); scope.updateValidity();
}); });
@ -139,15 +142,17 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
} }
return true; return true;
} catch (e) { } catch (e) {
var details = utils.parseException(e); if (!scope.hideErrorToast) {
var errorInfo = 'Error:'; var details = utils.parseException(e);
if (details.name) { var errorInfo = 'Error:';
errorInfo += ' ' + details.name + ':'; if (details.name) {
} errorInfo += ' ' + details.name + ':';
if (details.message) { }
errorInfo += ' ' + details.message; if (details.message) {
errorInfo += ' ' + details.message;
}
scope.showError(errorInfo);
} }
scope.showError(errorInfo);
return false; return false;
} }
}; };
@ -169,7 +174,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
}); });
$compile(element.contents())(scope); $compile(element.contents())(scope);
} };
return { return {
restrict: "E", restrict: "E",
@ -177,6 +182,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
scope: { scope: {
contentType: '=', contentType: '=',
validateContent: '=?', validateContent: '=?',
hideErrorToast: '=?',
readonly:'=ngReadonly', readonly:'=ngReadonly',
fillHeight:'=?' fillHeight:'=?'
}, },

2
ui/src/app/components/json-content.scss

@ -27,7 +27,7 @@ tb-json-content {
min-height: 15px; min-height: 15px;
padding: 4px; padding: 4px;
margin: 0 5px 0 0; margin: 0 5px 0 0;
font-size: .8rem; font-size: 12px;
line-height: 15px; line-height: 15px;
color: #7b7b7b; color: #7b7b7b;
background: rgba(220, 220, 220, .35); background: rgba(220, 220, 220, .35);

8
ui/src/app/components/json-content.tpl.html

@ -17,11 +17,15 @@
--> -->
<div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column"> <div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column">
<div layout="row" layout-align="start center" style="height: 40px;" class="tb-json-content-toolbar"> <div layout="row" layout-align="start center" style="height: 40px;" class="tb-json-content-toolbar">
<label class="tb-title no-padding">{{ label }}</label> <label class="tb-title no-padding"
ng-class="{'tb-error': !contentValid}">{{ label }}</label>
<span flex></span> <span flex></span>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJson()">{{ <md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJSON()">{{
'js-func.tidy' | translate }} 'js-func.tidy' | translate }}
</md-button> </md-button>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.mini' | translate }}" ng-click="minifyJSON()">{{
'js-func.mini' | translate }}
</md-button>
<md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button> <md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
</div> </div>
<div flex id="tb-json-panel" class="tb-json-content-panel" layout="column"> <div flex id="tb-json-panel" class="tb-json-content-panel" layout="column">

10
ui/src/app/components/json-object-edit.directive.js

@ -50,6 +50,14 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) {
updateEditorSize(); updateEditorSize();
}; };
scope.beautifyJSON = function () {
scope.contentBody = angular.toJson(scope.object, 4);
};
scope.minifyJSON = function () {
scope.contentBody = angular.toJson(scope.object);
};
function updateEditorSize() { function updateEditorSize() {
if (scope.json_editor) { if (scope.json_editor) {
scope.json_editor.resize(); scope.json_editor.resize();
@ -169,7 +177,7 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) {
}); });
$compile(element.contents())(scope); $compile(element.contents())(scope);
} };
return { return {
restrict: "E", restrict: "E",

13
ui/src/app/components/json-object-edit.scss

@ -21,6 +21,19 @@ tb-json-object-edit {
} }
} }
.tb-json-object-edit-toolbar {
.md-button.tidy {
min-width: 32px;
min-height: 15px;
padding: 4px;
margin: 0 5px 0 0;
font-size: 12px;
line-height: 15px;
color: #7b7b7b;
background: rgba(220, 220, 220, .35);
}
}
.tb-json-object-panel { .tb-json-object-panel {
height: 100%; height: 100%;
margin-left: 15px; margin-left: 15px;

8
ui/src/app/components/json-object-edit.tpl.html

@ -16,12 +16,18 @@
--> -->
<div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column"> <div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column">
<div layout="row" layout-align="start center"> <div layout="row" layout-align="start center" class="tb-json-object-edit-toolbar">
<label class="tb-title no-padding" <label class="tb-title no-padding"
ng-class="{'tb-required': required, ng-class="{'tb-required': required,
'tb-readonly': readonly, 'tb-readonly': readonly,
'tb-error': !objectValid}">{{ label }}</label> 'tb-error': !objectValid}">{{ label }}</label>
<span flex></span> <span flex></span>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJSON()">
{{'js-func.tidy' | translate }}
</md-button>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.mini' | translate }}" ng-click="minifyJSON()">
{{'js-func.mini' | translate }}
</md-button>
<md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button> <md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
</div> </div>
<div flex id="tb-json-panel" class="tb-json-object-panel" layout="column"> <div flex id="tb-json-panel" class="tb-json-object-panel" layout="column">

45
ui/src/app/components/tb-json-to-string.directive.js

@ -0,0 +1,45 @@
/*
* 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.
*/
export default angular.module('tbJsonToString', [])
.directive('tbJsonToString', InputJson)
.name;
function InputJson() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModelCtrl) {
function into(input) {
try {
ngModelCtrl.$setValidity('invalidJSON', true);
return angular.fromJson(input);
} catch (e) {
ngModelCtrl.$setValidity('invalidJSON', false);
}
}
function out(data) {
try {
ngModelCtrl.$setValidity('invalidJSON', true);
return angular.toJson(data);
} catch (e) {
ngModelCtrl.$setValidity('invalidJSON', false);
}
}
ngModelCtrl.$parsers.push(into);
ngModelCtrl.$formatters.push(out);
}
};
}

38
ui/src/app/entity/attribute/add-attribute-dialog.controller.js

@ -13,10 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* eslint-disable import/no-unresolved, import/default */
import attributeDialogEditJsonTemplate from './attribute-dialog-edit-json.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
import AttributeDialogEditJsonController from './attribute-dialog-edit-json.controller';
/*@ngInject*/ /*@ngInject*/
export default function AddAttributeDialogController($scope, $mdDialog, types, attributeService, entityType, entityId, attributeScope) { export default function AddAttributeDialogController($scope, $mdDialog, types, attributeService, entityType, entityId, attributeScope) {
var vm = this; let vm = this;
vm.attribute = {}; vm.attribute = {};
@ -40,11 +48,37 @@ export default function AddAttributeDialogController($scope, $mdDialog, types, a
); );
} }
$scope.$watch('vm.valueType', function() { $scope.$watch('vm.valueType', function () {
if (vm.valueType === types.valueType.boolean) { if (vm.valueType === types.valueType.boolean) {
vm.attribute.value = false; vm.attribute.value = false;
} else if (vm.valueType === types.valueType.json) {
vm.attribute.value = {};
} else { } else {
vm.attribute.value = null; vm.attribute.value = null;
} }
}); });
vm.addJSON = ($event) => {
showJsonDialog($event, vm.attribute.value, false).then((response) => {
vm.attribute.value = response;
})
};
function showJsonDialog($event, jsonValue, readOnly) {
if ($event) {
$event.stopPropagation();
}
return $mdDialog.show({
controller: AttributeDialogEditJsonController,
controllerAs: 'vm',
templateUrl: attributeDialogEditJsonTemplate,
locals: {
jsonValue: jsonValue,
readOnly: readOnly
},
targetEvent: $event,
fullscreen: true,
multiple: true,
});
}
} }

34
ui/src/app/entity/attribute/add-attribute-dialog.tpl.html

@ -26,7 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> <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> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
@ -58,7 +59,8 @@
</md-input-container> </md-input-container>
<md-input-container ng-if="vm.valueType===vm.valueTypes.integer" flex="60" class="md-block"> <md-input-container ng-if="vm.valueType===vm.valueTypes.integer" flex="60" class="md-block">
<label translate>value.integer-value</label> <label translate>value.integer-value</label>
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="vm.attribute.value"> <input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/"
ng-model="vm.attribute.value">
<div ng-messages="theForm.value.$error"> <div ng-messages="theForm.value.$error">
<div translate ng-message="required">attribute.value-required</div> <div translate ng-message="required">attribute.value-required</div>
<div translate ng-message="pattern">value.invalid-integer-value</div> <div translate ng-message="pattern">value.invalid-integer-value</div>
@ -71,11 +73,31 @@
<div translate ng-message="required">attribute.value-required</div> <div translate ng-message="required">attribute.value-required</div>
</div> </div>
</md-input-container> </md-input-container>
<div layout="column" layout-align="center" flex="60" ng-if="vm.valueType===vm.valueTypes.boolean"> <div layout="column" layout-align="center" flex="60"
<md-checkbox ng-model="vm.attribute.value" style="margin-bottom: 0px;"> ng-if="vm.valueType===vm.valueTypes.boolean">
<md-checkbox ng-model="vm.attribute.value" style="margin-bottom: 0;">
{{ (vm.attribute.value ? 'value.true' : 'value.false') | translate }} {{ (vm.attribute.value ? 'value.true' : 'value.false') | translate }}
</md-checkbox> </md-checkbox>
</div> </div>
<div layout="row" layout-align="center" flex="60"
ng-if="vm.valueType===vm.valueTypes.json" class="md-block">
<md-input-container flex class="md-block">
<label translate>value.json-value</label>
<input required tb-json-to-string name="value" ng-model="vm.attribute.value">
<div ng-messages="theForm.value.$error">
<div translate ng-message="required">attribute.value-required</div>
</div>
</md-input-container>
<md-button class="md-icon-button"
ng-click="vm.addJSON($event)"
style="margin: 18px 0;"
aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.edit' | translate }}
</md-tooltip>
<ng-md-icon size="20" icon="open_in_new"></ng-md-icon>
</md-button>
</div>
</section> </section>
</fieldset> </fieldset>
</md-content> </md-content>
@ -87,8 +109,8 @@
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>

1
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js

@ -151,5 +151,4 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
} }
); );
} }
} }

20
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html

@ -26,7 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> <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> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
@ -37,10 +38,10 @@
<section flex layout="column" style="width: 300px;"> <section flex layout="column" style="width: 300px;">
<span translate style="padding-bottom: 10px;">dashboard.select-existing</span> <span translate style="padding-bottom: 10px;">dashboard.select-existing</span>
<tb-dashboard-autocomplete the-form="theForm" <tb-dashboard-autocomplete the-form="theForm"
ng-disabled="$root.loading || vm.addToDashboardType != 0" ng-disabled="$root.loading || vm.addToDashboardType != 0"
tb-required="vm.addToDashboardType === 0" tb-required="vm.addToDashboardType === 0"
ng-model="vm.dashboardId" ng-model="vm.dashboardId"
select-first-dashboard="false"> select-first-dashboard="false">
</tb-dashboard-autocomplete> </tb-dashboard-autocomplete>
</section> </section>
</md-radio-button> </md-radio-button>
@ -49,7 +50,8 @@
<span translate>dashboard.create-new</span> <span translate>dashboard.create-new</span>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>dashboard.new-dashboard-title</label> <label translate>dashboard.new-dashboard-title</label>
<input ng-required="vm.addToDashboardType === 1" name="title" ng-model="vm.newDashboard.title"> <input ng-required="vm.addToDashboardType === 1" name="title"
ng-model="vm.newDashboard.title">
<div ng-messages="theForm.title.$error"> <div ng-messages="theForm.title.$error">
<div translate ng-message="required">dashboard.title-required</div> <div translate ng-message="required">dashboard.title-required</div>
</div> </div>
@ -66,15 +68,15 @@
<md-checkbox <md-checkbox
ng-model="vm.openDashboard" ng-model="vm.openDashboard"
aria-label="{{ 'dashboard.open-dashboard' | translate }}" aria-label="{{ 'dashboard.open-dashboard' | translate }}"
style="margin-bottom: 0px; padding-right: 20px;"> style="margin-bottom: 0; padding-right: 20px;">
{{ 'dashboard.open-dashboard' | translate }} {{ 'dashboard.open-dashboard' | translate }}
</md-checkbox> </md-checkbox>
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>

35
ui/src/app/entity/attribute/attribute-dialog-edit-json.controller.js

@ -0,0 +1,35 @@
/*
* 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.
*/
/* eslint-enable import/no-unresolved, import/default */
import './attribute-dialog-edit-json.scss';
/*@ngInject*/
export default function AttributeDialogEditJsonController($mdDialog, types, jsonValue, readOnly) {
let vm = this;
vm.json = angular.toJson(jsonValue, 4);
vm.readOnly = readOnly;
vm.contentType = types.contentType.JSON.value;
vm.save = () => {
$mdDialog.hide(angular.fromJson(vm.json));
};
vm.cancel = () => {
$mdDialog.cancel();
};
}

24
ui/src/app/entity/attribute/attribute-dialog-edit-json.scss

@ -0,0 +1,24 @@
/**
* 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.
*/
.attribute-edit-json-dialog{
min-width: 400px;
}
@media (max-width: 425px){
.attribute-edit-json-dialog {
min-width: 200px;
}
}

54
ui/src/app/entity/attribute/attribute-dialog-edit-json.tpl.html

@ -0,0 +1,54 @@
<!--
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.
-->
<md-dialog class="attribute-edit-json-dialog">
<form name="theForm" ng-submit="vm.save()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>{{ 'details.edit-json' | translate }}</h2>
<span flex></span>
<md-button class="md-icon-button"
ng-click="vm.cancel()"
aria-label="{{'action.close'|translate}}">
<ng-md-icon icon="close" aria-label="Close"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content">
<tb-json-content
ng-model="vm.json"
label="{{ 'value.json-value' | translate }}"
content-type="vm.contentType"
validate-content="true"
hide-error-toast="true"
fill-height="false"
ng-readonly="vm.readOnly">
</tb-json-content>
</div>
</md-dialog-content>
<md-dialog-actions layout="row" layout-align="end center" class="action-buttons">
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary">
{{'action.save'|translate}}
</md-button>
<md-button id="cancel-btn" ng-disabled="$root.loading" ng-click="vm.cancel()">
{{'action.cancel'|translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

143
ui/src/app/entity/attribute/attribute-table.directive.js

@ -39,7 +39,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
element.html(template); element.html(template);
var getAttributeScopeByValue = function(attributeScopeValue) { var getAttributeScopeByValue = function (attributeScopeValue) {
if (scope.types.latestTelemetry.value === attributeScopeValue) { if (scope.types.latestTelemetry.value === attributeScopeValue) {
return scope.types.latestTelemetry; return scope.types.latestTelemetry;
} }
@ -48,7 +48,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
return scope.attributeScopes[attrScope]; return scope.attributeScopes[attrScope];
} }
} }
} };
scope.types = types; scope.types = types;
@ -87,14 +87,14 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
search: null search: null
}; };
scope.$watch("entityId", function(newVal) { scope.$watch("entityId", function (newVal) {
if (newVal) { if (newVal) {
scope.resetFilter(); scope.resetFilter();
scope.getEntityAttributes(false, true); scope.getEntityAttributes(false, true);
} }
}); });
scope.$watch("attributeScope", function(newVal, prevVal) { scope.$watch("attributeScope", function (newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) { if (newVal && !angular.equals(newVal, prevVal)) {
scope.mode = 'default'; scope.mode = 'default';
scope.query.search = null; scope.query.search = null;
@ -103,30 +103,30 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
}); });
scope.resetFilter = function() { scope.resetFilter = function () {
scope.mode = 'default'; scope.mode = 'default';
scope.query.search = null; scope.query.search = null;
scope.selectedAttributes = []; scope.selectedAttributes = [];
scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope); scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
} };
scope.enterFilterMode = function(event) { scope.enterFilterMode = function (event) {
let $button = angular.element(event.currentTarget); let $button = angular.element(event.currentTarget);
let $toolbarsContainer = $button.closest('.toolbarsContainer'); let $toolbarsContainer = $button.closest('.toolbarsContainer');
scope.query.search = ''; scope.query.search = '';
$timeout(()=>{ $timeout(() => {
$toolbarsContainer.find('.searchInput').focus(); $toolbarsContainer.find('.searchInput').focus();
}) })
} };
scope.exitFilterMode = function() { scope.exitFilterMode = function () {
scope.query.search = null; scope.query.search = null;
scope.getEntityAttributes(); scope.getEntityAttributes();
} };
scope.$watch("query.search", function(newVal, prevVal) { scope.$watch("query.search", function (newVal, prevVal) {
if (!angular.equals(newVal, prevVal) && scope.query.search != null) { if (!angular.equals(newVal, prevVal) && scope.query.search != null) {
scope.getEntityAttributes(); scope.getEntityAttributes();
} }
@ -142,15 +142,15 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
} }
scope.onReorder = function() { scope.onReorder = function () {
scope.getEntityAttributes(false, false); scope.getEntityAttributes(false, false);
} };
scope.onPaginate = function() { scope.onPaginate = function () {
scope.getEntityAttributes(false, false); scope.getEntityAttributes(false, false);
} };
scope.getEntityAttributes = function(forceUpdate, reset) { scope.getEntityAttributes = function (forceUpdate, reset) {
if (scope.attributesDeferred) { if (scope.attributesDeferred) {
scope.attributesDeferred.resolve(); scope.attributesDeferred.resolve();
} }
@ -163,7 +163,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
scope.checkSubscription(); scope.checkSubscription();
scope.attributesDeferred = attributeService.getEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.attributesDeferred = attributeService.getEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value,
scope.query, function(attributes, update, apply) { scope.query, function (attributes, update, apply) {
success(attributes, update || forceUpdate, apply); success(attributes, update || forceUpdate, apply);
} }
); );
@ -176,9 +176,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}); });
deferred.resolve(); deferred.resolve();
} }
} };
scope.checkSubscription = function() { scope.checkSubscription = function () {
var newSubscriptionId = null; var newSubscriptionId = null;
if (scope.entityId && scope.entityType && scope.attributeScope.clientSide && scope.mode != 'widget') { if (scope.entityId && scope.entityType && scope.attributeScope.clientSide && scope.mode != 'widget') {
newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value); newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value);
@ -187,36 +187,38 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
attributeService.unsubscribeForEntityAttributes(scope.subscriptionId); attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
} }
scope.subscriptionId = newSubscriptionId; scope.subscriptionId = newSubscriptionId;
} };
scope.$on('$destroy', function() { scope.$on('$destroy', function () {
if (scope.subscriptionId) { if (scope.subscriptionId) {
attributeService.unsubscribeForEntityAttributes(scope.subscriptionId); attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
} }
}); });
scope.editAttribute = function($event, attribute) { scope.editAttribute = function ($event, attribute) {
if (!scope.attributeScope.clientSide) { if (!scope.attributeScope.clientSide) {
$event.stopPropagation(); $event.stopPropagation();
$mdEditDialog.show({ $mdEditDialog.show({
controller: EditAttributeValueController, controller: EditAttributeValueController,
templateUrl: editAttributeValueTemplate, templateUrl: editAttributeValueTemplate,
locals: {attributeValue: attribute.value, locals: {
save: function (model) { attributeValue: attribute.value,
var updatedAttribute = angular.copy(attribute); save: function (model) {
updatedAttribute.value = model.value; var updatedAttribute = angular.copy(attribute);
attributeService.saveEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, [updatedAttribute]).then( updatedAttribute.value = model.value;
function success() { attributeService.saveEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, [updatedAttribute]).then(
scope.getEntityAttributes(); function success() {
} scope.getEntityAttributes();
); }
}}, );
}
},
targetEvent: $event targetEvent: $event
}); });
} }
} };
scope.addAttribute = function($event) { scope.addAttribute = function ($event) {
if (!scope.attributeScope.clientSide) { if (!scope.attributeScope.clientSide) {
$event.stopPropagation(); $event.stopPropagation();
$mdDialog.show({ $mdDialog.show({
@ -224,16 +226,20 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: addAttributeDialogTemplate, templateUrl: addAttributeDialogTemplate,
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
locals: {entityType: scope.entityType, entityId: scope.entityId, attributeScope: scope.attributeScope.value}, locals: {
entityType: scope.entityType,
entityId: scope.entityId,
attributeScope: scope.attributeScope.value
},
fullscreen: true, fullscreen: true,
targetEvent: $event targetEvent: $event
}).then(function () { }).then(function () {
scope.getEntityAttributes(); scope.getEntityAttributes();
}); });
} }
} };
scope.deleteAttributes = function($event) { scope.deleteAttributes = function ($event) {
if (!scope.attributeScope.clientSide) { if (!scope.attributeScope.clientSide) {
$event.stopPropagation(); $event.stopPropagation();
var confirm = $mdDialog.confirm() var confirm = $mdDialog.confirm()
@ -244,33 +250,33 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
.cancel($translate.instant('action.no')) .cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes')); .ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () { $mdDialog.show(confirm).then(function () {
attributeService.deleteEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.selectedAttributes).then( attributeService.deleteEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.selectedAttributes).then(
function success() { function success() {
scope.selectedAttributes = []; scope.selectedAttributes = [];
scope.getEntityAttributes(); scope.getEntityAttributes();
} }
) )
}); });
} }
} };
scope.nextWidget = function() { scope.nextWidget = function () {
$mdUtil.nextTick(function () { $mdUtil.nextTick(function () {
if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) { if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
scope.widgetsCarousel.index++; scope.widgetsCarousel.index++;
} }
}); });
} };
scope.prevWidget = function() { scope.prevWidget = function () {
$mdUtil.nextTick(function () { $mdUtil.nextTick(function () {
if (scope.widgetsCarousel.index > 0) { if (scope.widgetsCarousel.index > 0) {
scope.widgetsCarousel.index--; scope.widgetsCarousel.index--;
} }
}); });
} };
scope.enterWidgetMode = function() { scope.enterWidgetMode = function () {
if (scope.widgetsIndexWatch) { if (scope.widgetsIndexWatch) {
scope.widgetsIndexWatch(); scope.widgetsIndexWatch();
@ -303,7 +309,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
entitiAliases[entityAlias.id] = entityAlias; entitiAliases[entityAlias.id] = entityAlias;
var stateController = { var stateController = {
getStateParams: function() { getStateParams: function () {
return {}; return {};
} }
}; };
@ -317,9 +323,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
type: types.datasourceType.entity, type: types.datasourceType.entity,
entityAliasId: entityAlias.id, entityAliasId: entityAlias.id,
dataKeys: [] dataKeys: []
} };
var i = 0; var i = 0;
for (var attr =0; attr < scope.selectedAttributes.length;attr++) { for (var attr = 0; attr < scope.selectedAttributes.length; attr++) {
var attribute = scope.selectedAttributes[attr]; var attribute = scope.selectedAttributes[attr];
var dataKey = { var dataKey = {
name: attribute.key, name: attribute.key,
@ -328,12 +334,12 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
color: utils.getMaterialColor(i), color: utils.getMaterialColor(i),
settings: {}, settings: {},
_hash: Math.random() _hash: Math.random()
} };
datasource.dataKeys.push(dataKey); datasource.dataKeys.push(dataKey);
i++; i++;
} }
scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function(newVal, prevVal) { scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function (newVal, prevVal) {
if (scope.mode === 'widget' && (newVal != prevVal)) { if (scope.mode === 'widget' && (newVal != prevVal)) {
var index = scope.widgetsCarousel.index; var index = scope.widgetsCarousel.index;
for (var i = 0; i < scope.widgetsList.length; i++) { for (var i = 0; i < scope.widgetsList.length; i++) {
@ -345,7 +351,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
}); });
scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function(newVal, prevVal) { scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function (newVal, prevVal) {
if (scope.mode === 'widget' && (scope.firstBundle === true || newVal != prevVal)) { if (scope.mode === 'widget' && (scope.firstBundle === true || newVal != prevVal)) {
scope.widgetsList = []; scope.widgetsList = [];
scope.widgetsListCache = []; scope.widgetsListCache = [];
@ -358,7 +364,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
widgetService.getBundleWidgetTypes(scope.widgetsBundle.alias, isSystem).then( widgetService.getBundleWidgetTypes(scope.widgetsBundle.alias, isSystem).then(
function success(widgetTypes) { function success(widgetTypes) {
widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type','-createdTime']); widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type', '-createdTime']);
for (var i = 0; i < widgetTypes.length; i++) { for (var i = 0; i < widgetTypes.length; i++) {
var widgetType = widgetTypes[i]; var widgetType = widgetTypes[i];
@ -398,9 +404,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
} }
}); });
} };
scope.exitWidgetMode = function() { scope.exitWidgetMode = function () {
if (scope.widgetsBundleWatch) { if (scope.widgetsBundleWatch) {
scope.widgetsBundleWatch(); scope.widgetsBundleWatch();
scope.widgetsBundleWatch = null; scope.widgetsBundleWatch = null;
@ -412,9 +418,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.selectedWidgetsBundleAlias = null; scope.selectedWidgetsBundleAlias = null;
scope.mode = 'default'; scope.mode = 'default';
scope.getEntityAttributes(true); scope.getEntityAttributes(true);
} };
scope.addWidgetToDashboard = function($event) { scope.addWidgetToDashboard = function ($event) {
if (scope.mode === 'widget' && scope.widgetsListCache.length > 0) { if (scope.mode === 'widget' && scope.widgetsListCache.length > 0) {
var widget = scope.widgetsListCache[scope.widgetsCarousel.index][0]; var widget = scope.widgetsListCache[scope.widgetsCarousel.index][0];
$event.stopPropagation(); $event.stopPropagation();
@ -423,21 +429,26 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
controllerAs: 'vm', controllerAs: 'vm',
templateUrl: addWidgetToDashboardDialogTemplate, templateUrl: addWidgetToDashboardDialogTemplate,
parent: angular.element($document[0].body), parent: angular.element($document[0].body),
locals: {entityId: scope.entityId, entityType: scope.entityType, entityName: scope.entityName, widget: angular.copy(widget)}, locals: {
entityId: scope.entityId,
entityType: scope.entityType,
entityName: scope.entityName,
widget: angular.copy(widget)
},
fullscreen: true, fullscreen: true,
targetEvent: $event targetEvent: $event
}).then(function () { }).then(function () {
}); });
} }
} };
scope.loading = function() { scope.loading = function () {
return $rootScope.loading; return $rootScope.loading;
} };
$compile(element.contents())(scope); $compile(element.contents())(scope);
} };
return { return {
restrict: "E", restrict: "E",

8
ui/src/app/entity/attribute/attribute-table.scss

@ -58,3 +58,11 @@ md-toolbar.md-table-toolbar.alternate {
} }
} }
} }
md-edit-dialog.tb-edit-dialog{
z-index: 78;
}
md-backdrop.md-edit-dialog-backdrop{
z-index: 77;
}

4
ui/src/app/entity/attribute/attribute-table.tpl.html

@ -77,9 +77,7 @@
</md-toolbar> </md-toolbar>
<md-toolbar class="md-table-toolbar alternate" ng-show="mode==='default' && selectedAttributes.length"> <md-toolbar class="md-table-toolbar alternate" ng-show="mode==='default' && selectedAttributes.length">
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
<span translate="{{attributeScope === types.latestTelemetry <span translate="{{(attributeScope === types.latestTelemetry) ? 'attribute.selected-telemetry' : 'attribute.selected-attributes'}}"
? 'attribute.selected-telemetry'
: 'attribute.selected-attributes'}}"
translate-values="{count: selectedAttributes.length}" translate-values="{count: selectedAttributes.length}"
translate-interpolation="messageformat"></span> translate-interpolation="messageformat"></span>
<span flex></span> <span flex></span>

58
ui/src/app/entity/attribute/edit-attribute-value.controller.js

@ -13,14 +13,19 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* eslint-enable import/no-unresolved, import/default */
import AttributeDialogEditJsonController from "./attribute-dialog-edit-json.controller";
import attributeDialogEditJsonTemplate from "./attribute-dialog-edit-json.tpl.html";
/*@ngInject*/ /*@ngInject*/
export default function EditAttributeValueController($scope, $q, $element, types, attributeValue, save) { export default function EditAttributeValueController($scope, $mdDialog, $q, $element, $document, types, attributeValue, save) {
$scope.valueTypes = types.valueType; $scope.valueTypes = types.valueType;
$scope.model = {}; $scope.model = {
value: attributeValue
$scope.model.value = attributeValue; };
if ($scope.model.value === true || $scope.model.value === false) { if ($scope.model.value === true || $scope.model.value === false) {
$scope.valueType = types.valueType.boolean; $scope.valueType = types.valueType.boolean;
@ -30,26 +35,27 @@ export default function EditAttributeValueController($scope, $q, $element, types
} else { } else {
$scope.valueType = types.valueType.double; $scope.valueType = types.valueType.double;
} }
} else if (angular.isObject($scope.model.value)) {
$scope.valueType = types.valueType.json;
} else { } else {
$scope.valueType = types.valueType.string; $scope.valueType = types.valueType.string;
} }
$scope.submit = submit; $scope.submit = submit;
$scope.dismiss = dismiss; $scope.dismiss = dismiss;
$scope.editJSON = editJSON;
function dismiss() { function dismiss() {
$element.remove(); $element.remove();
} }
function update() { function update() {
if($scope.editDialog.$invalid) { if ($scope.editDialog.$invalid) {
return $q.reject(); return $q.reject();
} }
if (angular.isFunction(save)) {
if(angular.isFunction(save)) {
return $q.when(save($scope.model)); return $q.when(save($scope.model));
} }
return $q.resolve(); return $q.resolve();
} }
@ -59,13 +65,45 @@ export default function EditAttributeValueController($scope, $q, $element, types
}); });
} }
$scope.$watch('valueType', function(newVal, prevVal) {
if (newVal != prevVal) { $scope.$watch('valueType', function (newVal, prevVal) {
if (newVal !== prevVal) {
if ($scope.valueType === types.valueType.boolean) { if ($scope.valueType === types.valueType.boolean) {
$scope.model.value = false; $scope.model.value = false;
} else if ($scope.valueType === types.valueType.json) {
$scope.model.value = {};
} else { } else {
$scope.model.value = null; $scope.model.value = null;
} }
} }
}); });
function editJSON($event) {
$scope.hideDialog = true;
showJsonDialog($event, $scope.model.value, false).then((response) => {
$scope.hideDialog = false;
if (!angular.equals(response, $scope.model.value)) {
$scope.editDialog.$setDirty();
}
$scope.model.value = response;
})
}
function showJsonDialog($event, jsonValue, readOnly) {
if ($event) {
$event.stopPropagation();
}
return $mdDialog.show({
controller: AttributeDialogEditJsonController,
controllerAs: 'vm',
templateUrl: attributeDialogEditJsonTemplate,
locals: {
jsonValue: jsonValue,
readOnly: readOnly
},
targetEvent: $event,
fullscreen: true,
multiple: true
});
}
} }

30
ui/src/app/entity/attribute/edit-attribute-value.tpl.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<md-edit-dialog> <md-edit-dialog class="tb-edit-dialog">
<form name="editDialog" ng-submit="submit()"> <form name="editDialog" ng-submit="submit()" >
<div layout="column" class="md-content" style="width: 400px;"> <div layout="column" class="md-content" style="width: 400px;">
<fieldset> <fieldset>
<section layout="row"> <section layout="row">
@ -38,7 +38,8 @@
</md-input-container> </md-input-container>
<md-input-container ng-if="valueType===valueTypes.integer" flex="60" class="md-block"> <md-input-container ng-if="valueType===valueTypes.integer" flex="60" class="md-block">
<label translate>value.integer-value</label> <label translate>value.integer-value</label>
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="model.value"> <input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/"
ng-model="model.value">
<div ng-messages="editDialog.value.$error"> <div ng-messages="editDialog.value.$error">
<div translate ng-message="required">attribute.value-required</div> <div translate ng-message="required">attribute.value-required</div>
<div translate ng-message="pattern">value.invalid-integer-value</div> <div translate ng-message="pattern">value.invalid-integer-value</div>
@ -52,16 +53,31 @@
</div> </div>
</md-input-container> </md-input-container>
<div layout="column" layout-align="center" flex="60" ng-if="valueType===valueTypes.boolean"> <div layout="column" layout-align="center" flex="60" ng-if="valueType===valueTypes.boolean">
<md-checkbox ng-model="model.value" style="margin-bottom: 0px;"> <md-checkbox ng-model="model.value" style="margin-bottom: 0;">
{{ (model.value ? 'value.true' : 'value.false') | translate }} {{ (model.value ? 'value.true' : 'value.false') | translate }}
</md-checkbox> </md-checkbox>
</div> </div>
<div layout="row" layout-align="center" ng-if="valueType===valueTypes.json" flex="60">
<md-input-container class="md-block">
<label translate>value.json-value</label>
<input required tb-json-to-string name="value" ng-model="model.value">
<div ng-messages="editDialog.value.$error">
<div translate ng-message="required">attribute.value-required</div>
</div>
</md-input-container>
<md-button class="md-icon-button" style="margin: 18px 0;"
ng-click="editJSON($event)" aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.edit' | translate }}
</md-tooltip>
<ng-md-icon size="20" icon="open_in_new"></ng-md-icon>
</md-button>
</div>
</section> </section>
</fieldset> </fieldset>
</div> </div>
<div layout="row" layout-align="end" class="md-actions"> <div layout="row" layout-align="end" class="md-actions">
<md-button ng-click="dismiss()">{{ 'action.cancel' | <md-button ng-click="dismiss()">{{ 'action.cancel' | translate }}
translate }}
</md-button> </md-button>
<md-button ng-disabled="editDialog.$invalid || !editDialog.$dirty" type="submit" <md-button ng-disabled="editDialog.$invalid || !editDialog.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
@ -69,4 +85,4 @@
</md-button> </md-button>
</div> </div>
</form> </form>
</md-edit-dialog> </md-edit-dialog>

2
ui/src/app/locale/locale.constant-el_GR.json

@ -2232,7 +2232,7 @@
"last": "Τελευταίος", "last": "Τελευταίος",
"time-period": "Χρονική Περίοδος" "time-period": "Χρονική Περίοδος"
}, },
"user": { "user": {
"user": "Χρήστης", "user": "Χρήστης",
"users": "Χρήστες", "users": "Χρήστες",
"management": "Διαχείριση Χρηστών", "management": "Διαχείριση Χρηστών",

8
ui/src/app/locale/locale.constant-en_US.json

@ -607,6 +607,7 @@
}, },
"details": { "details": {
"edit-mode": "Edit mode", "edit-mode": "Edit mode",
"edit-json": "Edit JSON",
"toggle-edit-mode": "Toggle edit mode" "toggle-edit-mode": "Toggle edit mode"
}, },
"device": { "device": {
@ -1270,7 +1271,8 @@
"js-func": { "js-func": {
"no-return-error": "Function must return value!", "no-return-error": "Function must return value!",
"return-type-mismatch": "Function must return value of '{{type}}' type!", "return-type-mismatch": "Function must return value of '{{type}}' type!",
"tidy": "Tidy" "tidy": "Tidy",
"mini": "Mini"
}, },
"key-val": { "key-val": {
"key": "Key", "key": "Key",
@ -1593,7 +1595,9 @@
"boolean-value": "Boolean value", "boolean-value": "Boolean value",
"false": "False", "false": "False",
"true": "True", "true": "True",
"long": "Long" "long": "Long",
"json": "JSON",
"json-value": "JSON value"
}, },
"widget": { "widget": {
"widget-library": "Widgets Library", "widget-library": "Widgets Library",

4
ui/src/app/locale/locale.constant-ru_RU.json

@ -607,6 +607,7 @@
}, },
"details": { "details": {
"edit-mode": "Режим редактирования", "edit-mode": "Режим редактирования",
"edit-json": "Редактировать JSON",
"toggle-edit-mode": "Режим редактирования" "toggle-edit-mode": "Режим редактирования"
}, },
"device": { "device": {
@ -1191,8 +1192,7 @@
}, },
"js-func": { "js-func": {
"no-return-error": "Функция должна возвращать значение!", "no-return-error": "Функция должна возвращать значение!",
"return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!", "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!"
"tidy": "Tidy"
}, },
"key-val": { "key-val": {
"key": "Ключ", "key": "Ключ",

4
ui/src/app/locale/locale.constant-uk_UA.json

@ -724,6 +724,7 @@
"details": { "details": {
"details": "Деталі", "details": "Деталі",
"edit-mode": "Режим редагування", "edit-mode": "Режим редагування",
"edit-json": "Редагувати JSON",
"toggle-edit-mode": "Перемкнути режим редагування" "toggle-edit-mode": "Перемкнути режим редагування"
}, },
"device": { "device": {
@ -1606,8 +1607,7 @@
}, },
"js-func": { "js-func": {
"no-return-error": "Функція повинна повертати значення!", "no-return-error": "Функція повинна повертати значення!",
"return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!", "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!"
"tidy": "Tidy"
}, },
"key-val": { "key-val": {
"key": "Ключ", "key": "Ключ",

Loading…
Cancel
Save