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,7 +31,8 @@ function AssetService($http, $q, customerService, userService) { | ||
31 | getTenantAssets: getTenantAssets, | 31 | getTenantAssets: getTenantAssets, |
32 | getCustomerAssets: getCustomerAssets, | 32 | getCustomerAssets: getCustomerAssets, |
33 | findByQuery: findByQuery, | 33 | findByQuery: findByQuery, |
34 | - fetchAssetsByNameFilter: fetchAssetsByNameFilter | 34 | + fetchAssetsByNameFilter: fetchAssetsByNameFilter, |
35 | + getAssetTypes: getAssetTypes | ||
35 | } | 36 | } |
36 | 37 | ||
37 | return service; | 38 | return service; |
@@ -152,7 +153,7 @@ function AssetService($http, $q, customerService, userService) { | @@ -152,7 +153,7 @@ function AssetService($http, $q, customerService, userService) { | ||
152 | return deferred.promise; | 153 | return deferred.promise; |
153 | } | 154 | } |
154 | 155 | ||
155 | - function getTenantAssets(pageLink, applyCustomersInfo, config) { | 156 | + function getTenantAssets(pageLink, applyCustomersInfo, config, type) { |
156 | var deferred = $q.defer(); | 157 | var deferred = $q.defer(); |
157 | var url = '/api/tenant/assets?limit=' + pageLink.limit; | 158 | var url = '/api/tenant/assets?limit=' + pageLink.limit; |
158 | if (angular.isDefined(pageLink.textSearch)) { | 159 | if (angular.isDefined(pageLink.textSearch)) { |
@@ -164,6 +165,9 @@ function AssetService($http, $q, customerService, userService) { | @@ -164,6 +165,9 @@ function AssetService($http, $q, customerService, userService) { | ||
164 | if (angular.isDefined(pageLink.textOffset)) { | 165 | if (angular.isDefined(pageLink.textOffset)) { |
165 | url += '&textOffset=' + pageLink.textOffset; | 166 | url += '&textOffset=' + pageLink.textOffset; |
166 | } | 167 | } |
168 | + if (angular.isDefined(type) && type.length) { | ||
169 | + url += '&type=' + type; | ||
170 | + } | ||
167 | $http.get(url, config).then(function success(response) { | 171 | $http.get(url, config).then(function success(response) { |
168 | if (applyCustomersInfo) { | 172 | if (applyCustomersInfo) { |
169 | customerService.applyAssignedCustomersInfo(response.data.data).then( | 173 | customerService.applyAssignedCustomersInfo(response.data.data).then( |
@@ -184,7 +188,7 @@ function AssetService($http, $q, customerService, userService) { | @@ -184,7 +188,7 @@ function AssetService($http, $q, customerService, userService) { | ||
184 | return deferred.promise; | 188 | return deferred.promise; |
185 | } | 189 | } |
186 | 190 | ||
187 | - function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config) { | 191 | + function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config, type) { |
188 | var deferred = $q.defer(); | 192 | var deferred = $q.defer(); |
189 | var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit; | 193 | var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit; |
190 | if (angular.isDefined(pageLink.textSearch)) { | 194 | if (angular.isDefined(pageLink.textSearch)) { |
@@ -196,6 +200,9 @@ function AssetService($http, $q, customerService, userService) { | @@ -196,6 +200,9 @@ function AssetService($http, $q, customerService, userService) { | ||
196 | if (angular.isDefined(pageLink.textOffset)) { | 200 | if (angular.isDefined(pageLink.textOffset)) { |
197 | url += '&textOffset=' + pageLink.textOffset; | 201 | url += '&textOffset=' + pageLink.textOffset; |
198 | } | 202 | } |
203 | + if (angular.isDefined(type) && type.length) { | ||
204 | + url += '&type=' + type; | ||
205 | + } | ||
199 | $http.get(url, config).then(function success(response) { | 206 | $http.get(url, config).then(function success(response) { |
200 | if (applyCustomersInfo) { | 207 | if (applyCustomersInfo) { |
201 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( | 208 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
@@ -258,4 +265,15 @@ function AssetService($http, $q, customerService, userService) { | @@ -258,4 +265,15 @@ function AssetService($http, $q, customerService, userService) { | ||
258 | return deferred.promise; | 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,12 +41,13 @@ function DeviceService($http, $q, attributeService, customerService, types) { | ||
41 | deleteDeviceAttributes: deleteDeviceAttributes, | 41 | deleteDeviceAttributes: deleteDeviceAttributes, |
42 | sendOneWayRpcCommand: sendOneWayRpcCommand, | 42 | sendOneWayRpcCommand: sendOneWayRpcCommand, |
43 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, | 43 | sendTwoWayRpcCommand: sendTwoWayRpcCommand, |
44 | - findByQuery: findByQuery | 44 | + findByQuery: findByQuery, |
45 | + getDeviceTypes: getDeviceTypes | ||
45 | } | 46 | } |
46 | 47 | ||
47 | return service; | 48 | return service; |
48 | 49 | ||
49 | - function getTenantDevices(pageLink, applyCustomersInfo, config) { | 50 | + function getTenantDevices(pageLink, applyCustomersInfo, config, type) { |
50 | var deferred = $q.defer(); | 51 | var deferred = $q.defer(); |
51 | var url = '/api/tenant/devices?limit=' + pageLink.limit; | 52 | var url = '/api/tenant/devices?limit=' + pageLink.limit; |
52 | if (angular.isDefined(pageLink.textSearch)) { | 53 | if (angular.isDefined(pageLink.textSearch)) { |
@@ -58,6 +59,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { | @@ -58,6 +59,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { | ||
58 | if (angular.isDefined(pageLink.textOffset)) { | 59 | if (angular.isDefined(pageLink.textOffset)) { |
59 | url += '&textOffset=' + pageLink.textOffset; | 60 | url += '&textOffset=' + pageLink.textOffset; |
60 | } | 61 | } |
62 | + if (angular.isDefined(type) && type.length) { | ||
63 | + url += '&type=' + type; | ||
64 | + } | ||
61 | $http.get(url, config).then(function success(response) { | 65 | $http.get(url, config).then(function success(response) { |
62 | if (applyCustomersInfo) { | 66 | if (applyCustomersInfo) { |
63 | customerService.applyAssignedCustomersInfo(response.data.data).then( | 67 | customerService.applyAssignedCustomersInfo(response.data.data).then( |
@@ -78,7 +82,7 @@ function DeviceService($http, $q, attributeService, customerService, types) { | @@ -78,7 +82,7 @@ function DeviceService($http, $q, attributeService, customerService, types) { | ||
78 | return deferred.promise; | 82 | return deferred.promise; |
79 | } | 83 | } |
80 | 84 | ||
81 | - function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config) { | 85 | + function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config, type) { |
82 | var deferred = $q.defer(); | 86 | var deferred = $q.defer(); |
83 | var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit; | 87 | var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit; |
84 | if (angular.isDefined(pageLink.textSearch)) { | 88 | if (angular.isDefined(pageLink.textSearch)) { |
@@ -90,6 +94,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { | @@ -90,6 +94,9 @@ function DeviceService($http, $q, attributeService, customerService, types) { | ||
90 | if (angular.isDefined(pageLink.textOffset)) { | 94 | if (angular.isDefined(pageLink.textOffset)) { |
91 | url += '&textOffset=' + pageLink.textOffset; | 95 | url += '&textOffset=' + pageLink.textOffset; |
92 | } | 96 | } |
97 | + if (angular.isDefined(type) && type.length) { | ||
98 | + url += '&type=' + type; | ||
99 | + } | ||
93 | $http.get(url, config).then(function success(response) { | 100 | $http.get(url, config).then(function success(response) { |
94 | if (applyCustomersInfo) { | 101 | if (applyCustomersInfo) { |
95 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( | 102 | customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( |
@@ -286,4 +293,15 @@ function DeviceService($http, $q, attributeService, customerService, types) { | @@ -286,4 +293,15 @@ function DeviceService($http, $q, attributeService, customerService, types) { | ||
286 | return deferred.promise; | 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,10 +160,6 @@ export default function AppConfig($provide, | ||
160 | indigoTheme(); | 160 | indigoTheme(); |
161 | } | 161 | } |
162 | 162 | ||
163 | - $mdThemingProvider.theme('tb-search-input', 'default') | ||
164 | - .primaryPalette('tb-primary') | ||
165 | - .backgroundPalette('tb-primary'); | ||
166 | - | ||
167 | $mdThemingProvider.setDefaultTheme('default'); | 163 | $mdThemingProvider.setDefaultTheme('default'); |
168 | //$mdThemingProvider.alwaysWatchTheme(true); | 164 | //$mdThemingProvider.alwaysWatchTheme(true); |
169 | } | 165 | } |
@@ -15,5 +15,8 @@ | @@ -15,5 +15,8 @@ | ||
15 | limitations under the License. | 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,13 +56,13 @@ | ||
56 | <div translate ng-message="required">asset.name-required</div> | 56 | <div translate ng-message="required">asset.name-required</div> |
57 | </div> | 57 | </div> |
58 | </md-input-container> | 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 | <md-input-container class="md-block"> | 66 | <md-input-container class="md-block"> |
67 | <label translate>asset.description</label> | 67 | <label translate>asset.description</label> |
68 | <textarea ng-model="asset.additionalInfo.description" rows="2"></textarea> | 68 | <textarea ng-model="asset.additionalInfo.description" rows="2"></textarea> |
@@ -47,7 +47,8 @@ export function AssetCardController(types) { | @@ -47,7 +47,8 @@ export function AssetCardController(types) { | ||
47 | 47 | ||
48 | 48 | ||
49 | /*@ngInject*/ | 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 | var customerId = $stateParams.customerId; | 53 | var customerId = $stateParams.customerId; |
53 | 54 | ||
@@ -129,8 +130,8 @@ export function AssetController(userService, assetService, customerService, $sta | @@ -129,8 +130,8 @@ export function AssetController(userService, assetService, customerService, $sta | ||
129 | } | 130 | } |
130 | 131 | ||
131 | if (vm.assetsScope === 'tenant') { | 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 | deleteAssetFunction = function (assetId) { | 136 | deleteAssetFunction = function (assetId) { |
136 | return assetService.deleteAsset(assetId); | 137 | return assetService.deleteAsset(assetId); |
@@ -229,8 +230,8 @@ export function AssetController(userService, assetService, customerService, $sta | @@ -229,8 +230,8 @@ export function AssetController(userService, assetService, customerService, $sta | ||
229 | 230 | ||
230 | 231 | ||
231 | } else if (vm.assetsScope === 'customer' || vm.assetsScope === 'customer_user') { | 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 | deleteAssetFunction = function (assetId) { | 236 | deleteAssetFunction = function (assetId) { |
236 | return assetService.unassignAssetFromCustomer(assetId); | 237 | return assetService.unassignAssetFromCustomer(assetId); |
@@ -333,6 +334,7 @@ export function AssetController(userService, assetService, customerService, $sta | @@ -333,6 +334,7 @@ export function AssetController(userService, assetService, customerService, $sta | ||
333 | var deferred = $q.defer(); | 334 | var deferred = $q.defer(); |
334 | assetService.saveAsset(asset).then( | 335 | assetService.saveAsset(asset).then( |
335 | function success(savedAsset) { | 336 | function success(savedAsset) { |
337 | + $rootScope.$broadcast('assetSaved'); | ||
336 | var assets = [ savedAsset ]; | 338 | var assets = [ savedAsset ]; |
337 | customerService.applyAssignedCustomersInfo(assets).then( | 339 | customerService.applyAssignedCustomersInfo(assets).then( |
338 | function success(items) { | 340 | function success(items) { |
@@ -25,6 +25,7 @@ export default function AssetDirective($compile, $templateCache, toast, $transla | @@ -25,6 +25,7 @@ export default function AssetDirective($compile, $templateCache, toast, $transla | ||
25 | var template = $templateCache.get(assetFieldsetTemplate); | 25 | var template = $templateCache.get(assetFieldsetTemplate); |
26 | element.html(template); | 26 | element.html(template); |
27 | 27 | ||
28 | + scope.types = types; | ||
28 | scope.isAssignedToCustomer = false; | 29 | scope.isAssignedToCustomer = false; |
29 | scope.isPublic = false; | 30 | scope.isPublic = false; |
30 | scope.assignedCustomer = null; | 31 | scope.assignedCustomer = null; |
@@ -20,7 +20,7 @@ import assetsTemplate from './assets.tpl.html'; | @@ -20,7 +20,7 @@ import assetsTemplate from './assets.tpl.html'; | ||
20 | /* eslint-enable import/no-unresolved, import/default */ | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | ||
22 | /*@ngInject*/ | 22 | /*@ngInject*/ |
23 | -export default function AssetRoutes($stateProvider) { | 23 | +export default function AssetRoutes($stateProvider, types) { |
24 | $stateProvider | 24 | $stateProvider |
25 | .state('home.assets', { | 25 | .state('home.assets', { |
26 | url: '/assets', | 26 | url: '/assets', |
@@ -37,6 +37,8 @@ export default function AssetRoutes($stateProvider) { | @@ -37,6 +37,8 @@ export default function AssetRoutes($stateProvider) { | ||
37 | data: { | 37 | data: { |
38 | assetsType: 'tenant', | 38 | assetsType: 'tenant', |
39 | searchEnabled: true, | 39 | searchEnabled: true, |
40 | + searchByEntitySubtype: true, | ||
41 | + searchEntityType: types.entityType.asset, | ||
40 | pageTitle: 'asset.assets' | 42 | pageTitle: 'asset.assets' |
41 | }, | 43 | }, |
42 | ncyBreadcrumb: { | 44 | ncyBreadcrumb: { |
@@ -58,6 +60,8 @@ export default function AssetRoutes($stateProvider) { | @@ -58,6 +60,8 @@ export default function AssetRoutes($stateProvider) { | ||
58 | data: { | 60 | data: { |
59 | assetsType: 'customer', | 61 | assetsType: 'customer', |
60 | searchEnabled: true, | 62 | searchEnabled: true, |
63 | + searchByEntitySubtype: true, | ||
64 | + searchEntityType: types.entityType.asset, | ||
61 | pageTitle: 'customer.assets' | 65 | pageTitle: 'customer.assets' |
62 | }, | 66 | }, |
63 | ncyBreadcrumb: { | 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 | -} |
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 | -} |
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> |
@@ -197,7 +197,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | @@ -197,7 +197,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | ||
197 | }, | 197 | }, |
198 | 198 | ||
199 | getLength: function () { | 199 | getLength: function () { |
200 | - if (vm.items.hasNext) { | 200 | + if (vm.items.hasNext && !vm.items.pending) { |
201 | return vm.items.rowData.length + pageSize; | 201 | return vm.items.rowData.length + pageSize; |
202 | } else { | 202 | } else { |
203 | return vm.items.rowData.length; | 203 | return vm.items.rowData.length; |
@@ -206,7 +206,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | @@ -206,7 +206,7 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | ||
206 | 206 | ||
207 | fetchMoreItems_: function () { | 207 | fetchMoreItems_: function () { |
208 | if (vm.items.hasNext && !vm.items.pending) { | 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 | if (promise) { | 210 | if (promise) { |
211 | vm.items.pending = true; | 211 | vm.items.pending = true; |
212 | promise.then( | 212 | promise.then( |
@@ -433,6 +433,10 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | @@ -433,6 +433,10 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | ||
433 | reload(); | 433 | reload(); |
434 | }); | 434 | }); |
435 | 435 | ||
436 | + $scope.$on('searchEntitySubtypeUpdated', function () { | ||
437 | + reload(); | ||
438 | + }); | ||
439 | + | ||
436 | vm.onGridInited(vm); | 440 | vm.onGridInited(vm); |
437 | 441 | ||
438 | vm.itemRows.getItemAtIndex(pageSize); | 442 | vm.itemRows.getItemAtIndex(pageSize); |
@@ -441,18 +445,16 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | @@ -441,18 +445,16 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra | ||
441 | if (vm.items && vm.items.pending) { | 445 | if (vm.items && vm.items.pending) { |
442 | vm.items.reloadPending = true; | 446 | vm.items.reloadPending = true; |
443 | } else { | 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 | vm.detailsConfig.isDetailsOpen = false; | 458 | vm.detailsConfig.isDetailsOpen = false; |
457 | vm.items.reloadPending = false; | 459 | vm.items.reloadPending = false; |
458 | vm.itemRows.getItemAtIndex(pageSize); | 460 | vm.itemRows.getItemAtIndex(pageSize); |
@@ -24,9 +24,8 @@ | @@ -24,9 +24,8 @@ | ||
24 | <md-virtual-repeat-container ng-show="vm.hasData()" tb-scope-element="repeatContainer" id="tb-vertical-container" md-top-index="vm.topIndex" flex> | 24 | <md-virtual-repeat-container ng-show="vm.hasData()" tb-scope-element="repeatContainer" id="tb-vertical-container" md-top-index="vm.topIndex" flex> |
25 | <div class="md-padding" layout="column"> | 25 | <div class="md-padding" layout="column"> |
26 | <section layout="row" md-virtual-repeat="rowItem in vm.itemRows" md-on-demand md-item-size="vm.itemHeight"> | 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 | class="repeated-item tb-card-item" ng-style="{'height':(vm.itemHeight-16)+'px','cursor':'pointer'}" | 29 | class="repeated-item tb-card-item" ng-style="{'height':(vm.itemHeight-16)+'px','cursor':'pointer'}" |
31 | ng-click="vm.clickItemFunc($event, rowItem[n])"> | 30 | ng-click="vm.clickItemFunc($event, rowItem[n])"> |
32 | <section layout="row" layout-wrap> | 31 | <section layout="row" layout-wrap> |
@@ -43,7 +42,7 @@ | @@ -43,7 +42,7 @@ | ||
43 | </md-card-title> | 42 | </md-card-title> |
44 | </section> | 43 | </section> |
45 | <md-card-content flex> | 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 | </md-card-content> | 46 | </md-card-content> |
48 | <md-card-actions layout="row" layout-align="end end"> | 47 | <md-card-actions layout="row" layout-align="end end"> |
49 | <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList" | 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,6 +55,8 @@ | ||
56 | </md-card-actions> | 55 | </md-card-actions> |
57 | </md-card> | 56 | </md-card> |
58 | </div> | 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 | </section> | 60 | </section> |
60 | </div> | 61 | </div> |
61 | </md-virtual-repeat-container> | 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 */ |
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> |
@@ -58,7 +58,7 @@ | @@ -58,7 +58,7 @@ | ||
58 | {{ 'dashboard.search-states' | translate }} | 58 | {{ 'dashboard.search-states' | translate }} |
59 | </md-tooltip> | 59 | </md-tooltip> |
60 | </md-button> | 60 | </md-button> |
61 | - <md-input-container md-theme="tb-search-input" flex> | 61 | + <md-input-container flex> |
62 | <label> </label> | 62 | <label> </label> |
63 | <input ng-model="vm.query.search" placeholder="{{ 'dashboard.search-states' | translate }}"/> | 63 | <input ng-model="vm.query.search" placeholder="{{ 'dashboard.search-states' | translate }}"/> |
64 | </md-input-container> | 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 | -} |
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> |
@@ -15,5 +15,8 @@ | @@ -15,5 +15,8 @@ | ||
15 | limitations under the License. | 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,6 +66,13 @@ | ||
66 | <div translate ng-message="required">device.name-required</div> | 66 | <div translate ng-message="required">device.name-required</div> |
67 | </div> | 67 | </div> |
68 | </md-input-container> | 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 | <md-input-container class="md-block"> | 76 | <md-input-container class="md-block"> |
70 | <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}" | 77 | <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}" |
71 | ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }} | 78 | ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }} |
@@ -48,7 +48,8 @@ export function DeviceCardController(types) { | @@ -48,7 +48,8 @@ export function DeviceCardController(types) { | ||
48 | 48 | ||
49 | 49 | ||
50 | /*@ngInject*/ | 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 | var customerId = $stateParams.customerId; | 54 | var customerId = $stateParams.customerId; |
54 | 55 | ||
@@ -131,8 +132,8 @@ export function DeviceController(userService, deviceService, customerService, $s | @@ -131,8 +132,8 @@ export function DeviceController(userService, deviceService, customerService, $s | ||
131 | } | 132 | } |
132 | 133 | ||
133 | if (vm.devicesScope === 'tenant') { | 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 | deleteDeviceFunction = function (deviceId) { | 138 | deleteDeviceFunction = function (deviceId) { |
138 | return deviceService.deleteDevice(deviceId); | 139 | return deviceService.deleteDevice(deviceId); |
@@ -242,8 +243,8 @@ export function DeviceController(userService, deviceService, customerService, $s | @@ -242,8 +243,8 @@ export function DeviceController(userService, deviceService, customerService, $s | ||
242 | 243 | ||
243 | 244 | ||
244 | } else if (vm.devicesScope === 'customer' || vm.devicesScope === 'customer_user') { | 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 | deleteDeviceFunction = function (deviceId) { | 249 | deleteDeviceFunction = function (deviceId) { |
249 | return deviceService.unassignDeviceFromCustomer(deviceId); | 250 | return deviceService.unassignDeviceFromCustomer(deviceId); |
@@ -368,6 +369,7 @@ export function DeviceController(userService, deviceService, customerService, $s | @@ -368,6 +369,7 @@ export function DeviceController(userService, deviceService, customerService, $s | ||
368 | var deferred = $q.defer(); | 369 | var deferred = $q.defer(); |
369 | deviceService.saveDevice(device).then( | 370 | deviceService.saveDevice(device).then( |
370 | function success(savedDevice) { | 371 | function success(savedDevice) { |
372 | + $rootScope.$broadcast('deviceSaved'); | ||
371 | var devices = [ savedDevice ]; | 373 | var devices = [ savedDevice ]; |
372 | customerService.applyAssignedCustomersInfo(devices).then( | 374 | customerService.applyAssignedCustomersInfo(devices).then( |
373 | function success(items) { | 375 | function success(items) { |
@@ -25,6 +25,7 @@ export default function DeviceDirective($compile, $templateCache, toast, $transl | @@ -25,6 +25,7 @@ export default function DeviceDirective($compile, $templateCache, toast, $transl | ||
25 | var template = $templateCache.get(deviceFieldsetTemplate); | 25 | var template = $templateCache.get(deviceFieldsetTemplate); |
26 | element.html(template); | 26 | element.html(template); |
27 | 27 | ||
28 | + scope.types = types; | ||
28 | scope.isAssignedToCustomer = false; | 29 | scope.isAssignedToCustomer = false; |
29 | scope.isPublic = false; | 30 | scope.isPublic = false; |
30 | scope.assignedCustomer = null; | 31 | scope.assignedCustomer = null; |
@@ -20,7 +20,7 @@ import devicesTemplate from './devices.tpl.html'; | @@ -20,7 +20,7 @@ import devicesTemplate from './devices.tpl.html'; | ||
20 | /* eslint-enable import/no-unresolved, import/default */ | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | ||
22 | /*@ngInject*/ | 22 | /*@ngInject*/ |
23 | -export default function DeviceRoutes($stateProvider) { | 23 | +export default function DeviceRoutes($stateProvider, types) { |
24 | $stateProvider | 24 | $stateProvider |
25 | .state('home.devices', { | 25 | .state('home.devices', { |
26 | url: '/devices', | 26 | url: '/devices', |
@@ -37,6 +37,8 @@ export default function DeviceRoutes($stateProvider) { | @@ -37,6 +37,8 @@ export default function DeviceRoutes($stateProvider) { | ||
37 | data: { | 37 | data: { |
38 | devicesType: 'tenant', | 38 | devicesType: 'tenant', |
39 | searchEnabled: true, | 39 | searchEnabled: true, |
40 | + searchByEntitySubtype: true, | ||
41 | + searchEntityType: types.entityType.device, | ||
40 | pageTitle: 'device.devices' | 42 | pageTitle: 'device.devices' |
41 | }, | 43 | }, |
42 | ncyBreadcrumb: { | 44 | ncyBreadcrumb: { |
@@ -58,6 +60,8 @@ export default function DeviceRoutes($stateProvider) { | @@ -58,6 +60,8 @@ export default function DeviceRoutes($stateProvider) { | ||
58 | data: { | 60 | data: { |
59 | devicesType: 'customer', | 61 | devicesType: 'customer', |
60 | searchEnabled: true, | 62 | searchEnabled: true, |
63 | + searchByEntitySubtype: true, | ||
64 | + searchEntityType: types.entityType.device, | ||
61 | pageTitle: 'customer.devices' | 65 | pageTitle: 'customer.devices' |
62 | }, | 66 | }, |
63 | ncyBreadcrumb: { | 67 | ncyBreadcrumb: { |
@@ -63,7 +63,7 @@ | @@ -63,7 +63,7 @@ | ||
63 | {{ 'action.search' | translate }} | 63 | {{ 'action.search' | translate }} |
64 | </md-tooltip> | 64 | </md-tooltip> |
65 | </md-button> | 65 | </md-button> |
66 | - <md-input-container md-theme="tb-search-input" flex> | 66 | + <md-input-container flex> |
67 | <label> </label> | 67 | <label> </label> |
68 | <input ng-model="query.search" placeholder="{{ 'common.enter-search' | translate }}"/> | 68 | <input ng-model="query.search" placeholder="{{ 'common.enter-search' | translate }}"/> |
69 | </md-input-container> | 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,14 +13,10 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 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 | display: block; | 18 | display: block; |
19 | - line-height: 1.5; | ||
20 | height: 48px; | 19 | height: 48px; |
21 | - .tb-no-entries { | ||
22 | - line-height: 48px; | ||
23 | - } | ||
24 | } | 20 | } |
25 | li { | 21 | li { |
26 | height: auto !important; | 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,12 +14,6 @@ | ||
14 | * limitations under the License. | 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,19 +15,14 @@ | ||
15 | limitations under the License. | 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,6 +16,8 @@ | ||
16 | 16 | ||
17 | import EntityAliasesController from './entity-aliases.controller'; | 17 | import EntityAliasesController from './entity-aliases.controller'; |
18 | import EntityTypeSelectDirective from './entity-type-select.directive'; | 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 | import EntityFilterDirective from './entity-filter.directive'; | 21 | import EntityFilterDirective from './entity-filter.directive'; |
20 | import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; | 22 | import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; |
21 | import AliasesEntitySelectDirective from './aliases-entity-select.directive'; | 23 | import AliasesEntitySelectDirective from './aliases-entity-select.directive'; |
@@ -29,6 +31,8 @@ export default angular.module('thingsboard.entity', []) | @@ -29,6 +31,8 @@ export default angular.module('thingsboard.entity', []) | ||
29 | .controller('AddAttributeDialogController', AddAttributeDialogController) | 31 | .controller('AddAttributeDialogController', AddAttributeDialogController) |
30 | .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController) | 32 | .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController) |
31 | .directive('tbEntityTypeSelect', EntityTypeSelectDirective) | 33 | .directive('tbEntityTypeSelect', EntityTypeSelectDirective) |
34 | + .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective) | ||
35 | + .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective) | ||
32 | .directive('tbEntityFilter', EntityFilterDirective) | 36 | .directive('tbEntityFilter', EntityFilterDirective) |
33 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) | 37 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) |
34 | .directive('tbAttributeTable', AttributeTableDirective) | 38 | .directive('tbAttributeTable', AttributeTableDirective) |
@@ -25,7 +25,7 @@ import logoSvg from '../../svg/logo_title_white.svg'; | @@ -25,7 +25,7 @@ import logoSvg from '../../svg/logo_title_white.svg'; | ||
25 | /* eslint-disable angular/angularelement */ | 25 | /* eslint-disable angular/angularelement */ |
26 | 26 | ||
27 | /*@ngInject*/ | 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 | $window, $log, $mdMedia, $animate, $timeout) { | 29 | $window, $log, $mdMedia, $animate, $timeout) { |
30 | 30 | ||
31 | var siteSideNav = $('.tb-site-sidenav', $element); | 31 | var siteSideNav = $('.tb-site-sidenav', $element); |
@@ -38,8 +38,11 @@ export default function HomeController(loginService, userService, deviceService, | @@ -38,8 +38,11 @@ export default function HomeController(loginService, userService, deviceService, | ||
38 | if (angular.isUndefined($rootScope.searchConfig)) { | 38 | if (angular.isUndefined($rootScope.searchConfig)) { |
39 | $rootScope.searchConfig = { | 39 | $rootScope.searchConfig = { |
40 | searchEnabled: false, | 40 | searchEnabled: false, |
41 | + searchByEntitySubtype: false, | ||
42 | + searchEntityType: null, | ||
41 | showSearch: false, | 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,6 +50,7 @@ export default function HomeController(loginService, userService, deviceService, | ||
47 | vm.isLockSidenav = false; | 50 | vm.isLockSidenav = false; |
48 | 51 | ||
49 | vm.displaySearchMode = displaySearchMode; | 52 | vm.displaySearchMode = displaySearchMode; |
53 | + vm.displayEntitySubtypeSearch = displayEntitySubtypeSearch; | ||
50 | vm.openSidenav = openSidenav; | 54 | vm.openSidenav = openSidenav; |
51 | vm.goBack = goBack; | 55 | vm.goBack = goBack; |
52 | vm.searchTextUpdated = searchTextUpdated; | 56 | vm.searchTextUpdated = searchTextUpdated; |
@@ -54,25 +58,35 @@ export default function HomeController(loginService, userService, deviceService, | @@ -54,25 +58,35 @@ export default function HomeController(loginService, userService, deviceService, | ||
54 | vm.toggleFullscreen = toggleFullscreen; | 58 | vm.toggleFullscreen = toggleFullscreen; |
55 | 59 | ||
56 | $scope.$on('$stateChangeSuccess', function (evt, to, toParams, from) { | 60 | $scope.$on('$stateChangeSuccess', function (evt, to, toParams, from) { |
61 | + watchEntitySubtype(false); | ||
57 | if (angular.isDefined(to.data.searchEnabled)) { | 62 | if (angular.isDefined(to.data.searchEnabled)) { |
58 | $scope.searchConfig.searchEnabled = to.data.searchEnabled; | 63 | $scope.searchConfig.searchEnabled = to.data.searchEnabled; |
64 | + $scope.searchConfig.searchByEntitySubtype = to.data.searchByEntitySubtype; | ||
65 | + $scope.searchConfig.searchEntityType = to.data.searchEntityType; | ||
59 | if ($scope.searchConfig.searchEnabled === false || to.name !== from.name) { | 66 | if ($scope.searchConfig.searchEnabled === false || to.name !== from.name) { |
60 | $scope.searchConfig.showSearch = false; | 67 | $scope.searchConfig.showSearch = false; |
61 | $scope.searchConfig.searchText = ""; | 68 | $scope.searchConfig.searchText = ""; |
69 | + $scope.searchConfig.searchEntitySubtype = ""; | ||
62 | } | 70 | } |
63 | } else { | 71 | } else { |
64 | $scope.searchConfig.searchEnabled = false; | 72 | $scope.searchConfig.searchEnabled = false; |
73 | + $scope.searchConfig.searchByEntitySubtype = false; | ||
74 | + $scope.searchConfig.searchEntityType = null; | ||
65 | $scope.searchConfig.showSearch = false; | 75 | $scope.searchConfig.showSearch = false; |
66 | $scope.searchConfig.searchText = ""; | 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 | vm.isLockSidenav = true; | 84 | vm.isLockSidenav = true; |
72 | $animate.enabled(siteSideNav, false); | 85 | $animate.enabled(siteSideNav, false); |
73 | } | 86 | } |
74 | 87 | ||
75 | $scope.$watch(function() { return $mdMedia('gt-sm'); }, function(isGtSm) { | 88 | $scope.$watch(function() { return $mdMedia('gt-sm'); }, function(isGtSm) { |
89 | + vm.isGtSm = isGtSm; | ||
76 | vm.isLockSidenav = isGtSm; | 90 | vm.isLockSidenav = isGtSm; |
77 | vm.isShowSidenav = isGtSm; | 91 | vm.isShowSidenav = isGtSm; |
78 | if (!isGtSm) { | 92 | if (!isGtSm) { |
@@ -84,11 +98,28 @@ export default function HomeController(loginService, userService, deviceService, | @@ -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 | function displaySearchMode() { | 114 | function displaySearchMode() { |
88 | return $scope.searchConfig.searchEnabled && | 115 | return $scope.searchConfig.searchEnabled && |
89 | $scope.searchConfig.showSearch; | 116 | $scope.searchConfig.showSearch; |
90 | } | 117 | } |
91 | 118 | ||
119 | + function displayEntitySubtypeSearch() { | ||
120 | + return $scope.searchConfig.searchByEntitySubtype && vm.isGtSm; | ||
121 | + } | ||
122 | + | ||
92 | function toggleFullscreen() { | 123 | function toggleFullscreen() { |
93 | if (Fullscreen.isEnabled()) { | 124 | if (Fullscreen.isEnabled()) { |
94 | Fullscreen.cancel(); | 125 | Fullscreen.cancel(); |
@@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
39 | </md-sidenav> | 39 | </md-sidenav> |
40 | 40 | ||
41 | <div flex layout="column" tabIndex="-1" role="main"> | 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 | <div layout="row" flex class="md-toolbar-tools"> | 43 | <div layout="row" flex class="md-toolbar-tools"> |
44 | <md-button id="main" hide-gt-sm ng-show="!forceFullscreen" | 44 | <md-button id="main" hide-gt-sm ng-show="!forceFullscreen" |
45 | class="md-icon-button" ng-click="vm.openSidenav()" aria-label="{{ 'home.menu' | translate }}" ng-class="{'tb-invisible': vm.displaySearchMode()}"> | 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,10 +55,18 @@ | ||
55 | <div flex layout="row" ng-show="!vm.displaySearchMode()" tb-no-animate class="md-toolbar-tools"> | 55 | <div flex layout="row" ng-show="!vm.displaySearchMode()" tb-no-animate class="md-toolbar-tools"> |
56 | <span ng-cloak ncy-breadcrumb></span> | 56 | <span ng-cloak ncy-breadcrumb></span> |
57 | </div> | 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 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}" ng-show="searchConfig.searchEnabled" ng-click="searchConfig.showSearch = !searchConfig.showSearch"> | 70 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}" ng-show="searchConfig.searchEnabled" ng-click="searchConfig.showSearch = !searchConfig.showSearch"> |
63 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> | 71 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
64 | </md-button> | 72 | </md-button> |
@@ -48,16 +48,10 @@ function UserMenuController($scope, userService, $translate, $state) { | @@ -48,16 +48,10 @@ function UserMenuController($scope, userService, $translate, $state) { | ||
48 | var dashboardUser = userService.getCurrentUser(); | 48 | var dashboardUser = userService.getCurrentUser(); |
49 | 49 | ||
50 | vm.authorityName = authorityName; | 50 | vm.authorityName = authorityName; |
51 | - vm.displaySearchMode = displaySearchMode; | ||
52 | vm.logout = logout; | 51 | vm.logout = logout; |
53 | vm.openProfile = openProfile; | 52 | vm.openProfile = openProfile; |
54 | vm.userDisplayName = userDisplayName; | 53 | vm.userDisplayName = userDisplayName; |
55 | 54 | ||
56 | - function displaySearchMode() { | ||
57 | - return $scope.searchConfig.searchEnabled && | ||
58 | - $scope.searchConfig.showSearch; | ||
59 | - } | ||
60 | - | ||
61 | function authorityName() { | 55 | function authorityName() { |
62 | var name = "user.anonymous"; | 56 | var name = "user.anonymous"; |
63 | if (dashboardUser) { | 57 | if (dashboardUser) { |
@@ -124,6 +124,9 @@ export default angular.module('thingsboard.locale', []) | @@ -124,6 +124,9 @@ export default angular.module('thingsboard.locale', []) | ||
124 | "unassign-from-customer": "Unassign from customer", | 124 | "unassign-from-customer": "Unassign from customer", |
125 | "delete": "Delete asset", | 125 | "delete": "Delete asset", |
126 | "asset-public": "Asset is public", | 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 | "name": "Name", | 130 | "name": "Name", |
128 | "name-required": "Name is required.", | 131 | "name-required": "Name is required.", |
129 | "description": "Description", | 132 | "description": "Description", |
@@ -477,6 +480,9 @@ export default angular.module('thingsboard.locale', []) | @@ -477,6 +480,9 @@ export default angular.module('thingsboard.locale', []) | ||
477 | "rsa-key-required": "RSA public key is required.", | 480 | "rsa-key-required": "RSA public key is required.", |
478 | "secret": "Secret", | 481 | "secret": "Secret", |
479 | "secret-required": "Secret is required.", | 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 | "name": "Name", | 486 | "name": "Name", |
481 | "name-required": "Name is required.", | 487 | "name-required": "Name is required.", |
482 | "description": "Description", | 488 | "description": "Description", |
@@ -521,6 +527,7 @@ export default angular.module('thingsboard.locale', []) | @@ -521,6 +527,7 @@ export default angular.module('thingsboard.locale', []) | ||
521 | "entity-list-empty": "No entities selected.", | 527 | "entity-list-empty": "No entities selected.", |
522 | "entity-name-filter-required": "Entity name filter is required.", | 528 | "entity-name-filter-required": "Entity name filter is required.", |
523 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", | 529 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", |
530 | + "all-subtypes": "All", | ||
524 | "type": "Type", | 531 | "type": "Type", |
525 | "type-device": "Device", | 532 | "type-device": "Device", |
526 | "type-asset": "Asset", | 533 | "type-asset": "Asset", |