Commit 8ea7058da49e42439b94802515ca098e3a6d05a4

Authored by Igor Kulikov
1 parent b46d6b03

Alarms and Entities table widgets improvements.

... ... @@ -267,6 +267,14 @@ export default class Subscription {
267 267 } else {
268 268 this.startWatchingTimewindow();
269 269 }
  270 + registration = this.ctx.$scope.$watch(function () {
  271 + return subscription.alarmSearchStatus;
  272 + }, function (newAlarmSearchStatus, prevAlarmSearchStatus) {
  273 + if (!angular.equals(newAlarmSearchStatus, prevAlarmSearchStatus)) {
  274 + subscription.update();
  275 + }
  276 + }, true);
  277 + this.registrations.push(registration);
270 278 }
271 279
272 280 initDataSubscription() {
... ...
... ... @@ -133,8 +133,13 @@
133 133 "min-polling-interval-message": "At least 1 sec polling interval is allowed.",
134 134 "aknowledge-alarms-title": "Acknowledge { count, plural, 1 {1 alarm} other {# alarms} }",
135 135 "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, plural, 1 {1 alarm} other {# alarms} }?",
  136 + "aknowledge-alarm-title": "Acknowledge Alarm",
  137 + "aknowledge-alarm-text": "Are you sure you want to acknowledge Alarm?",
136 138 "clear-alarms-title": "Clear { count, plural, 1 {1 alarm} other {# alarms} }",
137   - "clear-alarms-text": "Are you sure you want to clear { count, plural, 1 {1 alarm} other {# alarms} }?"
  139 + "clear-alarms-text": "Are you sure you want to clear { count, plural, 1 {1 alarm} other {# alarms} }?",
  140 + "clear-alarm-title": "Clear Alarm",
  141 + "clear-alarm-text": "Are you sure you want to clear Alarm?",
  142 + "alarm-status-filter": "Alarm Status Filter"
138 143 },
139 144 "alias": {
140 145 "add": "Add alias",
... ... @@ -748,7 +753,8 @@
748 753 "entity-name": "Entity name",
749 754 "details": "Entity details",
750 755 "no-entities-prompt": "No entities found",
751   - "no-data": "No data to display"
  756 + "no-data": "No data to display",
  757 + "columns-to-display": "Columns to Display"
752 758 },
753 759 "event": {
754 760 "event-type": "Event type",
... ...
  1 +/**
  2 + * Copyright © 2016-2018 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 +.tb-alarm-status-filter-panel {
  18 + min-width: 300px;
  19 + overflow: hidden;
  20 + background: #fff;
  21 + border-radius: 4px;
  22 + box-shadow:
  23 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  24 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  25 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  26 +
  27 + md-content {
  28 + overflow: hidden;
  29 + background-color: #fff;
  30 + }
  31 +}
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 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 +<md-content style="height: 100%" flex layout="column" class="md-padding">
  20 + <label class="tb-title" translate>alarm.alarm-status-filter</label>
  21 + <md-radio-group ng-model="vm.subscription.alarmSearchStatus" class="md-primary">
  22 + <md-radio-button ng-value="searchStatus"
  23 + aria-label="{{ ('alarm.search-status.' + searchStatus) | translate }}"
  24 + class="md-primary md-align-top-left md-radio-interactive" ng-repeat="searchStatus in vm.types.alarmSearchStatus">
  25 + {{ ('alarm.search-status.' + searchStatus) | translate }}
  26 + </md-radio-button>
  27 + </md-radio-group>
  28 +</md-content>
... ...
... ... @@ -14,11 +14,15 @@
14 14 * limitations under the License.
15 15 */
16 16 import './alarms-table-widget.scss';
  17 +import './display-columns-panel.scss';
  18 +import './alarm-status-filter-panel.scss';
17 19
18 20 /* eslint-disable import/no-unresolved, import/default */
19 21
20 22 import alarmsTableWidgetTemplate from './alarms-table-widget.tpl.html';
21 23 import alarmDetailsDialogTemplate from '../../alarm/alarm-details-dialog.tpl.html';
  24 +import displayColumnsPanelTemplate from './display-columns-panel.tpl.html';
  25 +import alarmStatusFilterPanelTemplate from './alarm-status-filter-panel.tpl.html';
22 26
23 27 /* eslint-enable import/no-unresolved, import/default */
24 28
... ... @@ -45,7 +49,7 @@ function AlarmsTableWidget() {
45 49 }
46 50
47 51 /*@ngInject*/
48   -function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $document, $translate, $q, $timeout, alarmService, utils, types) {
  52 +function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $mdPanel, $document, $translate, $q, $timeout, alarmService, utils, types) {
49 53 var vm = this;
50 54
51 55 vm.stylesInfo = {};
... ... @@ -60,6 +64,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
60 64 vm.selectedAlarms = []
61 65
62 66 vm.alarmSource = null;
  67 + vm.alarmSearchStatus = null;
63 68 vm.allAlarms = [];
64 69
65 70 vm.currentAlarm = null;
... ... @@ -95,14 +100,20 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
95 100 vm.onPaginate = onPaginate;
96 101 vm.onRowClick = onRowClick;
97 102 vm.onActionButtonClick = onActionButtonClick;
  103 + vm.actionEnabled = actionEnabled;
98 104 vm.isCurrent = isCurrent;
99 105 vm.openAlarmDetails = openAlarmDetails;
100 106 vm.ackAlarms = ackAlarms;
  107 + vm.ackAlarm = ackAlarm;
101 108 vm.clearAlarms = clearAlarms;
  109 + vm.clearAlarm = clearAlarm;
102 110
103 111 vm.cellStyle = cellStyle;
104 112 vm.cellContent = cellContent;
105 113
  114 + vm.editAlarmStatusFilter = editAlarmStatusFilter;
  115 + vm.editColumnsToDisplay = editColumnsToDisplay;
  116 +
106 117 $scope.$watch('vm.ctx', function() {
107 118 if (vm.ctx) {
108 119 vm.settings = vm.ctx.settings;
... ... @@ -158,7 +169,41 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
158 169
159 170 vm.ctx.widgetActions = [ vm.searchAction ];
160 171
161   - vm.actionCellDescriptors = vm.ctx.actionsApi.getActionDescriptors('actionCellButton');
  172 + vm.displayDetails = angular.isDefined(vm.settings.displayDetails) ? vm.settings.displayDetails : true;
  173 + vm.allowAcknowledgment = angular.isDefined(vm.settings.allowAcknowledgment) ? vm.settings.allowAcknowledgment : true;
  174 + vm.allowClear = angular.isDefined(vm.settings.allowClear) ? vm.settings.allowClear : true;
  175 +
  176 + if (vm.displayDetails) {
  177 + vm.actionCellDescriptors.push(
  178 + {
  179 + displayName: $translate.instant('alarm.details'),
  180 + icon: 'more_horiz',
  181 + details: true
  182 + }
  183 + );
  184 + }
  185 +
  186 + if (vm.allowAcknowledgment) {
  187 + vm.actionCellDescriptors.push(
  188 + {
  189 + displayName: $translate.instant('alarm.acknowledge'),
  190 + icon: 'done',
  191 + acknowledge: true
  192 + }
  193 + );
  194 + }
  195 +
  196 + if (vm.allowClear) {
  197 + vm.actionCellDescriptors.push(
  198 + {
  199 + displayName: $translate.instant('alarm.clear'),
  200 + icon: 'clear',
  201 + clear: true
  202 + }
  203 + );
  204 + }
  205 +
  206 + vm.actionCellDescriptors = vm.actionCellDescriptors.concat(vm.ctx.actionsApi.getActionDescriptors('actionCellButton'));
162 207
163 208 if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
164 209 vm.alarmsTitle = utils.customTranslation(vm.settings.alarmsTitle, vm.settings.alarmsTitle);
... ... @@ -170,9 +215,6 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
170 215
171 216 vm.enableSelection = angular.isDefined(vm.settings.enableSelection) ? vm.settings.enableSelection : true;
172 217 vm.searchAction.show = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true;
173   - vm.displayDetails = angular.isDefined(vm.settings.displayDetails) ? vm.settings.displayDetails : true;
174   - vm.allowAcknowledgment = angular.isDefined(vm.settings.allowAcknowledgment) ? vm.settings.allowAcknowledgment : true;
175   - vm.allowClear = angular.isDefined(vm.settings.allowClear) ? vm.settings.allowClear : true;
176 218 if (!vm.allowAcknowledgment && !vm.allowClear) {
177 219 vm.enableSelection = false;
178 220 }
... ... @@ -305,16 +347,35 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
305 347 }
306 348
307 349 function onActionButtonClick($event, alarm, actionDescriptor) {
308   - if ($event) {
309   - $event.stopPropagation();
  350 + if (actionDescriptor.details) {
  351 + vm.openAlarmDetails($event, alarm);
  352 + } else if (actionDescriptor.acknowledge) {
  353 + vm.ackAlarm($event, alarm);
  354 + } else if (actionDescriptor.clear) {
  355 + vm.clearAlarm($event, alarm);
  356 + } else {
  357 + if ($event) {
  358 + $event.stopPropagation();
  359 + }
  360 + var entityId;
  361 + var entityName;
  362 + if (alarm && alarm.originator) {
  363 + entityId = alarm.originator;
  364 + entityName = alarm.originatorName;
  365 + }
  366 + vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {alarm: alarm});
310 367 }
311   - var entityId;
312   - var entityName;
313   - if (alarm && alarm.originator) {
314   - entityId = alarm.originator;
315   - entityName = alarm.originatorName;
  368 + }
  369 +
  370 + function actionEnabled(alarm, actionDescriptor) {
  371 + if (actionDescriptor.acknowledge) {
  372 + return (alarm.status == types.alarmStatus.activeUnack ||
  373 + alarm.status == types.alarmStatus.clearedUnack);
  374 + } else if (actionDescriptor.clear) {
  375 + return (alarm.status == types.alarmStatus.activeAck ||
  376 + alarm.status == types.alarmStatus.activeUnack);
316 377 }
317   - vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, { alarm: alarm });
  378 + return true;
318 379 }
319 380
320 381 function isCurrent(alarm) {
... ... @@ -387,6 +448,25 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
387 448 }
388 449 }
389 450
  451 + function ackAlarm($event, alarm) {
  452 + if ($event) {
  453 + $event.stopPropagation();
  454 + }
  455 + var confirm = $mdDialog.confirm()
  456 + .targetEvent($event)
  457 + .title($translate.instant('alarm.aknowledge-alarm-title'))
  458 + .htmlContent($translate.instant('alarm.aknowledge-alarm-text'))
  459 + .ariaLabel($translate.instant('alarm.acknowledge'))
  460 + .cancel($translate.instant('action.no'))
  461 + .ok($translate.instant('action.yes'));
  462 + $mdDialog.show(confirm).then(function () {
  463 + alarmService.ackAlarm(alarm.id.id).then(function () {
  464 + vm.selectedAlarms = [];
  465 + vm.subscription.update();
  466 + });
  467 + });
  468 + }
  469 +
390 470 function clearAlarms($event) {
391 471 if ($event) {
392 472 $event.stopPropagation();
... ... @@ -420,6 +500,24 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
420 500 }
421 501 }
422 502
  503 + function clearAlarm($event, alarm) {
  504 + if ($event) {
  505 + $event.stopPropagation();
  506 + }
  507 + var confirm = $mdDialog.confirm()
  508 + .targetEvent($event)
  509 + .title($translate.instant('alarm.clear-alarm-title'))
  510 + .htmlContent($translate.instant('alarm.clear-alarm-text'))
  511 + .ariaLabel($translate.instant('alarm.clear'))
  512 + .cancel($translate.instant('action.no'))
  513 + .ok($translate.instant('action.yes'));
  514 + $mdDialog.show(confirm).then(function () {
  515 + alarmService.clearAlarm(alarm.id.id).then(function () {
  516 + vm.selectedAlarms = [];
  517 + vm.subscription.update();
  518 + });
  519 + });
  520 + }
423 521
424 522 function updateAlarms(preserveSelections) {
425 523 if (!preserveSelections) {
... ... @@ -558,6 +656,54 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
558 656 }
559 657 }
560 658
  659 + function editAlarmStatusFilter($event) {
  660 + var element = angular.element($event.target);
  661 + var position = $mdPanel.newPanelPosition()
  662 + .relativeTo(element)
  663 + .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
  664 + var config = {
  665 + attachTo: angular.element($document[0].body),
  666 + controller: AlarmStatusFilterPanelController,
  667 + controllerAs: 'vm',
  668 + templateUrl: alarmStatusFilterPanelTemplate,
  669 + panelClass: 'tb-alarm-status-filter-panel',
  670 + position: position,
  671 + fullscreen: false,
  672 + locals: {
  673 + 'subscription': vm.subscription
  674 + },
  675 + openFrom: $event,
  676 + clickOutsideToClose: true,
  677 + escapeToClose: true,
  678 + focusOnOpen: false
  679 + };
  680 + $mdPanel.open(config);
  681 + }
  682 +
  683 + function editColumnsToDisplay($event) {
  684 + var element = angular.element($event.target);
  685 + var position = $mdPanel.newPanelPosition()
  686 + .relativeTo(element)
  687 + .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
  688 + var config = {
  689 + attachTo: angular.element($document[0].body),
  690 + controller: DisplayColumnsPanelController,
  691 + controllerAs: 'vm',
  692 + templateUrl: displayColumnsPanelTemplate,
  693 + panelClass: 'tb-display-columns-panel',
  694 + position: position,
  695 + fullscreen: false,
  696 + locals: {
  697 + 'columns': vm.alarmSource.dataKeys
  698 + },
  699 + openFrom: $event,
  700 + clickOutsideToClose: true,
  701 + escapeToClose: true,
  702 + focusOnOpen: false
  703 + };
  704 + $mdPanel.open(config);
  705 + }
  706 +
561 707 function updateAlarmSource() {
562 708
563 709 vm.ctx.widgetTitle = utils.createLabelFromDatasource(vm.alarmSource, vm.alarmsTitle);
... ... @@ -570,6 +716,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
570 716 var dataKey = vm.alarmSource.dataKeys[d];
571 717
572 718 dataKey.title = utils.customTranslation(dataKey.label, dataKey.label);
  719 + dataKey.display = true;
573 720
574 721 var keySettings = dataKey.settings;
575 722
... ... @@ -618,4 +765,19 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
618 765 }
619 766 }
620 767
621   -}
\ No newline at end of file
  768 +}
  769 +
  770 +/*@ngInject*/
  771 +function DisplayColumnsPanelController(columns) { //eslint-disable-line
  772 +
  773 + var vm = this;
  774 + vm.columns = columns;
  775 +}
  776 +
  777 +/*@ngInject*/
  778 +function AlarmStatusFilterPanelController(subscription, types) { //eslint-disable-line
  779 +
  780 + var vm = this;
  781 + vm.types = types;
  782 + vm.subscription = subscription;
  783 +}
... ...
... ... @@ -44,6 +44,27 @@
44 44 &.tb-data-table {
45 45 table.md-table,
46 46 table.md-table.md-row-select {
  47 + th.md-column {
  48 + &.tb-action-cell {
  49 + .md-button {
  50 + /* stylelint-disable-next-line selector-max-class */
  51 + &.md-icon-button {
  52 + width: 36px;
  53 + height: 36px;
  54 + padding: 6px;
  55 + margin: 0;
  56 + /* stylelint-disable-next-line selector-max-class */
  57 + md-icon {
  58 + width: 24px;
  59 + height: 24px;
  60 + font-size: 24px !important;
  61 + line-height: 24px !important;
  62 + }
  63 + }
  64 + }
  65 + }
  66 + }
  67 +
47 68 tbody {
48 69 tr {
49 70 td {
... ... @@ -51,6 +72,15 @@
51 72 width: 36px;
52 73 min-width: 36px;
53 74 max-width: 36px;
  75 +
  76 + .md-button[disabled] {
  77 + &.md-icon-button {
  78 + /* stylelint-disable-next-line selector-max-class */
  79 + md-icon {
  80 + color: rgba(0, 0, 0, .38);
  81 + }
  82 + }
  83 + }
54 84 }
55 85 }
56 86 }
... ...
... ... @@ -62,33 +62,45 @@
62 62 <table md-table md-row-select="vm.enableSelection" multiple="" ng-model="vm.selectedAlarms">
63 63 <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
64 64 <tr md-row>
65   - <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th>
66   - <th md-column ng-if="vm.displayDetails"><span>&nbsp</span></th>
67   - <th md-column ng-if="vm.actionCellDescriptors.length"><span>&nbsp</span></th>
  65 + <th ng-if="key.display" md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th>
  66 + <th md-column class="tb-action-cell" layout="row" layout-align="end center">
  67 + <md-button class="md-icon-button"
  68 + aria-label="{{'alarm.alarm-status-filter' | translate}}"
  69 + ng-click="vm.editAlarmStatusFilter($event)">
  70 + <md-icon aria-label="{{'alarm.alarm-status-filter' | translate}}"
  71 + class="material-icons">filter_list
  72 + </md-icon>
  73 + <md-tooltip md-direction="top">
  74 + {{'alarm.alarm-status-filter' | translate}}
  75 + </md-tooltip>
  76 + </md-button>
  77 + <md-button class="md-icon-button"
  78 + aria-label="{{'entity.columns-to-display' | translate}}"
  79 + ng-click="vm.editColumnsToDisplay($event)">
  80 + <md-icon aria-label="{{'entity.columns-to-display' | translate}}"
  81 + class="material-icons">view_column
  82 + </md-icon>
  83 + <md-tooltip md-direction="top">
  84 + {{'entity.columns-to-display' | translate}}
  85 + </md-tooltip>
  86 + </md-button>
  87 + </th>
68 88 </tr>
69 89 </thead>
70 90 <tbody md-body>
71 91 <tr ng-show="vm.alarms.length" md-row md-select="alarm"
72 92 md-select-id="id.id" md-auto-select="false" ng-repeat="alarm in vm.alarms"
73 93 ng-click="vm.onRowClick($event, alarm)" ng-class="{'tb-current': vm.isCurrent(alarm)}">
74   - <td md-cell flex ng-repeat="key in vm.alarmSource.dataKeys"
  94 + <td ng-if="key.display" md-cell flex ng-repeat="key in vm.alarmSource.dataKeys"
75 95 ng-style="vm.cellStyle(alarm, key)"
76 96 ng-bind-html="vm.cellContent(alarm, key)">
77 97 </td>
78   - <td md-cell ng-if="vm.displayDetails" class="tb-action-cell">
79   - <md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}"
80   - ng-click="vm.openAlarmDetails($event, alarm)" ng-disabled="$root.loading">
81   - <md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon>
82   - <md-tooltip md-direction="top">
83   - {{ 'alarm.details' | translate }}
84   - </md-tooltip>
85   - </md-button>
86   - </td>
87   - <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell"
  98 + <td md-cell class="tb-action-cell"
88 99 ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px',
89 100 maxWidth: vm.actionCellDescriptors.length*36+'px',
90 101 width: vm.actionCellDescriptors.length*36+'px'}">
91 102 <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
  103 + ng-disabled="!vm.actionEnabled(alarm, actionDescriptor)"
92 104 aria-label="{{ actionDescriptor.displayName }}"
93 105 ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)" ng-disabled="$root.loading">
94 106 <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
... ...
  1 +/**
  2 + * Copyright © 2016-2018 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 +.tb-display-columns-panel {
  18 + min-width: 300px;
  19 + overflow: hidden;
  20 + background: #fff;
  21 + border-radius: 4px;
  22 + box-shadow:
  23 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  24 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  25 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  26 +
  27 + md-content {
  28 + overflow: hidden;
  29 + background-color: #fff;
  30 + }
  31 +}
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 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 +<md-content style="height: 100%" flex layout="column" class="md-padding">
  20 + <label class="tb-title" translate>entity.columns-to-display</label>
  21 + <md-checkbox aria-label="{{ 'entity.columns-to-display' | translate }}" ng-repeat="column in vm.columns"
  22 + ng-model="column.display">{{ column.title }}
  23 + </md-checkbox>
  24 +</md-content>
... ...
... ... @@ -14,11 +14,13 @@
14 14 * limitations under the License.
15 15 */
16 16 import './entities-table-widget.scss';
  17 +import './display-columns-panel.scss';
17 18
18 19 /* eslint-disable import/no-unresolved, import/default */
19 20
20 21 import entitiesTableWidgetTemplate from './entities-table-widget.tpl.html';
21 22 //import entityDetailsDialogTemplate from './entitiy-details-dialog.tpl.html';
  23 +import displayColumnsPanelTemplate from './display-columns-panel.tpl.html';
22 24
23 25 /* eslint-enable import/no-unresolved, import/default */
24 26
... ... @@ -45,7 +47,7 @@ function EntitiesTableWidget() {
45 47 }
46 48
47 49 /*@ngInject*/
48   -function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $translate, $timeout, utils, types) {
  50 +function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $mdPanel, $document, $translate, $timeout, utils, types) {
49 51 var vm = this;
50 52
51 53 vm.stylesInfo = {};
... ... @@ -98,6 +100,8 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
98 100 vm.cellStyle = cellStyle;
99 101 vm.cellContent = cellContent;
100 102
  103 + vm.editColumnsToDisplay = editColumnsToDisplay;
  104 +
101 105 $scope.$watch('vm.ctx', function() {
102 106 if (vm.ctx && vm.ctx.defaultSubscription) {
103 107 vm.settings = vm.ctx.settings;
... ... @@ -414,12 +418,37 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
414 418 }
415 419 }
416 420
  421 + function editColumnsToDisplay($event) {
  422 + var element = angular.element($event.target);
  423 + var position = $mdPanel.newPanelPosition()
  424 + .relativeTo(element)
  425 + .addPanelPosition($mdPanel.xPosition.ALIGN_END, $mdPanel.yPosition.BELOW);
  426 + var config = {
  427 + attachTo: angular.element($document[0].body),
  428 + controller: DisplayColumnsPanelController,
  429 + controllerAs: 'vm',
  430 + templateUrl: displayColumnsPanelTemplate,
  431 + panelClass: 'tb-display-columns-panel',
  432 + position: position,
  433 + fullscreen: false,
  434 + locals: {
  435 + 'columns': vm.columns
  436 + },
  437 + openFrom: $event,
  438 + clickOutsideToClose: true,
  439 + escapeToClose: true,
  440 + focusOnOpen: false
  441 + };
  442 + $mdPanel.open(config);
  443 + }
  444 +
417 445 function updateDatasources() {
418 446
419 447 vm.stylesInfo = {};
420 448 vm.contentsInfo = {};
421 449 vm.columnWidth = {};
422 450 vm.dataKeys = [];
  451 + vm.columns = [];
423 452 vm.allEntities = [];
424 453
425 454 var datasource;
... ... @@ -429,6 +458,42 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
429 458
430 459 vm.ctx.widgetTitle = utils.createLabelFromDatasource(datasource, vm.entitiesTitle);
431 460
  461 + if (vm.displayEntityName) {
  462 + vm.columns.push(
  463 + {
  464 + name: 'entityName',
  465 + label: 'entityName',
  466 + title: vm.entityNameColumnTitle,
  467 + display: true
  468 + }
  469 + );
  470 + vm.contentsInfo['entityName'] = {
  471 + useCellContentFunction: false
  472 + };
  473 + vm.stylesInfo['entityName'] = {
  474 + useCellStyleFunction: false
  475 + };
  476 + vm.columnWidth['entityName'] = '0px';
  477 + }
  478 +
  479 + if (vm.displayEntityType) {
  480 + vm.columns.push(
  481 + {
  482 + name: 'entityType',
  483 + label: 'entityType',
  484 + title: $translate.instant('entity.entity-type'),
  485 + display: true
  486 + }
  487 + );
  488 + vm.contentsInfo['entityType'] = {
  489 + useCellContentFunction: false
  490 + };
  491 + vm.stylesInfo['entityType'] = {
  492 + useCellStyleFunction: false
  493 + };
  494 + vm.columnWidth['entityType'] = '0px';
  495 + }
  496 +
432 497 for (var d = 0; d < datasource.dataKeys.length; d++ ) {
433 498 dataKey = angular.copy(datasource.dataKeys[d]);
434 499 if (dataKey.type == types.dataKeyType.function) {
... ... @@ -482,6 +547,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
482 547
483 548 var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px';
484 549 vm.columnWidth[dataKey.label] = columnWidth;
  550 +
  551 + dataKey.display = true;
  552 + vm.columns.push(dataKey);
485 553 }
486 554
487 555 for (var i=0;i<vm.datasources.length;i++) {
... ... @@ -511,4 +579,11 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
511 579
512 580 }
513 581
514   -}
\ No newline at end of file
  582 +}
  583 +
  584 +/*@ngInject*/
  585 +function DisplayColumnsPanelController(columns) { //eslint-disable-line
  586 +
  587 + var vm = this;
  588 + vm.columns = columns;
  589 +}
... ...
... ... @@ -44,6 +44,27 @@
44 44 &.tb-data-table {
45 45 table.md-table,
46 46 table.md-table.md-row-select {
  47 + th.md-column {
  48 + &.tb-action-cell {
  49 + .md-button {
  50 + /* stylelint-disable-next-line selector-max-class */
  51 + &.md-icon-button {
  52 + width: 36px;
  53 + height: 36px;
  54 + padding: 6px;
  55 + margin: 0;
  56 + /* stylelint-disable-next-line selector-max-class */
  57 + md-icon {
  58 + width: 24px;
  59 + height: 24px;
  60 + font-size: 24px !important;
  61 + line-height: 24px !important;
  62 + }
  63 + }
  64 + }
  65 + }
  66 + }
  67 +
47 68 tbody {
48 69 tr {
49 70 td {
... ... @@ -51,6 +72,15 @@
51 72 width: 36px;
52 73 min-width: 36px;
53 74 max-width: 36px;
  75 +
  76 + .md-button[disabled] {
  77 + &.md-icon-button {
  78 + /* stylelint-disable-next-line selector-max-class */
  79 + md-icon {
  80 + color: rgba(0, 0, 0, .38);
  81 + }
  82 + }
  83 + }
54 84 }
55 85 }
56 86 }
... ...
... ... @@ -41,23 +41,30 @@
41 41 <table md-table>
42 42 <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
43 43 <tr md-row>
44   - <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span>{{vm.entityNameColumnTitle}}</span></th>
45   - <th md-column ng-if="vm.displayEntityType" md-order-by="entityType"><span translate>entity.entity-type</span></th>
46   - <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.dataKeys"><span>{{ key.title }}</span></th>
47   - <th md-column ng-if="vm.actionCellDescriptors.length"><span>&nbsp</span></th>
  44 + <th ng-if="column.display" md-column md-order-by="{{ column.name }}" ng-repeat="column in vm.columns"><span>{{ column.title }}</span></th>
  45 + <th md-column class="tb-action-cell" layout="row" layout-align="end center">
  46 + <md-button class="md-icon-button"
  47 + aria-label="{{'entity.columns-to-display' | translate}}"
  48 + ng-click="vm.editColumnsToDisplay($event)">
  49 + <md-icon aria-label="{{'entity.columns-to-display' | translate}}"
  50 + class="material-icons">view_column
  51 + </md-icon>
  52 + <md-tooltip md-direction="top">
  53 + {{'entity.columns-to-display' | translate}}
  54 + </md-tooltip>
  55 + </md-button>
  56 + </th>
48 57 </tr>
49 58 </thead>
50 59 <tbody md-body>
51 60 <tr ng-show="vm.entities.length" md-row md-select="entity"
52 61 md-select-id="id.id" md-auto-select="false" ng-repeat="entity in vm.entities"
53 62 ng-click="vm.onRowClick($event, entity)" ng-class="{'tb-current': vm.isCurrent(entity)}">
54   - <td md-cell flex ng-if="vm.displayEntityName">{{entity.entityName}}</td>
55   - <td md-cell flex ng-if="vm.displayEntityType">{{entity.entityType}}</td>
56   - <td md-cell flex ng-repeat="key in vm.dataKeys"
57   - ng-style="vm.cellStyle(entity, key)"
58   - ng-bind-html="vm.cellContent(entity, key)">
  63 + <td ng-if="column.display" md-cell flex ng-repeat="column in vm.columns"
  64 + ng-style="vm.cellStyle(entity, column)"
  65 + ng-bind-html="vm.cellContent(entity, column)">
59 66 </td>
60   - <td md-cell ng-if="vm.actionCellDescriptors.length" class="tb-action-cell"
  67 + <td md-cell class="tb-action-cell"
61 68 ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px',
62 69 maxWidth: vm.actionCellDescriptors.length*36+'px',
63 70 width: vm.actionCellDescriptors.length*36+'px'}">
... ...