From 1b1f3b4d7b52f8a9cc62181006a6e7938a17cca4 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 23 Jan 2023 14:54:15 +0200 Subject: [PATCH] UI: Add rule table --- .../notification-center.component.html | 6 +- .../notification-center.module.ts | 6 +- .../notification-table.component.ts | 7 + .../notification-table/rule-table-config.ts | 120 ++++++++++++++++ .../rule-notification-dialog.component.html | 132 ++++++++++++++++++ .../rule-notification-dialog.component.scss | 29 ++++ .../rule-notification-dialog.component.ts | 109 +++++++++++++++ .../rule-table-header.component.html | 24 ++++ .../rule-table-header.component.scss | 18 +++ .../rule-table/rule-table-header.component.ts | 38 +++++ .../app/shared/models/notification.models.ts | 3 +- .../assets/locale/locale.constant-en_US.json | 13 +- 12 files changed, 499 insertions(+), 6 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/notification-table/rule-table-config.ts create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.ts diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html index bd7925d7a1..eaa9a183a0 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html @@ -41,9 +41,9 @@ - - Content 1 - Loaded - + + diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts index 59ce9cc77e..fd8d1a6723 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts @@ -42,6 +42,8 @@ import { TemplateAutocompleteComponent } from '@home/pages/notification-center/template-table/template-autocomplete.component'; import { InboxTableHeaderComponent } from '@home/pages/notification-center/inbox-table/inbox-table-header.component'; +import { RuleTableHeaderComponent } from '@home/pages/notification-center/rule-table/rule-table-header.component'; +import { RuleNotificationDialogComponent } from '@home/pages/notification-center/rule-table/rule-notification-dialog.component'; @NgModule({ declarations: [ @@ -53,7 +55,9 @@ import { InboxTableHeaderComponent } from '@home/pages/notification-center/inbox TemplateTableHeaderComponent, NotificationTableComponent, TemplateAutocompleteComponent, - InboxTableHeaderComponent + InboxTableHeaderComponent, + RuleTableHeaderComponent, + RuleNotificationDialogComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts index a07b5dbb46..10c3134459 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts @@ -26,6 +26,7 @@ import { InboxTableConfig } from '@home/pages/notification-center/notification-t import { DatePipe } from '@angular/common'; import { TemplateTableConfig } from '@home/pages/notification-center/notification-table/template-table-config'; import { RequestTableConfig } from '@home/pages/notification-center/notification-table/request-table-config'; +import { RuleTableConfig } from '@home/pages/notification-center/notification-table/rule-table-config'; @Component({ selector: 'tb-notification-table', @@ -85,6 +86,12 @@ export class NotificationTableComponent implements OnInit { this.dialog, this.datePipe ); + case EntityType.NOTIFICATION_RULE: + return new RuleTableConfig( + this.notificationService, + this.translate, + this.dialog + ); } } } diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/rule-table-config.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/rule-table-config.ts new file mode 100644 index 0000000000..732fa8e16e --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-table/rule-table-config.ts @@ -0,0 +1,120 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + CellActionDescriptor, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { EntityTypeResource } from '@shared/models/entity-type.models'; +import { Direction } from '@shared/models/page/sort-order'; +import { + NotificationRule, + NotificationTarget, + NotificationTargetConfigTypeTranslateMap +} from '@shared/models/notification.models'; +import { NotificationService } from '@core/http/notification.service'; +import { TranslateService } from '@ngx-translate/core'; +import { + TargetNotificationDialogComponent, + TargetsNotificationDialogData +} from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; +import { MatDialog } from '@angular/material/dialog'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { + TargetTableHeaderComponent +} from '@home/pages/notification-center/targets-table/target-table-header.component'; +import { RuleTableHeaderComponent } from '@home/pages/notification-center/rule-table/rule-table-header.component'; +import { + RuleNotificationDialogComponent, + RuleNotificationDialogData +} from '@home/pages/notification-center/rule-table/rule-notification-dialog.component'; + +export class RuleTableConfig extends EntityTableConfig { + + constructor(private notificationService: NotificationService, + private translate: TranslateService, + private dialog: MatDialog) { + super(); + this.loadDataOnInit = false; + this.entityTranslations = { + noEntities: 'notification.no-rules-notification', + search: 'notification.search-rules' + }; + this.entityResources = {} as EntityTypeResource; + + this.entitiesFetchFunction = pageLink => this.notificationService.getNotificationRules(pageLink); + + this.deleteEntityTitle = rule => this.translate.instant('notification.delete-rule-title', {ruleName: rule.name}); + this.deleteEntityContent = () => this.translate.instant('notification.delete-rule-text'); + this.deleteEntity = id => this.notificationService.deleteNotificationRule(id.id); + + this.cellActionDescriptors = this.configureCellActions(); + + this.headerComponent = RuleTableHeaderComponent; + this.onEntityAction = action => this.onTargetAction(action); + + this.defaultSortOrder = {property: 'name', direction: Direction.ASC}; + + this.columns.push( + new EntityTableColumn('name', 'notification.rule-name', '30%'), + new EntityTableColumn('templateId', 'notification.template', '30%', + (rule) => `${rule.templateId}`, + () => ({}), false), + new EntityTableColumn('configuration.description', 'notification.description', '40%', + (rule) => rule.configuration.description || '', + () => ({}), false) + ); + } + + private configureCellActions(): Array> { + return [{ + name: this.translate.instant('device.make-public'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.editTarget($event, entity) + }]; + } + + private editTarget($event: Event, rule: NotificationRule, isAdd = false) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(RuleNotificationDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + isAdd, + rule + } + }).afterClosed() + .subscribe((res) => { + if (res) { + this.updateData(); + } + }); + } + + private onTargetAction(action: EntityAction): boolean { + switch (action.action) { + case 'add': + this.editTarget(action.event, action.entity, true); + return true; + } + return false; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html new file mode 100644 index 0000000000..a51900720f --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html @@ -0,0 +1,132 @@ + +
+ +

{{ (isAdd ? 'notification.add-notification-target' : 'notification.edit-notification-target') | translate }}

+ + +
+ + +
+
+ + notification.rule-name + + + {{ 'notification.rule-name-required' | translate }} + + + + +
+ +
+ notification.hierarchy-of-receiving +
+
+ notification.rule + notification.recipient +
+
+
+
+
+
+ First recipient + After +
+
+ After + + +
+ + +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + notification.description + + +
+
+ +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss new file mode 100644 index 0000000000..07d9023294 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:host { + .fields-group { + padding: 0 16px 8px; + margin-bottom: 10px; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + .fields-group-title { + display: inline-block; + font-weight: 500; + padding: 12px 0; + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts new file mode 100644 index 0000000000..b8bb7bd00e --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts @@ -0,0 +1,109 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + NotificationRule, + NotificationTarget, + NotificationTargetConfigType, + NotificationTargetConfigTypeTranslateMap +} from '@shared/models/notification.models'; +import { Component, Inject, OnDestroy } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NotificationService } from '@core/http/notification.service'; +import { EntityType } from '@shared/models/entity-type.models'; +import { deepTrim, isDefined } from '@core/utils'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +export interface RuleNotificationDialogData { + rule?: NotificationRule; + isAdd?: boolean; +} + +@Component({ + selector: 'tb-rule-notification-dialog', + templateUrl: './rule-notification-dialog.component.html', + styleUrls: ['rule-notification-dialog.component.scss'] +}) +export class RuleNotificationDialogComponent extends + DialogComponent implements OnDestroy { + + ruleNotificationForm: FormGroup; + // notificationTargetConfigType = NotificationTargetConfigType; + // notificationTargetConfigTypes: NotificationTargetConfigType[] = Object.values(NotificationTargetConfigType); + // notificationTargetConfigTypeTranslateMap = NotificationTargetConfigTypeTranslateMap; + entityType = EntityType; + isAdd = true; + + private readonly destroy$ = new Subject(); + + constructor(protected store: Store, + protected router: Router, + protected dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: RuleNotificationDialogData, + private fb: FormBuilder, + private notificationService: NotificationService) { + super(store, router, dialogRef); + + if (isDefined(data.isAdd)) { + this.isAdd = data.isAdd; + } + + this.ruleNotificationForm = this.fb.group({ + name: [null, Validators.required], + templateId: [null, Validators.required], + configuration: this.fb.group({ + ids: [], + interval: [], + // type: [NotificationTargetConfigType.ALL_USERS], + description: [null], + // usersIds: [{value: null, disabled: true}, Validators.required], + // customerId: [{value: null, disabled: true}, Validators.required], + // getCustomerIdFromOriginatorEntity: [{value: false, disabled: true}], + }) + }); + + // if (isDefined(data.target)) { + // this.targetNotificationForm.patchValue(data.target, {emitEvent: false}); + // this.targetNotificationForm.get('configuration.type').updateValueAndValidity({onlySelf: true}); + // } + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.destroy$.next(); + this.destroy$.complete(); + } + + cancel(): void { + this.dialogRef.close(null); + } + + save() { + let formValue = deepTrim(this.ruleNotificationForm.value); + if (isDefined(this.data.rule)) { + formValue = Object.assign({}, this.data.rule, formValue); + } + this.notificationService.saveNotificationRule(formValue).subscribe( + (rule) => this.dialogRef.close(rule) + ); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.html new file mode 100644 index 0000000000..01c921cb5b --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.html @@ -0,0 +1,24 @@ + +
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.scss b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.scss new file mode 100644 index 0000000000..f3554a23f8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.scss @@ -0,0 +1,18 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host{ + width: 100%; +} diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.ts new file mode 100644 index 0000000000..7107a3901d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-table-header.component.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { NotificationRule } from '@shared/models/notification.models'; + +@Component({ + selector: 'tb-rule-table-header', + templateUrl: './rule-table-header.component.html', + styleUrls: ['rule-table-header.component.scss'] +}) +export class RuleTableHeaderComponent extends EntityTableHeaderComponent { + + constructor(protected store: Store) { + super(store); + } + + createTarget($event: Event) { + this.entitiesTableConfig.onEntityAction({event: $event, action: 'add', entity: null}); + } + +} diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 83aa994c3c..44d5848e49 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -96,13 +96,14 @@ export interface SlackConversation { export interface NotificationRule extends Omit, 'label'>{ tenantId: TenantId; templateId: NotificationTemplateId; - deliveryMethods: Array; + // deliveryMethods: Array; configuration: NotificationRuleConfig; } export interface NotificationRuleConfig { initialNotificationTargetId: NotificationTargetId; escalationConfig: NotificationEscalationConfig; + description?: string; } export interface NotificationEscalationConfig { 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 55bd9ee662..34fe63b5bd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2680,6 +2680,7 @@ "button-text": "Button text", "button-text-required": "Button text is required", "compose": "Compose", + "add-rule": "Add rule", "copy-notification-template": "Copy notification template", "create-target": "Create target", "created-time": "Created time", @@ -2697,6 +2698,8 @@ "slack": "Slack", "sms": "SMS" }, + "delete-rule-text": "Be careful, after the confirmation the notification rule will become unrecoverable.", + "delete-rule-title": "Are you sure you want to delete the notification rule '{{ruleName}}'?", "description": "Description", "edit-notification-target": "Edit notification target", "edit-notification-template": "Edit notification template", @@ -2726,10 +2729,13 @@ "sent": "Sent" }, "review": "Review", + "no-rules-notification": "No rules notification", + "rule": "Rule", "rules": "Rules", "search-notification": "Search notification", "search-targets": "Search targets", "search-templates": "Search templates", + "search-rules": "Search rules", "send-notification": "Send notification", "send-test-notification": "Send test notification", "sent": "Sent", @@ -2750,6 +2756,7 @@ }, "targets": "Targets", "targets-required": "Targets is required", + "recipient": "Recipient", "template": "Template", "template-required": "Template is required", "template-type": { @@ -2761,7 +2768,11 @@ "type": "Type", "unread": "Unread", "view-all": "View al;", - "web-settings": "Web settings" + "web-settings": "Web settings", + "rule-name": "Rule name", + "rule-name-required": "Name is required", + "template-name": "Template name", + "hierarchy-of-receiving": "Hierarchy of receiving notifications" }, "ota-update": { "add": "Add package",