Browse Source

Entity View feature - UI

pull/1070/head
Volodymyr Babak 8 years ago
parent
commit
40017c46df
  1. 237
      ui/src/app/api/entity-view.service.js
  2. 2
      ui/src/app/app.js
  3. 3
      ui/src/app/common/types.constant.js
  4. 45
      ui/src/app/entity-view/add-entity-view.tpl.html
  5. 123
      ui/src/app/entity-view/add-entity-views-to-customer.controller.js
  6. 77
      ui/src/app/entity-view/add-entity-views-to-customer.tpl.html
  7. 123
      ui/src/app/entity-view/assign-to-customer.controller.js
  8. 76
      ui/src/app/entity-view/assign-to-customer.tpl.html
  9. 22
      ui/src/app/entity-view/entity-view-card.tpl.html
  10. 64
      ui/src/app/entity-view/entity-view-fieldset.tpl.html
  11. 483
      ui/src/app/entity-view/entity-view.controller.js
  12. 67
      ui/src/app/entity-view/entity-view.directive.js
  13. 72
      ui/src/app/entity-view/entity-view.routes.js
  14. 83
      ui/src/app/entity-view/entity-views.tpl.html
  15. 41
      ui/src/app/entity-view/index.js
  16. 73
      ui/src/app/locale/locale.constant-en_US.json

237
ui/src/app/api/entity-view.service.js

@ -0,0 +1,237 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import thingsboardTypes from '../common/types.constant';
export default angular.module('thingsboard.api.entityView', [thingsboardTypes])
.factory('entityViewService', EntityViewService)
.name;
/*@ngInject*/
function EntityViewService($http, $q, $window, userService, attributeService, customerService, types) {
var service = {
assignEntityViewToCustomer: assignEntityViewToCustomer,
deleteEntityView: deleteEntityView,
getCustomerEntityViews: getCustomerEntityViews,
getEntityView: getEntityView,
getEntityViews: getEntityViews,
getTenantEntityViews: getTenantEntityViews,
saveEntityView: saveEntityView,
unassignEntityViewFromCustomer: unassignEntityViewFromCustomer,
getEntityViewAttributes: getEntityViewAttributes,
subscribeForEntityViewAttributes: subscribeForEntityViewAttributes,
unsubscribeForEntityViewAttributes: unsubscribeForEntityViewAttributes,
findByQuery: findByQuery,
getEntityViewTypes: getEntityViewTypes
}
return service;
function getTenantEntityViews(pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/tenant/entityViews?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getCustomerEntityViews(customerId, pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/entityViews?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEntityView(entityViewId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/entityView/' + entityViewId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function getEntityViews(entityViewIds, config) {
var deferred = $q.defer();
var ids = '';
for (var i=0;i<entityViewIds.length;i++) {
if (i>0) {
ids += ',';
}
ids += entityViewIds[i];
}
var url = '/api/entityViews?entityViewIds=' + ids;
$http.get(url, config).then(function success(response) {
var entityViews = response.data;
entityViews.sort(function (entityView1, entityView2) {
var id1 = entityView1.id.id;
var id2 = entityView2.id.id;
var index1 = entityViewIds.indexOf(id1);
var index2 = entityViewIds.indexOf(id2);
return index1 - index2;
});
deferred.resolve(entityViews);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function saveEntityView(entityView) {
var deferred = $q.defer();
var url = '/api/entityView';
$http.post(url, entityView).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function deleteEntityView(entityViewId) {
var deferred = $q.defer();
var url = '/api/entityView/' + entityViewId;
$http.delete(url).then(function success() {
deferred.resolve();
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function assignEntityViewToCustomer(customerId, entityViewId) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/entityView/' + entityViewId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignEntityViewFromCustomer(entityViewId) {
var deferred = $q.defer();
var url = '/api/customer/entityView/' + entityViewId;
$http.delete(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEntityViewAttributes(entityViewId, attributeScope, query, successCallback, config) {
return attributeService.getEntityAttributes(types.entityType.entityView, entityViewId, attributeScope, query, successCallback, config);
}
function subscribeForEntityViewAttributes(entityViewId, attributeScope) {
return attributeService.subscribeForEntityAttributes(types.entityType.entityView, entityViewId, attributeScope);
}
function unsubscribeForEntityViewAttributes(subscriptionId) {
attributeService.unsubscribeForEntityAttributes(subscriptionId);
}
function findByQuery(query, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/entityViews';
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.post(url, query, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEntityViewTypes(config) {
var deferred = $q.defer();
var url = '/api/entityView/types';
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

2
ui/src/app/app.js

@ -67,6 +67,7 @@ import thingsboardClipboard from './services/clipboard.service';
import thingsboardHome from './layout';
import thingsboardApiLogin from './api/login.service';
import thingsboardApiDevice from './api/device.service';
import thingsboardApiEntityView from './api/entity-view.service';
import thingsboardApiUser from './api/user.service';
import thingsboardApiEntityRelation from './api/entity-relation.service';
import thingsboardApiAsset from './api/asset.service';
@ -133,6 +134,7 @@ angular.module('thingsboard', [
thingsboardHome,
thingsboardApiLogin,
thingsboardApiDevice,
thingsboardApiEntityView,
thingsboardApiUser,
thingsboardApiEntityRelation,
thingsboardApiAsset,

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

@ -327,7 +327,8 @@ export default angular.module('thingsboard.types', [])
dashboard: "DASHBOARD",
alarm: "ALARM",
rulechain: "RULE_CHAIN",
rulenode: "RULE_NODE"
rulenode: "RULE_NODE",
entityview: "ENTITY_VIEW"
},
aliasEntityType: {
current_customer: "CURRENT_CUSTOMER"

45
ui/src/app/entity-view/add-entity-view.tpl.html

@ -0,0 +1,45 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'entity-view.add' | translate }}" tb-help="'entityViews'" help-container-id="help-container">
<form name="theForm" ng-submit="vm.add()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>entity-view.add</h2>
<span flex></span>
<div id="help-container"></div>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<tb-entity-view entity-view="vm.item" is-edit="true" the-form="theForm"></tb-entity-view>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/entity-view/add-entity-views-to-customer.controller.js

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddEntityViewsToCustomerController(entityViewService, $mdDialog, $q, customerId, entityViews) {
var vm = this;
vm.entityViews = entityViews;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchEntityViewTextUpdated = searchEntityViewTextUpdated;
vm.toggleEntityViewSelection = toggleEntityViewSelection;
vm.theEntityViews = {
getItemAtIndex: function (index) {
if (index > vm.entityViews.data.length) {
vm.theEntityViews.fetchMoreItems_(index);
return null;
}
var item = vm.entityViews.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.entityViews.hasNext) {
return vm.entityViews.data.length + vm.entityViews.nextPageLink.limit;
} else {
return vm.entityViews.data.length;
}
},
fetchMoreItems_: function () {
if (vm.entityViews.hasNext && !vm.entityViews.pending) {
vm.entityViews.pending = true;
entityViewService.getTenantEntityViews(vm.entityViews.nextPageLink, false).then(
function success(entityViews) {
vm.entityViews.data = vm.entityViews.data.concat(entityViews.data);
vm.entityViews.nextPageLink = entityViews.nextPageLink;
vm.entityViews.hasNext = entityViews.hasNext;
if (vm.entityViews.hasNext) {
vm.entityViews.nextPageLink.limit = vm.entityViews.pageSize;
}
vm.entityViews.pending = false;
},
function fail() {
vm.entityViews.hasNext = false;
vm.entityViews.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var entityViewId in vm.entityViews.selections) {
tasks.push(entityViewService.assignEntityViewToCustomer(customerId, entityViewId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.entityViews.data.length == 0 && !vm.entityViews.hasNext;
}
function hasData() {
return vm.entityViews.data.length > 0;
}
function toggleEntityViewSelection($event, entityView) {
$event.stopPropagation();
var selected = angular.isDefined(entityView.selected) && entityView.selected;
entityView.selected = !selected;
if (entityView.selected) {
vm.entityViews.selections[entityView.id.id] = true;
vm.entityViews.selectedCount++;
} else {
delete vm.entityViews.selections[entityView.id.id];
vm.entityViews.selectedCount--;
}
}
function searchEntityViewTextUpdated() {
vm.entityViews = {
pageSize: vm.entityViews.pageSize,
data: [],
nextPageLink: {
limit: vm.entityViews.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/entity-view/add-entity-views-to-customer.tpl.html

@ -0,0 +1,77 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'entity-view.assign-to-customer' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>entity-view.assign-entity-view-to-customer</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>entity-view.assign-entity-view-to-customer-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="entity-view-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchEntityViewTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">entity-view.no-entity-views-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="entityView in vm.theEntityViews" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleEntityViewSelection($event, entityView)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="entityView.selected"></md-checkbox>
<span> {{ entityView.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.entityViews.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/entity-view/assign-to-customer.controller.js

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AssignEntityViewToCustomerController(customerService, entityViewService, $mdDialog, $q, entityViewIds, customers) {
var vm = this;
vm.customers = customers;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isCustomerSelected = isCustomerSelected;
vm.hasData = hasData;
vm.noData = noData;
vm.searchCustomerTextUpdated = searchCustomerTextUpdated;
vm.toggleCustomerSelection = toggleCustomerSelection;
vm.theCustomers = {
getItemAtIndex: function (index) {
if (index > vm.customers.data.length) {
vm.theCustomers.fetchMoreItems_(index);
return null;
}
var item = vm.customers.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.customers.hasNext) {
return vm.customers.data.length + vm.customers.nextPageLink.limit;
} else {
return vm.customers.data.length;
}
},
fetchMoreItems_: function () {
if (vm.customers.hasNext && !vm.customers.pending) {
vm.customers.pending = true;
customerService.getCustomers(vm.customers.nextPageLink).then(
function success(customers) {
vm.customers.data = vm.customers.data.concat(customers.data);
vm.customers.nextPageLink = customers.nextPageLink;
vm.customers.hasNext = customers.hasNext;
if (vm.customers.hasNext) {
vm.customers.nextPageLink.limit = vm.customers.pageSize;
}
vm.customers.pending = false;
},
function fail() {
vm.customers.hasNext = false;
vm.customers.pending = false;
});
}
}
};
function cancel() {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var i=0; i < entityViewIds.length;i++) {
tasks.push(entityViewService.assignEntityViewToCustomer(vm.customers.selection.id.id, entityViewIds[i]));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.customers.data.length == 0 && !vm.customers.hasNext;
}
function hasData() {
return vm.customers.data.length > 0;
}
function toggleCustomerSelection($event, customer) {
$event.stopPropagation();
if (vm.isCustomerSelected(customer)) {
vm.customers.selection = null;
} else {
vm.customers.selection = customer;
}
}
function isCustomerSelected(customer) {
return vm.customers.selection != null && customer &&
customer.id.id === vm.customers.selection.id.id;
}
function searchCustomerTextUpdated() {
vm.customers = {
pageSize: vm.customers.pageSize,
data: [],
nextPageLink: {
limit: vm.customers.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

76
ui/src/app/entity-view/assign-to-customer.tpl.html

@ -0,0 +1,76 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'entity-view.assign-entity-view-to-customer' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>entity-view.assign-entity-view-to-customer</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>entity-view.assign-to-customer-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="customer-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchCustomerTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">customer.no-customers-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="vm.isCustomerSelected(customer)"></md-checkbox>
<span> {{ customer.title }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

22
ui/src/app/entity-view/entity-view-card.tpl.html

@ -0,0 +1,22 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div flex layout="column" style="margin-top: -10px;">
<div style="text-transform: uppercase; padding-bottom: 5px;">{{vm.item.type}}</div>
<div class="tb-card-description">{{vm.item.additionalInfo.description}}</div>
<div style="padding-top: 5px;" class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'entity-view.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
</div>

64
ui/src/app/entity-view/entity-view-fieldset.tpl.html

@ -0,0 +1,64 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-button ng-click="onAssignToCustomer({event: $event})"
ng-show="!isEdit && entityViewScope === 'tenant' && !isAssignedToCustomer"
class="md-raised md-primary">{{ 'entity-view.assign-to-customer' | translate }}</md-button>
<md-button ng-click="onUnassignFromCustomer({event: $event})"
ng-show="!isEdit && (entityViewScope === 'customer' || entityViewScope === 'tenant') && isAssignedToCustomer"
class="md-raised md-primary">{{'entity-view.unassign-from-customer' | translate }}</md-button>
<md-button ng-click="onDeleteEntityView({event: $event})"
ng-show="!isEdit && entityViewScope === 'tenant'"
class="md-raised md-primary">{{ 'entity-view.delete' | translate }}</md-button>
<div layout="row">
<md-button ngclipboard data-clipboard-action="copy"
ngclipboard-success="onEntityViewIdCopied(e)"
data-clipboard-text="{{entityView.id.id}}" ng-show="!isEdit"
class="md-raised">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<span translate>entity-view.copyId</span>
</md-button>
</div>
<md-content class="md-padding" layout="column">
<md-input-container class="md-block"
ng-show="!isEdit && isAssignedToCustomer && entityViewScope === 'tenant'">
<label translate>entity-view.assignedToCustomer</label>
<input ng-model="assignedCustomer.title" disabled>
</md-input-container>
<fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block">
<label translate>entity-view.name</label>
<input required name="name" ng-model="entityView.name">
<div ng-messages="theForm.name.$error">
<div translate ng-message="required">entity-view.name-required</div>
</div>
</md-input-container>
<tb-entity-subtype-autocomplete
ng-disabled="$root.loading || !isEdit"
tb-required="true"
the-form="theForm"
ng-model="entityView.type"
entity-type="types.entityType.entityview">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>entity-view.description</label>
<textarea ng-model="entityView.additionalInfo.description" rows="2"></textarea>
</md-input-container>
</fieldset>
</md-content>

483
ui/src/app/entity-view/entity-view.controller.js

@ -0,0 +1,483 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import addEntityViewTemplate from './add-entity-view.tpl.html';
import entityViewCard from './entity-view-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addEntityViewsToCustomerTemplate from './add-entity-views-to-customer.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export function EntityViewCardController(types) {
var vm = this;
vm.types = types;
vm.isAssignedToCustomer = function() {
if (vm.item && vm.item.customerId && vm.parentCtl.entityViewsScope === 'tenant' &&
vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
vm.isPublic = function() {
if (vm.item && vm.item.assignedCustomer && vm.parentCtl.entityViewsScope === 'tenant' && vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
}
/*@ngInject*/
export function EntityViewController($rootScope, userService, entityViewService, customerService, $state, $stateParams,
$document, $mdDialog, $q, $translate, types) {
var customerId = $stateParams.customerId;
var entityViewActionsList = [];
var entityViewGroupActionsList = [];
var vm = this;
vm.types = types;
vm.entityViewGridConfig = {
deleteItemTitleFunc: deleteEntityViewTitle,
deleteItemContentFunc: deleteEntityViewText,
deleteItemsTitleFunc: deleteEntityViewsTitle,
deleteItemsActionTitleFunc: deleteEntityViewsActionTitle,
deleteItemsContentFunc: deleteEntityViewsText,
saveItemFunc: saveEntityView,
getItemTitleFunc: getEntityViewTitle,
itemCardController: 'EntityViewCardController',
itemCardTemplateUrl: entityViewCard,
parentCtl: vm,
actionsList: entityViewActionsList,
groupActionsList: entityViewGroupActionsList,
onGridInited: gridInited,
addItemTemplateUrl: addEntityViewTemplate,
addItemText: function() { return $translate.instant('entity-view.add-entity-view-text') },
noItemsText: function() { return $translate.instant('entity-view.no-entity-views-text') },
itemDetailsText: function() { return $translate.instant('entity-view.entity-view-details') },
isDetailsReadOnly: isCustomerUser,
isSelectionEnabled: function () {
return !isCustomerUser();
}
};
if (angular.isDefined($stateParams.items) && $stateParams.items !== null) {
vm.entityViewGridConfig.items = $stateParams.items;
}
if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) {
vm.entityViewGridConfig.topIndex = $stateParams.topIndex;
}
vm.entityViewsScope = $state.$current.data.entityViewsType;
vm.assignToCustomer = assignToCustomer;
vm.makePublic = makePublic;
vm.unassignFromCustomer = unassignFromCustomer;
initController();
function initController() {
var fetchEntityViewsFunction = null;
var deleteEntityViewFunction = null;
var refreshEntityViewsParamsFunction = null;
var user = userService.getCurrentUser();
if (user.authority === 'CUSTOMER_USER') {
vm.entityViewsScope = 'customer_user';
customerId = user.customerId;
}
if (customerId) {
vm.customerEntityViewsTitle = $translate.instant('customer.entity-views');
customerService.getShortCustomerInfo(customerId).then(
function success(info) {
if (info.isPublic) {
vm.customerEntityViewsTitle = $translate.instant('customer.public-entity-views');
}
}
);
}
if (vm.entityViewsScope === 'tenant') {
fetchEntityViewsFunction = function (pageLink, entityViewType) {
return entityViewService.getTenantEntityViews(pageLink, true, null, entityViewType);
};
deleteEntityViewFunction = function (entityViewId) {
return entityViewService.deleteEntityView(entityViewId);
};
refreshEntityViewsParamsFunction = function() {
return {"topIndex": vm.topIndex};
};
entityViewActionsList.push(
{
onAction: function ($event, item) {
assignToCustomer($event, [ item.id.id ]);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('entity-view.assign-to-customer') },
icon: "assignment_ind",
isEnabled: function(entityView) {
return entityView && (!entityView.customerId || entityView.customerId.id === types.id.nullUid);
}
}
);
entityViewActionsList.push(
{
onAction: function ($event, item) {
unassignFromCustomer($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('entity-view.unassign-from-customer') },
icon: "assignment_return",
isEnabled: function(entityView) {
return entityView && entityView.customerId && entityView.customerId.id !== types.id.nullUid && !entityView.assignedCustomer.isPublic;
}
}
);
entityViewActionsList.push({
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('entity-view.make-private') },
icon: "reply",
isEnabled: function(entityView) {
return entityView && entityView.customerId && entityView.customerId.id !== types.id.nullUid && entityView.assignedCustomer.isPublic;
}
});
entityViewActionsList.push(
{
onAction: function ($event, item) {
vm.grid.deleteItem($event, item);
},
name: function() { return $translate.instant('action.delete') },
details: function() { return $translate.instant('entity-view.delete') },
icon: "delete"
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event, items) {
assignEntiyViewsToCustomer($event, items);
},
name: function() { return $translate.instant('entity-view.assign-entity-views') },
details: function(selectedCount) {
return $translate.instant('entity-view.assign-entity-views-text', {count: selectedCount}, "messageformat");
},
icon: "assignment_ind"
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event) {
vm.grid.deleteItems($event);
},
name: function() { return $translate.instant('entity-view.delete-entity-views') },
details: deleteEntityViewsActionTitle,
icon: "delete"
}
);
} else if (vm.entityViewsScope === 'customer' || vm.entityViewsScope === 'customer_user') {
fetchEntityViewsFunction = function (pageLink, entityViewType) {
return entityViewService.getCustomerEntityViews(customerId, pageLink, true, null, entityViewType);
};
deleteentityViewFunction = function (entityViewId) {
return entityViewService.unassignEntityViewFromCustomer(entityViewId);
};
refreshentityViewsParamsFunction = function () {
return {"customerId": customerId, "topIndex": vm.topIndex};
};
if (vm.entityViewsScope === 'customer') {
entityViewActionsList.push(
{
onAction: function ($event, item) {
unassignFromCustomer($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('entity-view.unassign-from-customer') },
icon: "assignment_return",
isEnabled: function(entityView) {
return entityView && !entityView.assignedCustomer.isPublic;
}
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event, items) {
unassignEntityViewsFromCustomer($event, items);
},
name: function() { return $translate.instant('entity-view.unassign-entity-views') },
details: function(selectedCount) {
return $translate.instant('entity-view.unassign-entity-views-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.entityViewGridConfig.addItemAction = {
onAction: function ($event) {
addEntityViewsToCustomer($event);
},
name: function() { return $translate.instant('entity-view.assign-entity-views') },
details: function() { return $translate.instant('entity-view.assign-new-entity-view') },
icon: "add"
};
} else if (vm.entityViewsScope === 'customer_user') {
vm.entityViewGridConfig.addItemAction = {};
}
}
vm.entityViewGridConfig.refreshParamsFunc = refreshentityViewsParamsFunction;
vm.entityViewGridConfig.fetchItemsFunc = fetchentityViewsFunction;
vm.entityViewGridConfig.deleteItemFunc = deleteentityViewFunction;
}
function deleteEntityViewTitle(entityView) {
return $translate.instant('entity-view.delete-entity-view-title', {entityViewName: entityView.name});
}
function deleteEntityViewText() {
return $translate.instant('entity-view.delete-entity-view-text');
}
function deleteEntityViewsTitle(selectedCount) {
return $translate.instant('entity-view.delete-entity-views-title', {count: selectedCount}, 'messageformat');
}
function deleteEntityViewsActionTitle(selectedCount) {
return $translate.instant('entity-view.delete-entity-views-action-title', {count: selectedCount}, 'messageformat');
}
function deleteEntityViewsText () {
return $translate.instant('entity-view.delete-entity-views-text');
}
function gridInited(grid) {
vm.grid = grid;
}
function getEntityViewTitle(entityView) {
return entityView ? entityView.name : '';
}
function saveEntityView(entityView) {
var deferred = $q.defer();
entityViewService.saveEntityView(entityView).then(
function success(savedEntityView) {
$rootScope.$broadcast('entityViewSaved');
var entityViews = [ savedEntityView ];
customerService.applyAssignedCustomersInfo(entityViews).then(
function success(items) {
if (items && items.length == 1) {
deferred.resolve(items[0]);
} else {
deferred.reject();
}
},
function fail() {
deferred.reject();
}
);
},
function fail() {
deferred.reject();
}
);
return deferred.promise;
}
function isCustomerUser() {
return vm.entityViewsScope === 'customer_user';
}
function assignToCustomer($event, entityViewIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
customerService.getCustomers({limit: pageSize, textSearch: ''}).then(
function success(_customers) {
var customers = {
pageSize: pageSize,
data: _customers.data,
nextPageLink: _customers.nextPageLink,
selection: null,
hasNext: _customers.hasNext,
pending: false
};
if (customers.hasNext) {
customers.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignEntityViewToCustomerController',
controllerAs: 'vm',
templateUrl: assignToCustomerTemplate,
locals: {entityViewIds: entityViewIds, customers: customers},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function addEntityViewsToCustomer($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
entityViewService.getTenantEntityViews({limit: pageSize, textSearch: ''}, false).then(
function success(_entityViews) {
var entityViews = {
pageSize: pageSize,
data: _entityViews.data,
nextPageLink: _entityViews.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _entityViews.hasNext,
pending: false
};
if (entityViews.hasNext) {
entityViews.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddEntityViewsToCustomerController',
controllerAs: 'vm',
templateUrl: addEntityViewsToCustomerTemplate,
locals: {customerId: customerId, entityViews: entityViews},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignEntityViewsToCustomer($event, items) {
var entityViewIds = [];
for (var id in items.selections) {
entityViewIds.push(id);
}
assignToCustomer($event, entityViewIds);
}
function unassignFromCustomer($event, entityView, isPublic) {
if ($event) {
$event.stopPropagation();
}
var title;
var content;
var label;
if (isPublic) {
title = $translate.instant('entity-view.make-private-entity-view-title', {entityViewName: entityView.name});
content = $translate.instant('entity-view.make-private-entity-view-text');
label = $translate.instant('entity-view.make-private');
} else {
title = $translate.instant('entity-view.unassign-entity-view-title', {entityViewName: entityView.name});
content = $translate.instant('entity-view.unassign-entity-view-text');
label = $translate.instant('entity-view.unassign-entity-view');
}
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title(title)
.htmlContent(content)
.ariaLabel(label)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
entityViewService.unassignEntityViewFromCustomer(entityView.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function unassignEntityViewsFromCustomer($event, items) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('entity-view.unassign-entity-views-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('entity-view.unassign-entity-views-text'))
.ariaLabel($translate.instant('entity-view.unassign-entity-view'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
var tasks = [];
for (var id in items.selections) {
tasks.push(entityViewService.unassignEntityViewFromCustomer(id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
function makePublic($event, entityView) {
if ($event) {
$event.stopPropagation();
}
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('entity-view.make-public-entity-view-title', {entityViewName: entityView.name}))
.htmlContent($translate.instant('entity-view.make-public-entity-view-text'))
.ariaLabel($translate.instant('entity-view.make-public'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
entityViewService.makeEntityViewPublic(entityView.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
}

67
ui/src/app/entity-view/entity-view.directive.js

@ -0,0 +1,67 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import entityViewFieldsetTemplate from './entity-view-fieldset.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EntityViewDirective($compile, $templateCache, toast, $translate, types, clipboardService, entityViewService, customerService) {
var linker = function (scope, element) {
var template = $templateCache.get(entityViewFieldsetTemplate);
element.html(template);
scope.types = types;
scope.isAssignedToCustomer = false;
scope.assignedCustomer = null;
scope.$watch('entityView', function(newVal) {
if (newVal) {
if (scope.entityView.customerId && scope.entityView.customerId.id !== types.id.nullUid) {
scope.isAssignedToCustomer = true;
customerService.getShortCustomerInfo(scope.entityView.customerId.id).then(
function success(customer) {
scope.assignedCustomer = customer;
}
);
} else {
scope.isAssignedToCustomer = false;
scope.assignedCustomer = null;
}
}
});
scope.onEntityViewIdCopied = function() {
toast.showSuccess($translate.instant('entity-view.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left');
};
$compile(element.contents())(scope);
}
return {
restrict: "E",
link: linker,
scope: {
entityView: '=',
isEdit: '=',
entityViewScope: '=',
theForm: '=',
onAssignToCustomer: '&',
onUnassignFromCustomer: '&',
onDeleteEntityView: '&'
}
};
}

72
ui/src/app/entity-view/entity-view.routes.js

@ -0,0 +1,72 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import entityViewsTemplate from './entity-views.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EntityViewRoutes($stateProvider, types) {
$stateProvider
.state('home.entityViews', {
url: '/entityViews',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
views: {
"content@home": {
templateUrl: entityViewsTemplate,
controller: 'EntityViewController',
controllerAs: 'vm'
}
},
data: {
entityViewsTypes: 'tenant',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.entityview,
pageTitle: 'entity-views.entity-views'
},
ncyBreadcrumb: {
label: '{"icon": "devices_other", "label": "entity-view.entity-views"}'
}
})
.state('home.customers.entityViews', {
url: '/:customerId/entityViews',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: entityViewsTemplate,
controllerAs: 'vm',
controller: 'EntityViewController'
}
},
data: {
entityViewsTypes: 'customer',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.entityview,
pageTitle: 'customer.entity-views'
},
ncyBreadcrumb: {
label: '{"icon": "devices_other", "label": "{{ vm.customerEntityViewsTitle }}", "translate": "false"}'
}
});
}

83
ui/src/app/entity-view/entity-views.tpl.html

@ -0,0 +1,83 @@
<!--
Copyright © 2016-2018 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tb-grid grid-configuration="vm.entityViewGridConfig">
<details-buttons tb-help="'entityViews'" help-container-id="help-container">
<div id="help-container"></div>
</details-buttons>
<md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}"
id="tabs" md-border-bottom flex class="tb-absolute-fill">
<md-tab label="{{ 'entity-view.details' | translate }}">
<tb-entity-view entity-view="vm.grid.operatingItem()"
is-edit="vm.grid.detailsConfig.isDetailsEditMode"
entity-view-scope="vm.entityViewsScope"
the-form="vm.grid.detailsForm"
on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)"
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)"
on-manage-credentials="vm.manageCredentials(event, vm.grid.detailsConfig.currentItem)"
on-delete-entity-view="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-entity-view>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
<tb-attribute-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.entityview}}"
entity-name="vm.grid.operatingItem().name"
default-attribute-scope="{{vm.types.attributesScope.client.value}}">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}">
<tb-attribute-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.entityview}}"
entity-name="vm.grid.operatingItem().name"
default-attribute-scope="{{vm.types.latestTelemetry.value}}"
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.entityview"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'entity-view.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.entityview"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.entityview}}">
</tb-relation-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}">
<tb-extension-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-name="vm.grid.operatingItem().name"
entity-type="{{vm.types.entityType.entityview}}">
</tb-extension-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.isTenantAdmin()" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table flex entity-type="vm.types.entityType.entityview"
entity-id="vm.grid.operatingItem().id.id"
audit-log-mode="{{vm.types.auditLogMode.entity}}">
</tb-audit-log-table>
</md-tab>
</tb-grid>

41
ui/src/app/entity-view/index.js

@ -0,0 +1,41 @@
/*
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardApiUser from '../api/user.service';
import thingsboardApiEntityView from '../api/entity-view.service';
import thingsboardApiCustomer from '../api/customer.service';
import EntityViewRoutes from './entity-view.routes';
import EntityViewCardController from './entity-view.controller';
import AssignEntityViewToCustomerController from './assign-to-customer.controller';
import AddEntityViewsToCustomerController from './add-entity-views-to-customer.controller';
import EntityViewDirective from './entity-view.directive';
export default angular.module('thingsboard.entityView', [
uiRouter,
thingsboardGrid,
thingsboardApiUser,
thingsboardApiEntityView,
thingsboardApiCustomer
])
.config(EntityViewRoutes)
.controller('EntityViewController', EntityViewCardController)
.controller('EntityViewCardController', EntityViewCardController)
.controller('AssignEntityViewToCustomerController', AssignEntityViewToCustomerController)
.controller('AddEntityViewsToCustomerController', AddEntityViewsToCustomerController)
.directive('tbEntityView', EntityViewDirective)
.name;

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

@ -338,10 +338,12 @@
"dashboard": "Customer Dashboard",
"dashboards": "Customer Dashboards",
"devices": "Customer Devices",
"entity-views": "Customer Entity Views",
"assets": "Customer Assets",
"public-dashboards": "Public Dashboards",
"public-devices": "Public Devices",
"public-assets": "Public Assets",
"public-entity-views": "Public Entity Views",
"add": "Add Customer",
"delete": "Delete customer",
"manage-customer-users": "Manage customer users",
@ -750,6 +752,77 @@
"no-entities-prompt": "No entities found",
"no-data": "No data to display"
},
"entity-view": {
"entity-view": "Entity View",
"entity-views": "Entity Views",
"management": "Entity View management",
"view-entity-views": "View Entity Views",
"entity-view-alias": "Entity View alias",
"aliases": "Entity View aliases",
"no-alias-matching": "'{{alias}}' not found.",
"no-aliases-found": "No aliases found.",
"no-key-matching": "'{{key}}' not found.",
"no-keys-found": "No keys found.",
"create-new-alias": "Create a new one!",
"create-new-key": "Create a new one!",
"duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity View aliases must be unique whithin the dashboard.",
"configure-alias": "Configure '{{alias}}' alias",
"no-entity-views-matching": "No entity views matching '{{entity}}' were found.",
"alias": "Alias",
"alias-required": "Entity View alias is required.",
"remove-alias": "Remove entity view alias",
"add-alias": "Add entity view alias",
"name-starts-with": "Entity View name starts with",
"entity-view-list": "Entity View list",
"use-entity-view-name-filter": "Use filter",
"entity-view-list-empty": "No entity views selected.",
"entity-view-name-filter-required": "Entity view name filter is required.",
"entity-view-name-filter-no-entity-view-matched": "No entity views starting with '{{entityView}}' were found.",
"add": "Add Entity View",
"assign-to-customer": "Assign to customer",
"assign-entity-view-to-customer": "Assign Entity View(s) To Customer",
"assign-entity-view-to-customer-text": "Please select the entity views to assign to the customer",
"no-entity-views-text": "No entity views found",
"assign-to-customer-text": "Please select the customer to assign the entity view(s)",
"entity-view-details": "Entity view details",
"add-entity-view-text": "Add new entity view",
"delete": "Delete entity view",
"assign-entity-views": "Assign entity views",
"assign-entity-views-text": "Assign { count, plural, 1 {1 entityView} other {# entityViews} } to customer",
"delete-entity-views": "Delete entity views",
"unassign-from-customer": "Unassign from customer",
"unassign-entity-views": "Unassign entity views",
"unassign-entity-views-action-title": "Unassign { count, plural, 1 {1 entityView} other {# entityViews} } from customer",
"assign-new-entity-view": "Assign new entity view",
"delete-entity-view-title": "Are you sure you want to delete the entity view '{{entityViewName}}'?",
"delete-entity-view-text": "Be careful, after the confirmation the entity view and all related data will become unrecoverable.",
"delete-entity-views-title": "Are you sure you want to entity view { count, plural, 1 {1 entityView} other {# entityViews} }?",
"delete-entity-views-action-title": "Delete { count, plural, 1 {1 entityView} other {# entityViews} }",
"delete-entity-views-text": "Be careful, after the confirmation all selected entity views will be removed and all related data will become unrecoverable.",
"unassign-entity-view-title": "Are you sure you want to unassign the entity view '{{entityViewName}}'?",
"unassign-entity-view-text": "After the confirmation the entity view will be unassigned and won't be accessible by the customer.",
"unassign-entity-view": "Unassign entity view",
"unassign-entity-views-title": "Are you sure you want to unassign { count, plural, 1 {1 entityView} other {# entityViews} }?",
"unassign-entity-views-text": "After the confirmation all selected entity views will be unassigned and won't be accessible by the customer.",
"entity-view-type": "Entity View type",
"entity-view-type-required": "Entity View type is required.",
"select-entity-view-type": "Select entity view type",
"enter-entity-view-type": "Enter entity view type",
"any-entity-view": "Any entity view",
"no-entity-view-types-matching": "No entity view types matching '{{entitySubtype}}' were found.",
"entity-view-type-list-empty": "No entity view types selected.",
"entity-view-types": "Entity View types",
"name": "Name",
"name-required": "Name is required.",
"description": "Description",
"events": "Events",
"details": "Details",
"copyId": "Copy entity view Id",
"assignedToCustomer": "Assigned to customer",
"unable-entity-view-device-alias-title": "Unable to delete entity view alias",
"unable-entity-view-device-alias-text": "Device alias '{{entityViewAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
"select-entity-view": "Select entity view"
},
"event": {
"event-type": "Event type",
"type-error": "Error",

Loading…
Cancel
Save