21 changed files with 1028 additions and 9 deletions
@ -0,0 +1,41 @@ |
|||
///
|
|||
/// 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 { NgModule } from '@angular/core'; |
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
import { Authority } from '@shared/models/authority.enum'; |
|||
import { NotificationCenterComponent } from '@home/pages/notification-center/notification-center.component'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
path: 'notification-center', |
|||
component: NotificationCenterComponent, |
|||
data: { |
|||
auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], |
|||
title: 'notification.notification-center', |
|||
breadcrumb: { |
|||
label: 'notification.notification-center', |
|||
icon: 'notifications' |
|||
} |
|||
} |
|||
} |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule] |
|||
}) |
|||
export class NotificationCenterRoutingModule { } |
|||
@ -0,0 +1,76 @@ |
|||
<!-- |
|||
|
|||
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 class="tb-notification-center"> |
|||
<div class="tb-notification-center-content"> |
|||
<section fxLayout="row" fxLayoutAlign="space-between center" class="title-container"> |
|||
<h2 translate class="title">notification.notification-center</h2> |
|||
<section class="action-buttons" fxLayoutAlign="end center"> |
|||
<button mat-icon-button [disabled]="isLoading$ | async" (click)="openSetting()" |
|||
matTooltip="{{ 'notification.settings' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>settings</mat-icon> |
|||
</button> |
|||
<button mat-icon-button [disabled]="isLoading$ | async" (click)="updateData()" |
|||
matTooltip="{{ 'action.refresh' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>refresh</mat-icon> |
|||
</button> |
|||
</section> |
|||
</section> |
|||
<div class="tabs-container"> |
|||
<mat-tab-group #matTabGroup (selectedIndexChange)="updateData()"> |
|||
<mat-tab label="{{ 'notification.inbox' | translate }}" #inboxTab="matTab"> |
|||
<tb-notification-table |
|||
[notificationType]="entityType.NOTIFICATION"> |
|||
</tb-notification-table> |
|||
</mat-tab> |
|||
<mat-tab label="{{ 'notification.sent' | translate }}"> |
|||
<tb-notification-table |
|||
[notificationType]="entityType.NOTIFICATION_REQUEST"> |
|||
</tb-notification-table> |
|||
</mat-tab> |
|||
<mat-tab label="{{ 'notification.templates' | translate }}" #templateTab="matTab"> |
|||
<tb-notification-table |
|||
[notificationType]="entityType.NOTIFICATION_TEMPLATE"> |
|||
</tb-notification-table> |
|||
</mat-tab> |
|||
<mat-tab label="{{ 'notification.targets' | translate }}" #targetsTab="matTab"> |
|||
<tb-notification-table |
|||
[notificationType]="entityType.NOTIFICATION_TARGET"> |
|||
</tb-notification-table> |
|||
</mat-tab> |
|||
<mat-tab label="{{ 'notification.rules' | translate }}"> |
|||
<ng-template matTabContent> |
|||
Content 1 - Loaded |
|||
</ng-template> |
|||
</mat-tab> |
|||
<mat-tab disabled> |
|||
<ng-template mat-tab-label> |
|||
<div fxFlex="100" fxLayout="row" fxLayoutAlign="end center"> |
|||
<button mat-flat-button color="primary" *ngIf="targetsTab.isActive" (click)="createTarget($event)"> |
|||
<div fxLayout="row" fxLayoutAlign="start center"> |
|||
<mat-icon>add</mat-icon>{{ 'notification.create-target' | translate }} |
|||
</div> |
|||
</button> |
|||
</div> |
|||
</ng-template> |
|||
</mat-tab> |
|||
</mat-tab-group> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,177 @@ |
|||
/** |
|||
* 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 '../../../../../scss/constants'; |
|||
|
|||
:host { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: block; |
|||
.tb-notification-center { |
|||
padding: 8px; |
|||
width: 100%; |
|||
height: 100%; |
|||
display: block; |
|||
|
|||
.tb-notification-center-content { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: #fff; |
|||
border-radius: 4px; |
|||
box-shadow: 0 4px 10px rgba(23, 33, 90, 0.08); |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.title-container { |
|||
padding: 16px 16px 0; |
|||
|
|||
.title { |
|||
margin: 8px; |
|||
letter-spacing: 0.1px; |
|||
font-size: 24px; |
|||
line-height: 24px; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.action-buttons { |
|||
color: rgba(0,0,0,.54); |
|||
} |
|||
} |
|||
|
|||
.tabs-container { |
|||
position: relative; |
|||
flex-direction: column; |
|||
flex: 1 1 100%; |
|||
} |
|||
|
|||
//.mat-toolbar-tools{ |
|||
// min-height: auto; |
|||
//} |
|||
// |
|||
//.title-container{ |
|||
// overflow: hidden; |
|||
//} |
|||
// |
|||
//.tb-entity-table-title { |
|||
// padding-right: 20px; |
|||
// white-space: nowrap; |
|||
// overflow: hidden; |
|||
// text-overflow: ellipsis; |
|||
//} |
|||
// |
|||
//.table-container { |
|||
// overflow: auto; |
|||
//} |
|||
// |
|||
//.tb-entity-table-info{ |
|||
// white-space: nowrap; |
|||
// overflow: hidden; |
|||
// text-overflow: ellipsis; |
|||
//} |
|||
// |
|||
//.button-widget-action{ |
|||
// margin-left: auto; |
|||
// overflow: hidden; |
|||
// text-overflow: ellipsis; |
|||
//} |
|||
} |
|||
} |
|||
|
|||
//@media #{$mat-xs} { |
|||
// .mat-toolbar { |
|||
// height: auto; |
|||
// min-height: 100px; |
|||
// |
|||
// .tb-entity-table-title{ |
|||
// padding-bottom: 5px; |
|||
// width: 100%; |
|||
// } |
|||
// } |
|||
//} |
|||
|
|||
@media #{$mat-xs} { |
|||
.tb-notification-center { |
|||
.tb-notification-center-content { |
|||
.title-container { |
|||
padding: 8px; |
|||
|
|||
.title { |
|||
margin: 4px 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host ::ng-deep { |
|||
.tabs-container { |
|||
.mat-tab-group { |
|||
> .mat-tab-body-wrapper { |
|||
position: absolute; |
|||
top: 49px; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
} |
|||
> .mat-tab-header { |
|||
.mat-tab-label { |
|||
min-width: 40px; |
|||
|
|||
&.mat-tab-disabled { |
|||
opacity: 1; |
|||
width: 100%; |
|||
|
|||
.mat-tab-label-content { |
|||
width: 100%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tb-entity-table { |
|||
padding: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//:host ::ng-deep { |
|||
// .mat-sort-header-sorted .mat-sort-header-arrow { |
|||
// opacity: 1 !important; |
|||
// } |
|||
// tb-branch-autocomplete { |
|||
// mat-form-field { |
|||
// font-size: 16px; |
|||
// width: 250px; |
|||
// |
|||
// .mat-form-field-wrapper { |
|||
// padding-bottom: 0; |
|||
// } |
|||
// |
|||
// .mat-form-field-underline { |
|||
// bottom: 0; |
|||
// } |
|||
// |
|||
// @media #{$mat-xs} { |
|||
// width: 100%; |
|||
// |
|||
// .mat-form-field-infix { |
|||
// width: auto !important; |
|||
// } |
|||
// } |
|||
// } |
|||
// } |
|||
//} |
|||
@ -0,0 +1,60 @@ |
|||
///
|
|||
/// 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, QueryList, ViewChild, ViewChildren } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { MatTabGroup } from '@angular/material/tabs'; |
|||
import { |
|||
NotificationTableComponent |
|||
} from '@home/pages/notification-center/notification-table/notification-table.component'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-notification-center', |
|||
templateUrl: './notification-center.component.html', |
|||
styleUrls: ['notification-center.component.scss'] |
|||
}) |
|||
export class NotificationCenterComponent extends PageComponent { |
|||
|
|||
entityType = EntityType; |
|||
|
|||
@ViewChild('matTabGroup', {static: true}) matTabs: MatTabGroup; |
|||
@ViewChildren(NotificationTableComponent) tableComponent: QueryList<NotificationTableComponent>; |
|||
|
|||
|
|||
constructor( |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
updateData() { |
|||
this.currentTableComponent?.updateData(); |
|||
} |
|||
|
|||
openSetting() { |
|||
|
|||
} |
|||
|
|||
private get currentTableComponent(): NotificationTableComponent { |
|||
return this.tableComponent.get(this.matTabs.selectedIndex); |
|||
} |
|||
|
|||
createTarget($event: Event) { |
|||
this.currentTableComponent?.onEntityAction({event: $event, action: 'add', entity: null}); |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
///
|
|||
/// 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 { NgModule } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { SharedModule } from '@shared/shared.module'; |
|||
import { NotificationCenterRoutingModule } from './notification-center-routing.module'; |
|||
import { NotificationCenterComponent } from './notification-center.component'; |
|||
import { HomeComponentsModule } from '@home/components/home-components.module'; |
|||
import { |
|||
TargetNotificationDialogComponent |
|||
} from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; |
|||
import { NotificationTableComponent } from '@home/pages/notification-center/notification-table/notification-table.component'; |
|||
|
|||
@NgModule({ |
|||
declarations: [ |
|||
NotificationCenterComponent, |
|||
TargetNotificationDialogComponent, |
|||
NotificationTableComponent |
|||
], |
|||
imports: [ |
|||
CommonModule, |
|||
SharedModule, |
|||
NotificationCenterRoutingModule, |
|||
HomeComponentsModule |
|||
] |
|||
}) |
|||
export class NotificationCenterModule { } |
|||
@ -0,0 +1,54 @@ |
|||
///
|
|||
/// 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 { |
|||
DateEntityTableColumn, |
|||
EntityTableColumn, |
|||
EntityTableConfig |
|||
} from '@home/models/entity/entities-table-config.models'; |
|||
import { EntityTypeResource } from '@shared/models/entity-type.models'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { Direction } from '@shared/models/page/sort-order'; |
|||
import { Notification } from '@shared/models/notification.models'; |
|||
import { NotificationService } from '@core/http/notification.service'; |
|||
|
|||
export class InboxTableConfig extends EntityTableConfig<Notification> { |
|||
|
|||
constructor(private notificationService: NotificationService, |
|||
private datePipe: DatePipe, |
|||
updateOnInit = true) { |
|||
super(); |
|||
this.loadDataOnInit = true; |
|||
this.entitiesDeleteEnabled = false; |
|||
this.entityTranslations = { |
|||
noEntities: 'notification.no-inbox-notification' |
|||
}; |
|||
this.entityResources = {} as EntityTypeResource<Notification>; |
|||
|
|||
this.entitiesFetchFunction = pageLink => this.notificationService.getNotifications(pageLink); |
|||
|
|||
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; |
|||
|
|||
this.columns.push( |
|||
new DateEntityTableColumn<Notification>('createdTime', 'notification.created-time', this.datePipe, '150px'), |
|||
new EntityTableColumn<Notification>('type', 'notification.type', '10%'), |
|||
new EntityTableColumn<Notification>('text', 'notification.text', '40%'), |
|||
new EntityTableColumn<Notification>('info.description', 'notification.description', '50%') |
|||
); |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -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. |
|||
|
|||
--> |
|||
<tb-entities-table [entitiesTableConfig]="entityTableConfig"></tb-entities-table> |
|||
@ -0,0 +1,94 @@ |
|||
///
|
|||
/// 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, Input, OnInit, ViewChild } from '@angular/core'; |
|||
import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; |
|||
import { NotificationService } from '@core/http/notification.service'; |
|||
import { TargetsTableConfig } from '@home/pages/notification-center/notification-table/targets-table-config'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { MatDialog } from '@angular/material/dialog'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
|||
import { InboxTableConfig } from '@home/pages/notification-center/notification-table/inbox-table-config'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { TemplateTableConfig } from '@home/pages/notification-center/notification-table/template-table-config'; |
|||
import { EntityAction } from '@home/models/entity/entity-component.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-notification-table', |
|||
templateUrl: './notification-table.component.html' |
|||
}) |
|||
export class NotificationTableComponent implements OnInit { |
|||
|
|||
@Input() |
|||
notificationType = EntityType.NOTIFICATION; |
|||
|
|||
@ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; |
|||
|
|||
entityTableConfig: EntityTableConfig<any>; |
|||
|
|||
constructor(private notificationService: NotificationService, |
|||
private translate: TranslateService, |
|||
private datePipe: DatePipe, |
|||
private dialog: MatDialog) { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.entityTableConfig = this.getTableConfig(); |
|||
this.entityTableConfig.pageMode = false; |
|||
this.entityTableConfig.showTitle = false; |
|||
this.entityTableConfig.detailsPanelEnabled = false; |
|||
this.entityTableConfig.selectionEnabled = false; |
|||
this.entityTableConfig.searchEnabled = false; |
|||
this.entityTableConfig.addEnabled = false; |
|||
} |
|||
|
|||
updateData() { |
|||
this.entitiesTable.updateData(); |
|||
} |
|||
|
|||
onEntityAction(action: EntityAction<any>) { |
|||
if (this.entityTableConfig.onEntityAction) { |
|||
this.entityTableConfig.onEntityAction(action); |
|||
} |
|||
} |
|||
|
|||
private getTableConfig(): EntityTableConfig<any> { |
|||
switch (this.notificationType) { |
|||
case EntityType.NOTIFICATION_TARGET: |
|||
return new TargetsTableConfig( |
|||
this.notificationService, |
|||
this.translate, |
|||
this.dialog |
|||
); |
|||
case EntityType.NOTIFICATION: |
|||
return new InboxTableConfig( |
|||
this.notificationService, |
|||
this.datePipe |
|||
); |
|||
case EntityType.NOTIFICATION_TEMPLATE: |
|||
return new TemplateTableConfig( |
|||
this.notificationService, |
|||
this.datePipe |
|||
); |
|||
case EntityType.NOTIFICATION_REQUEST: |
|||
return new TemplateTableConfig( |
|||
this.notificationService, |
|||
this.datePipe |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
///
|
|||
/// 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 { 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'; |
|||
|
|||
export class TargetsTableConfig extends EntityTableConfig<NotificationTarget> { |
|||
|
|||
constructor(private notificationService: NotificationService, |
|||
private translate: TranslateService, |
|||
private dialog: MatDialog) { |
|||
super(); |
|||
this.loadDataOnInit = false; |
|||
this.entityTranslations = { |
|||
noEntities: 'notification.no-targets-notification' |
|||
}; |
|||
this.entityResources = {} as EntityTypeResource<NotificationTarget>; |
|||
|
|||
this.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTargets(pageLink); |
|||
|
|||
this.deleteEntityTitle = target => this.translate.instant('notification.delete-target-title', {targetName: target.name}); |
|||
this.deleteEntityContent = () => this.translate.instant('notification.delete-target-text'); |
|||
this.deleteEntity = id => this.notificationService.deleteNotificationTarget(id.id); |
|||
|
|||
this.cellActionDescriptors = this.configureCellActions(); |
|||
this.onEntityAction = action => this.onTargetAction(action); |
|||
|
|||
this.defaultSortOrder = {property: 'name', direction: Direction.ASC}; |
|||
|
|||
this.columns.push( |
|||
new EntityTableColumn<NotificationTarget>('name', 'notification.notification-target', '30%'), |
|||
new EntityTableColumn<NotificationTarget>('configuration.type', 'notification.type', '40%', |
|||
(target) => this.translate.instant(NotificationTargetConfigTypeTranslateMap.get(target.configuration.type)), |
|||
() => ({}), false) |
|||
); |
|||
} |
|||
|
|||
private configureCellActions(): Array<CellActionDescriptor<NotificationTarget>> { |
|||
return [{ |
|||
name: this.translate.instant('device.make-public'), |
|||
icon: 'edit', |
|||
isEnabled: () => true, |
|||
onAction: ($event, entity) => this.editTarget($event, entity) |
|||
}]; |
|||
} |
|||
|
|||
private editTarget($event: Event, target: NotificationTarget, isAdd = false) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
this.dialog.open<TargetNotificationDialogComponent, TargetsNotificationDialogData, |
|||
NotificationTarget>(TargetNotificationDialogComponent, { |
|||
disableClose: true, |
|||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
|||
data: { |
|||
isAdd, |
|||
target |
|||
} |
|||
}).afterClosed() |
|||
.subscribe((res) => { |
|||
if (res) { |
|||
this.updateData(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private onTargetAction(action: EntityAction<NotificationTarget>): boolean { |
|||
switch (action.action) { |
|||
case 'add': |
|||
this.editTarget(action.event, action.entity, true); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
///
|
|||
/// 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 { |
|||
DateEntityTableColumn, |
|||
EntityTableColumn, |
|||
EntityTableConfig |
|||
} from '@home/models/entity/entities-table-config.models'; |
|||
import { EntityTypeResource } from '@shared/models/entity-type.models'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { Direction } from '@shared/models/page/sort-order'; |
|||
import { NotificationTemplate } from '@shared/models/notification.models'; |
|||
import { NotificationService } from '@core/http/notification.service'; |
|||
|
|||
export class TemplateTableConfig extends EntityTableConfig<NotificationTemplate> { |
|||
|
|||
constructor(private notificationService: NotificationService, |
|||
private datePipe: DatePipe) { |
|||
super(); |
|||
this.loadDataOnInit = false; |
|||
this.entitiesDeleteEnabled = false; |
|||
this.entityTranslations = { |
|||
noEntities: 'notification.no-inbox-notification' |
|||
}; |
|||
this.entityResources = {} as EntityTypeResource<NotificationTemplate>; |
|||
|
|||
this.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTemplates(pageLink); |
|||
|
|||
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; |
|||
|
|||
this.columns.push( |
|||
new DateEntityTableColumn<NotificationTemplate>('createdTime', 'notification.created-time', this.datePipe, '150px'), |
|||
new EntityTableColumn<NotificationTemplate>('type', 'notification.type', '10%'), |
|||
new EntityTableColumn<NotificationTemplate>('text', 'notification.text', '40%'), |
|||
new EntityTableColumn<NotificationTemplate>('info.description', 'notification.description', '50%') |
|||
); |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<form [formGroup]="targetNotificationForm" style="min-width: 560px"> |
|||
<mat-toolbar color="primary"> |
|||
<h2>{{ (isAdd ? 'notification.add-notification-target' : 'notification.edit-notification-target') | 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> |
|||
<div mat-dialog-content> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>notification.target-name</mat-label> |
|||
<input matInput formControlName="name" required> |
|||
<mat-error *ngIf="targetNotificationForm.get('name').hasError('required')"> |
|||
{{ 'notification.target-name-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<section formGroupName="configuration"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>notification.target-type.type</mat-label> |
|||
<mat-select formControlName="type"> |
|||
<mat-option *ngFor="let type of notificationTargetConfigTypes" [value]="type"> |
|||
{{ notificationTargetConfigTypeTranslateMap.get(type) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<tb-entity-list |
|||
required |
|||
formControlName="usersIds" |
|||
[entityType]="entityType.USER" |
|||
*ngIf="targetNotificationForm.get('configuration.type').value === notificationTargetConfigType.USER_LIST"> |
|||
</tb-entity-list> |
|||
<section |
|||
*ngIf="targetNotificationForm.get('configuration.type').value === notificationTargetConfigType.CUSTOMER_USERS"> |
|||
<mat-slide-toggle formControlName="getCustomerIdFromOriginatorEntity"> |
|||
{{ 'notification.get-customer-id-from-originator' | translate }} |
|||
</mat-slide-toggle> |
|||
<tb-entity-autocomplete |
|||
required |
|||
formControlName="customerId" |
|||
[entityType]="entityType.CUSTOMER" |
|||
*ngIf="!targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').value"> |
|||
</tb-entity-autocomplete> |
|||
</section> |
|||
</section> |
|||
</div> |
|||
<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) || targetNotificationForm.invalid || !targetNotificationForm.dirty" |
|||
color="primary" |
|||
(click)="save()">{{ (isAdd ? 'action.add' : 'action.save') | translate }}</button> |
|||
</div> |
|||
</form> |
|||
@ -0,0 +1,129 @@ |
|||
///
|
|||
/// 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 { |
|||
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 TargetsNotificationDialogData { |
|||
target?: NotificationTarget; |
|||
isAdd?: boolean; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-target-notification-dialog', |
|||
templateUrl: './target-notification-dialog.component.html', |
|||
styleUrls: [] |
|||
}) |
|||
export class TargetNotificationDialogComponent extends |
|||
DialogComponent<TargetNotificationDialogComponent, NotificationTarget> implements OnDestroy { |
|||
|
|||
targetNotificationForm: FormGroup; |
|||
notificationTargetConfigType = NotificationTargetConfigType; |
|||
notificationTargetConfigTypes: NotificationTargetConfigType[] = Object.values(NotificationTargetConfigType); |
|||
notificationTargetConfigTypeTranslateMap = NotificationTargetConfigTypeTranslateMap; |
|||
entityType = EntityType; |
|||
isAdd = true; |
|||
|
|||
private readonly destroy$ = new Subject<void>(); |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected router: Router, |
|||
protected dialogRef: MatDialogRef<TargetNotificationDialogComponent, NotificationTarget>, |
|||
@Inject(MAT_DIALOG_DATA) public data: TargetsNotificationDialogData, |
|||
private fb: FormBuilder, |
|||
private notificationService: NotificationService) { |
|||
super(store, router, dialogRef); |
|||
|
|||
if (isDefined(data.isAdd)) { |
|||
this.isAdd = data.isAdd; |
|||
} |
|||
|
|||
this.targetNotificationForm = this.fb.group({ |
|||
name: [null, Validators.required], |
|||
configuration: this.fb.group({ |
|||
type: [NotificationTargetConfigType.ALL_USERS], |
|||
usersIds: [{value: null, disabled: true}, Validators.required], |
|||
customerId: [{value: null, disabled: true}, Validators.required], |
|||
getCustomerIdFromOriginatorEntity: [{value: false, disabled: true}], |
|||
}) |
|||
}); |
|||
|
|||
this.targetNotificationForm.get('configuration.type').valueChanges.pipe( |
|||
takeUntil(this.destroy$) |
|||
).subscribe((type: NotificationTargetConfigType) => { |
|||
this.targetNotificationForm.get('configuration').disable({emitEvent: false}); |
|||
switch (type) { |
|||
case NotificationTargetConfigType.USER_LIST: |
|||
this.targetNotificationForm.get('configuration.usersIds').enable({emitEvent: false}); |
|||
break; |
|||
case NotificationTargetConfigType.CUSTOMER_USERS: |
|||
this.targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').enable({emitEvent: false}); |
|||
this.targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').updateValueAndValidity({onlySelf: true}); |
|||
break; |
|||
} |
|||
this.targetNotificationForm.get('configuration.type').enable({emitEvent: false}); |
|||
}); |
|||
this.targetNotificationForm.get('configuration.getCustomerIdFromOriginatorEntity').valueChanges.pipe( |
|||
takeUntil(this.destroy$) |
|||
).subscribe((value: boolean) => { |
|||
if (value) { |
|||
this.targetNotificationForm.get('configuration.customerId').disable({emitEvent: false}); |
|||
} else { |
|||
this.targetNotificationForm.get('configuration.customerId').enable({emitEvent: false}); |
|||
} |
|||
}); |
|||
|
|||
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.targetNotificationForm.value); |
|||
if (isDefined(this.data.target)) { |
|||
formValue = Object.assign({}, this.data.target, formValue); |
|||
} |
|||
this.notificationService.saveNotificationTarget(formValue).subscribe( |
|||
(target) => this.dialogRef.close(target) |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue