Browse Source

UI: Refactoring notification center and added notification template

pull/7980/head
Vladyslav_Prykhodko 4 years ago
parent
commit
154aa30b4c
  1. 2
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  2. 1
      ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
  3. 34
      ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html
  4. 8
      ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss
  5. 4
      ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts
  6. 16
      ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts
  7. 15
      ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts
  8. 15
      ui-ngx/src/app/modules/home/pages/notification-center/notification-table/targets-table-config.ts
  9. 72
      ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts
  10. 4
      ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-notification-dialog.component.html
  11. 2
      ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-notification-dialog.componet.ts
  12. 24
      ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-table-header.component.html
  13. 18
      ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-table-header.component.scss
  14. 38
      ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-table-header.component.ts
  15. 74
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.html
  16. 25
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.scss
  17. 95
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.ts
  18. 24
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-table-header.component.html
  19. 18
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-table-header.component.scss
  20. 38
      ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-table-header.component.ts
  21. 22
      ui-ngx/src/app/shared/models/notification.models.ts
  22. 55
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -35,7 +35,7 @@
<mat-drawer-content> <mat-drawer-content>
<div class="mat-padding tb-entity-table tb-absolute-fill"> <div class="mat-padding tb-entity-table tb-absolute-fill">
<div fxLayout="column" class="mat-elevation-z1 tb-entity-table-content"> <div fxLayout="column" class="mat-elevation-z1 tb-entity-table-content">
<mat-toolbar class="mat-table-toolbar" [fxShow]="!textSearchMode && dataSource.selection.isEmpty() && entitiesTableConfig.showTitle"> <mat-toolbar class="mat-table-toolbar" [fxShow]="!textSearchMode && dataSource.selection.isEmpty()">
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container"> <div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">
<span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span> <span *ngIf="entitiesTableConfig.tableTitle" class="tb-entity-table-title">{{ entitiesTableConfig.tableTitle }}</span>

1
ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts

@ -148,7 +148,6 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
useTimePageLink = false; useTimePageLink = false;
defaultTimewindowInterval = historyInterval(DAY); defaultTimewindowInterval = historyInterval(DAY);
entityType: EntityType = null; entityType: EntityType = null;
showTitle = true;
tableTitle = ''; tableTitle = '';
selectionEnabled = true; selectionEnabled = true;
searchEnabled = true; searchEnabled = true;

34
ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html

@ -17,21 +17,21 @@
--> -->
<div class="tb-notification-center"> <div class="tb-notification-center">
<div class="tb-notification-center-content"> <div class="tb-notification-center-content">
<section fxLayout="row" fxLayoutAlign="space-between center" class="title-container"> <!-- <section fxLayout="row" fxLayoutAlign="space-between center" class="title-container">-->
<h2 translate class="title">notification.notification-center</h2> <!-- <h2 translate class="title">notification.notification-center</h2>-->
<section class="action-buttons" fxLayoutAlign="end center"> <!-- <section class="action-buttons" fxLayoutAlign="end center">-->
<button mat-icon-button [disabled]="isLoading$ | async" (click)="openSetting()" <!-- <button mat-icon-button [disabled]="isLoading$ | async" (click)="openSetting()"-->
matTooltip="{{ 'notification.settings' | translate }}" <!-- matTooltip="{{ 'notification.settings' | translate }}"-->
matTooltipPosition="above"> <!-- matTooltipPosition="above">-->
<mat-icon>settings</mat-icon> <!-- <mat-icon>settings</mat-icon>-->
</button> <!-- </button>-->
<button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()" <!-- <button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()"-->
matTooltip="{{ 'action.refresh' | translate }}" <!-- matTooltip="{{ 'action.refresh' | translate }}"-->
matTooltipPosition="above"> <!-- matTooltipPosition="above">-->
<mat-icon>refresh</mat-icon> <!-- <mat-icon>refresh</mat-icon>-->
</button> <!-- </button>-->
</section> <!-- </section>-->
</section> <!-- </section>-->
<div class="tabs-container"> <div class="tabs-container">
<mat-tab-group #matTabGroup (selectedIndexChange)="updateData()"> <mat-tab-group #matTabGroup (selectedIndexChange)="updateData()">
<mat-tab label="{{ 'notification.inbox' | translate }}" #inboxTab="matTab"> <mat-tab label="{{ 'notification.inbox' | translate }}" #inboxTab="matTab">
@ -62,9 +62,9 @@
<mat-tab disabled> <mat-tab disabled>
<ng-template mat-tab-label> <ng-template mat-tab-label>
<div fxFlex="100" fxLayout="row" fxLayoutAlign="end center"> <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
<button mat-flat-button color="primary" *ngIf="targetsTab.isActive" (click)="createTarget($event)"> <button mat-flat-button color="primary">
<div fxLayout="row" fxLayoutAlign="start center"> <div fxLayout="row" fxLayoutAlign="start center">
<mat-icon>add</mat-icon>{{ 'notification.create-target' | translate }} <mat-icon>send</mat-icon>{{ 'notification.send-notification' | translate }}
</div> </div>
</button> </button>
</div> </div>

8
ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.scss

@ -144,6 +144,14 @@
.tb-entity-table { .tb-entity-table {
padding: 0; padding: 0;
.mat-toolbar-single-row, .mat-toolbar-tools {
height: 56px;
}
.title-container {
width: 100%;
}
} }
} }
} }

4
ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts

@ -53,8 +53,4 @@ export class NotificationCenterComponent extends PageComponent {
private get currentTableComponent(): NotificationTableComponent { private get currentTableComponent(): NotificationTableComponent {
return this.tableComponent.get(this.matTabs.selectedIndex); return this.tableComponent.get(this.matTabs.selectedIndex);
} }
createTarget($event: Event) {
this.currentTableComponent?.onEntityAction({event: $event, action: 'add', entity: null});
}
} }

16
ui-ngx/src/app/modules/home/pages/notification-center/notification-center.module.ts

@ -23,12 +23,26 @@ import { HomeComponentsModule } from '@home/components/home-components.module';
import { import {
TargetNotificationDialogComponent TargetNotificationDialogComponent
} from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; } from '@home/pages/notification-center/targets-table/target-notification-dialog.componet';
import { NotificationTableComponent } from '@home/pages/notification-center/notification-table/notification-table.component'; import {
NotificationTableComponent
} from '@home/pages/notification-center/notification-table/notification-table.component';
import {
TemplateNotificationDialogComponent
} from '@home/pages/notification-center/template-dialog/template-notification-dialog.component';
import {
TargetTableHeaderComponent
} from '@home/pages/notification-center/targets-table/target-table-header.component';
import {
TemplateTableHeaderComponent
} from '@home/pages/notification-center/template-dialog/template-table-header.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
NotificationCenterComponent, NotificationCenterComponent,
TargetNotificationDialogComponent, TargetNotificationDialogComponent,
TemplateNotificationDialogComponent,
TargetTableHeaderComponent,
TemplateTableHeaderComponent,
NotificationTableComponent NotificationTableComponent
], ],
imports: [ imports: [

15
ui-ngx/src/app/modules/home/pages/notification-center/notification-table/notification-table.component.ts

@ -25,7 +25,6 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod
import { InboxTableConfig } from '@home/pages/notification-center/notification-table/inbox-table-config'; import { InboxTableConfig } from '@home/pages/notification-center/notification-table/inbox-table-config';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { TemplateTableConfig } from '@home/pages/notification-center/notification-table/template-table-config'; import { TemplateTableConfig } from '@home/pages/notification-center/notification-table/template-table-config';
import { EntityAction } from '@home/models/entity/entity-component.models';
@Component({ @Component({
selector: 'tb-notification-table', selector: 'tb-notification-table',
@ -49,10 +48,8 @@ export class NotificationTableComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.entityTableConfig = this.getTableConfig(); this.entityTableConfig = this.getTableConfig();
this.entityTableConfig.pageMode = false; this.entityTableConfig.pageMode = false;
this.entityTableConfig.showTitle = false;
this.entityTableConfig.detailsPanelEnabled = false; this.entityTableConfig.detailsPanelEnabled = false;
this.entityTableConfig.selectionEnabled = false; this.entityTableConfig.selectionEnabled = false;
this.entityTableConfig.searchEnabled = false;
this.entityTableConfig.addEnabled = false; this.entityTableConfig.addEnabled = false;
} }
@ -60,12 +57,6 @@ export class NotificationTableComponent implements OnInit {
this.entitiesTable.updateData(); this.entitiesTable.updateData();
} }
onEntityAction(action: EntityAction<any>) {
if (this.entityTableConfig.onEntityAction) {
this.entityTableConfig.onEntityAction(action);
}
}
private getTableConfig(): EntityTableConfig<any> { private getTableConfig(): EntityTableConfig<any> {
switch (this.notificationType) { switch (this.notificationType) {
case EntityType.NOTIFICATION_TARGET: case EntityType.NOTIFICATION_TARGET:
@ -82,12 +73,14 @@ export class NotificationTableComponent implements OnInit {
case EntityType.NOTIFICATION_TEMPLATE: case EntityType.NOTIFICATION_TEMPLATE:
return new TemplateTableConfig( return new TemplateTableConfig(
this.notificationService, this.notificationService,
this.datePipe this.translate,
this.dialog
); );
case EntityType.NOTIFICATION_REQUEST: case EntityType.NOTIFICATION_REQUEST:
return new TemplateTableConfig( return new TemplateTableConfig(
this.notificationService, this.notificationService,
this.datePipe this.translate,
this.dialog
); );
} }
} }

15
ui-ngx/src/app/modules/home/pages/notification-center/notification-table/targets-table-config.ts

@ -30,6 +30,9 @@ import {
} from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; } from '@home/pages/notification-center/targets-table/target-notification-dialog.componet';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { EntityAction } from '@home/models/entity/entity-component.models'; import { EntityAction } from '@home/models/entity/entity-component.models';
import {
TargetTableHeaderComponent
} from '@home/pages/notification-center/targets-table/target-table-header.component';
export class TargetsTableConfig extends EntityTableConfig<NotificationTarget> { export class TargetsTableConfig extends EntityTableConfig<NotificationTarget> {
@ -39,7 +42,8 @@ export class TargetsTableConfig extends EntityTableConfig<NotificationTarget> {
super(); super();
this.loadDataOnInit = false; this.loadDataOnInit = false;
this.entityTranslations = { this.entityTranslations = {
noEntities: 'notification.no-targets-notification' noEntities: 'notification.no-targets-notification',
search: 'notification.search-targets'
}; };
this.entityResources = {} as EntityTypeResource<NotificationTarget>; this.entityResources = {} as EntityTypeResource<NotificationTarget>;
@ -50,15 +54,20 @@ export class TargetsTableConfig extends EntityTableConfig<NotificationTarget> {
this.deleteEntity = id => this.notificationService.deleteNotificationTarget(id.id); this.deleteEntity = id => this.notificationService.deleteNotificationTarget(id.id);
this.cellActionDescriptors = this.configureCellActions(); this.cellActionDescriptors = this.configureCellActions();
this.headerComponent = TargetTableHeaderComponent;
this.onEntityAction = action => this.onTargetAction(action); this.onEntityAction = action => this.onTargetAction(action);
this.defaultSortOrder = {property: 'name', direction: Direction.ASC}; this.defaultSortOrder = {property: 'name', direction: Direction.ASC};
this.columns.push( this.columns.push(
new EntityTableColumn<NotificationTarget>('name', 'notification.notification-target', '30%'), new EntityTableColumn<NotificationTarget>('name', 'notification.notification-target', '20%'),
new EntityTableColumn<NotificationTarget>('configuration.type', 'notification.type', '40%', new EntityTableColumn<NotificationTarget>('configuration.type', 'notification.type', '40%',
(target) => this.translate.instant(NotificationTargetConfigTypeTranslateMap.get(target.configuration.type)), (target) => this.translate.instant(NotificationTargetConfigTypeTranslateMap.get(target.configuration.type)),
() => ({}), false) () => ({}), false),
new EntityTableColumn<NotificationTarget>('configuration.description', 'notification.description', '40%',
(target) => target.configuration.description || '',
() => ({}), false)
); );
} }

72
ui-ngx/src/app/modules/home/pages/notification-center/notification-table/template-table-config.ts

@ -15,39 +15,91 @@
/// ///
import { import {
DateEntityTableColumn, CellActionDescriptor,
EntityTableColumn, EntityTableColumn,
EntityTableConfig EntityTableConfig
} from '@home/models/entity/entities-table-config.models'; } from '@home/models/entity/entities-table-config.models';
import { EntityTypeResource } from '@shared/models/entity-type.models'; import { EntityTypeResource } from '@shared/models/entity-type.models';
import { DatePipe } from '@angular/common';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { NotificationTemplate } from '@shared/models/notification.models'; import { NotificationTemplate } from '@shared/models/notification.models';
import { NotificationService } from '@core/http/notification.service'; import { NotificationService } from '@core/http/notification.service';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { MatDialog } from '@angular/material/dialog';
import {
TemplateNotificationDialogComponent,
TemplateNotificationDialogData
} from '@home/pages/notification-center/template-dialog/template-notification-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import {
TemplateTableHeaderComponent
} from '@home/pages/notification-center/template-dialog/template-table-header.component';
export class TemplateTableConfig extends EntityTableConfig<NotificationTemplate> { export class TemplateTableConfig extends EntityTableConfig<NotificationTemplate> {
constructor(private notificationService: NotificationService, constructor(private notificationService: NotificationService,
private datePipe: DatePipe) { private translate: TranslateService,
private dialog: MatDialog) {
super(); super();
this.loadDataOnInit = false; this.loadDataOnInit = false;
this.entitiesDeleteEnabled = false;
this.entityTranslations = { this.entityTranslations = {
noEntities: 'notification.no-inbox-notification' noEntities: 'notification.no-notification-templates',
search: 'notification.search-templates'
}; };
this.entityResources = {} as EntityTypeResource<NotificationTemplate>; this.entityResources = {} as EntityTypeResource<NotificationTemplate>;
this.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTemplates(pageLink); this.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTemplates(pageLink);
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.deleteEntityTitle = template => this.translate.instant('notification.delete-template-title', {templateName: template.name});
this.deleteEntityContent = () => this.translate.instant('notification.delete-template-text');
this.deleteEntity = id => this.notificationService.deleteNotificationTemplate(id.id);
this.cellActionDescriptors = this.configureCellActions();
this.headerComponent = TemplateTableHeaderComponent;
this.onEntityAction = action => this.onTemplateAction(action);
this.defaultSortOrder = {property: 'notificationType', direction: Direction.ASC};
this.columns.push( this.columns.push(
new DateEntityTableColumn<NotificationTemplate>('createdTime', 'notification.created-time', this.datePipe, '150px'), new EntityTableColumn<NotificationTemplate>('notificationType', 'notification.type', '15%'),
new EntityTableColumn<NotificationTemplate>('type', 'notification.type', '10%'), new EntityTableColumn<NotificationTemplate>('name', 'notification.template', '25%'),
new EntityTableColumn<NotificationTemplate>('text', 'notification.text', '40%'), new EntityTableColumn<NotificationTemplate>('subject', 'notification.subject', '25%'),
new EntityTableColumn<NotificationTemplate>('info.description', 'notification.description', '50%') new EntityTableColumn<NotificationTemplate>('configuration.defaultTextTemplate', 'notification.message', '35%',
(template) => template.configuration.defaultTextTemplate, () => ({}), false)
); );
}
private configureCellActions(): Array<CellActionDescriptor<NotificationTemplate>> {
return [];
}
private editTemplate($event: Event, template: NotificationTemplate, isAdd = false) {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<TemplateNotificationDialogComponent, TemplateNotificationDialogData,
NotificationTemplate>(TemplateNotificationDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
isAdd,
template
}
}).afterClosed()
.subscribe((res) => {
if (res) {
this.updateData();
}
});
}
private onTemplateAction(action: EntityAction<NotificationTemplate>): boolean {
switch (action.action) {
case 'add':
this.editTemplate(action.event, action.entity, true);
return true;
}
return false;
} }
} }

4
ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-notification-dialog.component.html

@ -63,6 +63,10 @@
*ngIf="!targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').value"> *ngIf="!targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').value">
</tb-entity-autocomplete> </tb-entity-autocomplete>
</section> </section>
<mat-form-field class="mat-block">
<mat-label translate>notification.description</mat-label>
<input matInput formControlName="description">
</mat-form-field>
</section> </section>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>

2
ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-notification-dialog.componet.ts

@ -70,6 +70,7 @@ export class TargetNotificationDialogComponent extends
name: [null, Validators.required], name: [null, Validators.required],
configuration: this.fb.group({ configuration: this.fb.group({
type: [NotificationTargetConfigType.ALL_USERS], type: [NotificationTargetConfigType.ALL_USERS],
description: [null],
usersIds: [{value: null, disabled: true}, Validators.required], usersIds: [{value: null, disabled: true}, Validators.required],
customerId: [{value: null, disabled: true}, Validators.required], customerId: [{value: null, disabled: true}, Validators.required],
getCustomerIdFromOriginatorEntity: [{value: false, disabled: true}], getCustomerIdFromOriginatorEntity: [{value: false, disabled: true}],
@ -90,6 +91,7 @@ export class TargetNotificationDialogComponent extends
break; break;
} }
this.targetNotificationForm.get('configuration.type').enable({emitEvent: false}); this.targetNotificationForm.get('configuration.type').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.description').enable({emitEvent: false});
}); });
this.targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').valueChanges.pipe( this.targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').valueChanges.pipe(
takeUntil(this.destroy$) takeUntil(this.destroy$)

24
ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-table-header.component.html

@ -0,0 +1,24 @@
<!--
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.
-->
<div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" (click)="createTarget($event)">
<div fxLayout="row" fxLayoutAlign="start center">
<mat-icon>add</mat-icon>{{ 'notification.add-target' | translate }}
</div>
</button>
</div>

18
ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-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%;
}

38
ui-ngx/src/app/modules/home/pages/notification-center/targets-table/target-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 { NotificationTarget } from '@shared/models/notification.models';
@Component({
selector: 'tb-target-table-header',
templateUrl: './target-table-header.component.html',
styleUrls: ['target-table-header.component.scss']
})
export class TargetTableHeaderComponent extends EntityTableHeaderComponent<NotificationTarget> {
constructor(protected store: Store<AppState>) {
super(store);
}
createTarget($event: Event) {
this.entitiesTableConfig.onEntityAction({event: $event, action: 'add', entity: null});
}
}

74
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.html

@ -0,0 +1,74 @@
<!--
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.
-->
<mat-toolbar color="primary">
<h2>{{ dialogTitle | translate }}</h2>
<span fxFlex></span>
<button mat-icon-button
(click)="cancel()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<form mat-dialog-content [formGroup]="templateNotificationForm">
<mat-form-field class="mat-block">
<mat-label translate>notification.target-name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="templateNotificationForm.get('name').hasError('required')">
{{ 'notification.target-name-required' | translate }}
</mat-error>
</mat-form-field>
<label style="margin-bottom: 8px">{{ 'notification.type' | translate }}<sup> *</sup></label>
<mat-radio-group formControlName="type">
<mat-radio-button [value]="NotificationType.GENERIC">
<div fxLayout="column">
<div>Generic</div>
<div class="tb-hint">hint</div>
</div>
</mat-radio-button>
<mat-radio-button [value]="NotificationType.ALARM">
<div>Alarm</div>
<div class="tb-hint">hint</div>
</mat-radio-button>
</mat-radio-group>
<section formGroupName="configuration">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<input matInput formControlName="defaultTextTemplate" required>
<mat-error *ngIf="templateNotificationForm.get('configuration.defaultTextTemplate').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput required>
</mat-form-field>
</section>
</form>
<mat-divider></mat-divider>
<div mat-dialog-actions fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="end">
<button mat-button
[disabled]="(isLoading$ | async)"
(click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-raised-button
[disabled]="(isLoading$ | async) || templateNotificationForm.invalid || !templateNotificationForm.dirty"
color="primary"
(click)="save()">{{ saveButtonLabel | translate }}</button>
</div>

25
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.scss

@ -0,0 +1,25 @@
:host {
width: 600px;
.tb-hint {
padding: 0;
}
}
:host ::ng-deep {
.mat-radio-group {
display: flex;
flex-direction: row;
.mat-radio-button {
flex: 1 1 100%;
padding: 14px;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
&:not(:last-child){
margin-right: 8px;
}
}
}
}

95
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-notification-dialog.component.ts

@ -0,0 +1,95 @@
///
/// 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 { NotificationTemplate, NotificationType } 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 { deepTrim, isDefined } from '@core/utils';
import { Subject } from 'rxjs';
export interface TemplateNotificationDialogData {
template?: NotificationTemplate;
isAdd?: boolean;
isCopy?: boolean;
}
@Component({
selector: 'tb-template-notification-dialog',
templateUrl: './template-notification-dialog.component.html',
styleUrls: ['./template-notification-dialog.component.scss']
})
export class TemplateNotificationDialogComponent
extends DialogComponent<TemplateNotificationDialogComponent, NotificationTemplate> implements OnDestroy {
templateNotificationForm: FormGroup;
dialogTitle = 'notification.edit-notification-template';
saveButtonLabel = 'action.save';
NotificationType = NotificationType;
private readonly destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>,
protected router: Router,
protected dialogRef: MatDialogRef<TemplateNotificationDialogComponent, NotificationTemplate>,
@Inject(MAT_DIALOG_DATA) public data: TemplateNotificationDialogData,
private fb: FormBuilder,
private notificationService: NotificationService) {
super(store, router, dialogRef);
if (data.isAdd) {
this.dialogTitle = 'notification.add-notification-template';
this.saveButtonLabel = 'action.add';
}
if (data.isCopy) {
this.dialogTitle = 'notification.copy-notification-template';
}
this.templateNotificationForm = this.fb.group({
name: [null, Validators.required],
type: [NotificationType.GENERIC],
configuration: this.fb.group({
defaultTextTemplate: [null, Validators.required]
})
});
}
ngOnDestroy() {
super.ngOnDestroy();
this.destroy$.next();
this.destroy$.complete();
}
cancel(): void {
this.dialogRef.close(null);
}
save() {
let formValue = deepTrim(this.templateNotificationForm.value);
if (isDefined(this.data.template)) {
formValue = Object.assign({}, this.data.template, formValue);
}
this.notificationService.saveNotificationTemplate(formValue).subscribe(
(target) => this.dialogRef.close(target)
);
}
}

24
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-table-header.component.html

@ -0,0 +1,24 @@
<!--
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.
-->
<div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" (click)="createTemplate($event)">
<div fxLayout="row" fxLayoutAlign="start center">
<mat-icon>add</mat-icon>{{ 'notification.add-template' | translate }}
</div>
</button>
</div>

18
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-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%;
}

38
ui-ngx/src/app/modules/home/pages/notification-center/template-dialog/template-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 { NotificationTemplate } from '@shared/models/notification.models';
@Component({
selector: 'tb-template-table-header',
templateUrl: './template-table-header.component.html',
styleUrls: ['template-table-header.component.scss']
})
export class TemplateTableHeaderComponent extends EntityTableHeaderComponent<NotificationTemplate> {
constructor(protected store: Store<AppState>) {
super(store);
}
createTemplate($event: Event) {
this.entitiesTableConfig.onEntityAction({event: $event, action: 'add', entity: null});
}
}

22
ui-ngx/src/app/shared/models/notification.models.ts

@ -108,6 +108,7 @@ export interface NotificationTarget extends Omit<BaseData<NotificationTargetId>,
export interface NotificationTargetConfig extends export interface NotificationTargetConfig extends
Partial<UserListNotificationTargetConfig & CustomerUsersNotificationTargetConfig>{ Partial<UserListNotificationTargetConfig & CustomerUsersNotificationTargetConfig>{
type: NotificationTargetConfigType; type: NotificationTargetConfigType;
description?: string;
} }
interface UserListNotificationTargetConfig { interface UserListNotificationTargetConfig {
@ -121,21 +122,27 @@ interface CustomerUsersNotificationTargetConfig {
export interface NotificationTemplate extends Omit<BaseData<NotificationTemplateId>, 'label'>{ export interface NotificationTemplate extends Omit<BaseData<NotificationTemplateId>, 'label'>{
tenantId: TenantId; tenantId: TenantId;
notificationType: string; notificationType: NotificationType;
configuration: NotificationTemplateConfig; configuration: NotificationTemplateConfig;
} }
interface NotificationTemplateConfig { interface NotificationTemplateConfig {
defaultTextTemplate: string; defaultTextTemplate: string;
templates: Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate>; deliveryMethodsTemplates: Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate>;
} }
interface DeliveryMethodNotificationTemplate extends interface DeliveryMethodNotificationTemplate extends
Partial<EmailDeliveryMethodNotificationTemplate & SlackDeliveryMethodNotificationTemplate>{ Partial<PushDeliveryMethodNotificationTemplate & EmailDeliveryMethodNotificationTemplate & SlackDeliveryMethodNotificationTemplate>{
body: string; body?: string;
method: NotificationDeliveryMethod; method: NotificationDeliveryMethod;
} }
interface PushDeliveryMethodNotificationTemplate {
subject?: string;
icon?: string;
actionButtonConfig?: string;
}
interface EmailDeliveryMethodNotificationTemplate { interface EmailDeliveryMethodNotificationTemplate {
subject: string; subject: string;
} }
@ -157,7 +164,7 @@ export enum NotificationStatus {
} }
export enum NotificationDeliveryMethod { export enum NotificationDeliveryMethod {
WEBSOCKET = 'WEBSOCKET', PUSH = 'PUSH',
SMS = 'SMS', SMS = 'SMS',
EMAIL = 'EMAIL', EMAIL = 'EMAIL',
SLACK = 'SLACK' SLACK = 'SLACK'
@ -185,3 +192,8 @@ export const NotificationTargetConfigTypeTranslateMap = new Map<NotificationTarg
[NotificationTargetConfigType.USER_LIST, 'notification.target-type.user-list'], [NotificationTargetConfigType.USER_LIST, 'notification.target-type.user-list'],
[NotificationTargetConfigType.CUSTOMER_USERS, 'notification.target-type.customer-users'], [NotificationTargetConfigType.CUSTOMER_USERS, 'notification.target-type.customer-users'],
]); ]);
export enum NotificationType {
GENERIC = 'GENERIC',
ALARM = 'ALARM'
}

55
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2666,35 +2666,50 @@
"copied": "Copied!" "copied": "Copied!"
}, },
"notification": { "notification": {
"notification-center": "Notification center", "add-notification-target": "Add notification target",
"management": "Notification management", "add-notification-template": "Add notification template",
"settings": "Notification settings", "add-target": "Add target",
"inbox": "Inbox", "add-template": "Add template",
"sent": "Sent", "copy-notification-template": "Copy notification template",
"templates": "Templates", "create-target": "Create target",
"targets": "Targets",
"rules": "Rules",
"created-time": "Created time", "created-time": "Created time",
"type": "Type", "delete-target-text": "Be careful, after the confirmation the notification target will become unrecoverable.",
"delete-target-title": "Are you sure you want to delete the notification target '{{targetName}}'?",
"delete-template-text": "Be careful, after the confirmation the notification template will become unrecoverable.",
"delete-template-title": "Are you sure you want to delete the notification template '{{templateName}}'?",
"description": "Description",
"edit-notification-target": "Edit notification target",
"edit-notification-template": "Edit notification template",
"get-customer-id-from-originator": "Get Customer id from originator",
"inbox": "Inbox",
"management": "Notification management",
"message": "Message",
"message-required": "Message is required",
"no-inbox-notification": "No notification found", "no-inbox-notification": "No notification found",
"no-notification-templates": "No notification templates",
"no-targets-notification": "No targets notification", "no-targets-notification": "No targets notification",
"text": "Text", "notification-center": "Notification center",
"description": "Description",
"notification-target": "Notification target", "notification-target": "Notification target",
"create-target": "Create target", "rules": "Rules",
"add-notification-target": "Add notification target", "search-targets": "Search targets",
"edit-notification-target": "Edit notification target", "search-templates": "Search templates",
"send-notification": "Send notification",
"sent": "Sent",
"settings": "Notification settings",
"subject": "Subject",
"target-name": "Name", "target-name": "Name",
"target-name-required": "Name is required", "target-name-required": "Name is required",
"target-type": { "target-type": {
"type": "Type",
"all-users": "All users", "all-users": "All users",
"user-list": "User list", "customer-users": "Customer users",
"customer-users": "Customer users" "type": "Type",
"user-list": "User list"
}, },
"get-customer-id-from-originator": "Get Customer id from originator", "targets": "Targets",
"delete-target-title": "Are you sure you want to delete the notification target '{{targetName}}'?", "template": "Template",
"delete-target-text": "Be careful, after the confirmation the notification target will become unrecoverable." "templates": "Templates",
"text": "Text",
"type": "Type"
}, },
"ota-update": { "ota-update": {
"add": "Add package", "add": "Add package",

Loading…
Cancel
Save