Commit 6be5f9aa4d315facb772f681c95f05ff3546c698

Authored by Volodymyr Babak
1 parent f35c1156

Manage edge dashboards functionality

... ... @@ -689,4 +689,26 @@ public class DashboardController extends BaseController {
689 689 throw handleException(e);
690 690 }
691 691 }
  692 +
  693 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  694 + @RequestMapping(value = "/edge/{edgeId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
  695 + @ResponseBody
  696 + public TimePageData<DashboardInfo> getEdgeDashboards(
  697 + @PathVariable("edgeId") String strEdgeId,
  698 + @RequestParam int limit,
  699 + @RequestParam(required = false) Long startTime,
  700 + @RequestParam(required = false) Long endTime,
  701 + @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
  702 + @RequestParam(required = false) String offset) throws ThingsboardException {
  703 + checkParameter("edgeId", strEdgeId);
  704 + try {
  705 + TenantId tenantId = getCurrentUser().getTenantId();
  706 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  707 + checkEdgeId(edgeId, Operation.READ);
  708 + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
  709 + return checkNotNull(dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
  710 + } catch (Exception e) {
  711 + throw handleException(e);
  712 + }
  713 + }
692 714 }
... ...
... ... @@ -62,4 +62,6 @@ public interface DashboardService {
62 62 void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId);
63 63
64 64 void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId);
  65 +
  66 + ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
65 67 }
... ...
  1 +/**
  2 + * Copyright © 2016-2019 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 + */
1 16 package org.thingsboard.server.common.data;
2 17
3 18 import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
6 21 import org.thingsboard.server.common.data.id.EdgeId;
7 22
8   -@Data
9   -@NoArgsConstructor
10 23 @AllArgsConstructor
11 24 public class ShortEdgeInfo {
12 25
  26 + @Getter @Setter
13 27 private EdgeId edgeId;
  28 +
  29 + @Getter @Setter
14 30 private String title;
  31 +
  32 + @Override
  33 + public boolean equals(Object o) {
  34 + if (this == o) return true;
  35 + if (o == null || getClass() != o.getClass()) return false;
  36 +
  37 + ShortEdgeInfo that = (ShortEdgeInfo) o;
  38 +
  39 + return edgeId.equals(that.edgeId);
  40 + }
  41 +
  42 + @Override
  43 + public int hashCode() {
  44 + return edgeId.hashCode();
  45 + }
15 46 }
... ...
... ... @@ -299,6 +299,23 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
299 299 new EdgeDashboardsUpdater(edge).removeEntities(tenantId, edge);
300 300 }
301 301
  302 + @Override
  303 + public ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
  304 + log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
  305 + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  306 + Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
  307 + Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
  308 + ListenableFuture<List<DashboardInfo>> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
  309 +
  310 + return Futures.transform(dashboards, new Function<List<DashboardInfo>, TimePageData<DashboardInfo>>() {
  311 + @Nullable
  312 + @Override
  313 + public TimePageData<DashboardInfo> apply(@Nullable List<DashboardInfo> dashboards) {
  314 + return new TimePageData<>(dashboards, pageLink);
  315 + }
  316 + });
  317 + }
  318 +
302 319 private Dashboard updateAssignedEdge(TenantId tenantId, DashboardId dashboardId, Edge edge) {
303 320 Dashboard dashboard = findDashboardById(tenantId, dashboardId);
304 321 if (dashboard.updateAssignedEdge(edge)) {
... ...
... ... @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
25 25 import org.springframework.util.StringUtils;
26 26 import org.thingsboard.server.common.data.DashboardInfo;
27 27 import org.thingsboard.server.common.data.ShortCustomerInfo;
  28 +import org.thingsboard.server.common.data.ShortEdgeInfo;
28 29 import org.thingsboard.server.common.data.id.DashboardId;
29 30 import org.thingsboard.server.common.data.id.TenantId;
30 31 import org.thingsboard.server.dao.model.BaseSqlEntity;
... ... @@ -47,6 +48,8 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
47 48 private static final ObjectMapper objectMapper = new ObjectMapper();
48 49 private static final JavaType assignedCustomersType =
49 50 objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
  51 + private static final JavaType assignedEdgesType =
  52 + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
50 53
51 54 @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
52 55 private String tenantId;
... ... @@ -60,6 +63,9 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
60 63 @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
61 64 private String assignedCustomers;
62 65
  66 + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY)
  67 + private String assignedEdges;
  68 +
63 69 public DashboardInfoEntity() {
64 70 super();
65 71 }
... ... @@ -79,6 +85,13 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
79 85 log.error("Unable to serialize assigned customers to string!", e);
80 86 }
81 87 }
  88 + if (dashboardInfo.getAssignedEdges() != null) {
  89 + try {
  90 + this.assignedEdges = objectMapper.writeValueAsString(dashboardInfo.getAssignedEdges());
  91 + } catch (JsonProcessingException e) {
  92 + log.error("Unable to serialize assigned edges to string!", e);
  93 + }
  94 + }
82 95 }
83 96
84 97 @Override
... ... @@ -110,6 +123,13 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
110 123 log.warn("Unable to parse assigned customers!", e);
111 124 }
112 125 }
  126 + if (!StringUtils.isEmpty(assignedEdges)) {
  127 + try {
  128 + dashboardInfo.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
  129 + } catch (IOException e) {
  130 + log.warn("Unable to parse assigned edges!", e);
  131 + }
  132 + }
113 133 return dashboardInfo;
114 134 }
115 135
... ...
... ... @@ -45,7 +45,10 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
45 45 getPublicDashboardLink: getPublicDashboardLink,
46 46 updateDashboardEdges: updateDashboardEdges,
47 47 addDashboardEdges: addDashboardEdges,
48   - removeDashboardEdges: removeDashboardEdges
  48 + removeDashboardEdges: removeDashboardEdges,
  49 + getEdgeDashboards: getEdgeDashboards,
  50 + assignDashboardToEdge: assignDashboardToEdge,
  51 + unassignDashboardFromEdge: unassignDashboardFromEdge
49 52 }
50 53
51 54 return service;
... ... @@ -285,6 +288,20 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
285 288 }
286 289 dashboard.assignedCustomersText = assignedCustomersTitles.join(', ');
287 290 }
  291 + dashboard.assignedEdgesIds = [];
  292 + if (dashboard.assignedEdges && dashboard.assignedEdges.length) {
  293 + // var assignedEdgesTitles = [];
  294 + for (var j = 0; j < dashboard.assignedEdges.length; j++) {
  295 + var assignedEdge = dashboard.assignedEdges[j];
  296 + dashboard.assignedEdgesIds.push(assignedEdge.edgeId.id);
  297 + // if (assignedCustomer.public) {
  298 + // dashboard.publicCustomerId = assignedCustomer.customerId.id;
  299 + // } else {
  300 + // assignedCustomersTitles.push(assignedCustomer.title);
  301 + // }
  302 + }
  303 + // dashboard.assignedCustomersText = assignedCustomersTitles.join(', ');
  304 + }
288 305 return dashboard;
289 306 }
290 307
... ... @@ -292,6 +309,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
292 309 delete dashboard.publicCustomerId;
293 310 delete dashboard.assignedCustomersText;
294 311 delete dashboard.assignedCustomersIds;
  312 + delete dashboard.assignedEdgeIds;
295 313 return dashboard;
296 314 }
297 315
... ... @@ -327,4 +345,44 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
327 345 });
328 346 return deferred.promise;
329 347 }
  348 +
  349 + function getEdgeDashboards(edgeId, pageLink, config) {
  350 + var deferred = $q.defer();
  351 + var url = '/api/edge/' + edgeId + '/dashboards?limit=' + pageLink.limit;
  352 + if (angular.isDefined(pageLink.idOffset)) {
  353 + url += '&offset=' + pageLink.idOffset;
  354 + }
  355 + $http.get(url, config).then(function success(response) {
  356 + response.data = prepareDashboards(response.data);
  357 + if (pageLink.textSearch) {
  358 + response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch});
  359 + }
  360 + deferred.resolve(response.data);
  361 + }, function fail() {
  362 + deferred.reject();
  363 + });
  364 + return deferred.promise;
  365 + }
  366 +
  367 + function assignDashboardToEdge(edgeId, dashboardId) {
  368 + var deferred = $q.defer();
  369 + var url = '/api/edge/' + edgeId + '/dashboard/' + dashboardId;
  370 + $http.post(url, null).then(function success(response) {
  371 + deferred.resolve(prepareDashboard(response.data));
  372 + }, function fail() {
  373 + deferred.reject();
  374 + });
  375 + return deferred.promise;
  376 + }
  377 +
  378 + function unassignDashboardFromEdge(edgeId, dashboardId) {
  379 + var deferred = $q.defer();
  380 + var url = '/api/edge/' + edgeId + '/dashboard/' + dashboardId;
  381 + $http.delete(url).then(function success(response) {
  382 + deferred.resolve(prepareDashboard(response.data));
  383 + }, function fail() {
  384 + deferred.reject();
  385 + });
  386 + return deferred.promise;
  387 + }
330 388 }
... ...
... ... @@ -89,7 +89,7 @@ export default function CustomerController(customerService, $state, $stateParams
89 89 return $translate.instant('customer.manage-customer-edges')
90 90 }
91 91 },
92   - icon: "toys"
  92 + icon: "wifi_tethering"
93 93 },
94 94 {
95 95 onAction: function ($event, item) {
... ...
  1 +/*
  2 + * Copyright © 2016-2019 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 AddDashboardsToEdgeController(dashboardService, $mdDialog, $q, edgeId, dashboards) {
  18 +
  19 + var vm = this;
  20 +
  21 + vm.dashboards = dashboards;
  22 + vm.searchText = '';
  23 +
  24 + vm.assign = assign;
  25 + vm.cancel = cancel;
  26 + vm.hasData = hasData;
  27 + vm.noData = noData;
  28 + vm.searchDashboardTextUpdated = searchDashboardTextUpdated;
  29 + vm.toggleDashboardSelection = toggleDashboardSelection;
  30 +
  31 + vm.theDashboards = {
  32 + getItemAtIndex: function (index) {
  33 + if (index > vm.dashboards.data.length) {
  34 + vm.theDashboards.fetchMoreItems_(index);
  35 + return null;
  36 + }
  37 + var item = vm.dashboards.data[index];
  38 + if (item) {
  39 + item.indexNumber = index + 1;
  40 + }
  41 + return item;
  42 + },
  43 +
  44 + getLength: function () {
  45 + if (vm.dashboards.hasNext) {
  46 + return vm.dashboards.data.length + vm.dashboards.nextPageLink.limit;
  47 + } else {
  48 + return vm.dashboards.data.length;
  49 + }
  50 + },
  51 +
  52 + fetchMoreItems_: function () {
  53 + if (vm.dashboards.hasNext && !vm.dashboards.pending) {
  54 + vm.dashboards.pending = true;
  55 + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
  56 + function success(dashboards) {
  57 + vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
  58 + vm.dashboards.nextPageLink = dashboards.nextPageLink;
  59 + vm.dashboards.hasNext = dashboards.hasNext;
  60 + if (vm.dashboards.hasNext) {
  61 + vm.dashboards.nextPageLink.limit = vm.dashboards.pageSize;
  62 + }
  63 + vm.dashboards.pending = false;
  64 + },
  65 + function fail() {
  66 + vm.dashboards.hasNext = false;
  67 + vm.dashboards.pending = false;
  68 + });
  69 + }
  70 + }
  71 + }
  72 +
  73 + function cancel () {
  74 + $mdDialog.cancel();
  75 + }
  76 +
  77 + function assign () {
  78 + var tasks = [];
  79 + for (var dashboardId in vm.dashboards.selections) {
  80 + tasks.push(dashboardService.assignDashboardToEdge(edgeId, dashboardId));
  81 + }
  82 + $q.all(tasks).then(function () {
  83 + $mdDialog.hide();
  84 + });
  85 + }
  86 +
  87 + function noData () {
  88 + return vm.dashboards.data.length == 0 && !vm.dashboards.hasNext;
  89 + }
  90 +
  91 + function hasData () {
  92 + return vm.dashboards.data.length > 0;
  93 + }
  94 +
  95 + function toggleDashboardSelection ($event, dashboard) {
  96 + $event.stopPropagation();
  97 + var selected = angular.isDefined(dashboard.selected) && dashboard.selected;
  98 + dashboard.selected = !selected;
  99 + if (dashboard.selected) {
  100 + vm.dashboards.selections[dashboard.id.id] = true;
  101 + vm.dashboards.selectedCount++;
  102 + } else {
  103 + delete vm.dashboards.selections[dashboard.id.id];
  104 + vm.dashboards.selectedCount--;
  105 + }
  106 + }
  107 +
  108 + function searchDashboardTextUpdated () {
  109 + vm.dashboards = {
  110 + pageSize: vm.dashboards.pageSize,
  111 + data: [],
  112 + nextPageLink: {
  113 + limit: vm.dashboards.pageSize,
  114 + textSearch: vm.searchText
  115 + },
  116 + selections: {},
  117 + selectedCount: 0,
  118 + hasNext: true,
  119 + pending: false
  120 + };
  121 + }
  122 +}
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 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="{{ 'dashboard.assign-dashboard-to-edge' | translate }}">
  19 + <form name="theForm" ng-submit="vm.assign()">
  20 + <md-toolbar>
  21 + <div class="md-toolbar-tools">
  22 + <h2 translate>dashboard.assign-dashboard-to-edge</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="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
  31 + <md-dialog-content>
  32 + <div class="md-dialog-content">
  33 + <fieldset>
  34 + <span translate>dashboard.assign-dashboard-to-edge-text</span>
  35 + <md-input-container class="md-block" style='margin-bottom: 0px;'>
  36 + <label>&nbsp;</label>
  37 + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
  38 + search
  39 + </md-icon>
  40 + <input id="dashboard-search" autofocus ng-model="vm.searchText"
  41 + ng-change="vm.searchDashboardTextUpdated()"
  42 + placeholder="{{ 'common.enter-search' | translate }}"/>
  43 + </md-input-container>
  44 + <div style='min-height: 150px;'>
  45 + <span translate layout-align="center center"
  46 + style="text-transform: uppercase; display: flex; height: 150px;"
  47 + class="md-subhead"
  48 + ng-show="vm.noData()">dashboard.no-dashboards-text</span>
  49 + <md-virtual-repeat-container ng-show="vm.hasData()"
  50 + tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
  51 + style='min-height: 150px; width: 100%;'>
  52 + <md-list>
  53 + <md-list-item md-virtual-repeat="dashboard in vm.theDashboards" md-on-demand
  54 + class="repeated-item" flex>
  55 + <md-checkbox ng-click="vm.toggleDashboardSelection($event, dashboard)"
  56 + aria-label="{{ 'item.selected' | translate }}"
  57 + ng-checked="dashboard.selected"></md-checkbox>
  58 + <span> {{ dashboard.title }} </span>
  59 + </md-list-item>
  60 + </md-list>
  61 + </md-virtual-repeat-container>
  62 + </div>
  63 + </fieldset>
  64 + </div>
  65 + </md-dialog-content>
  66 + <md-dialog-actions layout="row">
  67 + <span flex></span>
  68 + <md-button ng-disabled="$root.loading || vm.dashboards.selectedCount == 0" type="submit"
  69 + class="md-raised md-primary">
  70 + {{ 'action.assign' | translate }}
  71 + </md-button>
  72 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  73 + translate }}
  74 + </md-button>
  75 + </md-dialog-actions>
  76 + </form>
  77 +</md-dialog>
\ No newline at end of file
... ...
... ... @@ -124,4 +124,25 @@ export default function DashboardRoutes($stateProvider) {
124 124 label: '{"icon": "dashboard", "label": "customer.dashboard"}'
125 125 }
126 126 })
  127 + .state('home.edges.dashboards', {
  128 + url: '/:edgeId/dashboards',
  129 + params: {'topIndex': 0},
  130 + module: 'private',
  131 + auth: ['TENANT_ADMIN'],
  132 + views: {
  133 + "content@home": {
  134 + templateUrl: dashboardsTemplate,
  135 + controllerAs: 'vm',
  136 + controller: 'DashboardsController'
  137 + }
  138 + },
  139 + data: {
  140 + dashboardsType: 'edge',
  141 + searchEnabled: true,
  142 + pageTitle: 'edge.dashboards'
  143 + },
  144 + ncyBreadcrumb: {
  145 + label: '{"icon": "dashboard", "label": "{{ vm.edgeDashboardsTitle }}", "translate": "false"}'
  146 + }
  147 + })
127 148 }
... ...
... ... @@ -21,6 +21,7 @@ import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.ht
21 21 import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html';
22 22 import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html';
23 23 import manageAssignedEdgesTemplate from './manage-assigned-edges.tpl.html';
  24 +import addDashboardsToEdgeTemplate from './add-dashboards-to-edge.tpl.html';
24 25
25 26 /* eslint-enable import/no-unresolved, import/default */
26 27
... ... @@ -60,6 +61,7 @@ export function DashboardsController(userService, dashboardService, customerServ
60 61 $state, $stateParams, $mdDialog, $document, $q, $translate) {
61 62
62 63 var customerId = $stateParams.customerId;
  64 + var edgeId = $stateParams.edgeId;
63 65
64 66 var dashboardActionsList = [
65 67 {
... ... @@ -215,7 +217,7 @@ export function DashboardsController(userService, dashboardService, customerServ
215 217 },
216 218 name: function() { return $translate.instant('action.assign') },
217 219 details: function() { return $translate.instant('dashboard.manage-assigned-edges') },
218   - icon: "assignment",
  220 + icon: "wifi_tethering",
219 221 isEnabled: function(dashboard) {
220 222 return dashboard;
221 223 }
... ... @@ -256,6 +258,32 @@ export function DashboardsController(userService, dashboardService, customerServ
256 258 );
257 259
258 260 dashboardGroupActionsList.push(
  261 + {
  262 + onAction: function ($event, items) {
  263 + assignDashboardsToEdges($event, items);
  264 + },
  265 + name: function() { return $translate.instant('dashboard.assign-dashboards') },
  266 + details: function(selectedCount) {
  267 + return $translate.instant('dashboard.assign-dashboards-to-edge-text', {count: selectedCount}, "messageformat");
  268 + },
  269 + icon: "wifi_tethering"
  270 + }
  271 + );
  272 +
  273 + dashboardGroupActionsList.push(
  274 + {
  275 + onAction: function ($event, items) {
  276 + unassignDashboardsFromEdges($event, items);
  277 + },
  278 + name: function() { return $translate.instant('dashboard.unassign-dashboards') },
  279 + details: function(selectedCount) {
  280 + return $translate.instant('dashboard.unassign-dashboards-from-edge-action-text', {count: selectedCount}, "messageformat");
  281 + },
  282 + icon: "portable_wifi_off"
  283 + }
  284 + );
  285 +
  286 + dashboardGroupActionsList.push(
259 287 {
260 288 onAction: function ($event, items) {
261 289 assignDashboardsToCustomers($event, items);
... ... @@ -386,6 +414,60 @@ export function DashboardsController(userService, dashboardService, customerServ
386 414 } else if (vm.dashboardsScope === 'customer_user') {
387 415 vm.dashboardGridConfig.addItemAction = {};
388 416 }
  417 + } else if (vm.dashboardsScope === 'edge') {
  418 + fetchDashboardsFunction = function (pageLink) {
  419 + return dashboardService.getEdgeDashboards(edgeId, pageLink);
  420 + };
  421 + deleteDashboardFunction = function (dashboardId) {
  422 + return dashboardService.unassignDashboardFromEdge(edgeId, dashboardId);
  423 + };
  424 + refreshDashboardsParamsFunction = function () {
  425 + return {"edgeId": edgeId, "topIndex": vm.topIndex};
  426 + };
  427 +
  428 + dashboardActionsList.push(
  429 + {
  430 + onAction: function ($event, item) {
  431 + exportDashboard($event, item);
  432 + },
  433 + name: function() { $translate.instant('action.export') },
  434 + details: function() { return $translate.instant('dashboard.export') },
  435 + icon: "file_download"
  436 + }
  437 + );
  438 +
  439 + dashboardActionsList.push(
  440 + {
  441 + onAction: function ($event, item) {
  442 + unassignFromEdge($event, item, edgeId);
  443 + },
  444 + name: function() { return $translate.instant('action.unassign') },
  445 + details: function() { return $translate.instant('dashboard.unassign-from-edge') },
  446 + icon: "assignment_return"
  447 + }
  448 + );
  449 +
  450 + dashboardGroupActionsList.push(
  451 + {
  452 + onAction: function ($event, items) {
  453 + unassignDashboardsFromEdge($event, items, edgeId);
  454 + },
  455 + name: function() { return $translate.instant('dashboard.unassign-dashboards') },
  456 + details: function(selectedCount) {
  457 + return $translate.instant('dashboard.unassign-dashboards-from-edge-action-title', {count: selectedCount}, "messageformat");
  458 + },
  459 + icon: "assignment_return"
  460 + }
  461 + );
  462 +
  463 + vm.dashboardGridConfig.addItemAction = {
  464 + onAction: function ($event) {
  465 + addDashboardsToEdge($event);
  466 + },
  467 + name: function() { return $translate.instant('dashboard.assign-dashboards') },
  468 + details: function() { return $translate.instant('dashboard.assign-new-dashboard') },
  469 + icon: "add"
  470 + };
389 471 }
390 472
391 473 vm.dashboardGridConfig.refreshParamsFunc = refreshDashboardsParamsFunction;
... ... @@ -628,21 +710,40 @@ export function DashboardsController(userService, dashboardService, customerServ
628 710 showManageAssignedEdgesDialog($event, [dashboard.id.id], 'manage', dashboard.assignedEdgesIds);
629 711 }
630 712
631   - // function assignDashboardsToEdges($event, items) {
632   - // var dashboardIds = [];
633   - // for (var id in items.selections) {
634   - // dashboardIds.push(id);
635   - // }
636   - // showManageAssignedEdgesDialog($event, dashboardIds, 'assign');
637   - // }
638   - //
639   - // function unassignDashboardsFromEdges($event, items) {
640   - // var dashboardIds = [];
641   - // for (var id in items.selections) {
642   - // dashboardIds.push(id);
643   - // }
644   - // showManageAssignedEdgesDialog($event, dashboardIds, 'unassign');
645   - // }
  713 + function assignDashboardsToEdges($event, items) {
  714 + var dashboardIds = [];
  715 + for (var id in items.selections) {
  716 + dashboardIds.push(id);
  717 + }
  718 + showManageAssignedEdgesDialog($event, dashboardIds, 'assign');
  719 + }
  720 +
  721 + function unassignDashboardsFromEdges($event, items) {
  722 + var dashboardIds = [];
  723 + for (var id in items.selections) {
  724 + dashboardIds.push(id);
  725 + }
  726 + showManageAssignedEdgesDialog($event, dashboardIds, 'unassign');
  727 + }
  728 +
  729 + function unassignDashboardsFromEdge($event, items, edgeId) {
  730 + var confirm = $mdDialog.confirm()
  731 + .targetEvent($event)
  732 + .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat'))
  733 + .htmlContent($translate.instant('dashboard.unassign-dashboards-from-edge-text'))
  734 + .ariaLabel($translate.instant('dashboard.unassign-dashboards'))
  735 + .cancel($translate.instant('action.no'))
  736 + .ok($translate.instant('action.yes'));
  737 + $mdDialog.show(confirm).then(function () {
  738 + var tasks = [];
  739 + for (var id in items.selections) {
  740 + tasks.push(dashboardService.unassignDashboardFromEdge(edgeId, id));
  741 + }
  742 + $q.all(tasks).then(function () {
  743 + vm.grid.refreshList();
  744 + });
  745 + });
  746 + }
646 747
647 748 function showManageAssignedEdgesDialog($event, dashboardIds, actionType, assignedEdges) {
648 749 if ($event) {
... ... @@ -661,4 +762,61 @@ export function DashboardsController(userService, dashboardService, customerServ
661 762 }, function () {
662 763 });
663 764 }
  765 +
  766 + function addDashboardsToEdge($event) {
  767 + if ($event) {
  768 + $event.stopPropagation();
  769 + }
  770 + var pageSize = 10;
  771 + dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then(
  772 + function success(_dashboards) {
  773 + var dashboards = {
  774 + pageSize: pageSize,
  775 + data: _dashboards.data,
  776 + nextPageLink: _dashboards.nextPageLink,
  777 + selections: {},
  778 + selectedCount: 0,
  779 + hasNext: _dashboards.hasNext,
  780 + pending: false
  781 + };
  782 + if (dashboards.hasNext) {
  783 + dashboards.nextPageLink.limit = pageSize;
  784 + }
  785 + $mdDialog.show({
  786 + controller: 'AddDashboardsToEdgeController',
  787 + controllerAs: 'vm',
  788 + templateUrl: addDashboardsToEdgeTemplate,
  789 + locals: {edgeId: edgeId, dashboards: dashboards},
  790 + parent: angular.element($document[0].body),
  791 + fullscreen: true,
  792 + targetEvent: $event
  793 + }).then(function () {
  794 + vm.grid.refreshList();
  795 + }, function () {
  796 + });
  797 + },
  798 + function fail() {
  799 + });
  800 + }
  801 +
  802 + function unassignFromEdge($event, dashboard, edgeId) {
  803 + if ($event) {
  804 + $event.stopPropagation();
  805 + }
  806 + var title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title});
  807 + var content = $translate.instant('dashboard.unassign-dashboard-from-edge-text');
  808 + var label = $translate.instant('dashboard.unassign-dashboard');
  809 + var confirm = $mdDialog.confirm()
  810 + .targetEvent($event)
  811 + .title(title)
  812 + .htmlContent(content)
  813 + .ariaLabel(label)
  814 + .cancel($translate.instant('action.no'))
  815 + .ok($translate.instant('action.yes'));
  816 + $mdDialog.show(confirm).then(function () {
  817 + dashboardService.unassignDashboardFromEdge(edgeId, dashboard.id.id).then(function success() {
  818 + vm.grid.refreshList();
  819 + });
  820 + });
  821 + }
664 822 }
... ...
... ... @@ -47,6 +47,7 @@ import AddWidgetController from './add-widget.controller';
47 47 import DashboardDirective from './dashboard.directive';
48 48 import EditWidgetDirective from './edit-widget.directive';
49 49 import DashboardToolbar from './dashboard-toolbar.directive';
  50 +import AddDashboardsToEdgeController from './add-dashboards-to-edge.controller';
50 51
51 52 export default angular.module('thingsboard.dashboard', [
52 53 uiRouter,
... ... @@ -79,6 +80,7 @@ export default angular.module('thingsboard.dashboard', [
79 80 .controller('ManageAssignedCustomersController', ManageAssignedCustomersController)
80 81 .controller('ManageAssignedEdgesController', ManageAssignedEdgesController)
81 82 .controller('AddWidgetController', AddWidgetController)
  83 + .controller('AddDashboardsToEdgeController', AddDashboardsToEdgeController)
82 84 .directive('tbDashboardDetails', DashboardDirective)
83 85 .directive('tbEditWidget', EditWidgetDirective)
84 86 .directive('tbDashboardToolbar', DashboardToolbar)
... ...
... ... @@ -219,6 +219,19 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
219 219 edgeActionsList.push(
220 220 {
221 221 onAction: function ($event, item) {
  222 + openEdgeDashboards($event, item);
  223 + },
  224 + name: function() { return $translate.instant('dashboard.dashboards') },
  225 + details: function() {
  226 + return $translate.instant('edge.manage-edge-dashboards');
  227 + },
  228 + icon: "dashboard"
  229 + }
  230 + );
  231 +
  232 + edgeActionsList.push(
  233 + {
  234 + onAction: function ($event, item) {
222 235 vm.grid.deleteItem($event, item);
223 236 },
224 237 name: function() { return $translate.instant('action.delete') },
... ... @@ -240,6 +253,8 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
240 253 }
241 254 );
242 255
  256 +
  257 +
243 258 edgeGroupActionsList.push(
244 259 {
245 260 onAction: function ($event) {
... ... @@ -531,4 +546,18 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
531 546 });
532 547 });
533 548 }
  549 +
  550 + function openEdgeDashboards($event, edge) {
  551 + if ($event) {
  552 + $event.stopPropagation();
  553 + }
  554 + $state.go('home.edges.dashboards', {edgeId: edge.id.id});
  555 + }
  556 +
  557 + // function openEdgeRuleChains($event, edge) {
  558 + // if ($event) {
  559 + // $event.stopPropagation();
  560 + // }
  561 + // $state.go('home.edges.rule-chains', {edgeId: edge.id.id});
  562 + // }
534 563 }
... ...
... ... @@ -569,7 +569,20 @@
569 569 "hide-details": "Hide details",
570 570 "select-state": "Select target state",
571 571 "state-controller": "State controller",
572   - "manage-assigned-edges": "Manage assigned edges"
  572 + "manage-assigned-edges": "Manage assigned edges",
  573 + "unassign-dashboard-from-edge-text": "After the confirmation the dashboard will be unassigned and won't be accessible by the edge.",
  574 + "assigned-edges": "Assigned edges",
  575 + "unassign-from-edge": "Unassign from edge",
  576 + "unassign-dashboards-from-edge-action-title": "Unassign { count, plural, 1 {1 dashboard} other {# dashboards} } from edge",
  577 + "unassign-dashboards-from-edge-text": "After the confirmation all selected dashboards will be unassigned and won't be accessible by the edge.",
  578 + "assign-dashboard-to-edge": "Assign Dashboard(s) To Edge",
  579 + "assign-dashboard-to-edge-text": "Please select the dashboards to assign to the edge",
  580 + "assign-dashboards-to-edge-text": "Assign { count, plural, 1 {1 dashboard} other {# dashboards} } to edges",
  581 + "unassign-dashboards-from-edge-action-text": "Unassign { count, plural, 1 {1 dashboard} other {# dashboards} } from edges",
  582 + "assign-to-edges": "Assign Dashboard(s) To Edges",
  583 + "assign-to-edges-text": "Please select the edges to assign the dashboard(s)",
  584 + "unassign-from-edges": "Unassign Dashboard(s) From Edges",
  585 + "unassign-from-edges-text": "Please select the edges to unassign from the dashboard(s)"
573 586 },
574 587 "datakey": {
575 588 "settings": "Settings",
... ... @@ -759,7 +772,10 @@
759 772 "make-private-edge-text": "After the confirmation the edge and all its data will be made private and won't be accessible by others.",
760 773 "import": "Import edge",
761 774 "label": "Label",
762   - "assign-new-edge": "Assign new edge"
  775 + "assign-new-edge": "Assign new edge",
  776 + "manage-edge-dashboards": "Manage edge dashboards",
  777 + "unassign-from-edge": "Unassign from edge",
  778 + "dashboards": "Edge Dashboards"
763 779 },
764 780 "error": {
765 781 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
... ...
... ... @@ -188,7 +188,7 @@ function Menu(userService, $state, $rootScope) {
188 188 name: 'edge.edges',
189 189 type: 'link',
190 190 state: 'home.edges',
191   - icon: 'toys'
  191 + icon: 'router'
192 192 },
193 193 {
194 194 name: 'widget.widget-library',
... ... @@ -265,7 +265,7 @@ function Menu(userService, $state, $rootScope) {
265 265 places: [
266 266 {
267 267 name: 'edge.edges',
268   - icon: 'toys',
  268 + icon: 'router',
269 269 state: 'home.edges'
270 270 }
271 271 ]
... ... @@ -326,7 +326,7 @@ function Menu(userService, $state, $rootScope) {
326 326 name: 'edge.edges',
327 327 type: 'link',
328 328 state: 'home.edges',
329   - icon: 'toys'
  329 + icon: 'router'
330 330 },
331 331 {
332 332 name: 'dashboard.dashboards',
... ... @@ -371,7 +371,7 @@ function Menu(userService, $state, $rootScope) {
371 371 places: [
372 372 {
373 373 name: 'edge.edges',
374   - icon: 'toys',
  374 + icon: 'router',
375 375 state: 'home.edges'
376 376 }
377 377 ]
... ...