Commit eda21c94efabb9f206273e5a4f114db898c4458b
1 parent
531f4245
TB-63: Alarms widget implementation
Showing
22 changed files
with
388 additions
and
73 deletions
@@ -23,11 +23,14 @@ import './alarm-details-dialog.scss'; | @@ -23,11 +23,14 @@ import './alarm-details-dialog.scss'; | ||
23 | const js_beautify = beautify.js; | 23 | const js_beautify = beautify.js; |
24 | 24 | ||
25 | /*@ngInject*/ | 25 | /*@ngInject*/ |
26 | -export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) { | 26 | +export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, |
27 | + alarmService, alarmId, allowAcknowledgment, allowClear, showingCallback) { | ||
27 | 28 | ||
28 | var vm = this; | 29 | var vm = this; |
29 | 30 | ||
30 | vm.alarmId = alarmId; | 31 | vm.alarmId = alarmId; |
32 | + vm.allowAcknowledgment = allowAcknowledgment; | ||
33 | + vm.allowClear = allowClear; | ||
31 | vm.types = types; | 34 | vm.types = types; |
32 | vm.alarm = null; | 35 | vm.alarm = null; |
33 | 36 |
@@ -84,16 +84,16 @@ | @@ -84,16 +84,16 @@ | ||
84 | </div> | 84 | </div> |
85 | </md-dialog-content> | 85 | </md-dialog-content> |
86 | <md-dialog-actions layout="row"> | 86 | <md-dialog-actions layout="row"> |
87 | - <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack || | ||
88 | - vm.alarm.status==vm.types.alarmStatus.clearedUnack" | 87 | + <md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack || |
88 | + vm.alarm.status==vm.types.alarmStatus.clearedUnack)" | ||
89 | class="md-raised md-primary" | 89 | class="md-raised md-primary" |
90 | ng-disabled="loading" | 90 | ng-disabled="loading" |
91 | ng-click="vm.acknowledge()" | 91 | ng-click="vm.acknowledge()" |
92 | style="margin-right:20px;">{{ 'alarm.acknowledge' | | 92 | style="margin-right:20px;">{{ 'alarm.acknowledge' | |
93 | translate }} | 93 | translate }} |
94 | </md-button> | 94 | </md-button> |
95 | - <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck || | ||
96 | - vm.alarm.status==vm.types.alarmStatus.activeUnack" | 95 | + <md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck || |
96 | + vm.alarm.status==vm.types.alarmStatus.activeUnack)" | ||
97 | class="md-raised md-primary" | 97 | class="md-raised md-primary" |
98 | ng-disabled="loading" | 98 | ng-disabled="loading" |
99 | ng-click="vm.clear()">{{ 'alarm.clear' | | 99 | ng-click="vm.clear()">{{ 'alarm.clear' | |
@@ -40,7 +40,12 @@ export default function AlarmRowDirective($compile, $templateCache, types, $mdDi | @@ -40,7 +40,12 @@ export default function AlarmRowDirective($compile, $templateCache, types, $mdDi | ||
40 | controller: 'AlarmDetailsDialogController', | 40 | controller: 'AlarmDetailsDialogController', |
41 | controllerAs: 'vm', | 41 | controllerAs: 'vm', |
42 | templateUrl: alarmDetailsDialogTemplate, | 42 | templateUrl: alarmDetailsDialogTemplate, |
43 | - locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback}, | 43 | + locals: { |
44 | + alarmId: scope.alarm.id.id, | ||
45 | + allowAcknowledgment: true, | ||
46 | + allowClear: true, | ||
47 | + showingCallback: onShowingCallback | ||
48 | + }, | ||
44 | parent: angular.element($document[0].body), | 49 | parent: angular.element($document[0].body), |
45 | targetEvent: $event, | 50 | targetEvent: $event, |
46 | fullscreen: true, | 51 | fullscreen: true, |
@@ -252,12 +252,12 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) { | @@ -252,12 +252,12 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) { | ||
252 | $timeout(function() { | 252 | $timeout(function() { |
253 | alarmSourceListener.alarmsUpdated([simulatedAlarm], false); | 253 | alarmSourceListener.alarmsUpdated([simulatedAlarm], false); |
254 | }); | 254 | }); |
255 | - } else { | ||
256 | - var pollingInterval = 5000; //TODO: | 255 | + } else if (alarmSource.entityType && alarmSource.entityId) { |
256 | + var pollingInterval = alarmSourceListener.alarmsPollingInterval; | ||
257 | alarmSourceListener.alarmsQuery = { | 257 | alarmSourceListener.alarmsQuery = { |
258 | entityType: alarmSource.entityType, | 258 | entityType: alarmSource.entityType, |
259 | entityId: alarmSource.entityId, | 259 | entityId: alarmSource.entityId, |
260 | - alarmSearchStatus: null, //TODO: | 260 | + alarmSearchStatus: alarmSourceListener.alarmSearchStatus, |
261 | alarmStatus: null | 261 | alarmStatus: null |
262 | } | 262 | } |
263 | var originatorKeys = $filter('filter')(alarmSource.dataKeys, {name: 'originator'}); | 263 | var originatorKeys = $filter('filter')(alarmSource.dataKeys, {name: 'originator'}); |
@@ -70,6 +70,12 @@ export default class Subscription { | @@ -70,6 +70,12 @@ export default class Subscription { | ||
70 | this.callbacks.dataLoading = this.callbacks.dataLoading || function(){}; | 70 | this.callbacks.dataLoading = this.callbacks.dataLoading || function(){}; |
71 | this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || function(){}; | 71 | this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || function(){}; |
72 | this.alarmSource = options.alarmSource; | 72 | this.alarmSource = options.alarmSource; |
73 | + | ||
74 | + this.alarmSearchStatus = angular.isDefined(options.alarmSearchStatus) ? | ||
75 | + options.alarmSearchStatus : this.ctx.types.alarmSearchStatus.any; | ||
76 | + this.alarmsPollingInterval = angular.isDefined(options.alarmsPollingInterval) ? | ||
77 | + options.alarmsPollingInterval : 5000; | ||
78 | + | ||
73 | this.alarmSourceListener = null; | 79 | this.alarmSourceListener = null; |
74 | this.alarms = []; | 80 | this.alarms = []; |
75 | 81 | ||
@@ -193,8 +199,7 @@ export default class Subscription { | @@ -193,8 +199,7 @@ export default class Subscription { | ||
193 | registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { | 199 | registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { |
194 | if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { | 200 | if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { |
195 | subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); | 201 | subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); |
196 | - subscription.unsubscribe(); | ||
197 | - subscription.subscribe(); | 202 | + subscription.update(); |
198 | } | 203 | } |
199 | }); | 204 | }); |
200 | this.registrations.push(registration); | 205 | this.registrations.push(registration); |
@@ -281,8 +286,7 @@ export default class Subscription { | @@ -281,8 +286,7 @@ export default class Subscription { | ||
281 | registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { | 286 | registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) { |
282 | if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { | 287 | if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) { |
283 | subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); | 288 | subscription.timeWindowConfig = angular.copy(newDashboardTimewindow); |
284 | - subscription.unsubscribe(); | ||
285 | - subscription.subscribe(); | 289 | + subscription.update(); |
286 | } | 290 | } |
287 | }); | 291 | }); |
288 | this.registrations.push(registration); | 292 | this.registrations.push(registration); |
@@ -298,8 +302,7 @@ export default class Subscription { | @@ -298,8 +302,7 @@ export default class Subscription { | ||
298 | return subscription.timeWindowConfig; | 302 | return subscription.timeWindowConfig; |
299 | }, function (newTimewindow, prevTimewindow) { | 303 | }, function (newTimewindow, prevTimewindow) { |
300 | if (!angular.equals(newTimewindow, prevTimewindow)) { | 304 | if (!angular.equals(newTimewindow, prevTimewindow)) { |
301 | - subscription.unsubscribe(); | ||
302 | - subscription.subscribe(); | 305 | + subscription.update(); |
303 | } | 306 | } |
304 | }, true); | 307 | }, true); |
305 | this.registrations.push(this.timeWindowWatchRegistration); | 308 | this.registrations.push(this.timeWindowWatchRegistration); |
@@ -502,8 +505,7 @@ export default class Subscription { | @@ -502,8 +505,7 @@ export default class Subscription { | ||
502 | this.timeWindowConfig = angular.copy(this.originalTimewindow); | 505 | this.timeWindowConfig = angular.copy(this.originalTimewindow); |
503 | this.originalTimewindow = null; | 506 | this.originalTimewindow = null; |
504 | this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); | 507 | this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); |
505 | - this.unsubscribe(); | ||
506 | - this.subscribe(); | 508 | + this.update(); |
507 | this.startWatchingTimewindow(); | 509 | this.startWatchingTimewindow(); |
508 | } | 510 | } |
509 | } | 511 | } |
@@ -519,8 +521,7 @@ export default class Subscription { | @@ -519,8 +521,7 @@ export default class Subscription { | ||
519 | } | 521 | } |
520 | this.timeWindowConfig = this.ctx.timeService.toHistoryTimewindow(this.timeWindowConfig, startTimeMs, endTimeMs); | 522 | this.timeWindowConfig = this.ctx.timeService.toHistoryTimewindow(this.timeWindowConfig, startTimeMs, endTimeMs); |
521 | this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); | 523 | this.callbacks.timeWindowUpdated(this, this.timeWindowConfig); |
522 | - this.unsubscribe(); | ||
523 | - this.subscribe(); | 524 | + this.update(); |
524 | this.startWatchingTimewindow(); | 525 | this.startWatchingTimewindow(); |
525 | } | 526 | } |
526 | } | 527 | } |
@@ -618,6 +619,11 @@ export default class Subscription { | @@ -618,6 +619,11 @@ export default class Subscription { | ||
618 | this.callbacks.legendDataUpdated(this, apply !== false); | 619 | this.callbacks.legendDataUpdated(this, apply !== false); |
619 | } | 620 | } |
620 | 621 | ||
622 | + update() { | ||
623 | + this.unsubscribe(); | ||
624 | + this.subscribe(); | ||
625 | + } | ||
626 | + | ||
621 | subscribe() { | 627 | subscribe() { |
622 | if (this.type === this.ctx.types.widgetType.rpc.value) { | 628 | if (this.type === this.ctx.types.widgetType.rpc.value) { |
623 | return; | 629 | return; |
@@ -688,6 +694,8 @@ export default class Subscription { | @@ -688,6 +694,8 @@ export default class Subscription { | ||
688 | this.alarmSourceListener = { | 694 | this.alarmSourceListener = { |
689 | subscriptionTimewindow: this.subscriptionTimewindow, | 695 | subscriptionTimewindow: this.subscriptionTimewindow, |
690 | alarmSource: this.alarmSource, | 696 | alarmSource: this.alarmSource, |
697 | + alarmSearchStatus: this.alarmSearchStatus, | ||
698 | + alarmsPollingInterval: this.alarmsPollingInterval, | ||
691 | alarmsUpdated: function(alarms, apply) { | 699 | alarmsUpdated: function(alarms, apply) { |
692 | subscription.alarmsUpdated(alarms, apply); | 700 | subscription.alarmsUpdated(alarms, apply); |
693 | } | 701 | } |
@@ -394,7 +394,8 @@ export default angular.module('thingsboard.types', []) | @@ -394,7 +394,8 @@ export default angular.module('thingsboard.types', []) | ||
394 | cards: "cards" | 394 | cards: "cards" |
395 | }, | 395 | }, |
396 | translate: { | 396 | translate: { |
397 | - dashboardStatePrefix: "dashboardState.state." | 397 | + dashboardStatePrefix: "dashboardState.state.", |
398 | + keyLabelPrefix: "key.label." | ||
398 | } | 399 | } |
399 | } | 400 | } |
400 | ).name; | 401 | ).name; |
@@ -63,6 +63,12 @@ function DatakeyConfig($compile, $templateCache, $q, types) { | @@ -63,6 +63,12 @@ function DatakeyConfig($compile, $templateCache, $q, types) { | ||
63 | element.html(template); | 63 | element.html(template); |
64 | 64 | ||
65 | scope.types = types; | 65 | scope.types = types; |
66 | + | ||
67 | + scope.alarmFields = []; | ||
68 | + for (var alarmField in types.alarmFields) { | ||
69 | + scope.alarmFields.push(alarmField); | ||
70 | + } | ||
71 | + | ||
66 | scope.selectedKey = null; | 72 | scope.selectedKey = null; |
67 | scope.keySearchText = null; | 73 | scope.keySearchText = null; |
68 | scope.usePostProcessing = false; | 74 | scope.usePostProcessing = false; |
@@ -112,21 +118,39 @@ function DatakeyConfig($compile, $templateCache, $q, types) { | @@ -112,21 +118,39 @@ function DatakeyConfig($compile, $templateCache, $q, types) { | ||
112 | }, true); | 118 | }, true); |
113 | 119 | ||
114 | scope.keysSearch = function (searchText) { | 120 | scope.keysSearch = function (searchText) { |
115 | - if (scope.entityAlias) { | ||
116 | - var deferred = $q.defer(); | ||
117 | - scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: scope.model.type}) | ||
118 | - .then(function (keys) { | ||
119 | - keys.push(searchText); | ||
120 | - deferred.resolve(keys); | ||
121 | - }, function (e) { | ||
122 | - deferred.reject(e); | ||
123 | - }); | ||
124 | - return deferred.promise; | 121 | + if (scope.model.type === types.dataKeyType.alarm) { |
122 | + var dataKeys = searchText ? scope.alarmFields.filter( | ||
123 | + scope.createFilterForDataKey(searchText)) : scope.alarmFields; | ||
124 | + dataKeys.push(searchText); | ||
125 | + return dataKeys; | ||
125 | } else { | 126 | } else { |
126 | - return $q.when([]); | 127 | + if (scope.entityAlias) { |
128 | + var deferred = $q.defer(); | ||
129 | + scope.fetchEntityKeys({ | ||
130 | + entityAliasId: scope.entityAlias.id, | ||
131 | + query: searchText, | ||
132 | + type: scope.model.type | ||
133 | + }) | ||
134 | + .then(function (keys) { | ||
135 | + keys.push(searchText); | ||
136 | + deferred.resolve(keys); | ||
137 | + }, function (e) { | ||
138 | + deferred.reject(e); | ||
139 | + }); | ||
140 | + return deferred.promise; | ||
141 | + } else { | ||
142 | + return $q.when([]); | ||
143 | + } | ||
127 | } | 144 | } |
128 | }; | 145 | }; |
129 | 146 | ||
147 | + scope.createFilterForDataKey = function (query) { | ||
148 | + var lowercaseQuery = angular.lowercase(query); | ||
149 | + return function filterFn(dataKey) { | ||
150 | + return (angular.lowercase(dataKey).indexOf(lowercaseQuery) === 0); | ||
151 | + }; | ||
152 | + }; | ||
153 | + | ||
130 | $compile(element.contents())(scope); | 154 | $compile(element.contents())(scope); |
131 | } | 155 | } |
132 | 156 |
@@ -16,7 +16,9 @@ | @@ -16,7 +16,9 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <md-content class="md-padding" layout="column"> | 18 | <md-content class="md-padding" layout="column"> |
19 | - <md-autocomplete ng-if="model.type === types.dataKeyType.timeseries || model.type === types.dataKeyType.attribute" | 19 | + <md-autocomplete ng-if="model.type === types.dataKeyType.timeseries || |
20 | + model.type === types.dataKeyType.attribute || | ||
21 | + model.type === types.dataKeyType.alarm" | ||
20 | style="padding-bottom: 8px;" | 22 | style="padding-bottom: 8px;" |
21 | ng-required="true" | 23 | ng-required="true" |
22 | md-no-cache="true" | 24 | md-no-cache="true" |
@@ -27,8 +29,8 @@ | @@ -27,8 +29,8 @@ | ||
27 | md-items="item in keysSearch(keySearchText)" | 29 | md-items="item in keysSearch(keySearchText)" |
28 | md-item-text="item" | 30 | md-item-text="item" |
29 | md-min-length="0" | 31 | md-min-length="0" |
30 | - placeholder="Key name" | ||
31 | - md-floating-label="Key"> | 32 | + placeholder="{{ 'entity.key-name' | translate }}" |
33 | + md-floating-label="{{ 'entity.key' | translate }}"> | ||
32 | <span md-highlight-text="keySearchText" md-highlight-flags="^i">{{item}}</span> | 34 | <span md-highlight-text="keySearchText" md-highlight-flags="^i">{{item}}</span> |
33 | </md-autocomplete> | 35 | </md-autocomplete> |
34 | <div layout="row" layout-align="start center"> | 36 | <div layout="row" layout-align="start center"> |
@@ -48,7 +50,7 @@ | @@ -48,7 +50,7 @@ | ||
48 | md-color-history="false"> | 50 | md-color-history="false"> |
49 | </div> | 51 | </div> |
50 | </div> | 52 | </div> |
51 | - <div layout="row" layout-align="start center"> | 53 | + <div layout="row" layout-align="start center" ng-if="model.type !== types.dataKeyType.alarm"> |
52 | <md-input-container flex> | 54 | <md-input-container flex> |
53 | <label translate>datakey.units</label> | 55 | <label translate>datakey.units</label> |
54 | <input name="units" ng-model="model.units"> | 56 | <input name="units" ng-model="model.units"> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | @import '../../scss/constants'; | 16 | @import '../../scss/constants'; |
17 | 17 | ||
18 | -.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete { | 18 | +.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete { |
19 | .tb-not-found { | 19 | .tb-not-found { |
20 | display: block; | 20 | display: block; |
21 | line-height: 1.5; | 21 | line-height: 1.5; |
@@ -133,7 +133,7 @@ | @@ -133,7 +133,7 @@ | ||
133 | ng-required="true" | 133 | ng-required="true" |
134 | ng-model="alarmDataKeys" md-autocomplete-snap | 134 | ng-model="alarmDataKeys" md-autocomplete-snap |
135 | md-transform-chip="transformAlarmDataKeyChip($chip)" | 135 | md-transform-chip="transformAlarmDataKeyChip($chip)" |
136 | - md-require-match="true"> | 136 | + md-require-match="false"> |
137 | <md-autocomplete | 137 | <md-autocomplete |
138 | md-no-cache="true" | 138 | md-no-cache="true" |
139 | id="alarm_datakey" | 139 | id="alarm_datakey" |
@@ -152,6 +152,9 @@ | @@ -152,6 +152,9 @@ | ||
152 | </div> | 152 | </div> |
153 | <div ng-if="textIsNotEmpty(alarmDataKeySearchText)"> | 153 | <div ng-if="textIsNotEmpty(alarmDataKeySearchText)"> |
154 | <span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span> | 154 | <span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span> |
155 | + <span> | ||
156 | + <a translate ng-click="createKey($event, '#alarm_datakey_chips')">entity.create-new-key</a> | ||
157 | + </span> | ||
155 | </div> | 158 | </div> |
156 | </div> | 159 | </div> |
157 | </md-not-found> | 160 | </md-not-found> |
@@ -48,9 +48,9 @@ | @@ -48,9 +48,9 @@ | ||
48 | <span translate>device.no-keys-found</span> | 48 | <span translate>device.no-keys-found</span> |
49 | </div> | 49 | </div> |
50 | <div ng-if="textIsNotEmpty(dataKeySearchText)"> | 50 | <div ng-if="textIsNotEmpty(dataKeySearchText)"> |
51 | - <span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span> | 51 | + <span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span> |
52 | <span> | 52 | <span> |
53 | - <a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a> | 53 | + <a translate ng-click="createKey($event, '#function_datakey_chips')">entity.create-new-key</a> |
54 | </span> | 54 | </span> |
55 | </div> | 55 | </div> |
56 | </div> | 56 | </div> |
@@ -81,7 +81,7 @@ | @@ -81,7 +81,7 @@ | ||
81 | ng-required="true" | 81 | ng-required="true" |
82 | ng-model="alarmDataKeys" md-autocomplete-snap | 82 | ng-model="alarmDataKeys" md-autocomplete-snap |
83 | md-transform-chip="transformAlarmDataKeyChip($chip)" | 83 | md-transform-chip="transformAlarmDataKeyChip($chip)" |
84 | - md-require-match="true"> | 84 | + md-require-match="false"> |
85 | <md-autocomplete | 85 | <md-autocomplete |
86 | md-no-cache="true" | 86 | md-no-cache="true" |
87 | id="alarm_datakey" | 87 | id="alarm_datakey" |
@@ -100,6 +100,9 @@ | @@ -100,6 +100,9 @@ | ||
100 | </div> | 100 | </div> |
101 | <div ng-if="textIsNotEmpty(alarmDataKeySearchText)"> | 101 | <div ng-if="textIsNotEmpty(alarmDataKeySearchText)"> |
102 | <span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span> | 102 | <span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span> |
103 | + <span> | ||
104 | + <a translate ng-click="createKey($event, '#alarm_datakey_chips')">entity.create-new-key</a> | ||
105 | + </span> | ||
103 | </div> | 106 | </div> |
104 | </div> | 107 | </div> |
105 | </md-not-found> | 108 | </md-not-found> |
@@ -144,6 +144,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | @@ -144,6 +144,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | ||
144 | scope.targetDeviceAlias.value = null; | 144 | scope.targetDeviceAlias.value = null; |
145 | } | 145 | } |
146 | } else if (scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) { | 146 | } else if (scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) { |
147 | + scope.alarmSearchStatus = angular.isDefined(config.alarmSearchStatus) ? | ||
148 | + config.alarmSearchStatus : types.alarmSearchStatus.any; | ||
149 | + scope.alarmsPollingInterval = angular.isDefined(config.alarmsPollingInterval) ? | ||
150 | + config.alarmsPollingInterval : 5; | ||
147 | if (config.alarmSource) { | 151 | if (config.alarmSource) { |
148 | scope.alarmSource.value = config.alarmSource; | 152 | scope.alarmSource.value = config.alarmSource; |
149 | } else { | 153 | } else { |
@@ -205,7 +209,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | @@ -205,7 +209,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | ||
205 | }; | 209 | }; |
206 | 210 | ||
207 | scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + | 211 | scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' + |
208 | - 'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + showLegend', function () { | 212 | + 'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' + |
213 | + 'alarmSearchStatus + alarmsPollingInterval + showLegend', function () { | ||
209 | if (ngModelCtrl.$viewValue) { | 214 | if (ngModelCtrl.$viewValue) { |
210 | var value = ngModelCtrl.$viewValue; | 215 | var value = ngModelCtrl.$viewValue; |
211 | if (value.config) { | 216 | if (value.config) { |
@@ -225,6 +230,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | @@ -225,6 +230,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout | ||
225 | config.units = scope.units; | 230 | config.units = scope.units; |
226 | config.decimals = scope.decimals; | 231 | config.decimals = scope.decimals; |
227 | config.useDashboardTimewindow = scope.useDashboardTimewindow; | 232 | config.useDashboardTimewindow = scope.useDashboardTimewindow; |
233 | + config.alarmSearchStatus = scope.alarmSearchStatus; | ||
234 | + config.alarmsPollingInterval = scope.alarmsPollingInterval; | ||
228 | config.showLegend = scope.showLegend; | 235 | config.showLegend = scope.showLegend; |
229 | } | 236 | } |
230 | if (value.layout) { | 237 | if (value.layout) { |
@@ -31,6 +31,30 @@ | @@ -31,6 +31,30 @@ | ||
31 | flex ng-model="timewindow"></tb-timewindow> | 31 | flex ng-model="timewindow"></tb-timewindow> |
32 | </section> | 32 | </section> |
33 | </div> | 33 | </div> |
34 | + <div ng-show="widgetType === types.widgetType.alarm.value" layout='column' layout-align="center" | ||
35 | + layout-gt-sm='row' layout-align-gt-sm="start center"> | ||
36 | + <md-input-container class="md-block" flex> | ||
37 | + <label translate>alarm.alarm-status</label> | ||
38 | + <md-select ng-model="alarmSearchStatus" style="padding-bottom: 24px;"> | ||
39 | + <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus"> | ||
40 | + {{ ('alarm.search-status.' + searchStatus) | translate }} | ||
41 | + </md-option> | ||
42 | + </md-select> | ||
43 | + </md-input-container> | ||
44 | + <md-input-container flex class="md-block"> | ||
45 | + <label translate>alarm.polling-interval</label> | ||
46 | + <input ng-required="widgetType === types.widgetType.alarm.value" | ||
47 | + type="number" | ||
48 | + step="1" | ||
49 | + min="1" | ||
50 | + name="alarmsPollingInterval" | ||
51 | + ng-model="alarmsPollingInterval"/> | ||
52 | + <div ng-messages="theForm.alarmsPollingInterval.$error" multiple md-auto-hide="false"> | ||
53 | + <div ng-message="required" translate>alarm.polling-interval-required</div> | ||
54 | + <div ng-message="min" translate>alarm.min-polling-interval-message</div> | ||
55 | + </div> | ||
56 | + </md-input-container> | ||
57 | + </div> | ||
34 | <v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default" | 58 | <v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default" |
35 | ng-show="widgetType !== types.widgetType.rpc.value | 59 | ng-show="widgetType !== types.widgetType.rpc.value |
36 | && widgetType !== types.widgetType.alarm.value | 60 | && widgetType !== types.widgetType.alarm.value |
@@ -289,6 +289,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -289,6 +289,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
289 | } | 289 | } |
290 | if (widget.type == types.widgetType.alarm.value) { | 290 | if (widget.type == types.widgetType.alarm.value) { |
291 | options.alarmSource = angular.copy(widget.config.alarmSource); | 291 | options.alarmSource = angular.copy(widget.config.alarmSource); |
292 | + options.alarmSearchStatus = angular.isDefined(widget.config.alarmSearchStatus) ? | ||
293 | + widget.config.alarmSearchStatus : types.alarmSearchStatus.any; | ||
294 | + options.alarmsPollingInterval = angular.isDefined(widget.config.alarmsPollingInterval) ? | ||
295 | + widget.config.alarmsPollingInterval * 1000 : 5000; | ||
292 | } else { | 296 | } else { |
293 | options.datasources = angular.copy(widget.config.datasources) | 297 | options.datasources = angular.copy(widget.config.datasources) |
294 | } | 298 | } |
@@ -243,11 +243,9 @@ export default function EntityStateController($scope, $location, $state, $stateP | @@ -243,11 +243,9 @@ export default function EntityStateController($scope, $location, $state, $stateP | ||
243 | } | 243 | } |
244 | 244 | ||
245 | function gotoState(stateId, update, openRightLayout) { | 245 | function gotoState(stateId, update, openRightLayout) { |
246 | - if (vm.dashboardCtrl.dashboardCtx.state != stateId) { | ||
247 | - vm.dashboardCtrl.openDashboardState(stateId, openRightLayout); | ||
248 | - if (update) { | ||
249 | - updateLocation(); | ||
250 | - } | 246 | + vm.dashboardCtrl.openDashboardState(stateId, openRightLayout); |
247 | + if (update) { | ||
248 | + updateLocation(); | ||
251 | } | 249 | } |
252 | } | 250 | } |
253 | 251 |
@@ -148,7 +148,14 @@ export default angular.module('thingsboard.locale', []) | @@ -148,7 +148,14 @@ export default angular.module('thingsboard.locale', []) | ||
148 | "clear": "Clear", | 148 | "clear": "Clear", |
149 | "search": "Search alarms", | 149 | "search": "Search alarms", |
150 | "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected", | 150 | "selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected", |
151 | - "no-data": "No data to display" | 151 | + "no-data": "No data to display", |
152 | + "polling-interval": "Alarms polling interval (sec)", | ||
153 | + "polling-interval-required": "Alarms polling interval is required.", | ||
154 | + "min-polling-interval-message": "At least 1 sec polling interval is allowed.", | ||
155 | + "aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }", | ||
156 | + "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?", | ||
157 | + "clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }", | ||
158 | + "clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?" | ||
152 | }, | 159 | }, |
153 | "alias": { | 160 | "alias": { |
154 | "add": "Add alias", | 161 | "add": "Add alias", |
@@ -643,6 +650,8 @@ export default angular.module('thingsboard.locale', []) | @@ -643,6 +650,8 @@ export default angular.module('thingsboard.locale', []) | ||
643 | "no-aliases-found": "No aliases found.", | 650 | "no-aliases-found": "No aliases found.", |
644 | "no-alias-matching": "'{{alias}}' not found.", | 651 | "no-alias-matching": "'{{alias}}' not found.", |
645 | "create-new-alias": "Create a new one!", | 652 | "create-new-alias": "Create a new one!", |
653 | + "key": "Key", | ||
654 | + "key-name": "Key name", | ||
646 | "no-keys-found": "No keys found.", | 655 | "no-keys-found": "No keys found.", |
647 | "no-key-matching": "'{{key}}' not found.", | 656 | "no-key-matching": "'{{key}}' not found.", |
648 | "create-new-key": "Create a new one!", | 657 | "create-new-key": "Create a new one!", |
@@ -18,7 +18,8 @@ | @@ -18,7 +18,8 @@ | ||
18 | export default function ThingsboardMissingTranslateHandler($log, types) { | 18 | export default function ThingsboardMissingTranslateHandler($log, types) { |
19 | 19 | ||
20 | return function (translationId) { | 20 | return function (translationId) { |
21 | - if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix)) { | 21 | + if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix) && |
22 | + !translationId.startsWith(types.translate.keyLabelPrefix)) { | ||
22 | $log.warn('Translation for ' + translationId + ' doesn\'t exist'); | 23 | $log.warn('Translation for ' + translationId + ' doesn\'t exist'); |
23 | } | 24 | } |
24 | }; | 25 | }; |
@@ -19,6 +19,7 @@ import './alarms-table-widget.scss'; | @@ -19,6 +19,7 @@ import './alarms-table-widget.scss'; | ||
19 | /* eslint-disable import/no-unresolved, import/default */ | 19 | /* eslint-disable import/no-unresolved, import/default */ |
20 | 20 | ||
21 | import alarmsTableWidgetTemplate from './alarms-table-widget.tpl.html'; | 21 | import alarmsTableWidgetTemplate from './alarms-table-widget.tpl.html'; |
22 | +import alarmDetailsDialogTemplate from '../../alarm/alarm-details-dialog.tpl.html'; | ||
22 | 23 | ||
23 | /* eslint-enable import/no-unresolved, import/default */ | 24 | /* eslint-enable import/no-unresolved, import/default */ |
24 | 25 | ||
@@ -46,11 +47,12 @@ function AlarmsTableWidget() { | @@ -46,11 +47,12 @@ function AlarmsTableWidget() { | ||
46 | } | 47 | } |
47 | 48 | ||
48 | /*@ngInject*/ | 49 | /*@ngInject*/ |
49 | -function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUtil, $translate, utils, types) { | 50 | +function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $document, $translate, $q, alarmService, utils, types) { |
50 | var vm = this; | 51 | var vm = this; |
51 | 52 | ||
52 | vm.stylesInfo = {}; | 53 | vm.stylesInfo = {}; |
53 | vm.contentsInfo = {}; | 54 | vm.contentsInfo = {}; |
55 | + vm.columnWidth = {}; | ||
54 | 56 | ||
55 | vm.showData = true; | 57 | vm.showData = true; |
56 | vm.hasData = false; | 58 | vm.hasData = false; |
@@ -64,19 +66,32 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -64,19 +66,32 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
64 | 66 | ||
65 | vm.currentAlarm = null; | 67 | vm.currentAlarm = null; |
66 | 68 | ||
69 | + vm.alarmsTitle = $translate.instant('alarm.alarms'); | ||
70 | + vm.enableSelection = true; | ||
71 | + vm.enableSearch = true; | ||
72 | + vm.displayDetails = true; | ||
73 | + vm.allowAcknowledgment = true; | ||
74 | + vm.allowClear = true; | ||
75 | + vm.displayPagination = true; | ||
76 | + vm.defaultPageSize = 10; | ||
77 | + vm.defaultSortOrder = '-'+types.alarmFields.createdTime.value; | ||
78 | + | ||
67 | vm.query = { | 79 | vm.query = { |
68 | - order: '-'+types.alarmFields.createdTime.value, | ||
69 | - limit: 10, | 80 | + order: vm.defaultSortOrder, |
81 | + limit: vm.defaultPageSize, | ||
70 | page: 1, | 82 | page: 1, |
71 | search: null | 83 | search: null |
72 | }; | 84 | }; |
73 | 85 | ||
74 | - vm.alarmsTitle = $translate.instant('alarm.alarms'); | ||
75 | - | ||
76 | vm.enterFilterMode = enterFilterMode; | 86 | vm.enterFilterMode = enterFilterMode; |
77 | vm.exitFilterMode = exitFilterMode; | 87 | vm.exitFilterMode = exitFilterMode; |
78 | vm.onReorder = onReorder; | 88 | vm.onReorder = onReorder; |
79 | vm.onPaginate = onPaginate; | 89 | vm.onPaginate = onPaginate; |
90 | + vm.onRowClick = onRowClick; | ||
91 | + vm.isCurrent = isCurrent; | ||
92 | + vm.openAlarmDetails = openAlarmDetails; | ||
93 | + vm.ackAlarms = ackAlarms; | ||
94 | + vm.clearAlarms = clearAlarms; | ||
80 | 95 | ||
81 | vm.cellStyle = cellStyle; | 96 | vm.cellStyle = cellStyle; |
82 | vm.cellContent = cellContent; | 97 | vm.cellContent = cellContent; |
@@ -119,7 +134,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -119,7 +134,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
119 | $scope.$watch(function() { return $mdMedia('gt-md'); }, function(isGtMd) { | 134 | $scope.$watch(function() { return $mdMedia('gt-md'); }, function(isGtMd) { |
120 | vm.isGtMd = isGtMd; | 135 | vm.isGtMd = isGtMd; |
121 | if (vm.isGtMd) { | 136 | if (vm.isGtMd) { |
122 | - vm.limitOptions = [5, 10, 15]; | 137 | + vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3]; |
123 | } else { | 138 | } else { |
124 | vm.limitOptions = null; | 139 | vm.limitOptions = null; |
125 | } | 140 | } |
@@ -130,7 +145,33 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -130,7 +145,33 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
130 | if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) { | 145 | if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) { |
131 | vm.alarmsTitle = vm.settings.alarmsTitle; | 146 | vm.alarmsTitle = vm.settings.alarmsTitle; |
132 | } | 147 | } |
133 | - //TODO: | 148 | + vm.enableSelection = angular.isDefined(vm.settings.enableSelection) ? vm.settings.enableSelection : true; |
149 | + vm.enableSearch = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true; | ||
150 | + vm.displayDetails = angular.isDefined(vm.settings.displayDetails) ? vm.settings.displayDetails : true; | ||
151 | + vm.allowAcknowledgment = angular.isDefined(vm.settings.allowAcknowledgment) ? vm.settings.allowAcknowledgment : true; | ||
152 | + vm.allowClear = angular.isDefined(vm.settings.allowClear) ? vm.settings.allowClear : true; | ||
153 | + if (!vm.allowAcknowledgment && !vm.allowClear) { | ||
154 | + vm.enableSelection = false; | ||
155 | + } | ||
156 | + | ||
157 | + vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true; | ||
158 | + | ||
159 | + var pageSize = vm.settings.defaultPageSize; | ||
160 | + if (angular.isDefined(pageSize) && Number.isInteger(pageSize) && pageSize > 0) { | ||
161 | + vm.defaultPageSize = pageSize; | ||
162 | + } | ||
163 | + | ||
164 | + if (vm.settings.defaultSortOrder && vm.settings.defaultSortOrder.length) { | ||
165 | + vm.defaultSortOrder = vm.settings.defaultSortOrder; | ||
166 | + } | ||
167 | + | ||
168 | + vm.query.order = vm.defaultSortOrder; | ||
169 | + vm.query.limit = vm.defaultPageSize; | ||
170 | + if (vm.isGtMd) { | ||
171 | + vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3]; | ||
172 | + } else { | ||
173 | + vm.limitOptions = null; | ||
174 | + } | ||
134 | 175 | ||
135 | var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)'; | 176 | var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)'; |
136 | var defaultColor = tinycolor(origColor); | 177 | var defaultColor = tinycolor(origColor); |
@@ -207,6 +248,115 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -207,6 +248,115 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
207 | updateAlarms(); | 248 | updateAlarms(); |
208 | } | 249 | } |
209 | 250 | ||
251 | + function onRowClick($event, alarm) { | ||
252 | + if (vm.currentAlarm != alarm) { | ||
253 | + vm.currentAlarm = alarm; | ||
254 | + } | ||
255 | + } | ||
256 | + | ||
257 | + function isCurrent(alarm) { | ||
258 | + return (vm.currentAlarm && alarm && vm.currentAlarm.id && alarm.id) && | ||
259 | + (vm.currentAlarm.id.id === alarm.id.id); | ||
260 | + } | ||
261 | + | ||
262 | + function openAlarmDetails($event, alarm) { | ||
263 | + if (alarm && alarm.id) { | ||
264 | + var onShowingCallback = { | ||
265 | + onShowing: function(){} | ||
266 | + } | ||
267 | + $mdDialog.show({ | ||
268 | + controller: 'AlarmDetailsDialogController', | ||
269 | + controllerAs: 'vm', | ||
270 | + templateUrl: alarmDetailsDialogTemplate, | ||
271 | + locals: { | ||
272 | + alarmId: alarm.id.id, | ||
273 | + allowAcknowledgment: vm.allowAcknowledgment, | ||
274 | + allowClear: vm.allowClear, | ||
275 | + showingCallback: onShowingCallback | ||
276 | + }, | ||
277 | + parent: angular.element($document[0].body), | ||
278 | + targetEvent: $event, | ||
279 | + fullscreen: true, | ||
280 | + skipHide: true, | ||
281 | + onShowing: function(scope, element) { | ||
282 | + onShowingCallback.onShowing(scope, element); | ||
283 | + } | ||
284 | + }).then(function (alarm) { | ||
285 | + if (alarm) { | ||
286 | + vm.subscription.update(); | ||
287 | + } | ||
288 | + }); | ||
289 | + | ||
290 | + } | ||
291 | + } | ||
292 | + | ||
293 | + function ackAlarms($event) { | ||
294 | + if ($event) { | ||
295 | + $event.stopPropagation(); | ||
296 | + } | ||
297 | + if (vm.selectedAlarms && vm.selectedAlarms.length > 0) { | ||
298 | + var title = $translate.instant('alarm.aknowledge-alarms-title', {count: vm.selectedAlarms.length}, 'messageformat'); | ||
299 | + var content = $translate.instant('alarm.aknowledge-alarms-text', {count: vm.selectedAlarms.length}, 'messageformat'); | ||
300 | + var confirm = $mdDialog.confirm() | ||
301 | + .targetEvent($event) | ||
302 | + .title(title) | ||
303 | + .htmlContent(content) | ||
304 | + .ariaLabel(title) | ||
305 | + .cancel($translate.instant('action.no')) | ||
306 | + .ok($translate.instant('action.yes')); | ||
307 | + $mdDialog.show(confirm).then(function () { | ||
308 | + var tasks = []; | ||
309 | + for (var i=0;i<vm.selectedAlarms.length;i++) { | ||
310 | + var alarm = vm.selectedAlarms[i]; | ||
311 | + if (alarm.id) { | ||
312 | + tasks.push(alarmService.ackAlarm(alarm.id.id)); | ||
313 | + } | ||
314 | + } | ||
315 | + if (tasks.length) { | ||
316 | + $q.all(tasks).then(function () { | ||
317 | + vm.selectedAlarms = []; | ||
318 | + vm.subscription.update(); | ||
319 | + }); | ||
320 | + } | ||
321 | + | ||
322 | + }); | ||
323 | + } | ||
324 | + } | ||
325 | + | ||
326 | + function clearAlarms($event) { | ||
327 | + if ($event) { | ||
328 | + $event.stopPropagation(); | ||
329 | + } | ||
330 | + if (vm.selectedAlarms && vm.selectedAlarms.length > 0) { | ||
331 | + var title = $translate.instant('alarm.clear-alarms-title', {count: vm.selectedAlarms.length}, 'messageformat'); | ||
332 | + var content = $translate.instant('alarm.clear-alarms-text', {count: vm.selectedAlarms.length}, 'messageformat'); | ||
333 | + var confirm = $mdDialog.confirm() | ||
334 | + .targetEvent($event) | ||
335 | + .title(title) | ||
336 | + .htmlContent(content) | ||
337 | + .ariaLabel(title) | ||
338 | + .cancel($translate.instant('action.no')) | ||
339 | + .ok($translate.instant('action.yes')); | ||
340 | + $mdDialog.show(confirm).then(function () { | ||
341 | + var tasks = []; | ||
342 | + for (var i=0;i<vm.selectedAlarms.length;i++) { | ||
343 | + var alarm = vm.selectedAlarms[i]; | ||
344 | + if (alarm.id) { | ||
345 | + tasks.push(alarmService.clearAlarm(alarm.id.id)); | ||
346 | + } | ||
347 | + } | ||
348 | + if (tasks.length) { | ||
349 | + $q.all(tasks).then(function () { | ||
350 | + vm.selectedAlarms = []; | ||
351 | + vm.subscription.update(); | ||
352 | + }); | ||
353 | + } | ||
354 | + | ||
355 | + }); | ||
356 | + } | ||
357 | + } | ||
358 | + | ||
359 | + | ||
210 | function updateAlarms(preserveSelections) { | 360 | function updateAlarms(preserveSelections) { |
211 | if (!preserveSelections) { | 361 | if (!preserveSelections) { |
212 | vm.selectedAlarms = []; | 362 | vm.selectedAlarms = []; |
@@ -216,8 +366,14 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -216,8 +366,14 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
216 | result = $filter('filter')(result, {$: vm.query.search}); | 366 | result = $filter('filter')(result, {$: vm.query.search}); |
217 | } | 367 | } |
218 | vm.alarmsCount = result.length; | 368 | vm.alarmsCount = result.length; |
219 | - var startIndex = vm.query.limit * (vm.query.page - 1); | ||
220 | - vm.alarms = result.slice(startIndex, startIndex + vm.query.limit); | 369 | + |
370 | + if (vm.displayPagination) { | ||
371 | + var startIndex = vm.query.limit * (vm.query.page - 1); | ||
372 | + vm.alarms = result.slice(startIndex, startIndex + vm.query.limit); | ||
373 | + } else { | ||
374 | + vm.alarms = result; | ||
375 | + } | ||
376 | + | ||
221 | if (preserveSelections) { | 377 | if (preserveSelections) { |
222 | var newSelectedAlarms = []; | 378 | var newSelectedAlarms = []; |
223 | if (vm.selectedAlarms && vm.selectedAlarms.length) { | 379 | if (vm.selectedAlarms && vm.selectedAlarms.length) { |
@@ -251,6 +407,10 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -251,6 +407,10 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
251 | style = defaultStyle(key, value); | 407 | style = defaultStyle(key, value); |
252 | } | 408 | } |
253 | } | 409 | } |
410 | + if (!style.width) { | ||
411 | + var columnWidth = vm.columnWidth[key.label]; | ||
412 | + style.width = columnWidth; | ||
413 | + } | ||
254 | return style; | 414 | return style; |
255 | } | 415 | } |
256 | 416 | ||
@@ -295,7 +455,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -295,7 +455,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
295 | return value; | 455 | return value; |
296 | } | 456 | } |
297 | } else { | 457 | } else { |
298 | - return ''; | 458 | + return value; |
299 | } | 459 | } |
300 | } else { | 460 | } else { |
301 | return ''; | 461 | return ''; |
@@ -313,6 +473,8 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -313,6 +473,8 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
313 | } else { | 473 | } else { |
314 | return {}; | 474 | return {}; |
315 | } | 475 | } |
476 | + } else { | ||
477 | + return {}; | ||
316 | } | 478 | } |
317 | } else { | 479 | } else { |
318 | return {}; | 480 | return {}; |
@@ -340,9 +502,19 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -340,9 +502,19 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
340 | 502 | ||
341 | vm.stylesInfo = {}; | 503 | vm.stylesInfo = {}; |
342 | vm.contentsInfo = {}; | 504 | vm.contentsInfo = {}; |
505 | + vm.columnWidth = {}; | ||
343 | 506 | ||
344 | for (var d = 0; d < vm.alarmSource.dataKeys.length; d++ ) { | 507 | for (var d = 0; d < vm.alarmSource.dataKeys.length; d++ ) { |
345 | var dataKey = vm.alarmSource.dataKeys[d]; | 508 | var dataKey = vm.alarmSource.dataKeys[d]; |
509 | + | ||
510 | + var translationId = types.translate.keyLabelPrefix + dataKey.label; | ||
511 | + var translation = $translate.instant(translationId); | ||
512 | + if (translation != translationId) { | ||
513 | + dataKey.title = translation; | ||
514 | + } else { | ||
515 | + dataKey.title = dataKey.label; | ||
516 | + } | ||
517 | + | ||
346 | var keySettings = dataKey.settings; | 518 | var keySettings = dataKey.settings; |
347 | 519 | ||
348 | var cellStyleFunction = null; | 520 | var cellStyleFunction = null; |
@@ -384,6 +556,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | @@ -384,6 +556,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti | ||
384 | useCellContentFunction: useCellContentFunction, | 556 | useCellContentFunction: useCellContentFunction, |
385 | cellContentFunction: cellContentFunction | 557 | cellContentFunction: cellContentFunction |
386 | }; | 558 | }; |
559 | + | ||
560 | + var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px'; | ||
561 | + vm.columnWidth[dataKey.label] = columnWidth; | ||
387 | } | 562 | } |
388 | } | 563 | } |
389 | 564 |
@@ -17,14 +17,14 @@ | @@ -17,14 +17,14 @@ | ||
17 | .tb-alarms-table { | 17 | .tb-alarms-table { |
18 | margin-top: 15px; | 18 | margin-top: 15px; |
19 | &.tb-data-table { | 19 | &.tb-data-table { |
20 | - table.md-table { | 20 | + table.md-table, table.md-table.md-row-select { |
21 | tbody { | 21 | tbody { |
22 | tr { | 22 | tr { |
23 | td { | 23 | td { |
24 | - &.ag-action-cell { | ||
25 | - min-width: 40px; | ||
26 | - max-width: 40px; | ||
27 | - width: 40px; | 24 | + &.tb-action-cell { |
25 | + min-width: 36px; | ||
26 | + max-width: 36px; | ||
27 | + width: 36px; | ||
28 | } | 28 | } |
29 | } | 29 | } |
30 | } | 30 | } |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | <div class="md-toolbar-tools"> | 22 | <div class="md-toolbar-tools"> |
23 | <span>{{ vm.alarmsTitle }}</span> | 23 | <span>{{ vm.alarmsTitle }}</span> |
24 | <span flex></span> | 24 | <span flex></span> |
25 | - <md-button class="md-icon-button" ng-click="vm.enterFilterMode()"> | 25 | + <md-button ng-if="vm.enableSearch" class="md-icon-button" ng-click="vm.enterFilterMode()"> |
26 | <md-icon>search</md-icon> | 26 | <md-icon>search</md-icon> |
27 | <md-tooltip md-direction="top"> | 27 | <md-tooltip md-direction="top"> |
28 | {{ 'action.search' | translate }} | 28 | {{ 'action.search' | translate }} |
@@ -57,13 +57,13 @@ | @@ -57,13 +57,13 @@ | ||
57 | translate-values="{count: vm.selectedAlarms.length}" | 57 | translate-values="{count: vm.selectedAlarms.length}" |
58 | translate-interpolation="messageformat"></span> | 58 | translate-interpolation="messageformat"></span> |
59 | <span flex></span> | 59 | <span flex></span> |
60 | - <md-button class="md-icon-button" ng-click="vm.ackAlarms($event)"> | 60 | + <md-button ng-if="vm.allowAcknowledgment" class="md-icon-button" ng-click="vm.ackAlarms($event)"> |
61 | <md-icon>done</md-icon> | 61 | <md-icon>done</md-icon> |
62 | <md-tooltip md-direction="top"> | 62 | <md-tooltip md-direction="top"> |
63 | {{ 'alarm.acknowledge' | translate }} | 63 | {{ 'alarm.acknowledge' | translate }} |
64 | </md-tooltip> | 64 | </md-tooltip> |
65 | </md-button> | 65 | </md-button> |
66 | - <md-button class="md-icon-button" ng-click="vm.clearAlarms($event)"> | 66 | + <md-button ng-if="vm.allowClear" class="md-icon-button" ng-click="vm.clearAlarms($event)"> |
67 | <md-icon>clear</md-icon> | 67 | <md-icon>clear</md-icon> |
68 | <md-tooltip md-direction="top"> | 68 | <md-tooltip md-direction="top"> |
69 | {{ 'alarm.clear' | translate }} | 69 | {{ 'alarm.clear' | translate }} |
@@ -72,22 +72,22 @@ | @@ -72,22 +72,22 @@ | ||
72 | </div> | 72 | </div> |
73 | </md-toolbar> | 73 | </md-toolbar> |
74 | <md-table-container flex> | 74 | <md-table-container flex> |
75 | - <table md-table md-row-select multiple="" ng-model="vm.selectedAlarms"> | 75 | + <table md-table md-row-select="vm.enableSelection" multiple="" ng-model="vm.selectedAlarms"> |
76 | <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> | 76 | <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> |
77 | <tr md-row> | 77 | <tr md-row> |
78 | - <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.label }}</span></th> | ||
79 | - <th md-column><span> </span></th> | 78 | + <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th> |
79 | + <th md-column ng-if="vm.displayDetails"><span> </span></th> | ||
80 | </tr> | 80 | </tr> |
81 | </thead> | 81 | </thead> |
82 | <tbody md-body> | 82 | <tbody md-body> |
83 | <tr ng-show="vm.alarms.length" md-row md-select="alarm" | 83 | <tr ng-show="vm.alarms.length" md-row md-select="alarm" |
84 | md-select-id="id.id" md-auto-select="false" ng-repeat="alarm in vm.alarms" | 84 | md-select-id="id.id" md-auto-select="false" ng-repeat="alarm in vm.alarms" |
85 | ng-click="vm.onRowClick($event, alarm)" ng-class="{'tb-current': vm.isCurrent(alarm)}"> | 85 | ng-click="vm.onRowClick($event, alarm)" ng-class="{'tb-current': vm.isCurrent(alarm)}"> |
86 | - <td md-cell ng-repeat="key in vm.alarmSource.dataKeys" | 86 | + <td md-cell flex ng-repeat="key in vm.alarmSource.dataKeys" |
87 | ng-style="vm.cellStyle(alarm, key)" | 87 | ng-style="vm.cellStyle(alarm, key)" |
88 | ng-bind-html="vm.cellContent(alarm, key)"> | 88 | ng-bind-html="vm.cellContent(alarm, key)"> |
89 | </td> | 89 | </td> |
90 | - <td md-cell class="tb-action-cell"> | 90 | + <td md-cell ng-if="vm.displayDetails" class="tb-action-cell"> |
91 | <md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}" | 91 | <md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}" |
92 | ng-click="vm.openAlarmDetails($event, alarm)"> | 92 | ng-click="vm.openAlarmDetails($event, alarm)"> |
93 | <md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon> | 93 | <md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon> |
@@ -104,7 +104,7 @@ | @@ -104,7 +104,7 @@ | ||
104 | layout-align="center center" | 104 | layout-align="center center" |
105 | class="no-data-found" translate>alarm.no-alarms-prompt</span> | 105 | class="no-data-found" translate>alarm.no-alarms-prompt</span> |
106 | </md-table-container> | 106 | </md-table-container> |
107 | - <md-table-pagination md-boundary-links md-limit="vm.query.limit" md-limit-options="vm.limitOptions" | 107 | + <md-table-pagination ng-if="vm.displayPagination" md-boundary-links md-limit="vm.query.limit" md-limit-options="vm.limitOptions" |
108 | md-page="vm.query.page" md-total="{{vm.alarmsCount}}" | 108 | md-page="vm.query.page" md-total="{{vm.alarmsCount}}" |
109 | md-on-paginate="vm.onPaginate" md-page-select="vm.isGtMd"> | 109 | md-on-paginate="vm.onPaginate" md-page-select="vm.isGtMd"> |
110 | </md-table-pagination> | 110 | </md-table-pagination> |
@@ -281,7 +281,55 @@ pre.tb-highlight { | @@ -281,7 +281,55 @@ pre.tb-highlight { | ||
281 | display: flex; | 281 | display: flex; |
282 | } | 282 | } |
283 | table.md-table { | 283 | table.md-table { |
284 | + &.md-row-select td.md-cell, | ||
285 | + &.md-row-select th.md-column { | ||
286 | + &:first-child { | ||
287 | + width: 20px; | ||
288 | + padding: 0 0 0 12px; | ||
289 | + } | ||
290 | + | ||
291 | + &:nth-child(2) { | ||
292 | + padding: 0 12px; | ||
293 | + } | ||
294 | + | ||
295 | + &:nth-child(n+3):nth-last-child(n+2) { | ||
296 | + padding: 0 28px 0 0; | ||
297 | + } | ||
298 | + } | ||
299 | + | ||
300 | + &:not(.md-row-select) td.md-cell, | ||
301 | + &:not(.md-row-select) th.md-column { | ||
302 | + &:first-child { | ||
303 | + padding: 0 12px; | ||
304 | + } | ||
305 | + | ||
306 | + &:nth-child(n+2):nth-last-child(n+2) { | ||
307 | + padding: 0 28px 0 0; | ||
308 | + } | ||
309 | + } | ||
310 | + | ||
311 | + td.md-cell, | ||
312 | + th.md-column { | ||
313 | + | ||
314 | + &:last-child { | ||
315 | + padding: 0 12px 0 0; | ||
316 | + } | ||
317 | + | ||
318 | + } | ||
319 | + } | ||
320 | + | ||
321 | + table.md-table, table.md-table.md-row-select { | ||
284 | tbody { | 322 | tbody { |
323 | + &.md-body { | ||
324 | + tr { | ||
325 | + &.md-row:not([disabled]) { | ||
326 | + outline: none; | ||
327 | + &.tb-current, &.tb-current:hover{ | ||
328 | + background-color: #dddddd !important; | ||
329 | + } | ||
330 | + } | ||
331 | + } | ||
332 | + } | ||
285 | tr { | 333 | tr { |
286 | td { | 334 | td { |
287 | &.tb-action-cell { | 335 | &.tb-action-cell { |