+ fxLayoutAlign.gt-sm="end center" fxLayoutAlign="space-between center" fxLayoutGap.lt-lg="6px" fxLayoutGap.gt-md="12px">
-
-
-
-
+
+
+
+
+
+
+
+
+ fxLayoutAlign.gt-sm="start center" fxLayoutAlign="space-between center" fxLayoutGap.lt-lg="6px" fxLayoutGap.gt-md="12px">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -117,24 +245,6 @@
tooltipPosition="below"
[aliasController]="dashboardCtx.aliasController">
-
-
-
{{ translatedDashboardTitle }}
-
+
dashboard.title
-
+
@@ -239,7 +240,8 @@
[disabled]="isLoading$ | async"
[fxShow]="actionDescriptor.isEnabled(entity)"
(click)="actionDescriptor.onAction($event, entity)">
-
+
{{actionDescriptor.icon}}
{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}
diff --git a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.scss b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.scss
index 72f9efe407..62d28d29ea 100644
--- a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.scss
+++ b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.scss
@@ -16,6 +16,9 @@
@import "../../../../../scss/constants";
:host {
+ &.tb-hide {
+ display: none;
+ }
section.tb-filters-edit {
min-height: 32px;
padding: 0 6px;
diff --git a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts
index 69099eb8f8..bae93af0e3 100644
--- a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts
+++ b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts
@@ -15,7 +15,7 @@
///
import {
- Component,
+ Component, HostBinding,
Injector,
Input,
OnDestroy,
@@ -48,6 +48,9 @@ import { MatDialog } from '@angular/material/dialog';
})
export class FiltersEditComponent implements OnInit, OnDestroy {
+ @HostBinding('class')
+ filtersEditClass = 'tb-hide';
+
aliasControllerValue: IAliasController;
@Input()
@@ -174,11 +177,13 @@ export class FiltersEditComponent implements OnInit, OnDestroy {
const allFilters = this.aliasController.getFilters();
this.filtersInfo = {};
this.hasEditableFilters = false;
+ this.filtersEditClass = 'tb-hide';
for (const filterId of Object.keys(allFilters)) {
const filterInfo = this.aliasController.getFilterInfo(filterId);
if (filterInfo && isFilterEditable(filterInfo)) {
this.filtersInfo[filterId] = deepClone(filterInfo);
this.hasEditableFilters = true;
+ this.filtersEditClass = '';
}
}
}
diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
index e7caba0e3c..e9fb01c54a 100644
--- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
+++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
@@ -58,6 +58,7 @@ export interface CellActionDescriptor> {
nameFunction?: (entity: T) => string;
icon?: string;
mdiIcon?: string;
+ mdiIconFunction?: (entity: T) => string;
style?: any;
isEnabled: (entity: T) => boolean;
onAction: ($event: MouseEvent, entity: T) => any;
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
index 5690052001..fc48392e8c 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
+++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
@@ -44,6 +44,9 @@
{{ 'notification.rule-name-required' | translate }}
+
+ {{ 'notification.rule-enable' | translate }}
+
notification.trigger.trigger
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts
index ea3c59992c..93b22911f3 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts
@@ -176,6 +176,7 @@ export class RuleNotificationDialogComponent extends
this.ruleNotificationForm = this.fb.group({
name: [null, Validators.required],
+ enabled: [true, Validators.required],
templateId: [null, Validators.required],
triggerType: [this.isSysAdmin() ? TriggerType.ENTITIES_LIMIT : TriggerType.ALARM, Validators.required],
recipientsConfig: this.fb.group({
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
index 4d8962150f..86f8120ae0 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
@@ -92,6 +92,15 @@ export class RuleTableConfigResolver implements Resolve> {
return [{
+ name: '',
+ nameFunction: (entity) =>
+ this.translate.instant(entity.enabled ? 'notification.rule-disable' : 'notification.rule-enable'),
+ mdiIcon: 'mdi:toggle-switch',
+ isEnabled: () => true,
+ mdiIconFunction: (entity) => entity.enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline',
+ onAction: ($event, entity) => this.toggleEnableMode($event, entity)
+ },
+ {
name: this.translate.instant('notification.copy-rule'),
icon: 'content_copy',
isEnabled: () => true,
@@ -128,4 +137,21 @@ export class RuleTableConfigResolver implements Resolve {
+ rule.enabled = notificationRule.enabled;
+ this.config.getTable().detectChanges();
+ });
+ }
}
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.html b/ui-ngx/src/app/shared/components/time/timewindow.component.html
index a9d5ce0eaa..91e5dd2f57 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.html
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.html
@@ -15,13 +15,13 @@
limitations under the License.
-->
-
+
query_builder
-
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.scss b/ui-ngx/src/app/shared/components/time/timewindow.component.scss
index 6e9e837283..695362197d 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.scss
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.scss
@@ -14,7 +14,7 @@
* limitations under the License.
*/
:host {
- min-width: 52px;
+ min-width: 48px;
margin: 8px 0;
max-width: 100%;
.mdc-button {
@@ -22,7 +22,7 @@
}
section.tb-timewindow {
min-height: 32px;
- padding: 0 6px;
+ padding: 0 8px;
span {
overflow: hidden;
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts
index 4532b7eeae..6a723c73bb 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts
@@ -45,8 +45,6 @@ import {
TimewindowPanelComponent,
TimewindowPanelData
} from '@shared/components/time/timewindow-panel.component';
-import { MediaBreakpoints } from '@shared/models/constants';
-import { BreakpointObserver } from '@angular/cdk/layout';
import { TimeService } from '@core/services/time.service';
import { TooltipPosition } from '@angular/material/tooltip';
import { deepClone, isDefinedAndNotNull } from '@core/utils';
@@ -135,17 +133,6 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
return this.timezoneValue;
}
- isToolbarValue = false;
-
- @Input()
- set isToolbar(val) {
- this.isToolbarValue = coerceBooleanProperty(val);
- }
-
- get isToolbar() {
- return this.isToolbarValue;
- }
-
asButtonValue = false;
@Input()
@@ -161,6 +148,18 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
@coerceBoolean()
strokedButton = false;
+ @Input()
+ @coerceBoolean()
+ flatButton = false;
+
+ @Input()
+ @coerceBoolean()
+ displayTimewindowValue = true;
+
+ @Input()
+ @coerceBoolean()
+ hideLabel = false;
+
isEditValue = false;
@Input()
@@ -194,8 +193,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
private datePipe: DatePipe,
private cd: ChangeDetectorRef,
private nativeElement: ElementRef,
- public viewContainerRef: ViewContainerRef,
- public breakpointObserver: BreakpointObserver) {
+ public viewContainerRef: ViewContainerRef) {
}
ngOnInit(): void {
@@ -300,6 +298,10 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
this.propagateChange(cloneSelectedTimewindow(this.innerValue));
}
+ displayValue(): string {
+ return this.displayTimewindowValue ? this.innerValue?.displayValue : this.translate.instant('timewindow.timewindow');
+ }
+
updateDisplayValue() {
if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) {
this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - ';
@@ -334,10 +336,6 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces
this.cd.detectChanges();
}
- hideLabel() {
- return this.isToolbar && !this.breakpointObserver.isMatched(MediaBreakpoints['gt-md']);
- }
-
private isTimewindowDisabled(): boolean {
return this.disabled ||
(!this.isEdit && (!this.innerValue || this.innerValue.hideInterval &&
diff --git a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js
index c309eb38aa..bc22e806c3 100644
--- a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js
+++ b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js
@@ -5026,6 +5026,21 @@ var JSHINT = (function() {
return that;
}, 20);
}
+ function nullSafeProperty(s) {
+ console.log("test " + s);
+ symbol(s, 20).exps = true;
+ return infix(s, function(context, left, that) {
+ if (state.option.bitwise) {
+ warning("W016", that, that.id);
+ }
+
+ checkLeftSideAssign(context, left, that);
+
+ that.right = expression(context, 10);
+
+ return that;
+ }, 20);
+ }
function suffix(s) {
var x = symbol(s, 150);
@@ -5539,6 +5554,9 @@ var JSHINT = (function() {
bitwiseassignop("<<=");
bitwiseassignop(">>=");
bitwiseassignop(">>>=");
+
+ nullSafeProperty(".?");
+
infix(",", function(context, left, that) {
if (state.option.nocomma) {
warning("W127", that);
@@ -9450,6 +9468,12 @@ Lexer.prototype = {
switch (ch1) {
case ".":
+ if (ch1 === "." && this.peek(1) === "?") {
+ return {
+ type: Token.Punctuator,
+ value: ".?"
+ };
+ }
if ((/^[0-9]$/).test(this.peek(1))) {
return null;
}
diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts
index 0acec83c47..59af9295e1 100644
--- a/ui-ngx/src/app/shared/models/notification.models.ts
+++ b/ui-ngx/src/app/shared/models/notification.models.ts
@@ -108,6 +108,7 @@ export interface SlackConversation {
export interface NotificationRule extends Omit, 'label'>, ExportableEntity {
tenantId: TenantId;
+ enabled: boolean;
templateId: NotificationTemplateId;
triggerType: TriggerType;
triggerConfig: NotificationRuleTriggerConfig;
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 6b3110771c..a9cb184d19 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -930,6 +930,7 @@
"no-dashboards-text": "No dashboards found",
"no-widgets": "No widgets configured",
"add-widget": "Add new widget",
+ "add-widget-button-text": "Add widget",
"title": "Title",
"image": "Dashboard image",
"mobile-app-settings": "Mobile application settings",
@@ -1058,6 +1059,7 @@
"public-link-copied-message": "Dashboard public link has been copied to clipboard",
"manage-states": "Manage dashboard states",
"states": "Dashboard states",
+ "states-short": "States",
"search-states": "Search dashboard states",
"selected-states": "{ count, plural, =1 {1 dashboard state} other {# dashboard states} } selected",
"edit-state": "Edit dashboard state",
@@ -1086,7 +1088,8 @@
"unassign-dashboards-from-edge-text": "After the confirmation all selected dashboards will be unassigned and won't be accessible by the edge.",
"assign-dashboard-to-edge": "Assign Dashboard(s) To Edge",
"assign-dashboard-to-edge-text": "Please select the dashboards to assign to the edge",
- "non-existent-dashboard-state-error": "Dashboard state with id \"{{ stateId }}\" is not found"
+ "non-existent-dashboard-state-error": "Dashboard state with id \"{{ stateId }}\" is not found",
+ "edit-mode": "Edit mode"
},
"datakey": {
"settings": "Settings",
@@ -1937,6 +1940,7 @@
"entities-count": "Entities count",
"alarms-count": "Alarms count",
"aliases": "Entity aliases",
+ "aliases-short": "Aliases",
"entity-alias": "Entity alias",
"unable-delete-entity-alias-title": "Unable to delete entity alias",
"unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}",
@@ -2704,6 +2708,7 @@
},
"layout": {
"layout": "Layout",
+ "layouts": "Layouts",
"manage": "Manage layouts",
"settings": "Layout settings",
"color": "Color",
@@ -2960,6 +2965,8 @@
"rule-engine-filter": "Rule engine filter",
"rule-name": "Rule name",
"rule-name-required": "Name is required",
+ "rule-disable": "Disable notification rule",
+ "rule-enable": "Enable notification rule",
"rule-node-filter": "Rule node filter",
"rules": "Rules",
"notification-rules": "Notifications / Rules",
@@ -3758,6 +3765,7 @@
"days": "Days"
},
"timewindow": {
+ "timewindow": "Timewindow",
"years": "{ years, plural, =1 { year } other {# years } }",
"months": "{ months, plural, =1 { month } other {# months } }",
"weeks": "{ weeks, plural, =1 { week } other {# weeks } }",
diff --git a/ui-ngx/src/theme-overwrites.scss b/ui-ngx/src/theme-overwrites.scss
index 394e4f0f12..6b4995664e 100644
--- a/ui-ngx/src/theme-overwrites.scss
+++ b/ui-ngx/src/theme-overwrites.scss
@@ -17,6 +17,7 @@
@mixin theme-overwrites($primary, $config-or-theme) {
@include _mat-form-field-overwrites($primary, $config-or-theme);
+ @include _mat-button-overwrites($primary, $config-or-theme);
}
@mixin _mat-form-field-overwrites($primary, $config-or-theme) {
@@ -52,3 +53,14 @@
}
}
}
+
+@mixin _mat-button-overwrites($primary, $config-or-theme) {
+ .mat-toolbar {
+ .mat-mdc-outlined-button {
+ --mdc-outlined-button-outline-color: inherit;
+ &:not(:disabled) {
+ border-color: var(--mdc-outlined-button-outline-color, inherit);
+ }
+ }
+ }
+}