Commit 90ef91e3a1d5ab1d018b746004a6a6619c785d65
1 parent
a741a59e
UI: Implement Asset/Device type management.
Showing
52 changed files
with
515 additions
and
2756 deletions
... | ... | @@ -31,7 +31,8 @@ function AssetService($http, $q, customerService, userService) { |
31 | 31 | getTenantAssets: getTenantAssets, |
32 | 32 | getCustomerAssets: getCustomerAssets, |
33 | 33 | findByQuery: findByQuery, |
34 | - fetchAssetsByNameFilter: fetchAssetsByNameFilter | |
34 | + fetchAssetsByNameFilter: fetchAssetsByNameFilter, | |
35 | + getAssetTypes: getAssetTypes | |
35 | 36 | } |
36 | 37 | |
37 | 38 | return service; |
... | ... | @@ -152,7 +153,7 @@ function AssetService($http, $q, customerService, userService) { |
152 | 153 | return deferred.promise; |
153 | 154 | } |
154 | 155 | |
155 | - function getTenantAssets(pageLink, applyCustomersInfo, config) { | |
156 | + function getTenantAssets(pageLink, applyCustomersInfo, config, type) { | |
156 | 157 | var deferred = $q.defer(); |
157 | 158 | var url = '/api/tenant/assets?limit=' + pageLink.limit; |
158 | 159 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -164,6 +165,9 @@ function AssetService($http, $q, customerService, userService) { |
164 | 165 | if (angular.isDefined(pageLink.textOffset)) { |
165 | 166 | url += '&textOffset=' + pageLink.textOffset; |
166 | 167 | } |
168 | + if (angular.isDefined(type) && type.length) { | |
169 | + url += '&type=' + type; | |
170 | + } | |
167 | 171 | $http.get(url, config).then(function success(response) { |
168 | 172 | if (applyCustomersInfo) { |
169 | 173 | customerService.applyAssignedCustomersInfo(response.data.data).then( |
... | ... | @@ -184,7 +188,7 @@ function AssetService($http, $q, customerService, userService) { |
184 | 188 | return deferred.promise; |
185 | 189 | } |
186 | 190 | |
187 | - function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config) { | |
191 | + function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config, type) { | |
188 | 192 | var deferred = $q.defer(); |
189 | 193 | var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit; |
190 | 194 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -196,6 +200,9 @@ function AssetService($http, $q, customerService, userService) { |
196 | 200 | if (angular.isDefined(pageLink.textOffset)) { |
197 | 201 | url += '&textOffset=' + pageLink.textOffset; |
198 | 202 | } |
203 | + if (angular.isDefined(type) && type.length) { | |
204 | + url += '&type=' + type; | |
205 | + } | |
199 | 206 | $http.get(url, config).then(function success(response) { |
200 | 207 | if (applyCustomersInfo) { |
201 | 208 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
... | ... | @@ -258,4 +265,15 @@ function AssetService($http, $q, customerService, userService) { |
258 | 265 | return deferred.promise; |
259 | 266 | } |
260 | 267 | |
268 | + function getAssetTypes() { | |
269 | + var deferred = $q.defer(); | |
270 | + var url = '/api/asset/types'; | |
271 | + $http.get(url).then(function success(response) { | |
272 | + deferred.resolve(response.data); | |
273 | + }, function fail() { | |
274 | + deferred.reject(); | |
275 | + }); | |
276 | + return deferred.promise; | |
277 | + } | |
278 | + | |
261 | 279 | } | ... | ... |
... | ... | @@ -41,12 +41,13 @@ function DeviceService($http, $q, attributeService, customerService, types) { |
41 | 41 | deleteDeviceAttributes: deleteDeviceAttributes, |
42 | 42 | sendOneWayRpcCommand: sendOneWayRpcCommand, |
43 | 43 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, |
44 | - findByQuery: findByQuery | |
44 | + findByQuery: findByQuery, | |
45 | + getDeviceTypes: getDeviceTypes | |
45 | 46 | } |
46 | 47 | |
47 | 48 | return service; |
48 | 49 | |
49 | - function getTenantDevices(pageLink, applyCustomersInfo, config) { | |
50 | + function getTenantDevices(pageLink, applyCustomersInfo, config, type) { | |
50 | 51 | var deferred = $q.defer(); |
51 | 52 | var url = '/api/tenant/devices?limit=' + pageLink.limit; |
52 | 53 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -58,6 +59,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { |
58 | 59 | if (angular.isDefined(pageLink.textOffset)) { |
59 | 60 | url += '&textOffset=' + pageLink.textOffset; |
60 | 61 | } |
62 | + if (angular.isDefined(type) && type.length) { | |
63 | + url += '&type=' + type; | |
64 | + } | |
61 | 65 | $http.get(url, config).then(function success(response) { |
62 | 66 | if (applyCustomersInfo) { |
63 | 67 | customerService.applyAssignedCustomersInfo(response.data.data).then( |
... | ... | @@ -78,7 +82,7 @@ function DeviceService($http, $q, attributeService, customerService, types) { |
78 | 82 | return deferred.promise; |
79 | 83 | } |
80 | 84 | |
81 | - function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config) { | |
85 | + function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config, type) { | |
82 | 86 | var deferred = $q.defer(); |
83 | 87 | var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit; |
84 | 88 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -90,6 +94,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { |
90 | 94 | if (angular.isDefined(pageLink.textOffset)) { |
91 | 95 | url += '&textOffset=' + pageLink.textOffset; |
92 | 96 | } |
97 | + if (angular.isDefined(type) && type.length) { | |
98 | + url += '&type=' + type; | |
99 | + } | |
93 | 100 | $http.get(url, config).then(function success(response) { |
94 | 101 | if (applyCustomersInfo) { |
95 | 102 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
... | ... | @@ -286,4 +293,15 @@ function DeviceService($http, $q, attributeService, customerService, types) { |
286 | 293 | return deferred.promise; |
287 | 294 | } |
288 | 295 | |
296 | + function getDeviceTypes() { | |
297 | + var deferred = $q.defer(); | |
298 | + var url = '/api/device/types'; | |
299 | + $http.get(url).then(function success(response) { | |
300 | + deferred.resolve(response.data); | |
301 | + }, function fail() { | |
302 | + deferred.reject(); | |
303 | + }); | |
304 | + return deferred.promise; | |
305 | + } | |
306 | + | |
289 | 307 | } | ... | ... |
... | ... | @@ -160,10 +160,6 @@ export default function AppConfig($provide, |
160 | 160 | indigoTheme(); |
161 | 161 | } |
162 | 162 | |
163 | - $mdThemingProvider.theme('tb-search-input', 'default') | |
164 | - .primaryPalette('tb-primary') | |
165 | - .backgroundPalette('tb-primary'); | |
166 | - | |
167 | 163 | $mdThemingProvider.setDefaultTheme('default'); |
168 | 164 | //$mdThemingProvider.alwaysWatchTheme(true); |
169 | 165 | } | ... | ... |
... | ... | @@ -15,5 +15,8 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
19 | -<div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div> | |
18 | +<div flex layout="column" style="margin-top: -10px;"> | |
19 | + <div flex style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div> | |
20 | + <div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
21 | + <div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div> | |
22 | +</div> | ... | ... |
... | ... | @@ -56,13 +56,13 @@ |
56 | 56 | <div translate ng-message="required">asset.name-required</div> |
57 | 57 | </div> |
58 | 58 | </md-input-container> |
59 | - <md-input-container class="md-block"> | |
60 | - <label translate>asset.type</label> | |
61 | - <input required name="type" ng-model="asset.type"> | |
62 | - <div ng-messages="theForm.name.$error"> | |
63 | - <div translate ng-message="required">asset.type-required</div> | |
64 | - </div> | |
65 | - </md-input-container> | |
59 | + <tb-entity-subtype-autocomplete | |
60 | + ng-disabled="loading || !isEdit" | |
61 | + tb-required="true" | |
62 | + the-form="theForm" | |
63 | + ng-model="asset.type" | |
64 | + entity-type="types.entityType.asset"> | |
65 | + </tb-entity-subtype-autocomplete> | |
66 | 66 | <md-input-container class="md-block"> |
67 | 67 | <label translate>asset.description</label> |
68 | 68 | <textarea ng-model="asset.additionalInfo.description" rows="2"></textarea> | ... | ... |
... | ... | @@ -47,7 +47,8 @@ export function AssetCardController(types) { |
47 | 47 | |
48 | 48 | |
49 | 49 | /*@ngInject*/ |
50 | -export function AssetController(userService, assetService, customerService, $state, $stateParams, $document, $mdDialog, $q, $translate, types) { | |
50 | +export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams, | |
51 | + $document, $mdDialog, $q, $translate, types) { | |
51 | 52 | |
52 | 53 | var customerId = $stateParams.customerId; |
53 | 54 | |
... | ... | @@ -129,8 +130,8 @@ export function AssetController(userService, assetService, customerService, $sta |
129 | 130 | } |
130 | 131 | |
131 | 132 | if (vm.assetsScope === 'tenant') { |
132 | - fetchAssetsFunction = function (pageLink) { | |
133 | - return assetService.getTenantAssets(pageLink, true); | |
133 | + fetchAssetsFunction = function (pageLink, assetType) { | |
134 | + return assetService.getTenantAssets(pageLink, true, null, assetType); | |
134 | 135 | }; |
135 | 136 | deleteAssetFunction = function (assetId) { |
136 | 137 | return assetService.deleteAsset(assetId); |
... | ... | @@ -229,8 +230,8 @@ export function AssetController(userService, assetService, customerService, $sta |
229 | 230 | |
230 | 231 | |
231 | 232 | } else if (vm.assetsScope === 'customer' || vm.assetsScope === 'customer_user') { |
232 | - fetchAssetsFunction = function (pageLink) { | |
233 | - return assetService.getCustomerAssets(customerId, pageLink, true); | |
233 | + fetchAssetsFunction = function (pageLink, assetType) { | |
234 | + return assetService.getCustomerAssets(customerId, pageLink, true, null, assetType); | |
234 | 235 | }; |
235 | 236 | deleteAssetFunction = function (assetId) { |
236 | 237 | return assetService.unassignAssetFromCustomer(assetId); |
... | ... | @@ -333,6 +334,7 @@ export function AssetController(userService, assetService, customerService, $sta |
333 | 334 | var deferred = $q.defer(); |
334 | 335 | assetService.saveAsset(asset).then( |
335 | 336 | function success(savedAsset) { |
337 | + $rootScope.$broadcast('assetSaved'); | |
336 | 338 | var assets = [ savedAsset ]; |
337 | 339 | customerService.applyAssignedCustomersInfo(assets).then( |
338 | 340 | function success(items) { | ... | ... |
... | ... | @@ -25,6 +25,7 @@ export default function AssetDirective($compile, $templateCache, toast, $transla |
25 | 25 | var template = $templateCache.get(assetFieldsetTemplate); |
26 | 26 | element.html(template); |
27 | 27 | |
28 | + scope.types = types; | |
28 | 29 | scope.isAssignedToCustomer = false; |
29 | 30 | scope.isPublic = false; |
30 | 31 | scope.assignedCustomer = null; | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import assetsTemplate from './assets.tpl.html'; |
20 | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function AssetRoutes($stateProvider) { | |
23 | +export default function AssetRoutes($stateProvider, types) { | |
24 | 24 | $stateProvider |
25 | 25 | .state('home.assets', { |
26 | 26 | url: '/assets', |
... | ... | @@ -37,6 +37,8 @@ export default function AssetRoutes($stateProvider) { |
37 | 37 | data: { |
38 | 38 | assetsType: 'tenant', |
39 | 39 | searchEnabled: true, |
40 | + searchByEntitySubtype: true, | |
41 | + searchEntityType: types.entityType.asset, | |
40 | 42 | pageTitle: 'asset.assets' |
41 | 43 | }, |
42 | 44 | ncyBreadcrumb: { |
... | ... | @@ -58,6 +60,8 @@ export default function AssetRoutes($stateProvider) { |
58 | 60 | data: { |
59 | 61 | assetsType: 'customer', |
60 | 62 | searchEnabled: true, |
63 | + searchByEntitySubtype: true, | |
64 | + searchEntityType: types.entityType.asset, | |
61 | 65 | pageTitle: 'customer.assets' |
62 | 66 | }, |
63 | 67 | ncyBreadcrumb: { | ... | ... |
ui/src/app/components/datasource-device.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import './datasource-device.scss'; | |
17 | - | |
18 | -import 'md-color-picker'; | |
19 | -import tinycolor from 'tinycolor2'; | |
20 | -import $ from 'jquery'; | |
21 | -import thingsboardTypes from '../common/types.constant'; | |
22 | -import thingsboardDatakeyConfigDialog from './datakey-config-dialog.controller'; | |
23 | -import thingsboardTruncate from './truncate.filter'; | |
24 | - | |
25 | -/* eslint-disable import/no-unresolved, import/default */ | |
26 | - | |
27 | -import datasourceDeviceTemplate from './datasource-device.tpl.html'; | |
28 | -import datakeyConfigDialogTemplate from './datakey-config-dialog.tpl.html'; | |
29 | - | |
30 | -/* eslint-enable import/no-unresolved, import/default */ | |
31 | - | |
32 | -/* eslint-disable angular/angularelement */ | |
33 | - | |
34 | -export default angular.module('thingsboard.directives.datasourceDevice', [thingsboardTruncate, thingsboardTypes, thingsboardDatakeyConfigDialog]) | |
35 | - .directive('tbDatasourceDevice', DatasourceDevice) | |
36 | - .name; | |
37 | - | |
38 | -/*@ngInject*/ | |
39 | -function DatasourceDevice($compile, $templateCache, $q, $mdDialog, $window, $document, $mdColorPicker, $mdConstant, types) { | |
40 | - | |
41 | - var linker = function (scope, element, attrs, ngModelCtrl) { | |
42 | - var template = $templateCache.get(datasourceDeviceTemplate); | |
43 | - element.html(template); | |
44 | - | |
45 | - scope.ngModelCtrl = ngModelCtrl; | |
46 | - scope.types = types; | |
47 | - | |
48 | - scope.selectedTimeseriesDataKey = null; | |
49 | - scope.timeseriesDataKeySearchText = null; | |
50 | - | |
51 | - scope.selectedAttributeDataKey = null; | |
52 | - scope.attributeDataKeySearchText = null; | |
53 | - | |
54 | - scope.updateValidity = function () { | |
55 | - if (ngModelCtrl.$viewValue) { | |
56 | - var value = ngModelCtrl.$viewValue; | |
57 | - var dataValid = angular.isDefined(value) && value != null; | |
58 | - ngModelCtrl.$setValidity('deviceData', dataValid); | |
59 | - if (dataValid) { | |
60 | - ngModelCtrl.$setValidity('deviceAlias', | |
61 | - angular.isDefined(value.deviceAliasId) && | |
62 | - value.deviceAliasId != null); | |
63 | - ngModelCtrl.$setValidity('deviceKeys', | |
64 | - angular.isDefined(value.dataKeys) && | |
65 | - value.dataKeys != null && | |
66 | - value.dataKeys.length > 0); | |
67 | - } | |
68 | - } | |
69 | - }; | |
70 | - | |
71 | - scope.$watch('deviceAlias', function () { | |
72 | - if (ngModelCtrl.$viewValue) { | |
73 | - if (scope.deviceAlias) { | |
74 | - ngModelCtrl.$viewValue.deviceAliasId = scope.deviceAlias.id; | |
75 | - } else { | |
76 | - ngModelCtrl.$viewValue.deviceAliasId = null; | |
77 | - } | |
78 | - scope.updateValidity(); | |
79 | - scope.selectedDeviceAliasChange(); | |
80 | - } | |
81 | - }); | |
82 | - | |
83 | - scope.$watch('timeseriesDataKeys', function () { | |
84 | - if (ngModelCtrl.$viewValue) { | |
85 | - var dataKeys = []; | |
86 | - dataKeys = dataKeys.concat(scope.timeseriesDataKeys); | |
87 | - dataKeys = dataKeys.concat(scope.attributeDataKeys); | |
88 | - ngModelCtrl.$viewValue.dataKeys = dataKeys; | |
89 | - scope.updateValidity(); | |
90 | - } | |
91 | - }, true); | |
92 | - | |
93 | - scope.$watch('attributeDataKeys', function () { | |
94 | - if (ngModelCtrl.$viewValue) { | |
95 | - var dataKeys = []; | |
96 | - dataKeys = dataKeys.concat(scope.timeseriesDataKeys); | |
97 | - dataKeys = dataKeys.concat(scope.attributeDataKeys); | |
98 | - ngModelCtrl.$viewValue.dataKeys = dataKeys; | |
99 | - scope.updateValidity(); | |
100 | - } | |
101 | - }, true); | |
102 | - | |
103 | - ngModelCtrl.$render = function () { | |
104 | - if (ngModelCtrl.$viewValue) { | |
105 | - var deviceAliasId = ngModelCtrl.$viewValue.deviceAliasId; | |
106 | - if (scope.deviceAliases[deviceAliasId]) { | |
107 | - scope.deviceAlias = {id: deviceAliasId, alias: scope.deviceAliases[deviceAliasId].alias, | |
108 | - deviceId: scope.deviceAliases[deviceAliasId].deviceId}; | |
109 | - } else { | |
110 | - scope.deviceAlias = null; | |
111 | - } | |
112 | - var timeseriesDataKeys = []; | |
113 | - var attributeDataKeys = []; | |
114 | - for (var d in ngModelCtrl.$viewValue.dataKeys) { | |
115 | - var dataKey = ngModelCtrl.$viewValue.dataKeys[d]; | |
116 | - if (dataKey.type === types.dataKeyType.timeseries) { | |
117 | - timeseriesDataKeys.push(dataKey); | |
118 | - } else if (dataKey.type === types.dataKeyType.attribute) { | |
119 | - attributeDataKeys.push(dataKey); | |
120 | - } | |
121 | - } | |
122 | - scope.timeseriesDataKeys = timeseriesDataKeys; | |
123 | - scope.attributeDataKeys = attributeDataKeys; | |
124 | - } | |
125 | - }; | |
126 | - | |
127 | - scope.textIsNotEmpty = function(text) { | |
128 | - return (text && text != null && text.length > 0) ? true : false; | |
129 | - } | |
130 | - | |
131 | - scope.selectedDeviceAliasChange = function () { | |
132 | - if (!scope.timeseriesDataKeySearchText || scope.timeseriesDataKeySearchText === '') { | |
133 | - scope.timeseriesDataKeySearchText = scope.timeseriesDataKeySearchText === '' ? null : ''; | |
134 | - } | |
135 | - if (!scope.attributeDataKeySearchText || scope.attributeDataKeySearchText === '') { | |
136 | - scope.attributeDataKeySearchText = scope.attributeDataKeySearchText === '' ? null : ''; | |
137 | - } | |
138 | - }; | |
139 | - | |
140 | - scope.transformTimeseriesDataKeyChip = function (chip) { | |
141 | - return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries}); | |
142 | - }; | |
143 | - | |
144 | - scope.transformAttributeDataKeyChip = function (chip) { | |
145 | - return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute}); | |
146 | - }; | |
147 | - | |
148 | - scope.showColorPicker = function (event, dataKey) { | |
149 | - $mdColorPicker.show({ | |
150 | - value: dataKey.color, | |
151 | - defaultValue: '#fff', | |
152 | - random: tinycolor.random(), | |
153 | - clickOutsideToClose: false, | |
154 | - hasBackdrop: false, | |
155 | - skipHide: true, | |
156 | - preserveScope: false, | |
157 | - | |
158 | - mdColorAlphaChannel: true, | |
159 | - mdColorSpectrum: true, | |
160 | - mdColorSliders: true, | |
161 | - mdColorGenericPalette: false, | |
162 | - mdColorMaterialPalette: true, | |
163 | - mdColorHistory: false, | |
164 | - mdColorDefaultTab: 2, | |
165 | - | |
166 | - $event: event | |
167 | - | |
168 | - }).then(function (color) { | |
169 | - dataKey.color = color; | |
170 | - ngModelCtrl.$setDirty(); | |
171 | - }); | |
172 | - } | |
173 | - | |
174 | - scope.editDataKey = function (event, dataKey, index) { | |
175 | - | |
176 | - $mdDialog.show({ | |
177 | - controller: 'DatakeyConfigDialogController', | |
178 | - controllerAs: 'vm', | |
179 | - templateUrl: datakeyConfigDialogTemplate, | |
180 | - locals: { | |
181 | - dataKey: angular.copy(dataKey), | |
182 | - dataKeySettingsSchema: scope.datakeySettingsSchema, | |
183 | - deviceAlias: scope.deviceAlias, | |
184 | - deviceAliases: scope.deviceAliases | |
185 | - }, | |
186 | - parent: angular.element($document[0].body), | |
187 | - fullscreen: true, | |
188 | - targetEvent: event, | |
189 | - skipHide: true, | |
190 | - onComplete: function () { | |
191 | - var w = angular.element($window); | |
192 | - w.triggerHandler('resize'); | |
193 | - } | |
194 | - }).then(function (dataKey) { | |
195 | - if (dataKey.type === types.dataKeyType.timeseries) { | |
196 | - scope.timeseriesDataKeys[index] = dataKey; | |
197 | - } else if (dataKey.type === types.dataKeyType.attribute) { | |
198 | - scope.attributeDataKeys[index] = dataKey; | |
199 | - } | |
200 | - ngModelCtrl.$setDirty(); | |
201 | - }, function () { | |
202 | - }); | |
203 | - }; | |
204 | - | |
205 | - scope.dataKeysSearch = function (searchText, type) { | |
206 | - if (scope.deviceAlias) { | |
207 | - var deferred = $q.defer(); | |
208 | - scope.fetchDeviceKeys({deviceAliasId: scope.deviceAlias.id, query: searchText, type: type}) | |
209 | - .then(function (dataKeys) { | |
210 | - deferred.resolve(dataKeys); | |
211 | - }, function (e) { | |
212 | - deferred.reject(e); | |
213 | - }); | |
214 | - return deferred.promise; | |
215 | - } else { | |
216 | - return $q.when([]); | |
217 | - } | |
218 | - }; | |
219 | - | |
220 | - scope.createKey = function (event, chipsId) { | |
221 | - var chipsChild = $(chipsId, element)[0].firstElementChild; | |
222 | - var el = angular.element(chipsChild); | |
223 | - var chipBuffer = el.scope().$mdChipsCtrl.getChipBuffer(); | |
224 | - event.preventDefault(); | |
225 | - event.stopPropagation(); | |
226 | - el.scope().$mdChipsCtrl.appendChip(chipBuffer.trim()); | |
227 | - el.scope().$mdChipsCtrl.resetChipBuffer(); | |
228 | - } | |
229 | - | |
230 | - $compile(element.contents())(scope); | |
231 | - } | |
232 | - | |
233 | - return { | |
234 | - restrict: "E", | |
235 | - require: "^ngModel", | |
236 | - scope: { | |
237 | - widgetType: '=', | |
238 | - deviceAliases: '=', | |
239 | - datakeySettingsSchema: '=', | |
240 | - generateDataKey: '&', | |
241 | - fetchDeviceKeys: '&', | |
242 | - onCreateDeviceAlias: '&' | |
243 | - }, | |
244 | - link: linker | |
245 | - }; | |
246 | -} | |
247 | - | |
248 | -/* eslint-enable angular/angularelement */ |
ui/src/app/components/datasource-device.scss
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -@import '../../scss/constants'; | |
17 | - | |
18 | -.tb-device-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete { | |
19 | - .tb-not-found { | |
20 | - display: block; | |
21 | - line-height: 1.5; | |
22 | - height: 48px; | |
23 | - .tb-no-entries { | |
24 | - line-height: 48px; | |
25 | - } | |
26 | - } | |
27 | - li { | |
28 | - height: auto !important; | |
29 | - white-space: normal !important; | |
30 | - } | |
31 | -} | |
32 | - | |
33 | -tb-datasource-device { | |
34 | - @media (min-width: $layout-breakpoint-gt-sm) { | |
35 | - padding-left: 4px; | |
36 | - padding-right: 4px; | |
37 | - } | |
38 | - tb-device-alias-select { | |
39 | - @media (min-width: $layout-breakpoint-gt-sm) { | |
40 | - width: 200px; | |
41 | - max-width: 200px; | |
42 | - } | |
43 | - } | |
44 | -} | |
\ No newline at end of file |
ui/src/app/components/datasource-device.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> | |
19 | - <tb-device-alias-select | |
20 | - tb-required="true" | |
21 | - device-aliases="deviceAliases" | |
22 | - ng-model="deviceAlias" | |
23 | - on-create-device-alias="onCreateDeviceAlias({event: event, alias: alias})"> | |
24 | - </tb-device-alias-select> | |
25 | - <section flex layout='column'> | |
26 | - <section flex layout='column' layout-align="center" style="padding-left: 4px;"> | |
27 | - <md-chips flex | |
28 | - id="timeseries_datakey_chips" | |
29 | - ng-required="true" | |
30 | - ng-model="timeseriesDataKeys" md-autocomplete-snap | |
31 | - md-transform-chip="transformTimeseriesDataKeyChip($chip)" | |
32 | - md-require-match="false"> | |
33 | - <md-autocomplete | |
34 | - md-no-cache="true" | |
35 | - id="timeseries_datakey" | |
36 | - md-selected-item="selectedTimeseriesDataKey" | |
37 | - md-search-text="timeseriesDataKeySearchText" | |
38 | - md-items="item in dataKeysSearch(timeseriesDataKeySearchText, types.dataKeyType.timeseries)" | |
39 | - md-item-text="item.name" | |
40 | - md-min-length="0" | |
41 | - placeholder="{{'datakey.timeseries' | translate }}" | |
42 | - md-menu-class="tb-timeseries-datakey-autocomplete"> | |
43 | - <span md-highlight-text="timeseriesDataKeySearchText" md-highlight-flags="^i">{{item}}</span> | |
44 | - <md-not-found> | |
45 | - <div class="tb-not-found"> | |
46 | - <div class="tb-no-entries" ng-if="!textIsNotEmpty(timeseriesDataKeySearchText)"> | |
47 | - <span translate>device.no-keys-found</span> | |
48 | - </div> | |
49 | - <div ng-if="textIsNotEmpty(timeseriesDataKeySearchText)"> | |
50 | - <span translate translate-values='{ key: "{{timeseriesDataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span> | |
51 | - <span> | |
52 | - <a translate ng-click="createKey($event, '#timeseries_datakey_chips')">device.create-new-key</a> | |
53 | - </span> | |
54 | - </div> | |
55 | - </div> | |
56 | - </md-not-found> | |
57 | - </md-autocomplete> | |
58 | - <md-chip-template> | |
59 | - <div layout="row" layout-align="start center" class="tb-attribute-chip"> | |
60 | - <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> | |
61 | - <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | |
62 | - </div> | |
63 | - <div layout="row" flex> | |
64 | - <div class="tb-chip-label"> | |
65 | - {{$chip.label}} | |
66 | - </div> | |
67 | - <div class="tb-chip-separator">: </div> | |
68 | - <div class="tb-chip-label"> | |
69 | - <strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong> | |
70 | - <strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong> | |
71 | - </div> | |
72 | - </div> | |
73 | - <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32"> | |
74 | - <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon> | |
75 | - </md-button> | |
76 | - </div> | |
77 | - </md-chip-template> | |
78 | - </md-chips> | |
79 | - <md-chips flex ng-if="widgetType === types.widgetType.latest.value" | |
80 | - id="attribute_datakey_chips" | |
81 | - ng-required="true" | |
82 | - ng-model="attributeDataKeys" md-autocomplete-snap | |
83 | - md-transform-chip="transformAttributeDataKeyChip($chip)" | |
84 | - md-require-match="false"> | |
85 | - <md-autocomplete | |
86 | - md-no-cache="true" | |
87 | - id="attribute_datakey" | |
88 | - md-selected-item="selectedAttributeDataKey" | |
89 | - md-search-text="attributeDataKeySearchText" | |
90 | - md-items="item in dataKeysSearch(attributeDataKeySearchText, types.dataKeyType.attribute)" | |
91 | - md-item-text="item.name" | |
92 | - md-min-length="0" | |
93 | - placeholder="{{'datakey.attributes' | translate }}" | |
94 | - md-menu-class="tb-attribute-datakey-autocomplete"> | |
95 | - <span md-highlight-text="attributeDataKeySearchText" md-highlight-flags="^i">{{item}}</span> | |
96 | - <md-not-found> | |
97 | - <div class="tb-not-found"> | |
98 | - <div class="tb-no-entries" ng-if="!textIsNotEmpty(attributeDataKeySearchText)"> | |
99 | - <span translate>device.no-keys-found</span> | |
100 | - </div> | |
101 | - <div ng-if="textIsNotEmpty(attributeDataKeySearchText)"> | |
102 | - <span translate translate-values='{ key: "{{attributeDataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span> | |
103 | - <span> | |
104 | - <a translate ng-click="createKey($event, '#attribute_datakey_chips')">device.create-new-key</a> | |
105 | - </span> | |
106 | - </div> | |
107 | - </div> | |
108 | - </md-not-found> | |
109 | - </md-autocomplete> | |
110 | - <md-chip-template> | |
111 | - <div layout="row" layout-align="start center" class="tb-attribute-chip"> | |
112 | - <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> | |
113 | - <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | |
114 | - </div> | |
115 | - <div layout="row" flex> | |
116 | - <div class="tb-chip-label"> | |
117 | - {{$chip.label}} | |
118 | - </div> | |
119 | - <div class="tb-chip-separator">: </div> | |
120 | - <div class="tb-chip-label"> | |
121 | - <strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong> | |
122 | - <strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong> | |
123 | - </div> | |
124 | - </div> | |
125 | - <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32"> | |
126 | - <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon> | |
127 | - </md-button> | |
128 | - </div> | |
129 | - </md-chip-template> | |
130 | - </md-chips> | |
131 | - </section> | |
132 | - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | |
133 | - <div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.timeseries.value" class="tb-error-message">datakey.timeseries-required</div> | |
134 | - <div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div> | |
135 | - </div> | |
136 | - </section> | |
137 | -</section> |
ui/src/app/components/device-alias-select.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import $ from 'jquery'; | |
17 | - | |
18 | -import './device-alias-select.scss'; | |
19 | - | |
20 | -/* eslint-disable import/no-unresolved, import/default */ | |
21 | - | |
22 | -import deviceAliasSelectTemplate from './device-alias-select.tpl.html'; | |
23 | - | |
24 | -/* eslint-enable import/no-unresolved, import/default */ | |
25 | - | |
26 | - | |
27 | -/* eslint-disable angular/angularelement */ | |
28 | - | |
29 | -export default angular.module('thingsboard.directives.deviceAliasSelect', []) | |
30 | - .directive('tbDeviceAliasSelect', DeviceAliasSelect) | |
31 | - .name; | |
32 | - | |
33 | -/*@ngInject*/ | |
34 | -function DeviceAliasSelect($compile, $templateCache, $mdConstant) { | |
35 | - | |
36 | - var linker = function (scope, element, attrs, ngModelCtrl) { | |
37 | - var template = $templateCache.get(deviceAliasSelectTemplate); | |
38 | - element.html(template); | |
39 | - | |
40 | - scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | |
41 | - | |
42 | - scope.ngModelCtrl = ngModelCtrl; | |
43 | - scope.deviceAliasList = []; | |
44 | - scope.deviceAlias = null; | |
45 | - | |
46 | - scope.updateValidity = function () { | |
47 | - var value = ngModelCtrl.$viewValue; | |
48 | - var valid = angular.isDefined(value) && value != null || !scope.tbRequired; | |
49 | - ngModelCtrl.$setValidity('deviceAlias', valid); | |
50 | - }; | |
51 | - | |
52 | - scope.$watch('deviceAliases', function () { | |
53 | - scope.deviceAliasList = []; | |
54 | - for (var aliasId in scope.deviceAliases) { | |
55 | - var deviceAlias = {id: aliasId, alias: scope.deviceAliases[aliasId].alias, deviceId: scope.deviceAliases[aliasId].deviceId}; | |
56 | - scope.deviceAliasList.push(deviceAlias); | |
57 | - } | |
58 | - }, true); | |
59 | - | |
60 | - scope.$watch('deviceAlias', function () { | |
61 | - scope.updateView(); | |
62 | - }); | |
63 | - | |
64 | - scope.deviceAliasSearch = function (deviceAliasSearchText) { | |
65 | - return deviceAliasSearchText ? scope.deviceAliasList.filter( | |
66 | - scope.createFilterForDeviceAlias(deviceAliasSearchText)) : scope.deviceAliasList; | |
67 | - }; | |
68 | - | |
69 | - scope.createFilterForDeviceAlias = function (query) { | |
70 | - var lowercaseQuery = angular.lowercase(query); | |
71 | - return function filterFn(deviceAlias) { | |
72 | - return (angular.lowercase(deviceAlias.alias).indexOf(lowercaseQuery) === 0); | |
73 | - }; | |
74 | - }; | |
75 | - | |
76 | - scope.updateView = function () { | |
77 | - ngModelCtrl.$setViewValue(scope.deviceAlias); | |
78 | - scope.updateValidity(); | |
79 | - } | |
80 | - | |
81 | - ngModelCtrl.$render = function () { | |
82 | - if (ngModelCtrl.$viewValue) { | |
83 | - scope.deviceAlias = ngModelCtrl.$viewValue; | |
84 | - } | |
85 | - } | |
86 | - | |
87 | - scope.textIsNotEmpty = function(text) { | |
88 | - return (text && text != null && text.length > 0) ? true : false; | |
89 | - } | |
90 | - | |
91 | - scope.deviceAliasEnter = function($event) { | |
92 | - if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) { | |
93 | - $event.preventDefault(); | |
94 | - if (!scope.deviceAlias) { | |
95 | - var found = scope.deviceAliasSearch(scope.deviceAliasSearchText); | |
96 | - found = found.length > 0; | |
97 | - if (!found) { | |
98 | - scope.createDeviceAlias($event, scope.deviceAliasSearchText); | |
99 | - } | |
100 | - } | |
101 | - } | |
102 | - } | |
103 | - | |
104 | - scope.createDeviceAlias = function (event, alias) { | |
105 | - var autoChild = $('#device-autocomplete', element)[0].firstElementChild; | |
106 | - var el = angular.element(autoChild); | |
107 | - el.scope().$mdAutocompleteCtrl.hidden = true; | |
108 | - el.scope().$mdAutocompleteCtrl.hasNotFound = false; | |
109 | - event.preventDefault(); | |
110 | - var promise = scope.onCreateDeviceAlias({event: event, alias: alias}); | |
111 | - if (promise) { | |
112 | - promise.then( | |
113 | - function success(newAlias) { | |
114 | - el.scope().$mdAutocompleteCtrl.hasNotFound = true; | |
115 | - if (newAlias) { | |
116 | - scope.deviceAliasList.push(newAlias); | |
117 | - scope.deviceAlias = newAlias; | |
118 | - } | |
119 | - }, | |
120 | - function fail() { | |
121 | - el.scope().$mdAutocompleteCtrl.hasNotFound = true; | |
122 | - } | |
123 | - ); | |
124 | - } else { | |
125 | - el.scope().$mdAutocompleteCtrl.hasNotFound = true; | |
126 | - } | |
127 | - }; | |
128 | - | |
129 | - $compile(element.contents())(scope); | |
130 | - } | |
131 | - | |
132 | - return { | |
133 | - restrict: "E", | |
134 | - require: "^ngModel", | |
135 | - link: linker, | |
136 | - scope: { | |
137 | - tbRequired: '=?', | |
138 | - deviceAliases: '=', | |
139 | - onCreateDeviceAlias: '&' | |
140 | - } | |
141 | - }; | |
142 | -} | |
143 | - | |
144 | -/* eslint-enable angular/angularelement */ |
ui/src/app/components/device-alias-select.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<section layout='column'> | |
19 | - <md-autocomplete id="device-autocomplete" | |
20 | - md-input-name="device_alias" | |
21 | - ng-required="tbRequired" | |
22 | - ng-model="deviceAlias" | |
23 | - md-selected-item="deviceAlias" | |
24 | - md-search-text="deviceAliasSearchText" | |
25 | - md-items="item in deviceAliasSearch(deviceAliasSearchText)" | |
26 | - md-item-text="item.alias" | |
27 | - tb-keydown="deviceAliasEnter($event)" | |
28 | - tb-keypress="deviceAliasEnter($event)" | |
29 | - md-min-length="0" | |
30 | - placeholder="{{ 'device.device-alias' | translate }}" | |
31 | - md-menu-class="tb-device-alias-autocomplete"> | |
32 | - <md-item-template> | |
33 | - <span md-highlight-text="deviceAliasSearchText" md-highlight-flags="^i">{{item.alias}}</span> | |
34 | - </md-item-template> | |
35 | - <md-not-found> | |
36 | - <div class="tb-not-found"> | |
37 | - <div class="tb-no-entries" ng-if="!textIsNotEmpty(deviceAliasSearchText)"> | |
38 | - <span translate>device.no-aliases-found</span> | |
39 | - </div> | |
40 | - <div ng-if="textIsNotEmpty(deviceAliasSearchText)"> | |
41 | - <span translate translate-values='{ alias: "{{deviceAliasSearchText | truncate:true:6:'...'}}" }'>device.no-alias-matching</span> | |
42 | - <span> | |
43 | - <a translate ng-click="createDeviceAlias($event, deviceAliasSearchText)">device.create-new-alias</a> | |
44 | - </span> | |
45 | - </div> | |
46 | - </div> | |
47 | - </md-not-found> | |
48 | - </md-autocomplete> | |
49 | - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | |
50 | - <div translate ng-message="deviceAlias" class="tb-error-message">device.alias-required</div> | |
51 | - </div> | |
52 | -</section> |
ui/src/app/components/device-filter.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -/* eslint-disable import/no-unresolved, import/default */ | |
18 | - | |
19 | -import deviceFilterTemplate from './device-filter.tpl.html'; | |
20 | - | |
21 | -/* eslint-enable import/no-unresolved, import/default */ | |
22 | - | |
23 | -import './device-filter.scss'; | |
24 | - | |
25 | -export default angular.module('thingsboard.directives.deviceFilter', []) | |
26 | - .directive('tbDeviceFilter', DeviceFilter) | |
27 | - .name; | |
28 | - | |
29 | -/*@ngInject*/ | |
30 | -function DeviceFilter($compile, $templateCache, $q, deviceService) { | |
31 | - | |
32 | - var linker = function (scope, element, attrs, ngModelCtrl) { | |
33 | - | |
34 | - var template = $templateCache.get(deviceFilterTemplate); | |
35 | - element.html(template); | |
36 | - | |
37 | - scope.ngModelCtrl = ngModelCtrl; | |
38 | - | |
39 | - scope.fetchDevices = function(searchText, limit) { | |
40 | - var pageLink = {limit: limit, textSearch: searchText}; | |
41 | - | |
42 | - var deferred = $q.defer(); | |
43 | - | |
44 | - deviceService.getTenantDevices(pageLink, false).then(function success(result) { | |
45 | - deferred.resolve(result.data); | |
46 | - }, function fail() { | |
47 | - deferred.reject(); | |
48 | - }); | |
49 | - | |
50 | - return deferred.promise; | |
51 | - } | |
52 | - | |
53 | - scope.updateValidity = function() { | |
54 | - if (ngModelCtrl.$viewValue) { | |
55 | - var value = ngModelCtrl.$viewValue; | |
56 | - var valid; | |
57 | - if (value.useFilter) { | |
58 | - ngModelCtrl.$setValidity('deviceList', true); | |
59 | - if (angular.isDefined(value.deviceNameFilter) && value.deviceNameFilter.length > 0) { | |
60 | - ngModelCtrl.$setValidity('deviceNameFilter', true); | |
61 | - valid = angular.isDefined(scope.model.matchingFilterDevice) && scope.model.matchingFilterDevice != null; | |
62 | - ngModelCtrl.$setValidity('deviceNameFilterDeviceMatch', valid); | |
63 | - } else { | |
64 | - ngModelCtrl.$setValidity('deviceNameFilter', false); | |
65 | - } | |
66 | - } else { | |
67 | - ngModelCtrl.$setValidity('deviceNameFilter', true); | |
68 | - ngModelCtrl.$setValidity('deviceNameFilterDeviceMatch', true); | |
69 | - valid = angular.isDefined(value.deviceList) && value.deviceList.length > 0; | |
70 | - ngModelCtrl.$setValidity('deviceList', valid); | |
71 | - } | |
72 | - } | |
73 | - } | |
74 | - | |
75 | - ngModelCtrl.$render = function () { | |
76 | - destroyWatchers(); | |
77 | - scope.model = { | |
78 | - useFilter: false, | |
79 | - deviceList: [], | |
80 | - deviceNameFilter: '' | |
81 | - } | |
82 | - if (ngModelCtrl.$viewValue) { | |
83 | - var value = ngModelCtrl.$viewValue; | |
84 | - var model = scope.model; | |
85 | - model.useFilter = value.useFilter === true ? true: false; | |
86 | - model.deviceList = []; | |
87 | - model.deviceNameFilter = value.deviceNameFilter || ''; | |
88 | - processDeviceNameFilter(model.deviceNameFilter).then( | |
89 | - function(device) { | |
90 | - scope.model.matchingFilterDevice = device; | |
91 | - if (value.deviceList && value.deviceList.length > 0) { | |
92 | - deviceService.getDevices(value.deviceList).then(function (devices) { | |
93 | - model.deviceList = devices; | |
94 | - updateMatchingDevice(); | |
95 | - initWatchers(); | |
96 | - }); | |
97 | - } else { | |
98 | - updateMatchingDevice(); | |
99 | - initWatchers(); | |
100 | - } | |
101 | - } | |
102 | - ) | |
103 | - } | |
104 | - } | |
105 | - | |
106 | - function updateMatchingDevice() { | |
107 | - if (scope.model.useFilter) { | |
108 | - scope.model.matchingDevice = scope.model.matchingFilterDevice; | |
109 | - } else { | |
110 | - if (scope.model.deviceList && scope.model.deviceList.length > 0) { | |
111 | - scope.model.matchingDevice = scope.model.deviceList[0]; | |
112 | - } else { | |
113 | - scope.model.matchingDevice = null; | |
114 | - } | |
115 | - } | |
116 | - } | |
117 | - | |
118 | - function processDeviceNameFilter(deviceNameFilter) { | |
119 | - var deferred = $q.defer(); | |
120 | - if (angular.isDefined(deviceNameFilter) && deviceNameFilter.length > 0) { | |
121 | - scope.fetchDevices(deviceNameFilter, 1).then(function (devices) { | |
122 | - if (devices && devices.length > 0) { | |
123 | - deferred.resolve(devices[0]); | |
124 | - } else { | |
125 | - deferred.resolve(null); | |
126 | - } | |
127 | - }); | |
128 | - } else { | |
129 | - deferred.resolve(null); | |
130 | - } | |
131 | - return deferred.promise; | |
132 | - } | |
133 | - | |
134 | - function destroyWatchers() { | |
135 | - if (scope.deviceListDeregistration) { | |
136 | - scope.deviceListDeregistration(); | |
137 | - scope.deviceListDeregistration = null; | |
138 | - } | |
139 | - if (scope.useFilterDeregistration) { | |
140 | - scope.useFilterDeregistration(); | |
141 | - scope.useFilterDeregistration = null; | |
142 | - } | |
143 | - if (scope.deviceNameFilterDeregistration) { | |
144 | - scope.deviceNameFilterDeregistration(); | |
145 | - scope.deviceNameFilterDeregistration = null; | |
146 | - } | |
147 | - if (scope.matchingDeviceDeregistration) { | |
148 | - scope.matchingDeviceDeregistration(); | |
149 | - scope.matchingDeviceDeregistration = null; | |
150 | - } | |
151 | - } | |
152 | - | |
153 | - function initWatchers() { | |
154 | - scope.deviceListDeregistration = scope.$watch('model.deviceList', function () { | |
155 | - if (ngModelCtrl.$viewValue) { | |
156 | - var value = ngModelCtrl.$viewValue; | |
157 | - value.deviceList = []; | |
158 | - if (scope.model.deviceList && scope.model.deviceList.length > 0) { | |
159 | - for (var i in scope.model.deviceList) { | |
160 | - value.deviceList.push(scope.model.deviceList[i].id.id); | |
161 | - } | |
162 | - } | |
163 | - updateMatchingDevice(); | |
164 | - ngModelCtrl.$setViewValue(value); | |
165 | - scope.updateValidity(); | |
166 | - } | |
167 | - }, true); | |
168 | - scope.useFilterDeregistration = scope.$watch('model.useFilter', function () { | |
169 | - if (ngModelCtrl.$viewValue) { | |
170 | - var value = ngModelCtrl.$viewValue; | |
171 | - value.useFilter = scope.model.useFilter; | |
172 | - updateMatchingDevice(); | |
173 | - ngModelCtrl.$setViewValue(value); | |
174 | - scope.updateValidity(); | |
175 | - } | |
176 | - }); | |
177 | - scope.deviceNameFilterDeregistration = scope.$watch('model.deviceNameFilter', function (newNameFilter, prevNameFilter) { | |
178 | - if (ngModelCtrl.$viewValue) { | |
179 | - if (!angular.equals(newNameFilter, prevNameFilter)) { | |
180 | - var value = ngModelCtrl.$viewValue; | |
181 | - value.deviceNameFilter = scope.model.deviceNameFilter; | |
182 | - processDeviceNameFilter(value.deviceNameFilter).then( | |
183 | - function(device) { | |
184 | - scope.model.matchingFilterDevice = device; | |
185 | - updateMatchingDevice(); | |
186 | - ngModelCtrl.$setViewValue(value); | |
187 | - scope.updateValidity(); | |
188 | - } | |
189 | - ); | |
190 | - } | |
191 | - } | |
192 | - }); | |
193 | - | |
194 | - scope.matchingDeviceDeregistration = scope.$watch('model.matchingDevice', function (newMatchingDevice, prevMatchingDevice) { | |
195 | - if (!angular.equals(newMatchingDevice, prevMatchingDevice)) { | |
196 | - if (scope.onMatchingDeviceChange) { | |
197 | - scope.onMatchingDeviceChange({device: newMatchingDevice}); | |
198 | - } | |
199 | - } | |
200 | - }); | |
201 | - } | |
202 | - | |
203 | - $compile(element.contents())(scope); | |
204 | - | |
205 | - } | |
206 | - | |
207 | - return { | |
208 | - restrict: "E", | |
209 | - require: "^ngModel", | |
210 | - link: linker, | |
211 | - scope: { | |
212 | - isEdit: '=', | |
213 | - onMatchingDeviceChange: '&' | |
214 | - } | |
215 | - }; | |
216 | - | |
217 | -} |
ui/src/app/components/device-filter.scss
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -.tb-device-filter { | |
17 | - #device_list_chips { | |
18 | - .md-chips { | |
19 | - padding-bottom: 1px; | |
20 | - } | |
21 | - } | |
22 | - .device-name-filter-input { | |
23 | - margin-top: 10px; | |
24 | - margin-bottom: 0px; | |
25 | - .md-errors-spacer { | |
26 | - min-height: 0px; | |
27 | - } | |
28 | - } | |
29 | - .tb-filter-switch { | |
30 | - padding-left: 10px; | |
31 | - .filter-switch { | |
32 | - margin: 0; | |
33 | - } | |
34 | - .filter-label { | |
35 | - margin: 5px 0; | |
36 | - } | |
37 | - } | |
38 | - .tb-error-messages { | |
39 | - margin-top: -11px; | |
40 | - height: 35px; | |
41 | - .tb-error-message { | |
42 | - padding-left: 1px; | |
43 | - } | |
44 | - } | |
45 | -} | |
\ No newline at end of file |
ui/src/app/components/device-filter.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<section layout='column' class="tb-device-filter"> | |
19 | - <section layout='row'> | |
20 | - <section layout="column" flex ng-show="!model.useFilter"> | |
21 | - <md-chips flex | |
22 | - id="device_list_chips" | |
23 | - ng-required="!useFilter" | |
24 | - ng-model="model.deviceList" md-autocomplete-snap | |
25 | - md-require-match="true"> | |
26 | - <md-autocomplete | |
27 | - md-no-cache="true" | |
28 | - id="device" | |
29 | - md-selected-item="selectedDevice" | |
30 | - md-search-text="deviceSearchText" | |
31 | - md-items="item in fetchDevices(deviceSearchText, 10)" | |
32 | - md-item-text="item.name" | |
33 | - md-min-length="0" | |
34 | - placeholder="{{ 'device.device-list' | translate }}"> | |
35 | - <md-item-template> | |
36 | - <span md-highlight-text="deviceSearchText" md-highlight-flags="^i">{{item.name}}</span> | |
37 | - </md-item-template> | |
38 | - <md-not-found> | |
39 | - <span translate translate-values='{ device: deviceSearchText }'>device.no-devices-matching</span> | |
40 | - </md-not-found> | |
41 | - </md-autocomplete> | |
42 | - <md-chip-template> | |
43 | - <span> | |
44 | - <strong>{{$chip.name}}</strong> | |
45 | - </span> | |
46 | - </md-chip-template> | |
47 | - </md-chips> | |
48 | - </section> | |
49 | - <section layout="row" flex ng-show="model.useFilter"> | |
50 | - <md-input-container flex class="device-name-filter-input"> | |
51 | - <label translate>device.name-starts-with</label> | |
52 | - <input ng-model="model.deviceNameFilter" aria-label="{{ 'device.name-starts-with' | translate }}"> | |
53 | - </md-input-container> | |
54 | - </section> | |
55 | - <section class="tb-filter-switch" layout="column" layout-align="center center"> | |
56 | - <label class="tb-small filter-label" translate>device.use-device-name-filter</label> | |
57 | - <md-switch class="filter-switch" ng-model="model.useFilter" aria-label="use-filter-switcher"> | |
58 | - </md-switch> | |
59 | - </section> | |
60 | - </section> | |
61 | - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | |
62 | - <div translate ng-message="deviceList" class="tb-error-message">device.device-list-empty</div> | |
63 | - <div translate ng-message="deviceNameFilter" class="tb-error-message">device.device-name-filter-required</div> | |
64 | - <div translate translate-values='{ device: model.deviceNameFilter }' ng-message="deviceNameFilterDeviceMatch" | |
65 | - class="tb-error-message">device.device-name-filter-no-device-matched</div> | |
66 | - </div> | |
67 | -</section> | |
\ No newline at end of file |
... | ... | @@ -197,7 +197,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra |
197 | 197 | }, |
198 | 198 | |
199 | 199 | getLength: function () { |
200 | - if (vm.items.hasNext) { | |
200 | + if (vm.items.hasNext && !vm.items.pending) { | |
201 | 201 | return vm.items.rowData.length + pageSize; |
202 | 202 | } else { |
203 | 203 | return vm.items.rowData.length; |
... | ... | @@ -206,7 +206,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra |
206 | 206 | |
207 | 207 | fetchMoreItems_: function () { |
208 | 208 | if (vm.items.hasNext && !vm.items.pending) { |
209 | - var promise = vm.fetchItemsFunc(vm.items.nextPageLink); | |
209 | + var promise = vm.fetchItemsFunc(vm.items.nextPageLink, $scope.searchConfig.searchEntitySubtype); | |
210 | 210 | if (promise) { |
211 | 211 | vm.items.pending = true; |
212 | 212 | promise.then( |
... | ... | @@ -433,6 +433,10 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra |
433 | 433 | reload(); |
434 | 434 | }); |
435 | 435 | |
436 | + $scope.$on('searchEntitySubtypeUpdated', function () { | |
437 | + reload(); | |
438 | + }); | |
439 | + | |
436 | 440 | vm.onGridInited(vm); |
437 | 441 | |
438 | 442 | vm.itemRows.getItemAtIndex(pageSize); |
... | ... | @@ -441,18 +445,16 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra |
441 | 445 | if (vm.items && vm.items.pending) { |
442 | 446 | vm.items.reloadPending = true; |
443 | 447 | } else { |
444 | - vm.items = { | |
445 | - data: [], | |
446 | - rowData: [], | |
447 | - nextPageLink: { | |
448 | - limit: pageSize, | |
449 | - textSearch: $scope.searchConfig.searchText | |
450 | - }, | |
451 | - selections: {}, | |
452 | - selectedCount: 0, | |
453 | - hasNext: true, | |
454 | - pending: false | |
448 | + vm.items.data.length = 0; | |
449 | + vm.items.rowData.length = 0; | |
450 | + vm.items.nextPageLink = { | |
451 | + limit: pageSize, | |
452 | + textSearch: $scope.searchConfig.searchText | |
455 | 453 | }; |
454 | + vm.items.selections = {}; | |
455 | + vm.items.selectedCount = 0; | |
456 | + vm.items.hasNext = true; | |
457 | + vm.items.pending = false; | |
456 | 458 | vm.detailsConfig.isDetailsOpen = false; |
457 | 459 | vm.items.reloadPending = false; |
458 | 460 | vm.itemRows.getItemAtIndex(pageSize); | ... | ... |
... | ... | @@ -24,9 +24,8 @@ |
24 | 24 | <md-virtual-repeat-container ng-show="vm.hasData()" tb-scope-element="repeatContainer" id="tb-vertical-container" md-top-index="vm.topIndex" flex> |
25 | 25 | <div class="md-padding" layout="column"> |
26 | 26 | <section layout="row" md-virtual-repeat="rowItem in vm.itemRows" md-on-demand md-item-size="vm.itemHeight"> |
27 | - <div flex ng-repeat="n in [] | range:vm.columns" ng-style="{'height':vm.itemHeight+'px'}"> | |
28 | - <md-card ng-if="rowItem[n]" | |
29 | - ng-class="{'tb-current-item': vm.isCurrentItem(rowItem[n])}" | |
27 | + <div flex ng-repeat="n in [] | range:vm.columns" ng-style="{'height':vm.itemHeight+'px'}" ng-if="rowItem[n]"> | |
28 | + <md-card ng-class="{'tb-current-item': vm.isCurrentItem(rowItem[n])}" | |
30 | 29 | class="repeated-item tb-card-item" ng-style="{'height':(vm.itemHeight-16)+'px','cursor':'pointer'}" |
31 | 30 | ng-click="vm.clickItemFunc($event, rowItem[n])"> |
32 | 31 | <section layout="row" layout-wrap> |
... | ... | @@ -43,7 +42,7 @@ |
43 | 42 | </md-card-title> |
44 | 43 | </section> |
45 | 44 | <md-card-content flex> |
46 | - <tb-grid-card-content grid-ctl="vm" parent-ctl="vm.parentCtl" item-controller="vm.itemCardController" item-template="vm.itemCardTemplate" item="rowItem[n]"></tb-grid-card-content> | |
45 | + <tb-grid-card-content flex grid-ctl="vm" parent-ctl="vm.parentCtl" item-controller="vm.itemCardController" item-template="vm.itemCardTemplate" item="rowItem[n]"></tb-grid-card-content> | |
47 | 46 | </md-card-content> |
48 | 47 | <md-card-actions layout="row" layout-align="end end"> |
49 | 48 | <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList" |
... | ... | @@ -56,6 +55,8 @@ |
56 | 55 | </md-card-actions> |
57 | 56 | </md-card> |
58 | 57 | </div> |
58 | + <div flex ng-repeat="n in [] | range:vm.columns" ng-style="{'height':vm.itemHeight+'px'}" ng-if="!rowItem[n]"> | |
59 | + </div> | |
59 | 60 | </section> |
60 | 61 | </div> |
61 | 62 | </md-virtual-repeat-container> | ... | ... |
ui/src/app/dashboard/aliases-device-select-button.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | - | |
19 | -<section class="tb-aliases-device-select" layout='row' layout-align="start center" ng-style="{minHeight: '32px', padding: '0 6px'}"> | |
20 | - <md-button class="md-icon-button" aria-label="{{ 'dashboard.select-devices' | translate }}" ng-click="openEditMode($event)"> | |
21 | - <md-tooltip md-direction="{{tooltipDirection}}"> | |
22 | - {{ 'dashboard.select-devices' | translate }} | |
23 | - </md-tooltip> | |
24 | - <md-icon aria-label="{{ 'dashboard.select-devices' | translate }}" class="material-icons">devices_other</md-icon> | |
25 | - </md-button> | |
26 | - <span hide-xs hide-sm ng-click="openEditMode($event)"> | |
27 | - <md-tooltip md-direction="{{tooltipDirection}}"> | |
28 | - {{ 'dashboard.select-devices' | translate }} | |
29 | - </md-tooltip> | |
30 | - {{displayValue}} | |
31 | - </span> | |
32 | -</section> |
ui/src/app/dashboard/aliases-device-select-panel.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -/*@ngInject*/ | |
18 | -export default function AliasesDeviceSelectPanelController(mdPanelRef, $scope, types, deviceAliases, deviceAliasesInfo, onDeviceAliasesUpdate) { | |
19 | - | |
20 | - var vm = this; | |
21 | - vm._mdPanelRef = mdPanelRef; | |
22 | - vm.deviceAliases = deviceAliases; | |
23 | - vm.deviceAliasesInfo = deviceAliasesInfo; | |
24 | - vm.onDeviceAliasesUpdate = onDeviceAliasesUpdate; | |
25 | - | |
26 | - $scope.$watch('vm.deviceAliases', function () { | |
27 | - if (onDeviceAliasesUpdate) { | |
28 | - onDeviceAliasesUpdate(vm.deviceAliases); | |
29 | - } | |
30 | - }, true); | |
31 | -} |
ui/src/app/dashboard/aliases-device-select.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -import './aliases-device-select.scss'; | |
18 | - | |
19 | -import $ from 'jquery'; | |
20 | - | |
21 | -/* eslint-disable import/no-unresolved, import/default */ | |
22 | - | |
23 | -import aliasesDeviceSelectButtonTemplate from './aliases-device-select-button.tpl.html'; | |
24 | -import aliasesDeviceSelectPanelTemplate from './aliases-device-select-panel.tpl.html'; | |
25 | - | |
26 | -/* eslint-enable import/no-unresolved, import/default */ | |
27 | - | |
28 | -/* eslint-disable angular/angularelement */ | |
29 | -/*@ngInject*/ | |
30 | -export default function AliasesDeviceSelectDirective($compile, $templateCache, $mdMedia, types, $mdPanel, $document, $translate) { | |
31 | - | |
32 | - var linker = function (scope, element, attrs, ngModelCtrl) { | |
33 | - | |
34 | - /* tbAliasesDeviceSelect (ng-model) | |
35 | - * { | |
36 | - * "aliasId": { | |
37 | - * alias: alias, | |
38 | - * deviceId: deviceId | |
39 | - * } | |
40 | - * } | |
41 | - */ | |
42 | - | |
43 | - var template = $templateCache.get(aliasesDeviceSelectButtonTemplate); | |
44 | - | |
45 | - scope.tooltipDirection = angular.isDefined(attrs.tooltipDirection) ? attrs.tooltipDirection : 'top'; | |
46 | - | |
47 | - element.html(template); | |
48 | - | |
49 | - scope.openEditMode = function (event) { | |
50 | - if (scope.disabled) { | |
51 | - return; | |
52 | - } | |
53 | - var position; | |
54 | - var panelHeight = $mdMedia('min-height: 350px') ? 250 : 150; | |
55 | - var panelWidth = 300; | |
56 | - var offset = element[0].getBoundingClientRect(); | |
57 | - var bottomY = offset.bottom - $(window).scrollTop(); //eslint-disable-line | |
58 | - var leftX = offset.left - $(window).scrollLeft(); //eslint-disable-line | |
59 | - var yPosition; | |
60 | - var xPosition; | |
61 | - if (bottomY + panelHeight > $( window ).height()) { //eslint-disable-line | |
62 | - yPosition = $mdPanel.yPosition.ABOVE; | |
63 | - } else { | |
64 | - yPosition = $mdPanel.yPosition.BELOW; | |
65 | - } | |
66 | - if (leftX + panelWidth > $( window ).width()) { //eslint-disable-line | |
67 | - xPosition = $mdPanel.xPosition.CENTER; | |
68 | - } else { | |
69 | - xPosition = $mdPanel.xPosition.ALIGN_START; | |
70 | - } | |
71 | - position = $mdPanel.newPanelPosition() | |
72 | - .relativeTo(element) | |
73 | - .addPanelPosition(xPosition, yPosition); | |
74 | - var config = { | |
75 | - attachTo: angular.element($document[0].body), | |
76 | - controller: 'AliasesDeviceSelectPanelController', | |
77 | - controllerAs: 'vm', | |
78 | - templateUrl: aliasesDeviceSelectPanelTemplate, | |
79 | - panelClass: 'tb-aliases-device-select-panel', | |
80 | - position: position, | |
81 | - fullscreen: false, | |
82 | - locals: { | |
83 | - 'deviceAliases': angular.copy(scope.model), | |
84 | - 'deviceAliasesInfo': scope.deviceAliasesInfo, | |
85 | - 'onDeviceAliasesUpdate': function (deviceAliases) { | |
86 | - scope.model = deviceAliases; | |
87 | - scope.updateView(); | |
88 | - } | |
89 | - }, | |
90 | - openFrom: event, | |
91 | - clickOutsideToClose: true, | |
92 | - escapeToClose: true, | |
93 | - focusOnOpen: false | |
94 | - }; | |
95 | - $mdPanel.open(config); | |
96 | - } | |
97 | - | |
98 | - scope.updateView = function () { | |
99 | - var value = angular.copy(scope.model); | |
100 | - ngModelCtrl.$setViewValue(value); | |
101 | - updateDisplayValue(); | |
102 | - } | |
103 | - | |
104 | - ngModelCtrl.$render = function () { | |
105 | - if (ngModelCtrl.$viewValue) { | |
106 | - var value = ngModelCtrl.$viewValue; | |
107 | - scope.model = angular.copy(value); | |
108 | - updateDisplayValue(); | |
109 | - } | |
110 | - } | |
111 | - | |
112 | - function updateDisplayValue() { | |
113 | - var displayValue; | |
114 | - var singleValue = true; | |
115 | - var currentAliasId; | |
116 | - for (var aliasId in scope.model) { | |
117 | - if (!currentAliasId) { | |
118 | - currentAliasId = aliasId; | |
119 | - } else { | |
120 | - singleValue = false; | |
121 | - break; | |
122 | - } | |
123 | - } | |
124 | - if (singleValue && currentAliasId) { | |
125 | - var deviceId = scope.model[currentAliasId].deviceId; | |
126 | - var devicesInfo = scope.deviceAliasesInfo[currentAliasId]; | |
127 | - for (var i=0;i<devicesInfo.length;i++) { | |
128 | - if (devicesInfo[i].id === deviceId) { | |
129 | - displayValue = devicesInfo[i].name; | |
130 | - break; | |
131 | - } | |
132 | - } | |
133 | - } else { | |
134 | - displayValue = $translate.instant('device.devices'); | |
135 | - } | |
136 | - scope.displayValue = displayValue; | |
137 | - } | |
138 | - | |
139 | - $compile(element.contents())(scope); | |
140 | - } | |
141 | - | |
142 | - return { | |
143 | - restrict: "E", | |
144 | - require: "^ngModel", | |
145 | - scope: { | |
146 | - deviceAliasesInfo:'=' | |
147 | - }, | |
148 | - link: linker | |
149 | - }; | |
150 | - | |
151 | -} | |
152 | - | |
153 | -/* eslint-enable angular/angularelement */ | |
\ No newline at end of file |
ui/src/app/dashboard/aliases-device-select.scss
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -.md-panel { | |
18 | - &.tb-aliases-device-select-panel { | |
19 | - position: absolute; | |
20 | - } | |
21 | -} | |
22 | - | |
23 | -.tb-aliases-device-select-panel { | |
24 | - max-height: 150px; | |
25 | - @media (min-height: 350px) { | |
26 | - max-height: 250px; | |
27 | - } | |
28 | - min-width: 300px; | |
29 | - background: white; | |
30 | - border-radius: 4px; | |
31 | - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), | |
32 | - 0 13px 19px 2px rgba(0, 0, 0, 0.14), | |
33 | - 0 5px 24px 4px rgba(0, 0, 0, 0.12); | |
34 | - overflow-x: hidden; | |
35 | - overflow-y: auto; | |
36 | - md-content { | |
37 | - background-color: #fff; | |
38 | - } | |
39 | -} | |
40 | - | |
41 | -.tb-aliases-device-select { | |
42 | - span { | |
43 | - pointer-events: all; | |
44 | - cursor: pointer; | |
45 | - } | |
46 | -} |
ui/src/app/dashboard/device-aliases.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import './device-aliases.scss'; | |
17 | - | |
18 | -/*@ngInject*/ | |
19 | -export default function DeviceAliasesController(deviceService, toast, $scope, $mdDialog, $document, $q, $translate, | |
20 | - types, config) { | |
21 | - | |
22 | - var vm = this; | |
23 | - | |
24 | - vm.isSingleDeviceAlias = config.isSingleDeviceAlias; | |
25 | - vm.singleDeviceAlias = config.singleDeviceAlias; | |
26 | - vm.deviceAliases = []; | |
27 | - vm.title = config.customTitle ? config.customTitle : 'device.aliases'; | |
28 | - vm.disableAdd = config.disableAdd; | |
29 | - vm.aliasToWidgetsMap = {}; | |
30 | - | |
31 | - | |
32 | - vm.onFilterDeviceChanged = onFilterDeviceChanged; | |
33 | - vm.addAlias = addAlias; | |
34 | - vm.removeAlias = removeAlias; | |
35 | - | |
36 | - vm.cancel = cancel; | |
37 | - vm.save = save; | |
38 | - | |
39 | - initController(); | |
40 | - | |
41 | - function initController() { | |
42 | - var aliasId; | |
43 | - if (config.widgets) { | |
44 | - var widgetsTitleList, widget; | |
45 | - if (config.isSingleWidget && config.widgets.length == 1) { | |
46 | - widget = config.widgets[0]; | |
47 | - widgetsTitleList = [widget.config.title]; | |
48 | - for (aliasId in config.deviceAliases) { | |
49 | - vm.aliasToWidgetsMap[aliasId] = widgetsTitleList; | |
50 | - } | |
51 | - } else { | |
52 | - for (var w in config.widgets) { | |
53 | - widget = config.widgets[w]; | |
54 | - if (widget.type === types.widgetType.rpc.value) { | |
55 | - if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) { | |
56 | - var targetDeviceAliasId = widget.config.targetDeviceAliasIds[0]; | |
57 | - widgetsTitleList = vm.aliasToWidgetsMap[targetDeviceAliasId]; | |
58 | - if (!widgetsTitleList) { | |
59 | - widgetsTitleList = []; | |
60 | - vm.aliasToWidgetsMap[targetDeviceAliasId] = widgetsTitleList; | |
61 | - } | |
62 | - widgetsTitleList.push(widget.config.title); | |
63 | - } | |
64 | - } else { | |
65 | - for (var i in widget.config.datasources) { | |
66 | - var datasource = widget.config.datasources[i]; | |
67 | - if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) { | |
68 | - widgetsTitleList = vm.aliasToWidgetsMap[datasource.deviceAliasId]; | |
69 | - if (!widgetsTitleList) { | |
70 | - widgetsTitleList = []; | |
71 | - vm.aliasToWidgetsMap[datasource.deviceAliasId] = widgetsTitleList; | |
72 | - } | |
73 | - widgetsTitleList.push(widget.config.title); | |
74 | - } | |
75 | - } | |
76 | - } | |
77 | - } | |
78 | - } | |
79 | - } | |
80 | - | |
81 | - if (vm.isSingleDeviceAlias) { | |
82 | - if (!vm.singleDeviceAlias.deviceFilter || vm.singleDeviceAlias.deviceFilter == null) { | |
83 | - vm.singleDeviceAlias.deviceFilter = { | |
84 | - useFilter: false, | |
85 | - deviceNameFilter: '', | |
86 | - deviceList: [], | |
87 | - }; | |
88 | - } | |
89 | - } | |
90 | - | |
91 | - for (aliasId in config.deviceAliases) { | |
92 | - var deviceAlias = config.deviceAliases[aliasId]; | |
93 | - var alias = deviceAlias.alias; | |
94 | - var deviceFilter; | |
95 | - if (!deviceAlias.deviceFilter) { | |
96 | - deviceFilter = { | |
97 | - useFilter: false, | |
98 | - deviceNameFilter: '', | |
99 | - deviceList: [], | |
100 | - }; | |
101 | - if (deviceAlias.deviceId) { | |
102 | - deviceFilter.deviceList = [deviceAlias.deviceId]; | |
103 | - } else { | |
104 | - deviceFilter.deviceList = []; | |
105 | - } | |
106 | - } else { | |
107 | - deviceFilter = deviceAlias.deviceFilter; | |
108 | - } | |
109 | - var result = {id: aliasId, alias: alias, deviceFilter: deviceFilter, changed: true}; | |
110 | - vm.deviceAliases.push(result); | |
111 | - } | |
112 | - } | |
113 | - | |
114 | - function onFilterDeviceChanged(device, deviceAlias) { | |
115 | - if (deviceAlias) { | |
116 | - if (!deviceAlias.alias || deviceAlias.alias.length == 0) { | |
117 | - deviceAlias.changed = false; | |
118 | - } | |
119 | - if (!deviceAlias.changed && device) { | |
120 | - deviceAlias.alias = device.name; | |
121 | - } | |
122 | - } | |
123 | - } | |
124 | - | |
125 | - function addAlias() { | |
126 | - var aliasId = 0; | |
127 | - for (var a in vm.deviceAliases) { | |
128 | - aliasId = Math.max(vm.deviceAliases[a].id, aliasId); | |
129 | - } | |
130 | - aliasId++; | |
131 | - var deviceAlias = {id: aliasId, alias: '', deviceFilter: {useFilter: false, deviceNameFilter: '', deviceList: []}, changed: false}; | |
132 | - vm.deviceAliases.push(deviceAlias); | |
133 | - } | |
134 | - | |
135 | - function removeAlias($event, deviceAlias) { | |
136 | - var index = vm.deviceAliases.indexOf(deviceAlias); | |
137 | - if (index > -1) { | |
138 | - var widgetsTitleList = vm.aliasToWidgetsMap[deviceAlias.id]; | |
139 | - if (widgetsTitleList) { | |
140 | - var widgetsListHtml = ''; | |
141 | - for (var t in widgetsTitleList) { | |
142 | - widgetsListHtml += '<br/>\'' + widgetsTitleList[t] + '\''; | |
143 | - } | |
144 | - var alert = $mdDialog.alert() | |
145 | - .parent(angular.element($document[0].body)) | |
146 | - .clickOutsideToClose(true) | |
147 | - .title($translate.instant('device.unable-delete-device-alias-title')) | |
148 | - .htmlContent($translate.instant('device.unable-delete-device-alias-text', {deviceAlias: deviceAlias.alias, widgetsList: widgetsListHtml})) | |
149 | - .ariaLabel($translate.instant('device.unable-delete-device-alias-title')) | |
150 | - .ok($translate.instant('action.close')) | |
151 | - .targetEvent($event); | |
152 | - alert._options.skipHide = true; | |
153 | - alert._options.fullscreen = true; | |
154 | - | |
155 | - $mdDialog.show(alert); | |
156 | - } else { | |
157 | - vm.deviceAliases.splice(index, 1); | |
158 | - if ($scope.theForm) { | |
159 | - $scope.theForm.$setDirty(); | |
160 | - } | |
161 | - } | |
162 | - } | |
163 | - } | |
164 | - | |
165 | - function cancel() { | |
166 | - $mdDialog.cancel(); | |
167 | - } | |
168 | - | |
169 | - function cleanupDeviceFilter(deviceFilter) { | |
170 | - if (deviceFilter.useFilter) { | |
171 | - deviceFilter.deviceList = []; | |
172 | - } else { | |
173 | - deviceFilter.deviceNameFilter = ''; | |
174 | - } | |
175 | - return deviceFilter; | |
176 | - } | |
177 | - | |
178 | - function save() { | |
179 | - | |
180 | - var deviceAliases = {}; | |
181 | - var uniqueAliasList = {}; | |
182 | - | |
183 | - var valid = true; | |
184 | - var aliasId, maxAliasId; | |
185 | - var alias; | |
186 | - var i; | |
187 | - | |
188 | - if (vm.isSingleDeviceAlias) { | |
189 | - maxAliasId = 0; | |
190 | - vm.singleDeviceAlias.deviceFilter = cleanupDeviceFilter(vm.singleDeviceAlias.deviceFilter); | |
191 | - for (i in vm.deviceAliases) { | |
192 | - aliasId = vm.deviceAliases[i].id; | |
193 | - alias = vm.deviceAliases[i].alias; | |
194 | - if (alias === vm.singleDeviceAlias.alias) { | |
195 | - valid = false; | |
196 | - break; | |
197 | - } | |
198 | - maxAliasId = Math.max(aliasId, maxAliasId); | |
199 | - } | |
200 | - maxAliasId++; | |
201 | - vm.singleDeviceAlias.id = maxAliasId; | |
202 | - } else { | |
203 | - for (i in vm.deviceAliases) { | |
204 | - aliasId = vm.deviceAliases[i].id; | |
205 | - alias = vm.deviceAliases[i].alias; | |
206 | - if (!uniqueAliasList[alias]) { | |
207 | - uniqueAliasList[alias] = alias; | |
208 | - deviceAliases[aliasId] = {alias: alias, deviceFilter: cleanupDeviceFilter(vm.deviceAliases[i].deviceFilter)}; | |
209 | - } else { | |
210 | - valid = false; | |
211 | - break; | |
212 | - } | |
213 | - } | |
214 | - } | |
215 | - if (valid) { | |
216 | - $scope.theForm.$setPristine(); | |
217 | - if (vm.isSingleDeviceAlias) { | |
218 | - $mdDialog.hide(vm.singleDeviceAlias); | |
219 | - } else { | |
220 | - $mdDialog.hide(deviceAliases); | |
221 | - } | |
222 | - } else { | |
223 | - toast.showError($translate.instant('device.duplicate-alias-error', {alias: alias})); | |
224 | - } | |
225 | - } | |
226 | - | |
227 | -} |
ui/src/app/dashboard/device-aliases.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-dialog class="tb-aliases-dialog" style="width: 700px;" aria-label="{{ vm.title | translate }}"> | |
19 | - <form name="theForm" ng-submit="vm.save()"> | |
20 | - <md-toolbar> | |
21 | - <div class="md-toolbar-tools"> | |
22 | - <h2>{{ vm.isSingleDeviceAlias ? ('device.configure-alias' | translate:vm.singleDeviceAlias ) : (vm.title | translate) }}</h2> | |
23 | - <span flex></span> | |
24 | - <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
25 | - <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | |
26 | - </md-button> | |
27 | - </div> | |
28 | - </md-toolbar> | |
29 | - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | |
30 | - <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | |
31 | - <md-dialog-content> | |
32 | - <div class="md-dialog-content"> | |
33 | - <fieldset ng-disabled="loading"> | |
34 | - <div ng-show="vm.isSingleDeviceAlias"> | |
35 | - <tb-device-filter ng-model="vm.singleDeviceAlias.deviceFilter"> | |
36 | - </tb-device-filter> | |
37 | - </div> | |
38 | - <div ng-show="!vm.isSingleDeviceAlias" flex layout="row" layout-align="start center"> | |
39 | - <span flex="5"></span> | |
40 | - <div flex layout="row" layout-align="start center" | |
41 | - style="padding: 0 0 0 10px; margin: 5px;"> | |
42 | - <span translate flex="40" style="min-width: 100px;">device.alias</span> | |
43 | - <span translate flex="60" style="min-width: 190px; padding-left: 10px;">device.devices</span> | |
44 | - <span style="min-width: 40px;"></span> | |
45 | - </div> | |
46 | - </div> | |
47 | - <div ng-show="!vm.isSingleDeviceAlias" style="max-height: 500px; overflow: auto; padding-bottom: 20px;"> | |
48 | - <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="deviceAlias in vm.deviceAliases track by $index"> | |
49 | - <span flex="5">{{$index + 1}}.</span> | |
50 | - <div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> | |
51 | - <md-input-container flex="40" style="min-width: 100px;" md-no-float class="md-block"> | |
52 | - <input required ng-change="deviceAlias.changed=true" name="alias" placeholder="{{ 'device.alias' | translate }}" ng-model="deviceAlias.alias"> | |
53 | - <div ng-messages="aliasForm.alias.$error"> | |
54 | - <div translate ng-message="required">device.alias-required</div> | |
55 | - </div> | |
56 | - </md-input-container> | |
57 | - <section flex="60" layout="column"> | |
58 | - <tb-device-filter style="padding-left: 10px;" | |
59 | - ng-model="deviceAlias.deviceFilter" | |
60 | - on-matching-device-change="vm.onFilterDeviceChanged(device, deviceAlias)"> | |
61 | - </tb-device-filter> | |
62 | - </section> | |
63 | - <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" | |
64 | - ng-click="vm.removeAlias($event, deviceAlias)" aria-label="{{ 'action.remove' | translate }}"> | |
65 | - <md-tooltip md-direction="top"> | |
66 | - {{ 'device.remove-alias' | translate }} | |
67 | - </md-tooltip> | |
68 | - <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons"> | |
69 | - close | |
70 | - </md-icon> | |
71 | - </md-button> | |
72 | - </div> | |
73 | - </div> | |
74 | - </div> | |
75 | - <div ng-show="!vm.isSingleDeviceAlias && !vm.disableAdd" style="padding-bottom: 10px;"> | |
76 | - <md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}"> | |
77 | - <md-tooltip md-direction="top"> | |
78 | - {{ 'device.add-alias' | translate }} | |
79 | - </md-tooltip> | |
80 | - <span translate>action.add</span> | |
81 | - </md-button> | |
82 | - </div> | |
83 | - </fieldset> | |
84 | - </div> | |
85 | - </md-dialog-content> | |
86 | - <md-dialog-actions layout="row"> | |
87 | - <span flex></span> | |
88 | - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> | |
89 | - {{ 'action.save' | translate }} | |
90 | - </md-button> | |
91 | - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> | |
92 | - </md-dialog-actions> | |
93 | - </form> | |
94 | -</md-dialog> | |
\ No newline at end of file |
... | ... | @@ -58,7 +58,7 @@ |
58 | 58 | {{ 'dashboard.search-states' | translate }} |
59 | 59 | </md-tooltip> |
60 | 60 | </md-button> |
61 | - <md-input-container md-theme="tb-search-input" flex> | |
61 | + <md-input-container flex> | |
62 | 62 | <label> </label> |
63 | 63 | <input ng-model="vm.query.search" placeholder="{{ 'dashboard.search-states' | translate }}"/> |
64 | 64 | </md-input-container> | ... | ... |
ui/src/app/device/attribute/add-attribute-dialog.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/*@ngInject*/ | |
17 | -export default function AddAttributeDialogController($scope, $mdDialog, types, deviceService, deviceId, attributeScope) { | |
18 | - | |
19 | - var vm = this; | |
20 | - | |
21 | - vm.attribute = {}; | |
22 | - | |
23 | - vm.valueTypes = types.valueType; | |
24 | - | |
25 | - vm.valueType = types.valueType.string; | |
26 | - | |
27 | - vm.add = add; | |
28 | - vm.cancel = cancel; | |
29 | - | |
30 | - function cancel() { | |
31 | - $mdDialog.cancel(); | |
32 | - } | |
33 | - | |
34 | - function add() { | |
35 | - $scope.theForm.$setPristine(); | |
36 | - deviceService.saveDeviceAttributes(deviceId, attributeScope, [vm.attribute]).then( | |
37 | - function success() { | |
38 | - $mdDialog.hide(); | |
39 | - } | |
40 | - ); | |
41 | - } | |
42 | - | |
43 | - $scope.$watch('vm.valueType', function() { | |
44 | - if (vm.valueType === types.valueType.boolean) { | |
45 | - vm.attribute.value = false; | |
46 | - } else { | |
47 | - vm.attribute.value = null; | |
48 | - } | |
49 | - }); | |
50 | -} |
ui/src/app/device/attribute/add-attribute-dialog.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-dialog aria-label="{{ 'attribute.add' | translate }}" style="min-width: 400px;"> | |
19 | - <form name="theForm" ng-submit="vm.add()"> | |
20 | - <md-toolbar> | |
21 | - <div class="md-toolbar-tools"> | |
22 | - <h2 translate>attribute.add</h2> | |
23 | - <span flex></span> | |
24 | - <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
25 | - <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | |
26 | - </md-button> | |
27 | - </div> | |
28 | - </md-toolbar> | |
29 | - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | |
30 | - <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | |
31 | - <md-dialog-content> | |
32 | - <div class="md-dialog-content"> | |
33 | - <md-content class="md-padding" layout="column"> | |
34 | - <fieldset ng-disabled="loading"> | |
35 | - <md-input-container class="md-block"> | |
36 | - <label translate>attribute.key</label> | |
37 | - <input required name="key" ng-model="vm.attribute.key"> | |
38 | - <div ng-messages="theForm.key.$error"> | |
39 | - <div translate ng-message="required">attribute.key-required</div> | |
40 | - </div> | |
41 | - </md-input-container> | |
42 | - <section layout="row"> | |
43 | - <md-input-container flex="40" class="md-block" style="width: 200px;"> | |
44 | - <label translate>value.type</label> | |
45 | - <md-select ng-model="vm.valueType" ng-disabled="loading()"> | |
46 | - <md-option ng-repeat="type in vm.valueTypes" ng-value="type"> | |
47 | - <md-icon md-svg-icon="{{ type.icon }}"></md-icon> | |
48 | - <span>{{type.name | translate}}</span> | |
49 | - </md-option> | |
50 | - </md-select> | |
51 | - </md-input-container> | |
52 | - <md-input-container ng-if="vm.valueType===vm.valueTypes.string" flex="60" class="md-block"> | |
53 | - <label translate>value.string-value</label> | |
54 | - <input required name="value" ng-model="vm.attribute.value"> | |
55 | - <div ng-messages="theForm.value.$error"> | |
56 | - <div translate ng-message="required">attribute.value-required</div> | |
57 | - </div> | |
58 | - </md-input-container> | |
59 | - <md-input-container ng-if="vm.valueType===vm.valueTypes.integer" flex="60" class="md-block"> | |
60 | - <label translate>value.integer-value</label> | |
61 | - <input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="vm.attribute.value"> | |
62 | - <div ng-messages="theForm.value.$error"> | |
63 | - <div translate ng-message="required">attribute.value-required</div> | |
64 | - <div translate ng-message="pattern">value.invalid-integer-value</div> | |
65 | - </div> | |
66 | - </md-input-container> | |
67 | - <md-input-container ng-if="vm.valueType===vm.valueTypes.double" flex="60" class="md-block"> | |
68 | - <label translate>value.double-value</label> | |
69 | - <input required name="value" type="number" step="any" ng-model="vm.attribute.value"> | |
70 | - <div ng-messages="theForm.value.$error"> | |
71 | - <div translate ng-message="required">attribute.value-required</div> | |
72 | - </div> | |
73 | - </md-input-container> | |
74 | - <div layout="column" layout-align="center" flex="60" ng-if="vm.valueType===vm.valueTypes.boolean"> | |
75 | - <md-checkbox ng-model="vm.attribute.value" style="margin-bottom: 0px;"> | |
76 | - {{ (vm.attribute.value ? 'value.true' : 'value.false') | translate }} | |
77 | - </md-checkbox> | |
78 | - </div> | |
79 | - </section> | |
80 | - </fieldset> | |
81 | - </md-content> | |
82 | - </div> | |
83 | - </md-dialog-content> | |
84 | - <md-dialog-actions layout="row"> | |
85 | - <span flex></span> | |
86 | - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" | |
87 | - class="md-raised md-primary"> | |
88 | - {{ 'action.add' | translate }} | |
89 | - </md-button> | |
90 | - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | | |
91 | - translate }} | |
92 | - </md-button> | |
93 | - </md-dialog-actions> | |
94 | - </form> | |
95 | -</md-dialog> |
ui/src/app/device/attribute/add-widget-to-dashboard-dialog.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/*@ngInject*/ | |
17 | -export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, itembuffer, dashboardService, deviceId, deviceName, widget) { | |
18 | - | |
19 | - var vm = this; | |
20 | - | |
21 | - vm.widget = widget; | |
22 | - vm.dashboardId = null; | |
23 | - vm.addToDashboardType = 0; | |
24 | - vm.newDashboard = {}; | |
25 | - vm.openDashboard = false; | |
26 | - | |
27 | - vm.add = add; | |
28 | - vm.cancel = cancel; | |
29 | - | |
30 | - function cancel() { | |
31 | - $mdDialog.cancel(); | |
32 | - } | |
33 | - | |
34 | - function add() { | |
35 | - $scope.theForm.$setPristine(); | |
36 | - if (vm.addToDashboardType === 0) { | |
37 | - dashboardService.getDashboard(vm.dashboardId).then( | |
38 | - function success(dashboard) { | |
39 | - addWidgetToDashboard(dashboard); | |
40 | - }, | |
41 | - function fail() {} | |
42 | - ); | |
43 | - } else { | |
44 | - addWidgetToDashboard(vm.newDashboard); | |
45 | - } | |
46 | - | |
47 | - } | |
48 | - | |
49 | - function addWidgetToDashboard(theDashboard) { | |
50 | - var aliasesInfo = { | |
51 | - datasourceAliases: {}, | |
52 | - targetDeviceAliases: {} | |
53 | - }; | |
54 | - aliasesInfo.datasourceAliases[0] = { | |
55 | - aliasName: deviceName, | |
56 | - deviceFilter: { | |
57 | - useFilter: false, | |
58 | - deviceNameFilter: '', | |
59 | - deviceList: [deviceId] | |
60 | - } | |
61 | - }; | |
62 | - theDashboard = itembuffer.addWidgetToDashboard(theDashboard, vm.widget, aliasesInfo, null, 48, -1, -1); | |
63 | - dashboardService.saveDashboard(theDashboard).then( | |
64 | - function success(dashboard) { | |
65 | - $mdDialog.hide(); | |
66 | - if (vm.openDashboard) { | |
67 | - $state.go('home.dashboards.dashboard', {dashboardId: dashboard.id.id}); | |
68 | - } | |
69 | - } | |
70 | - ); | |
71 | - } | |
72 | - | |
73 | -} |
ui/src/app/device/attribute/add-widget-to-dashboard-dialog.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-dialog aria-label="{{ 'attribute.add-widget-to-dashboard' | translate }}" style="min-width: 400px;"> | |
19 | - <form name="theForm" ng-submit="vm.add()"> | |
20 | - <md-toolbar> | |
21 | - <div class="md-toolbar-tools"> | |
22 | - <h2 translate>attribute.add-widget-to-dashboard</h2> | |
23 | - <span flex></span> | |
24 | - <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
25 | - <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | |
26 | - </md-button> | |
27 | - </div> | |
28 | - </md-toolbar> | |
29 | - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> | |
30 | - <span style="min-height: 5px;" flex="" ng-show="!loading"></span> | |
31 | - <md-dialog-content> | |
32 | - <div class="md-dialog-content"> | |
33 | - <md-content class="md-padding" layout="column"> | |
34 | - <fieldset ng-disabled="loading"> | |
35 | - <md-radio-group ng-model="vm.addToDashboardType" class="md-primary"> | |
36 | - <md-radio-button flex ng-value=0 class="md-primary md-align-top-left md-radio-interactive"> | |
37 | - <section flex layout="column" style="width: 300px;"> | |
38 | - <span translate style="padding-bottom: 10px;">dashboard.select-existing</span> | |
39 | - <tb-dashboard-autocomplete the-form="theForm" | |
40 | - ng-disabled="loading || vm.addToDashboardType != 0" | |
41 | - tb-required="vm.addToDashboardType === 0" | |
42 | - ng-model="vm.dashboardId" | |
43 | - select-first-dashboard="false"> | |
44 | - </tb-dashboard-autocomplete> | |
45 | - </section> | |
46 | - </md-radio-button> | |
47 | - <md-radio-button flex ng-value=1 class="md-primary md-align-top-left md-radio-interactive"> | |
48 | - <section flex layout="column" style="width: 300px;"> | |
49 | - <span translate>dashboard.create-new</span> | |
50 | - <md-input-container class="md-block"> | |
51 | - <label translate>dashboard.new-dashboard-title</label> | |
52 | - <input ng-required="vm.addToDashboardType === 1" name="title" ng-model="vm.newDashboard.title"> | |
53 | - <div ng-messages="theForm.title.$error"> | |
54 | - <div translate ng-message="required">dashboard.title-required</div> | |
55 | - </div> | |
56 | - </md-input-container> | |
57 | - </section> | |
58 | - </md-radio-button> | |
59 | - </md-radio-group> | |
60 | - </fieldset> | |
61 | - </md-content> | |
62 | - </div> | |
63 | - </md-dialog-content> | |
64 | - <md-dialog-actions layout="row"> | |
65 | - <span flex></span> | |
66 | - <md-checkbox | |
67 | - ng-model="vm.openDashboard" | |
68 | - aria-label="{{ 'dashboard.open-dashboard' | translate }}" | |
69 | - style="margin-bottom: 0px; padding-right: 20px;"> | |
70 | - {{ 'dashboard.open-dashboard' | translate }} | |
71 | - </md-checkbox> | |
72 | - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" | |
73 | - class="md-raised md-primary"> | |
74 | - {{ 'action.add' | translate }} | |
75 | - </md-button> | |
76 | - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | | |
77 | - translate }} | |
78 | - </md-button> | |
79 | - </md-dialog-actions> | |
80 | - </form> | |
81 | -</md-dialog> |
ui/src/app/device/attribute/attribute-table.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import 'angular-material-data-table/dist/md-data-table.min.css'; | |
17 | -import './attribute-table.scss'; | |
18 | - | |
19 | -/* eslint-disable import/no-unresolved, import/default */ | |
20 | - | |
21 | -import attributeTableTemplate from './attribute-table.tpl.html'; | |
22 | -import addAttributeDialogTemplate from './add-attribute-dialog.tpl.html'; | |
23 | -import addWidgetToDashboardDialogTemplate from './add-widget-to-dashboard-dialog.tpl.html'; | |
24 | -import editAttributeValueTemplate from './edit-attribute-value.tpl.html'; | |
25 | - | |
26 | -/* eslint-enable import/no-unresolved, import/default */ | |
27 | - | |
28 | -import EditAttributeValueController from './edit-attribute-value.controller'; | |
29 | - | |
30 | -/*@ngInject*/ | |
31 | -export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog, | |
32 | - $document, $translate, $filter, utils, types, dashboardService, deviceService, widgetService) { | |
33 | - | |
34 | - var linker = function (scope, element, attrs) { | |
35 | - | |
36 | - var template = $templateCache.get(attributeTableTemplate); | |
37 | - | |
38 | - element.html(template); | |
39 | - | |
40 | - scope.types = types; | |
41 | - scope.attributeScopes = types.deviceAttributesScope; | |
42 | - | |
43 | - var getAttributeScopeByValue = function(attributeScopeValue) { | |
44 | - if (scope.types.latestTelemetry.value === attributeScopeValue) { | |
45 | - return scope.types.latestTelemetry; | |
46 | - } | |
47 | - for (var attrScope in scope.attributeScopes) { | |
48 | - if (scope.attributeScopes[attrScope].value === attributeScopeValue) { | |
49 | - return scope.attributeScopes[attrScope]; | |
50 | - } | |
51 | - } | |
52 | - } | |
53 | - | |
54 | - scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope); | |
55 | - | |
56 | - scope.attributes = { | |
57 | - count: 0, | |
58 | - data: [] | |
59 | - }; | |
60 | - | |
61 | - scope.selectedAttributes = []; | |
62 | - scope.mode = 'default'; // 'widget' | |
63 | - scope.subscriptionId = null; | |
64 | - | |
65 | - scope.query = { | |
66 | - order: 'key', | |
67 | - limit: 5, | |
68 | - page: 1, | |
69 | - search: null | |
70 | - }; | |
71 | - | |
72 | - scope.$watch("deviceId", function(newVal, prevVal) { | |
73 | - if (newVal && !angular.equals(newVal, prevVal)) { | |
74 | - scope.resetFilter(); | |
75 | - scope.getDeviceAttributes(false, true); | |
76 | - } | |
77 | - }); | |
78 | - | |
79 | - scope.$watch("attributeScope", function(newVal, prevVal) { | |
80 | - if (newVal && !angular.equals(newVal, prevVal)) { | |
81 | - scope.mode = 'default'; | |
82 | - scope.query.search = null; | |
83 | - scope.selectedAttributes = []; | |
84 | - scope.getDeviceAttributes(false, true); | |
85 | - } | |
86 | - }); | |
87 | - | |
88 | - scope.resetFilter = function() { | |
89 | - scope.mode = 'default'; | |
90 | - scope.query.search = null; | |
91 | - scope.selectedAttributes = []; | |
92 | - scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope); | |
93 | - } | |
94 | - | |
95 | - scope.enterFilterMode = function() { | |
96 | - scope.query.search = ''; | |
97 | - } | |
98 | - | |
99 | - scope.exitFilterMode = function() { | |
100 | - scope.query.search = null; | |
101 | - scope.getDeviceAttributes(); | |
102 | - } | |
103 | - | |
104 | - scope.$watch("query.search", function(newVal, prevVal) { | |
105 | - if (!angular.equals(newVal, prevVal) && scope.query.search != null) { | |
106 | - scope.getDeviceAttributes(); | |
107 | - } | |
108 | - }); | |
109 | - | |
110 | - function success(attributes, update, apply) { | |
111 | - scope.attributes = attributes; | |
112 | - if (!update) { | |
113 | - scope.selectedAttributes = []; | |
114 | - } | |
115 | - if (apply) { | |
116 | - scope.$digest(); | |
117 | - } | |
118 | - } | |
119 | - | |
120 | - scope.onReorder = function() { | |
121 | - scope.getDeviceAttributes(false, false); | |
122 | - } | |
123 | - | |
124 | - scope.onPaginate = function() { | |
125 | - scope.getDeviceAttributes(false, false); | |
126 | - } | |
127 | - | |
128 | - scope.getDeviceAttributes = function(forceUpdate, reset) { | |
129 | - if (scope.attributesDeferred) { | |
130 | - scope.attributesDeferred.resolve(); | |
131 | - } | |
132 | - if (scope.deviceId && scope.attributeScope) { | |
133 | - if (reset) { | |
134 | - scope.attributes = { | |
135 | - count: 0, | |
136 | - data: [] | |
137 | - }; | |
138 | - } | |
139 | - scope.checkSubscription(); | |
140 | - scope.attributesDeferred = deviceService.getDeviceAttributes(scope.deviceId, scope.attributeScope.value, | |
141 | - scope.query, function(attributes, update, apply) { | |
142 | - success(attributes, update || forceUpdate, apply); | |
143 | - } | |
144 | - ); | |
145 | - } else { | |
146 | - var deferred = $q.defer(); | |
147 | - scope.attributesDeferred = deferred; | |
148 | - success({ | |
149 | - count: 0, | |
150 | - data: [] | |
151 | - }); | |
152 | - deferred.resolve(); | |
153 | - } | |
154 | - } | |
155 | - | |
156 | - scope.checkSubscription = function() { | |
157 | - var newSubscriptionId = null; | |
158 | - if (scope.deviceId && scope.attributeScope.clientSide && scope.mode != 'widget') { | |
159 | - newSubscriptionId = deviceService.subscribeForDeviceAttributes(scope.deviceId, scope.attributeScope.value); | |
160 | - } | |
161 | - if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) { | |
162 | - deviceService.unsubscribeForDeviceAttributes(scope.subscriptionId); | |
163 | - } | |
164 | - scope.subscriptionId = newSubscriptionId; | |
165 | - } | |
166 | - | |
167 | - scope.$on('$destroy', function() { | |
168 | - if (scope.subscriptionId) { | |
169 | - deviceService.unsubscribeForDeviceAttributes(scope.subscriptionId); | |
170 | - } | |
171 | - }); | |
172 | - | |
173 | - scope.editAttribute = function($event, attribute) { | |
174 | - if (!scope.attributeScope.clientSide) { | |
175 | - $event.stopPropagation(); | |
176 | - $mdEditDialog.show({ | |
177 | - controller: EditAttributeValueController, | |
178 | - templateUrl: editAttributeValueTemplate, | |
179 | - locals: {attributeValue: attribute.value, | |
180 | - save: function (model) { | |
181 | - var updatedAttribute = angular.copy(attribute); | |
182 | - updatedAttribute.value = model.value; | |
183 | - deviceService.saveDeviceAttributes(scope.deviceId, scope.attributeScope.value, [updatedAttribute]).then( | |
184 | - function success() { | |
185 | - scope.getDeviceAttributes(); | |
186 | - } | |
187 | - ); | |
188 | - }}, | |
189 | - targetEvent: $event | |
190 | - }); | |
191 | - } | |
192 | - } | |
193 | - | |
194 | - scope.addAttribute = function($event) { | |
195 | - if (!scope.attributeScope.clientSide) { | |
196 | - $event.stopPropagation(); | |
197 | - $mdDialog.show({ | |
198 | - controller: 'AddAttributeDialogController', | |
199 | - controllerAs: 'vm', | |
200 | - templateUrl: addAttributeDialogTemplate, | |
201 | - parent: angular.element($document[0].body), | |
202 | - locals: {deviceId: scope.deviceId, attributeScope: scope.attributeScope.value}, | |
203 | - fullscreen: true, | |
204 | - targetEvent: $event | |
205 | - }).then(function () { | |
206 | - scope.getDeviceAttributes(); | |
207 | - }); | |
208 | - } | |
209 | - } | |
210 | - | |
211 | - scope.deleteAttributes = function($event) { | |
212 | - if (!scope.attributeScope.clientSide) { | |
213 | - $event.stopPropagation(); | |
214 | - var confirm = $mdDialog.confirm() | |
215 | - .targetEvent($event) | |
216 | - .title($translate.instant('attribute.delete-attributes-title', {count: scope.selectedAttributes.length}, 'messageformat')) | |
217 | - .htmlContent($translate.instant('attribute.delete-attributes-text')) | |
218 | - .ariaLabel($translate.instant('attribute.delete-attributes')) | |
219 | - .cancel($translate.instant('action.no')) | |
220 | - .ok($translate.instant('action.yes')); | |
221 | - $mdDialog.show(confirm).then(function () { | |
222 | - deviceService.deleteDeviceAttributes(scope.deviceId, scope.attributeScope.value, scope.selectedAttributes).then( | |
223 | - function success() { | |
224 | - scope.selectedAttributes = []; | |
225 | - scope.getDeviceAttributes(); | |
226 | - } | |
227 | - ) | |
228 | - }); | |
229 | - } | |
230 | - } | |
231 | - | |
232 | - scope.nextWidget = function() { | |
233 | - if (scope.widgetsCarousel.index < scope.widgetsList.length-1) { | |
234 | - scope.widgetsCarousel.index++; | |
235 | - } | |
236 | - } | |
237 | - | |
238 | - scope.prevWidget = function() { | |
239 | - if (scope.widgetsCarousel.index > 0) { | |
240 | - scope.widgetsCarousel.index--; | |
241 | - } | |
242 | - } | |
243 | - | |
244 | - scope.enterWidgetMode = function() { | |
245 | - | |
246 | - if (scope.widgetsIndexWatch) { | |
247 | - scope.widgetsIndexWatch(); | |
248 | - scope.widgetsIndexWatch = null; | |
249 | - } | |
250 | - | |
251 | - if (scope.widgetsBundleWatch) { | |
252 | - scope.widgetsBundleWatch(); | |
253 | - scope.widgetsBundleWatch = null; | |
254 | - } | |
255 | - | |
256 | - scope.mode = 'widget'; | |
257 | - scope.checkSubscription(); | |
258 | - scope.widgetsList = []; | |
259 | - scope.widgetsListCache = []; | |
260 | - scope.widgetsLoaded = false; | |
261 | - scope.widgetsCarousel = { | |
262 | - index: 0 | |
263 | - } | |
264 | - scope.widgetsBundle = null; | |
265 | - scope.firstBundle = true; | |
266 | - scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards; | |
267 | - | |
268 | - scope.aliasesInfo = { | |
269 | - deviceAliases: { | |
270 | - '1': {alias: scope.deviceName, deviceId: scope.deviceId} | |
271 | - }, | |
272 | - deviceAliasesInfo: { | |
273 | - '1': [ | |
274 | - {name: scope.deviceName, id: scope.deviceId} | |
275 | - ] | |
276 | - } | |
277 | - }; | |
278 | - | |
279 | - var dataKeyType = scope.attributeScope === types.latestTelemetry ? | |
280 | - types.dataKeyType.timeseries : types.dataKeyType.attribute; | |
281 | - | |
282 | - var datasource = { | |
283 | - type: types.datasourceType.device, | |
284 | - deviceAliasId: '1', | |
285 | - dataKeys: [] | |
286 | - } | |
287 | - var i = 0; | |
288 | - for (var attr =0; attr < scope.selectedAttributes.length;attr++) { | |
289 | - var attribute = scope.selectedAttributes[attr]; | |
290 | - var dataKey = { | |
291 | - name: attribute.key, | |
292 | - label: attribute.key, | |
293 | - type: dataKeyType, | |
294 | - color: utils.getMaterialColor(i), | |
295 | - settings: {}, | |
296 | - _hash: Math.random() | |
297 | - } | |
298 | - datasource.dataKeys.push(dataKey); | |
299 | - i++; | |
300 | - } | |
301 | - | |
302 | - scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function(newVal, prevVal) { | |
303 | - if (scope.mode === 'widget' && (newVal != prevVal)) { | |
304 | - var index = scope.widgetsCarousel.index; | |
305 | - for (var i = 0; i < scope.widgetsList.length; i++) { | |
306 | - scope.widgetsList[i].splice(0, scope.widgetsList[i].length); | |
307 | - if (i === index) { | |
308 | - scope.widgetsList[i].push(scope.widgetsListCache[i][0]); | |
309 | - } | |
310 | - } | |
311 | - } | |
312 | - }); | |
313 | - | |
314 | - scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function(newVal, prevVal) { | |
315 | - if (scope.mode === 'widget' && (scope.firstBundle === true || newVal != prevVal)) { | |
316 | - scope.widgetsList = []; | |
317 | - scope.widgetsListCache = []; | |
318 | - scope.widgetsCarousel.index = 0; | |
319 | - scope.firstBundle = false; | |
320 | - if (scope.widgetsBundle) { | |
321 | - scope.widgetsLoaded = false; | |
322 | - var bundleAlias = scope.widgetsBundle.alias; | |
323 | - var isSystem = scope.widgetsBundle.tenantId.id === types.id.nullUid; | |
324 | - widgetService.getBundleWidgetTypes(scope.widgetsBundle.alias, isSystem).then( | |
325 | - function success(widgetTypes) { | |
326 | - | |
327 | - widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type','-createdTime']); | |
328 | - | |
329 | - for (var i = 0; i < widgetTypes.length; i++) { | |
330 | - var widgetType = widgetTypes[i]; | |
331 | - var widgetInfo = widgetService.toWidgetInfo(widgetType); | |
332 | - if (widgetInfo.type !== types.widgetType.static.value) { | |
333 | - var sizeX = widgetInfo.sizeX * 2; | |
334 | - var sizeY = widgetInfo.sizeY * 2; | |
335 | - var col = Math.floor(Math.max(0, (20 - sizeX) / 2)); | |
336 | - var widget = { | |
337 | - isSystemType: isSystem, | |
338 | - bundleAlias: bundleAlias, | |
339 | - typeAlias: widgetInfo.alias, | |
340 | - type: widgetInfo.type, | |
341 | - title: widgetInfo.widgetName, | |
342 | - sizeX: sizeX, | |
343 | - sizeY: sizeY, | |
344 | - row: 0, | |
345 | - col: col, | |
346 | - config: angular.fromJson(widgetInfo.defaultConfig) | |
347 | - }; | |
348 | - | |
349 | - widget.config.title = widgetInfo.widgetName; | |
350 | - widget.config.datasources = [datasource]; | |
351 | - var length; | |
352 | - if (scope.attributeScope === types.latestTelemetry && widgetInfo.type !== types.widgetType.rpc.value) { | |
353 | - length = scope.widgetsListCache.push([widget]); | |
354 | - scope.widgetsList.push(length === 1 ? [widget] : []); | |
355 | - } else if (widgetInfo.type === types.widgetType.latest.value) { | |
356 | - length = scope.widgetsListCache.push([widget]); | |
357 | - scope.widgetsList.push(length === 1 ? [widget] : []); | |
358 | - } | |
359 | - } | |
360 | - } | |
361 | - scope.widgetsLoaded = true; | |
362 | - } | |
363 | - ); | |
364 | - } | |
365 | - } | |
366 | - }); | |
367 | - } | |
368 | - | |
369 | - scope.exitWidgetMode = function() { | |
370 | - if (scope.widgetsBundleWatch) { | |
371 | - scope.widgetsBundleWatch(); | |
372 | - scope.widgetsBundleWatch = null; | |
373 | - } | |
374 | - if (scope.widgetsIndexWatch) { | |
375 | - scope.widgetsIndexWatch(); | |
376 | - scope.widgetsIndexWatch = null; | |
377 | - } | |
378 | - scope.selectedWidgetsBundleAlias = null; | |
379 | - scope.mode = 'default'; | |
380 | - scope.getDeviceAttributes(true); | |
381 | - } | |
382 | - | |
383 | - scope.getServerTimeDiff = function() { | |
384 | - return dashboardService.getServerTimeDiff(); | |
385 | - } | |
386 | - | |
387 | - scope.addWidgetToDashboard = function($event) { | |
388 | - if (scope.mode === 'widget' && scope.widgetsListCache.length > 0) { | |
389 | - var widget = scope.widgetsListCache[scope.widgetsCarousel.index][0]; | |
390 | - $event.stopPropagation(); | |
391 | - $mdDialog.show({ | |
392 | - controller: 'AddWidgetToDashboardDialogController', | |
393 | - controllerAs: 'vm', | |
394 | - templateUrl: addWidgetToDashboardDialogTemplate, | |
395 | - parent: angular.element($document[0].body), | |
396 | - locals: {deviceId: scope.deviceId, deviceName: scope.deviceName, widget: angular.copy(widget)}, | |
397 | - fullscreen: true, | |
398 | - targetEvent: $event | |
399 | - }).then(function () { | |
400 | - | |
401 | - }); | |
402 | - } | |
403 | - } | |
404 | - | |
405 | - scope.loading = function() { | |
406 | - return $rootScope.loading; | |
407 | - } | |
408 | - | |
409 | - $compile(element.contents())(scope); | |
410 | - } | |
411 | - | |
412 | - return { | |
413 | - restrict: "E", | |
414 | - link: linker, | |
415 | - scope: { | |
416 | - deviceId: '=', | |
417 | - deviceName: '=', | |
418 | - disableAttributeScopeSelection: '@?' | |
419 | - } | |
420 | - }; | |
421 | -} |
ui/src/app/device/attribute/attribute-table.scss
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -@import '../../../scss/constants'; | |
17 | - | |
18 | -$md-light: rgba(255, 255, 255, 100%); | |
19 | -$md-edit-icon-fill: #757575; | |
20 | - | |
21 | -md-toolbar.md-table-toolbar.alternate { | |
22 | - .md-toolbar-tools { | |
23 | - md-icon { | |
24 | - color: $md-light; | |
25 | - } | |
26 | - } | |
27 | -} | |
28 | - | |
29 | -.md-table { | |
30 | - .md-cell { | |
31 | - ng-md-icon { | |
32 | - fill: $md-edit-icon-fill; | |
33 | - float: right; | |
34 | - height: 16px; | |
35 | - } | |
36 | - } | |
37 | -} | |
38 | - | |
39 | -.widgets-carousel { | |
40 | - position: relative; | |
41 | - margin: 0px; | |
42 | - | |
43 | - min-height: 150px !important; | |
44 | - | |
45 | - tb-dashboard { | |
46 | - #gridster-parent { | |
47 | - padding: 0 7px; | |
48 | - } | |
49 | - } | |
50 | -} | |
\ No newline at end of file |
ui/src/app/device/attribute/attribute-table.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-content flex class="md-padding tb-absolute-fill" layout="column"> | |
19 | - <section layout="row" ng-show="!disableAttributeScopeSelection"> | |
20 | - <md-input-container class="md-block" style="width: 200px;"> | |
21 | - <label translate>attribute.attributes-scope</label> | |
22 | - <md-select ng-model="attributeScope" ng-disabled="loading()"> | |
23 | - <md-option ng-repeat="scope in attributeScopes" ng-value="scope"> | |
24 | - {{scope.name | translate}} | |
25 | - </md-option> | |
26 | - </md-select> | |
27 | - </md-input-container> | |
28 | - </section> | |
29 | - <div layout="column" class="md-whiteframe-z1" ng-class="{flex: mode==='widget'}"> | |
30 | - <md-toolbar class="md-table-toolbar md-default" ng-show="mode==='default' | |
31 | - && !selectedAttributes.length | |
32 | - && query.search === null"> | |
33 | - <div class="md-toolbar-tools"> | |
34 | - <span translate>{{ attributeScope.name }}</span> | |
35 | - <span flex></span> | |
36 | - <md-button ng-show="!attributeScope.clientSide" class="md-icon-button" ng-click="addAttribute($event)"> | |
37 | - <md-icon>add</md-icon> | |
38 | - <md-tooltip md-direction="top"> | |
39 | - {{ 'action.add' | translate }} | |
40 | - </md-tooltip> | |
41 | - </md-button> | |
42 | - <md-button class="md-icon-button" ng-click="enterFilterMode()"> | |
43 | - <md-icon>search</md-icon> | |
44 | - <md-tooltip md-direction="top"> | |
45 | - {{ 'action.search' | translate }} | |
46 | - </md-tooltip> | |
47 | - </md-button> | |
48 | - <md-button ng-show="!attributeScope.clientSide" class="md-icon-button" ng-click="getDeviceAttributes()"> | |
49 | - <md-icon>refresh</md-icon> | |
50 | - <md-tooltip md-direction="top"> | |
51 | - {{ 'action.refresh' | translate }} | |
52 | - </md-tooltip> | |
53 | - </md-button> | |
54 | - </div> | |
55 | - </md-toolbar> | |
56 | - <md-toolbar class="md-table-toolbar md-default" ng-show="mode==='default' | |
57 | - && !selectedAttributes.length | |
58 | - && query.search != null"> | |
59 | - <div class="md-toolbar-tools"> | |
60 | - <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> | |
61 | - <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> | |
62 | - <md-tooltip md-direction="top"> | |
63 | - {{ 'action.search' | translate }} | |
64 | - </md-tooltip> | |
65 | - </md-button> | |
66 | - <md-input-container md-theme="tb-search-input" flex> | |
67 | - <label> </label> | |
68 | - <input ng-model="query.search" placeholder="{{ 'common.enter-search' | translate }}"/> | |
69 | - </md-input-container> | |
70 | - <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="exitFilterMode()"> | |
71 | - <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon> | |
72 | - <md-tooltip md-direction="top"> | |
73 | - {{ 'action.close' | translate }} | |
74 | - </md-tooltip> | |
75 | - </md-button> | |
76 | - </div> | |
77 | - </md-toolbar> | |
78 | - <md-toolbar class="md-table-toolbar alternate" ng-show="mode==='default' && selectedAttributes.length"> | |
79 | - <div class="md-toolbar-tools"> | |
80 | - <span translate="{{attributeScope === types.latestTelemetry | |
81 | - ? 'attribute.selected-telemetry' | |
82 | - : 'attribute.selected-attributes'}}" | |
83 | - translate-values="{count: selectedAttributes.length}" | |
84 | - translate-interpolation="messageformat"></span> | |
85 | - <span flex></span> | |
86 | - <md-button ng-show="!attributeScope.clientSide" class="md-icon-button" ng-click="deleteAttributes($event)"> | |
87 | - <md-icon>delete</md-icon> | |
88 | - <md-tooltip md-direction="top"> | |
89 | - {{ 'action.delete' | translate }} | |
90 | - </md-tooltip> | |
91 | - </md-button> | |
92 | - <md-button ng-show="attributeScope.clientSide" class="md-accent md-hue-2 md-raised" ng-click="enterWidgetMode()"> | |
93 | - <md-tooltip md-direction="top"> | |
94 | - {{ 'attribute.show-on-widget' | translate }} | |
95 | - </md-tooltip> | |
96 | - <md-icon>now_widgets</md-icon> | |
97 | - <span translate>attribute.show-on-widget</span> | |
98 | - </md-button> | |
99 | - </div> | |
100 | - </md-toolbar> | |
101 | - <md-toolbar class="md-table-toolbar alternate" ng-show="mode==='widget'"> | |
102 | - <div class="md-toolbar-tools"> | |
103 | - <div flex layout="row" layout-align="start"> | |
104 | - <span class="tb-details-subtitle">{{ 'widgets-bundle.current' | translate }}</span> | |
105 | - <tb-widgets-bundle-select flex-offset="5" | |
106 | - flex | |
107 | - ng-model="widgetsBundle" | |
108 | - select-first-bundle="false" | |
109 | - select-bundle-alias="selectedWidgetsBundleAlias"> | |
110 | - </tb-widgets-bundle-select> | |
111 | - </div> | |
112 | - <md-button ng-show="widgetsList.length > 0" class="md-accent md-hue-2 md-raised" ng-click="addWidgetToDashboard($event)"> | |
113 | - <md-tooltip md-direction="top"> | |
114 | - {{ 'attribute.add-to-dashboard' | translate }} | |
115 | - </md-tooltip> | |
116 | - <md-icon>dashboard</md-icon> | |
117 | - <span translate>attribute.add-to-dashboard</span> | |
118 | - </md-button> | |
119 | - <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="exitWidgetMode()"> | |
120 | - <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon> | |
121 | - <md-tooltip md-direction="top"> | |
122 | - {{ 'action.close' | translate }} | |
123 | - </md-tooltip> | |
124 | - </md-button> | |
125 | - </div> | |
126 | - </md-toolbar> | |
127 | - <md-table-container ng-show="mode!='widget'"> | |
128 | - <table md-table md-row-select multiple="" ng-model="selectedAttributes" md-progress="attributesDeferred.promise"> | |
129 | - <thead md-head md-order="query.order" md-on-reorder="onReorder"> | |
130 | - <tr md-row> | |
131 | - <th md-column md-order-by="lastUpdateTs"><span>Last update time</span></th> | |
132 | - <th md-column md-order-by="key"><span>Key</span></th> | |
133 | - <th md-column>Value</th> | |
134 | - </tr> | |
135 | - </thead> | |
136 | - <tbody md-body> | |
137 | - <tr md-row md-select="attribute" md-select-id="key" md-auto-select ng-repeat="attribute in attributes.data"> | |
138 | - <td md-cell>{{attribute.lastUpdateTs | date : 'yyyy-MM-dd HH:mm:ss'}}</td> | |
139 | - <td md-cell>{{attribute.key}}</td> | |
140 | - <td md-cell ng-click="editAttribute($event, attribute)"> | |
141 | - <span>{{attribute.value}}</span> | |
142 | - <span ng-show="!attributeScope.clientSide"><ng-md-icon size="16" icon="edit"></ng-md-icon></span> | |
143 | - </td> | |
144 | - </tr> | |
145 | - </tbody> | |
146 | - </table> | |
147 | - </md-table-container> | |
148 | - <md-table-pagination ng-show="mode!='widget'" md-limit="query.limit" md-limit-options="[5, 10, 15]" | |
149 | - md-page="query.page" md-total="{{attributes.count}}" | |
150 | - md-on-paginate="onPaginate" md-page-select> | |
151 | - </md-table-pagination> | |
152 | - <ul flex rn-carousel ng-if="mode==='widget'" class="widgets-carousel" | |
153 | - rn-carousel-index="widgetsCarousel.index" | |
154 | - rn-carousel-buffered | |
155 | - rn-carousel-transition="fadeAndSlide" | |
156 | - rn-swipe-disabled="true"> | |
157 | - <li ng-repeat="widgets in widgetsList"> | |
158 | - <tb-dashboard | |
159 | - aliases-info="aliasesInfo" | |
160 | - widgets="widgets" | |
161 | - get-st-diff="getServerTimeDiff()" | |
162 | - columns="20" | |
163 | - is-edit="false" | |
164 | - is-mobile-disabled="true" | |
165 | - is-edit-action-enabled="false" | |
166 | - is-remove-action-enabled="false"> | |
167 | - </tb-dashboard> | |
168 | - </li> | |
169 | - <span translate ng-if="widgetsLoaded && | |
170 | - widgetsList.length === 0 && | |
171 | - widgetsBundle" | |
172 | - layout-align="center center" | |
173 | - style="text-transform: uppercase; display: flex;" | |
174 | - class="md-headline tb-absolute-fill">widgets-bundle.empty</span> | |
175 | - <span translate ng-if="!widgetsBundle" | |
176 | - layout-align="center center" | |
177 | - style="text-transform: uppercase; display: flex;" | |
178 | - class="md-headline tb-absolute-fill">widget.select-widgets-bundle</span> | |
179 | - <div ng-show="widgetsList.length > 1" | |
180 | - style="position: absolute; left: 0; height: 100%;" layout="column" layout-align="center"> | |
181 | - <md-button ng-show="widgetsCarousel.index > 0" | |
182 | - class="md-icon-button" | |
183 | - ng-click="prevWidget()"> | |
184 | - <md-icon>navigate_before</md-icon> | |
185 | - <md-tooltip md-direction="top"> | |
186 | - {{ 'attribute.prev-widget' | translate }} | |
187 | - </md-tooltip> | |
188 | - </md-button> | |
189 | - </div> | |
190 | - <div ng-show="widgetsList.length > 1" | |
191 | - style="position: absolute; right: 0; height: 100%;" layout="column" layout-align="center"> | |
192 | - <md-button ng-show="widgetsCarousel.index < widgetsList.length-1" | |
193 | - class="md-icon-button" | |
194 | - ng-click="nextWidget()"> | |
195 | - <md-icon>navigate_next</md-icon> | |
196 | - <md-tooltip md-direction="top"> | |
197 | - {{ 'attribute.next-widget' | translate }} | |
198 | - </md-tooltip> | |
199 | - </md-button> | |
200 | - </div> | |
201 | - <div style="position: absolute; bottom: 0; width: 100%; font-size: 24px;" layout="row" layout-align="center"> | |
202 | - <div rn-carousel-indicators | |
203 | - ng-if="widgetsList.length > 1" | |
204 | - slides="widgetsList" | |
205 | - rn-carousel-index="widgetsCarousel.index"> | |
206 | - </div> | |
207 | - </div> | |
208 | - </ul> | |
209 | - </div> | |
210 | -</md-content> |
ui/src/app/device/attribute/edit-attribute-value.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/*@ngInject*/ | |
17 | -export default function EditAttributeValueController($scope, $q, $element, types, attributeValue, save) { | |
18 | - | |
19 | - $scope.valueTypes = types.valueType; | |
20 | - | |
21 | - $scope.model = {}; | |
22 | - | |
23 | - $scope.model.value = attributeValue; | |
24 | - | |
25 | - if ($scope.model.value === true || $scope.model.value === false) { | |
26 | - $scope.valueType = types.valueType.boolean; | |
27 | - } else if (angular.isNumber($scope.model.value)) { | |
28 | - if ($scope.model.value.toString().indexOf('.') == -1) { | |
29 | - $scope.valueType = types.valueType.integer; | |
30 | - } else { | |
31 | - $scope.valueType = types.valueType.double; | |
32 | - } | |
33 | - } else { | |
34 | - $scope.valueType = types.valueType.string; | |
35 | - } | |
36 | - | |
37 | - $scope.submit = submit; | |
38 | - $scope.dismiss = dismiss; | |
39 | - | |
40 | - function dismiss() { | |
41 | - $element.remove(); | |
42 | - } | |
43 | - | |
44 | - function update() { | |
45 | - if($scope.editDialog.$invalid) { | |
46 | - return $q.reject(); | |
47 | - } | |
48 | - | |
49 | - if(angular.isFunction(save)) { | |
50 | - return $q.when(save($scope.model)); | |
51 | - } | |
52 | - | |
53 | - return $q.resolve(); | |
54 | - } | |
55 | - | |
56 | - function submit() { | |
57 | - update().then(function () { | |
58 | - $scope.dismiss(); | |
59 | - }); | |
60 | - } | |
61 | - | |
62 | - $scope.$watch('valueType', function(newVal, prevVal) { | |
63 | - if (newVal != prevVal) { | |
64 | - if ($scope.valueType === types.valueType.boolean) { | |
65 | - $scope.model.value = false; | |
66 | - } else { | |
67 | - $scope.model.value = null; | |
68 | - } | |
69 | - } | |
70 | - }); | |
71 | -} |
ui/src/app/device/attribute/edit-attribute-value.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2017 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-edit-dialog> | |
19 | - <form name="editDialog" ng-submit="submit()"> | |
20 | - <div layout="column" class="md-content" style="width: 400px;"> | |
21 | - <fieldset> | |
22 | - <section layout="row"> | |
23 | - <md-input-container flex="40" class="md-block"> | |
24 | - <label translate>value.type</label> | |
25 | - <md-select ng-model="valueType"> | |
26 | - <md-option ng-repeat="type in valueTypes" ng-value="type"> | |
27 | - <md-icon md-svg-icon="{{ type.icon }}"></md-icon> | |
28 | - <span>{{type.name | translate}}</span> | |
29 | - </md-option> | |
30 | - </md-select> | |
31 | - </md-input-container> | |
32 | - <md-input-container ng-if="valueType===valueTypes.string" flex="60" class="md-block"> | |
33 | - <label translate>value.string-value</label> | |
34 | - <input required name="value" ng-model="model.value"> | |
35 | - <div ng-messages="editDialog.value.$error"> | |
36 | - <div translate ng-message="required">attribute.value-required</div> | |
37 | - </div> | |
38 | - </md-input-container> | |
39 | - <md-input-container ng-if="valueType===valueTypes.integer" flex="60" class="md-block"> | |
40 | - <label translate>value.integer-value</label> | |
41 | - <input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="model.value"> | |
42 | - <div ng-messages="editDialog.value.$error"> | |
43 | - <div translate ng-message="required">attribute.value-required</div> | |
44 | - <div translate ng-message="pattern">value.invalid-integer-value</div> | |
45 | - </div> | |
46 | - </md-input-container> | |
47 | - <md-input-container ng-if="valueType===valueTypes.double" flex="60" class="md-block"> | |
48 | - <label translate>value.double-value</label> | |
49 | - <input required name="value" type="number" step="any" ng-model="model.value"> | |
50 | - <div ng-messages="editDialog.value.$error"> | |
51 | - <div translate ng-message="required">attribute.value-required</div> | |
52 | - </div> | |
53 | - </md-input-container> | |
54 | - <div layout="column" layout-align="center" flex="60" ng-if="valueType===valueTypes.boolean"> | |
55 | - <md-checkbox ng-model="model.value" style="margin-bottom: 0px;"> | |
56 | - {{ (model.value ? 'value.true' : 'value.false') | translate }} | |
57 | - </md-checkbox> | |
58 | - </div> | |
59 | - </section> | |
60 | - </fieldset> | |
61 | - </div> | |
62 | - <div layout="row" layout-align="end" class="md-actions"> | |
63 | - <md-button ng-click="dismiss()">{{ 'action.cancel' | | |
64 | - translate }} | |
65 | - </md-button> | |
66 | - <md-button ng-disabled="editDialog.$invalid || !editDialog.$dirty" type="submit" | |
67 | - class="md-raised md-primary"> | |
68 | - {{ 'action.update' | translate }} | |
69 | - </md-button> | |
70 | - </div> | |
71 | - </form> | |
72 | -</md-edit-dialog> | |
\ No newline at end of file |
... | ... | @@ -15,5 +15,8 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
19 | -<div class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div> | |
18 | +<div flex layout="column" style="margin-top: -10px;"> | |
19 | + <div flex style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div> | |
20 | + <div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
21 | + <div class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div> | |
22 | +</div> | ... | ... |
... | ... | @@ -66,6 +66,13 @@ |
66 | 66 | <div translate ng-message="required">device.name-required</div> |
67 | 67 | </div> |
68 | 68 | </md-input-container> |
69 | + <tb-entity-subtype-autocomplete | |
70 | + ng-disabled="loading || !isEdit" | |
71 | + tb-required="true" | |
72 | + the-form="theForm" | |
73 | + ng-model="device.type" | |
74 | + entity-type="types.entityType.device"> | |
75 | + </tb-entity-subtype-autocomplete> | |
69 | 76 | <md-input-container class="md-block"> |
70 | 77 | <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}" |
71 | 78 | ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }} | ... | ... |
... | ... | @@ -48,7 +48,8 @@ export function DeviceCardController(types) { |
48 | 48 | |
49 | 49 | |
50 | 50 | /*@ngInject*/ |
51 | -export function DeviceController(userService, deviceService, customerService, $state, $stateParams, $document, $mdDialog, $q, $translate, types) { | |
51 | +export function DeviceController($rootScope, userService, deviceService, customerService, $state, $stateParams, | |
52 | + $document, $mdDialog, $q, $translate, types) { | |
52 | 53 | |
53 | 54 | var customerId = $stateParams.customerId; |
54 | 55 | |
... | ... | @@ -131,8 +132,8 @@ export function DeviceController(userService, deviceService, customerService, $s |
131 | 132 | } |
132 | 133 | |
133 | 134 | if (vm.devicesScope === 'tenant') { |
134 | - fetchDevicesFunction = function (pageLink) { | |
135 | - return deviceService.getTenantDevices(pageLink, true); | |
135 | + fetchDevicesFunction = function (pageLink, deviceType) { | |
136 | + return deviceService.getTenantDevices(pageLink, true, null, deviceType); | |
136 | 137 | }; |
137 | 138 | deleteDeviceFunction = function (deviceId) { |
138 | 139 | return deviceService.deleteDevice(deviceId); |
... | ... | @@ -242,8 +243,8 @@ export function DeviceController(userService, deviceService, customerService, $s |
242 | 243 | |
243 | 244 | |
244 | 245 | } else if (vm.devicesScope === 'customer' || vm.devicesScope === 'customer_user') { |
245 | - fetchDevicesFunction = function (pageLink) { | |
246 | - return deviceService.getCustomerDevices(customerId, pageLink, true); | |
246 | + fetchDevicesFunction = function (pageLink, deviceType) { | |
247 | + return deviceService.getCustomerDevices(customerId, pageLink, true, null, deviceType); | |
247 | 248 | }; |
248 | 249 | deleteDeviceFunction = function (deviceId) { |
249 | 250 | return deviceService.unassignDeviceFromCustomer(deviceId); |
... | ... | @@ -368,6 +369,7 @@ export function DeviceController(userService, deviceService, customerService, $s |
368 | 369 | var deferred = $q.defer(); |
369 | 370 | deviceService.saveDevice(device).then( |
370 | 371 | function success(savedDevice) { |
372 | + $rootScope.$broadcast('deviceSaved'); | |
371 | 373 | var devices = [ savedDevice ]; |
372 | 374 | customerService.applyAssignedCustomersInfo(devices).then( |
373 | 375 | function success(items) { | ... | ... |
... | ... | @@ -25,6 +25,7 @@ export default function DeviceDirective($compile, $templateCache, toast, $transl |
25 | 25 | var template = $templateCache.get(deviceFieldsetTemplate); |
26 | 26 | element.html(template); |
27 | 27 | |
28 | + scope.types = types; | |
28 | 29 | scope.isAssignedToCustomer = false; |
29 | 30 | scope.isPublic = false; |
30 | 31 | scope.assignedCustomer = null; | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import devicesTemplate from './devices.tpl.html'; |
20 | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function DeviceRoutes($stateProvider) { | |
23 | +export default function DeviceRoutes($stateProvider, types) { | |
24 | 24 | $stateProvider |
25 | 25 | .state('home.devices', { |
26 | 26 | url: '/devices', |
... | ... | @@ -37,6 +37,8 @@ export default function DeviceRoutes($stateProvider) { |
37 | 37 | data: { |
38 | 38 | devicesType: 'tenant', |
39 | 39 | searchEnabled: true, |
40 | + searchByEntitySubtype: true, | |
41 | + searchEntityType: types.entityType.device, | |
40 | 42 | pageTitle: 'device.devices' |
41 | 43 | }, |
42 | 44 | ncyBreadcrumb: { |
... | ... | @@ -58,6 +60,8 @@ export default function DeviceRoutes($stateProvider) { |
58 | 60 | data: { |
59 | 61 | devicesType: 'customer', |
60 | 62 | searchEnabled: true, |
63 | + searchByEntitySubtype: true, | |
64 | + searchEntityType: types.entityType.device, | |
61 | 65 | pageTitle: 'customer.devices' |
62 | 66 | }, |
63 | 67 | ncyBreadcrumb: { | ... | ... |
... | ... | @@ -63,7 +63,7 @@ |
63 | 63 | {{ 'action.search' | translate }} |
64 | 64 | </md-tooltip> |
65 | 65 | </md-button> |
66 | - <md-input-container md-theme="tb-search-input" flex> | |
66 | + <md-input-container flex> | |
67 | 67 | <label> </label> |
68 | 68 | <input ng-model="query.search" placeholder="{{ 'common.enter-search' | translate }}"/> |
69 | 69 | </md-input-container> | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +import './entity-subtype-autocomplete.scss'; | |
17 | + | |
18 | +/* eslint-disable import/no-unresolved, import/default */ | |
19 | + | |
20 | +import entitySubtypeAutocompleteTemplate from './entity-subtype-autocomplete.tpl.html'; | |
21 | + | |
22 | +/* eslint-enable import/no-unresolved, import/default */ | |
23 | + | |
24 | +/*@ngInject*/ | |
25 | +export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, types) { | |
26 | + | |
27 | + var linker = function (scope, element, attrs, ngModelCtrl) { | |
28 | + var template = $templateCache.get(entitySubtypeAutocompleteTemplate); | |
29 | + element.html(template); | |
30 | + | |
31 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | |
32 | + scope.subType = null; | |
33 | + scope.subTypeSearchText = ''; | |
34 | + scope.entitySubtypes = null; | |
35 | + | |
36 | + scope.fetchSubTypes = function(searchText) { | |
37 | + var deferred = $q.defer(); | |
38 | + loadSubTypes().then( | |
39 | + function success(subTypes) { | |
40 | + var result = $filter('filter')(subTypes, {'$': searchText}); | |
41 | + if (result && result.length) { | |
42 | + deferred.resolve(result); | |
43 | + } else { | |
44 | + deferred.resolve([searchText]); | |
45 | + } | |
46 | + }, | |
47 | + function fail() { | |
48 | + deferred.reject(); | |
49 | + } | |
50 | + ); | |
51 | + return deferred.promise; | |
52 | + } | |
53 | + | |
54 | + scope.subTypeSearchTextChanged = function() { | |
55 | + } | |
56 | + | |
57 | + scope.updateView = function () { | |
58 | + if (!scope.disabled) { | |
59 | + ngModelCtrl.$setViewValue(scope.subType); | |
60 | + } | |
61 | + } | |
62 | + | |
63 | + ngModelCtrl.$render = function () { | |
64 | + scope.subType = ngModelCtrl.$viewValue; | |
65 | + } | |
66 | + | |
67 | + scope.$watch('entityType', function () { | |
68 | + load(); | |
69 | + }); | |
70 | + | |
71 | + scope.$watch('subType', function (newValue, prevValue) { | |
72 | + if (!angular.equals(newValue, prevValue)) { | |
73 | + scope.updateView(); | |
74 | + } | |
75 | + }); | |
76 | + | |
77 | + scope.$watch('disabled', function () { | |
78 | + scope.updateView(); | |
79 | + }); | |
80 | + | |
81 | + function loadSubTypes() { | |
82 | + var deferred = $q.defer(); | |
83 | + if (!scope.entitySubtypes) { | |
84 | + var entitySubtypesPromise; | |
85 | + if (scope.entityType == types.entityType.asset) { | |
86 | + entitySubtypesPromise = assetService.getAssetTypes(); | |
87 | + } else if (scope.entityType == types.entityType.device) { | |
88 | + entitySubtypesPromise = deviceService.getDeviceTypes(); | |
89 | + } | |
90 | + if (entitySubtypesPromise) { | |
91 | + entitySubtypesPromise.then( | |
92 | + function success(types) { | |
93 | + scope.entitySubtypes = []; | |
94 | + types.forEach(function (type) { | |
95 | + scope.entitySubtypes.push(type.type); | |
96 | + }); | |
97 | + deferred.resolve(scope.entitySubtypes); | |
98 | + }, | |
99 | + function fail() { | |
100 | + deferred.reject(); | |
101 | + } | |
102 | + ); | |
103 | + } else { | |
104 | + deferred.reject(); | |
105 | + } | |
106 | + } else { | |
107 | + deferred.resolve(scope.entitySubtypes); | |
108 | + } | |
109 | + return deferred.promise; | |
110 | + } | |
111 | + | |
112 | + function load() { | |
113 | + if (scope.entityType == types.entityType.asset) { | |
114 | + scope.selectEntitySubtypeText = 'asset.select-asset-type'; | |
115 | + scope.entitySubtypeText = 'asset.asset-type'; | |
116 | + scope.entitySubtypeRequiredText = 'asset.asset-type-required'; | |
117 | + } else if (scope.entityType == types.entityType.device) { | |
118 | + scope.selectEntitySubtypeText = 'device.select-device-type'; | |
119 | + scope.entitySubtypeText = 'device.device-type'; | |
120 | + scope.entitySubtypeRequiredText = 'device.device-type-required'; | |
121 | + scope.$on('deviceSaved', function() { | |
122 | + scope.entitySubtypes = null; | |
123 | + }); | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + $compile(element.contents())(scope); | |
128 | + } | |
129 | + | |
130 | + return { | |
131 | + restrict: "E", | |
132 | + require: "^ngModel", | |
133 | + link: linker, | |
134 | + scope: { | |
135 | + theForm: '=?', | |
136 | + tbRequired: '=?', | |
137 | + disabled:'=ngDisabled', | |
138 | + entityType: "=" | |
139 | + } | |
140 | + }; | |
141 | +} | ... | ... |
ui/src/app/entity/entity-subtype-autocomplete.scss
renamed from
ui/src/app/components/device-alias-select.scss
... | ... | @@ -13,14 +13,10 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -.tb-device-alias-autocomplete { | |
17 | - .tb-not-found { | |
16 | +.tb-entity-subtype-autocomplete { | |
17 | + .tb-entity-subtype-item { | |
18 | 18 | display: block; |
19 | - line-height: 1.5; | |
20 | 19 | height: 48px; |
21 | - .tb-no-entries { | |
22 | - line-height: 48px; | |
23 | - } | |
24 | 20 | } |
25 | 21 | li { |
26 | 22 | height: auto !important; | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-autocomplete ng-required="tbRequired" | |
19 | + ng-disabled="disabled" | |
20 | + md-no-cache="true" | |
21 | + md-input-name="subType" | |
22 | + ng-model="subType" | |
23 | + md-selected-item="subType" | |
24 | + md-search-text="subTypeSearchText" | |
25 | + md-search-text-change="subTypeSearchTextChanged()" | |
26 | + md-items="item in fetchSubTypes(subTypeSearchText)" | |
27 | + md-item-text="item" | |
28 | + md-min-length="0" | |
29 | + placeholder="{{ selectEntitySubtypeText | translate }}" | |
30 | + md-floating-label="{{ entitySubtypeText | translate }}" | |
31 | + md-select-on-match="true" | |
32 | + md-menu-class="tb-entity-subtype-autocomplete"> | |
33 | + <md-item-template> | |
34 | + <div class="tb-entity-subtype-item"> | |
35 | + <span md-highlight-text="subTypeSearchText" md-highlight-flags="^i">{{item}}</span> | |
36 | + </div> | |
37 | + </md-item-template> | |
38 | + <div ng-messages="theForm.subType.$error"> | |
39 | + <div translate ng-message="required">{{ entitySubtypeRequiredText }}</div> | |
40 | + </div> | |
41 | +</md-autocomplete> | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +import './entity-subtype-select.scss'; | |
18 | + | |
19 | +/* eslint-disable import/no-unresolved, import/default */ | |
20 | + | |
21 | +import entitySubtypeSelectTemplate from './entity-subtype-select.tpl.html'; | |
22 | + | |
23 | +/* eslint-enable import/no-unresolved, import/default */ | |
24 | + | |
25 | +/*@ngInject*/ | |
26 | +export default function EntitySubtypeSelect($compile, $templateCache, $translate, assetService, deviceService, types) { | |
27 | + | |
28 | + var linker = function (scope, element, attrs, ngModelCtrl) { | |
29 | + var template = $templateCache.get(entitySubtypeSelectTemplate); | |
30 | + element.html(template); | |
31 | + | |
32 | + if (angular.isDefined(attrs.hideLabel)) { | |
33 | + scope.showLabel = false; | |
34 | + } else { | |
35 | + scope.showLabel = true; | |
36 | + } | |
37 | + | |
38 | + scope.ngModelCtrl = ngModelCtrl; | |
39 | + | |
40 | + scope.entitySubtypes = []; | |
41 | + | |
42 | + scope.subTypeName = function(subType) { | |
43 | + if (subType && subType.length) { | |
44 | + if (scope.typeTranslatePrefix) { | |
45 | + return $translate.instant(scope.typeTranslatePrefix + '.' + subType); | |
46 | + } else { | |
47 | + return subType; | |
48 | + } | |
49 | + } else { | |
50 | + return $translate.instant('entity.all-subtypes'); | |
51 | + } | |
52 | + } | |
53 | + | |
54 | + scope.$watch('entityType', function () { | |
55 | + load(); | |
56 | + }); | |
57 | + | |
58 | + scope.$watch('entitySubtype', function (newValue, prevValue) { | |
59 | + if (!angular.equals(newValue, prevValue)) { | |
60 | + scope.updateView(); | |
61 | + } | |
62 | + }); | |
63 | + | |
64 | + scope.updateView = function () { | |
65 | + ngModelCtrl.$setViewValue(scope.entitySubtype); | |
66 | + }; | |
67 | + | |
68 | + ngModelCtrl.$render = function () { | |
69 | + scope.entitySubtype = ngModelCtrl.$viewValue; | |
70 | + }; | |
71 | + | |
72 | + function loadSubTypes() { | |
73 | + scope.entitySubtypes.length = 0; | |
74 | + var entitySubtypesPromise; | |
75 | + if (scope.entityType == types.entityType.asset) { | |
76 | + entitySubtypesPromise = assetService.getAssetTypes(); | |
77 | + } else if (scope.entityType == types.entityType.device) { | |
78 | + entitySubtypesPromise = deviceService.getDeviceTypes(); | |
79 | + } | |
80 | + if (entitySubtypesPromise) { | |
81 | + entitySubtypesPromise.then( | |
82 | + function success(types) { | |
83 | + scope.entitySubtypes.push(''); | |
84 | + types.forEach(function(type) { | |
85 | + scope.entitySubtypes.push(type.type); | |
86 | + }); | |
87 | + if (scope.entitySubtypes.indexOf(scope.entitySubtype) == -1) { | |
88 | + scope.entitySubtype = ''; | |
89 | + } | |
90 | + }, | |
91 | + function fail() {} | |
92 | + ); | |
93 | + } | |
94 | + | |
95 | + } | |
96 | + | |
97 | + function load() { | |
98 | + if (scope.entityType == types.entityType.asset) { | |
99 | + scope.entitySubtypeTitle = 'asset.asset-type'; | |
100 | + scope.entitySubtypeRequiredText = 'asset.asset-type-required'; | |
101 | + } else if (scope.entityType == types.entityType.device) { | |
102 | + scope.entitySubtypeTitle = 'device.device-type'; | |
103 | + scope.entitySubtypeRequiredText = 'device.device-type-required'; | |
104 | + } | |
105 | + scope.entitySubtypes.length = 0; | |
106 | + if (scope.entitySubtypesList && scope.entitySubtypesList.length) { | |
107 | + scope.entitySubtypesList.forEach(function(subType) { | |
108 | + scope.entitySubtypes.push(subType); | |
109 | + }); | |
110 | + } else { | |
111 | + loadSubTypes(); | |
112 | + if (scope.entityType == types.entityType.asset) { | |
113 | + scope.$on('assetSaved', function() { | |
114 | + loadSubTypes(); | |
115 | + }); | |
116 | + } else if (scope.entityType == types.entityType.device) { | |
117 | + scope.$on('deviceSaved', function() { | |
118 | + loadSubTypes(); | |
119 | + }); | |
120 | + } | |
121 | + } | |
122 | + } | |
123 | + | |
124 | + $compile(element.contents())(scope); | |
125 | + } | |
126 | + | |
127 | + return { | |
128 | + restrict: "E", | |
129 | + require: "^ngModel", | |
130 | + link: linker, | |
131 | + scope: { | |
132 | + theForm: '=?', | |
133 | + entityType: "=", | |
134 | + entitySubtypesList: "=?", | |
135 | + typeTranslatePrefix: "@?" | |
136 | + } | |
137 | + }; | |
138 | +} | ... | ... |
ui/src/app/entity/entity-subtype-select.scss
renamed from
ui/src/app/dashboard/device-aliases.scss
... | ... | @@ -14,12 +14,6 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | |
17 | -.tb-aliases-dialog { | |
18 | - .md-dialog-content { | |
19 | - padding-bottom: 0px; | |
20 | - } | |
21 | - .tb-alias { | |
22 | - padding: 10px 0 0 10px; | |
23 | - margin: 5px; | |
24 | - } | |
17 | +md-select.tb-entity-subtype-select { | |
18 | + min-width: 200px; | |
25 | 19 | } | ... | ... |
ui/src/app/entity/entity-subtype-select.tpl.html
renamed from
ui/src/app/dashboard/aliases-device-select-panel.tpl.html
... | ... | @@ -15,19 +15,14 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<md-content flex layout="column"> | |
19 | - <section flex layout="column"> | |
20 | - <md-content flex class="md-padding" layout="column"> | |
21 | - <div flex layout="row" ng-repeat="(aliasId, deviceAlias) in vm.deviceAliases"> | |
22 | - <md-input-container flex> | |
23 | - <label>{{deviceAlias.alias}}</label> | |
24 | - <md-select ng-model="vm.deviceAliases[aliasId].deviceId"> | |
25 | - <md-option ng-repeat="deviceInfo in vm.deviceAliasesInfo[aliasId]" ng-value="deviceInfo.id"> | |
26 | - {{deviceInfo.name}} | |
27 | - </md-option> | |
28 | - </md-select> | |
29 | - </md-input-container> | |
30 | - </div> | |
31 | - </md-content> | |
32 | - </section> | |
33 | -</md-content> | |
18 | +<md-input-container> | |
19 | + <label ng-if="showLabel">{{ entitySubtypeTitle | translate }}</label> | |
20 | + <md-select name="subType" ng-model="entitySubtype" class="tb-entity-subtype-select" aria-label="{{ entitySubtypeTitle | translate }}"> | |
21 | + <md-option ng-repeat="subType in entitySubtypes" ng-value="subType"> | |
22 | + {{ subTypeName(subType) }} | |
23 | + </md-option> | |
24 | + </md-select> | |
25 | + <div ng-messages="theForm.subType.$error"> | |
26 | + <div ng-message="required" translate>{{ entitySubtypeRequiredText }}</div> | |
27 | + </div> | |
28 | +</md-input-container> | ... | ... |
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | |
17 | 17 | import EntityAliasesController from './entity-aliases.controller'; |
18 | 18 | import EntityTypeSelectDirective from './entity-type-select.directive'; |
19 | +import EntitySubtypeSelectDirective from './entity-subtype-select.directive'; | |
20 | +import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive'; | |
19 | 21 | import EntityFilterDirective from './entity-filter.directive'; |
20 | 22 | import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; |
21 | 23 | import AliasesEntitySelectDirective from './aliases-entity-select.directive'; |
... | ... | @@ -29,6 +31,8 @@ export default angular.module('thingsboard.entity', []) |
29 | 31 | .controller('AddAttributeDialogController', AddAttributeDialogController) |
30 | 32 | .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController) |
31 | 33 | .directive('tbEntityTypeSelect', EntityTypeSelectDirective) |
34 | + .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective) | |
35 | + .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective) | |
32 | 36 | .directive('tbEntityFilter', EntityFilterDirective) |
33 | 37 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) |
34 | 38 | .directive('tbAttributeTable', AttributeTableDirective) | ... | ... |
... | ... | @@ -25,7 +25,7 @@ import logoSvg from '../../svg/logo_title_white.svg'; |
25 | 25 | /* eslint-disable angular/angularelement */ |
26 | 26 | |
27 | 27 | /*@ngInject*/ |
28 | -export default function HomeController(loginService, userService, deviceService, Fullscreen, $scope, $element, $rootScope, $document, $state, | |
28 | +export default function HomeController(types, loginService, userService, deviceService, Fullscreen, $scope, $element, $rootScope, $document, $state, | |
29 | 29 | $window, $log, $mdMedia, $animate, $timeout) { |
30 | 30 | |
31 | 31 | var siteSideNav = $('.tb-site-sidenav', $element); |
... | ... | @@ -38,8 +38,11 @@ export default function HomeController(loginService, userService, deviceService, |
38 | 38 | if (angular.isUndefined($rootScope.searchConfig)) { |
39 | 39 | $rootScope.searchConfig = { |
40 | 40 | searchEnabled: false, |
41 | + searchByEntitySubtype: false, | |
42 | + searchEntityType: null, | |
41 | 43 | showSearch: false, |
42 | - searchText: "" | |
44 | + searchText: "", | |
45 | + searchEntitySubtype: "" | |
43 | 46 | }; |
44 | 47 | } |
45 | 48 | |
... | ... | @@ -47,6 +50,7 @@ export default function HomeController(loginService, userService, deviceService, |
47 | 50 | vm.isLockSidenav = false; |
48 | 51 | |
49 | 52 | vm.displaySearchMode = displaySearchMode; |
53 | + vm.displayEntitySubtypeSearch = displayEntitySubtypeSearch; | |
50 | 54 | vm.openSidenav = openSidenav; |
51 | 55 | vm.goBack = goBack; |
52 | 56 | vm.searchTextUpdated = searchTextUpdated; |
... | ... | @@ -54,25 +58,35 @@ export default function HomeController(loginService, userService, deviceService, |
54 | 58 | vm.toggleFullscreen = toggleFullscreen; |
55 | 59 | |
56 | 60 | $scope.$on('$stateChangeSuccess', function (evt, to, toParams, from) { |
61 | + watchEntitySubtype(false); | |
57 | 62 | if (angular.isDefined(to.data.searchEnabled)) { |
58 | 63 | $scope.searchConfig.searchEnabled = to.data.searchEnabled; |
64 | + $scope.searchConfig.searchByEntitySubtype = to.data.searchByEntitySubtype; | |
65 | + $scope.searchConfig.searchEntityType = to.data.searchEntityType; | |
59 | 66 | if ($scope.searchConfig.searchEnabled === false || to.name !== from.name) { |
60 | 67 | $scope.searchConfig.showSearch = false; |
61 | 68 | $scope.searchConfig.searchText = ""; |
69 | + $scope.searchConfig.searchEntitySubtype = ""; | |
62 | 70 | } |
63 | 71 | } else { |
64 | 72 | $scope.searchConfig.searchEnabled = false; |
73 | + $scope.searchConfig.searchByEntitySubtype = false; | |
74 | + $scope.searchConfig.searchEntityType = null; | |
65 | 75 | $scope.searchConfig.showSearch = false; |
66 | 76 | $scope.searchConfig.searchText = ""; |
77 | + $scope.searchConfig.searchEntitySubtype = ""; | |
67 | 78 | } |
79 | + watchEntitySubtype($scope.searchConfig.searchByEntitySubtype); | |
68 | 80 | }); |
69 | 81 | |
70 | - if ($mdMedia('gt-sm')) { | |
82 | + vm.isGtSm = $mdMedia('gt-sm'); | |
83 | + if (vm.isGtSm) { | |
71 | 84 | vm.isLockSidenav = true; |
72 | 85 | $animate.enabled(siteSideNav, false); |
73 | 86 | } |
74 | 87 | |
75 | 88 | $scope.$watch(function() { return $mdMedia('gt-sm'); }, function(isGtSm) { |
89 | + vm.isGtSm = isGtSm; | |
76 | 90 | vm.isLockSidenav = isGtSm; |
77 | 91 | vm.isShowSidenav = isGtSm; |
78 | 92 | if (!isGtSm) { |
... | ... | @@ -84,11 +98,28 @@ export default function HomeController(loginService, userService, deviceService, |
84 | 98 | } |
85 | 99 | }); |
86 | 100 | |
101 | + function watchEntitySubtype(enableWatch) { | |
102 | + if ($scope.entitySubtypeWatch) { | |
103 | + $scope.entitySubtypeWatch(); | |
104 | + } | |
105 | + if (enableWatch) { | |
106 | + $scope.entitySubtypeWatch = $scope.$watch('searchConfig.searchEntitySubtype', function (newVal, prevVal) { | |
107 | + if (!angular.equals(newVal, prevVal)) { | |
108 | + $scope.$broadcast('searchEntitySubtypeUpdated'); | |
109 | + } | |
110 | + }); | |
111 | + } | |
112 | + } | |
113 | + | |
87 | 114 | function displaySearchMode() { |
88 | 115 | return $scope.searchConfig.searchEnabled && |
89 | 116 | $scope.searchConfig.showSearch; |
90 | 117 | } |
91 | 118 | |
119 | + function displayEntitySubtypeSearch() { | |
120 | + return $scope.searchConfig.searchByEntitySubtype && vm.isGtSm; | |
121 | + } | |
122 | + | |
92 | 123 | function toggleFullscreen() { |
93 | 124 | if (Fullscreen.isEnabled()) { |
94 | 125 | Fullscreen.cancel(); | ... | ... |
... | ... | @@ -39,7 +39,7 @@ |
39 | 39 | </md-sidenav> |
40 | 40 | |
41 | 41 | <div flex layout="column" tabIndex="-1" role="main"> |
42 | - <md-toolbar class="md-whiteframe-z1 tb-primary-toolbar" ng-class="{'md-hue-1': vm.displaySearchMode()}"> | |
42 | + <md-toolbar class="md-whiteframe-z1 tb-primary-toolbar"> | |
43 | 43 | <div layout="row" flex class="md-toolbar-tools"> |
44 | 44 | <md-button id="main" hide-gt-sm ng-show="!forceFullscreen" |
45 | 45 | class="md-icon-button" ng-click="vm.openSidenav()" aria-label="{{ 'home.menu' | translate }}" ng-class="{'tb-invisible': vm.displaySearchMode()}"> |
... | ... | @@ -55,10 +55,18 @@ |
55 | 55 | <div flex layout="row" ng-show="!vm.displaySearchMode()" tb-no-animate class="md-toolbar-tools"> |
56 | 56 | <span ng-cloak ncy-breadcrumb></span> |
57 | 57 | </div> |
58 | - <md-input-container ng-show="vm.displaySearchMode()" md-theme="tb-search-input" flex> | |
59 | - <label> </label> | |
60 | - <input ng-model="searchConfig.searchText" ng-change="vm.searchTextUpdated()" placeholder="{{ 'common.enter-search' | translate }}"/> | |
61 | - </md-input-container> | |
58 | + <div layout="row" ng-show="vm.displaySearchMode()" md-theme="tb-dark" flex> | |
59 | + <div class="tb-entity-subtype-search" layout="row" layout-align="start center" ng-if="vm.displayEntitySubtypeSearch()"> | |
60 | + <tb-entity-subtype-select | |
61 | + entity-type="searchConfig.searchEntityType" | |
62 | + ng-model="searchConfig.searchEntitySubtype"> | |
63 | + </tb-entity-subtype-select> | |
64 | + </div> | |
65 | + <md-input-container ng-class="{'tb-entity-search': vm.displayEntitySubtypeSearch()}" flex> | |
66 | + <label> </label> | |
67 | + <input ng-model="searchConfig.searchText" ng-change="vm.searchTextUpdated()" placeholder="{{ 'common.enter-search' | translate }}"/> | |
68 | + </md-input-container> | |
69 | + </div> | |
62 | 70 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}" ng-show="searchConfig.searchEnabled" ng-click="searchConfig.showSearch = !searchConfig.showSearch"> |
63 | 71 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
64 | 72 | </md-button> | ... | ... |
... | ... | @@ -48,16 +48,10 @@ function UserMenuController($scope, userService, $translate, $state) { |
48 | 48 | var dashboardUser = userService.getCurrentUser(); |
49 | 49 | |
50 | 50 | vm.authorityName = authorityName; |
51 | - vm.displaySearchMode = displaySearchMode; | |
52 | 51 | vm.logout = logout; |
53 | 52 | vm.openProfile = openProfile; |
54 | 53 | vm.userDisplayName = userDisplayName; |
55 | 54 | |
56 | - function displaySearchMode() { | |
57 | - return $scope.searchConfig.searchEnabled && | |
58 | - $scope.searchConfig.showSearch; | |
59 | - } | |
60 | - | |
61 | 55 | function authorityName() { |
62 | 56 | var name = "user.anonymous"; |
63 | 57 | if (dashboardUser) { | ... | ... |
... | ... | @@ -124,6 +124,9 @@ export default angular.module('thingsboard.locale', []) |
124 | 124 | "unassign-from-customer": "Unassign from customer", |
125 | 125 | "delete": "Delete asset", |
126 | 126 | "asset-public": "Asset is public", |
127 | + "asset-type": "Asset type", | |
128 | + "asset-type-required": "Asset type is required.", | |
129 | + "select-asset-type": "Select asset type", | |
127 | 130 | "name": "Name", |
128 | 131 | "name-required": "Name is required.", |
129 | 132 | "description": "Description", |
... | ... | @@ -477,6 +480,9 @@ export default angular.module('thingsboard.locale', []) |
477 | 480 | "rsa-key-required": "RSA public key is required.", |
478 | 481 | "secret": "Secret", |
479 | 482 | "secret-required": "Secret is required.", |
483 | + "device-type": "Device type", | |
484 | + "device-type-required": "Device type is required.", | |
485 | + "select-device-type": "Select device type", | |
480 | 486 | "name": "Name", |
481 | 487 | "name-required": "Name is required.", |
482 | 488 | "description": "Description", |
... | ... | @@ -521,6 +527,7 @@ export default angular.module('thingsboard.locale', []) |
521 | 527 | "entity-list-empty": "No entities selected.", |
522 | 528 | "entity-name-filter-required": "Entity name filter is required.", |
523 | 529 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", |
530 | + "all-subtypes": "All", | |
524 | 531 | "type": "Type", |
525 | 532 | "type-device": "Device", |
526 | 533 | "type-asset": "Asset", | ... | ... |