UI: Add new notiifcation target

This commit is contained in:
Vladyslav_Prykhodko 2023-03-17 16:35:34 +02:00
parent e6a56d0099
commit 615e4a2865
8 changed files with 215 additions and 49 deletions

View File

@ -89,6 +89,7 @@ import { DeviceProfileService } from '@core/http/device-profile.service';
import { QueueService } from '@core/http/queue.service';
import { AssetProfileService } from '@core/http/asset-profile.service';
import { NotificationService } from '@core/http/notification.service';
import { TenantProfileService } from '@core/http/tenant-profile.service';
@Injectable({
providedIn: 'root'
@ -112,6 +113,7 @@ export class EntityService {
private otaPackageService: OtaPackageService,
private widgetService: WidgetService,
private deviceProfileService: DeviceProfileService,
private tenantProfileService: TenantProfileService,
private assetProfileService: AssetProfileService,
private utils: UtilsService,
private queueService: QueueService,
@ -241,6 +243,11 @@ export class EntityService {
(id) => this.deviceProfileService.getDeviceProfileInfo(id, config),
entityIds);
break;
case EntityType.TENANT_PROFILE:
observable = this.getEntitiesByIdsObservable(
(id) => this.tenantProfileService.getTenantProfileInfo(id, config),
entityIds);
break;
case EntityType.ASSET_PROFILE:
observable = this.getEntitiesByIdsObservable(
(id) => this.assetProfileService.getAssetProfileInfo(id, config),
@ -399,6 +406,10 @@ export class EntityService {
pageLink.sortOrder.property = 'name';
entitiesObservable = this.deviceProfileService.getDeviceProfileInfos(pageLink, null, config);
break;
case EntityType.TENANT_PROFILE:
pageLink.sortOrder.property = 'name';
entitiesObservable = this.tenantProfileService.getTenantProfiles(pageLink, config);
break;
case EntityType.ASSET_PROFILE:
pageLink.sortOrder.property = 'name';
entitiesObservable = this.assetProfileService.getAssetProfileInfos(pageLink, config);

View File

@ -159,16 +159,16 @@
<mat-button-toggle-group class="tb-notification-unread-toggle-group"
style="width: 250px;"
formControlName="filterByDevice">
<mat-button-toggle fxFlex [value]=true>{{ 'notification.device' | translate }}</mat-button-toggle>
<mat-button-toggle fxFlex [value]=false>{{ 'notification.device-profile' | translate }}</mat-button-toggle>
<mat-button-toggle fxFlex [value]=true>{{ 'device.device' | translate }}</mat-button-toggle>
<mat-button-toggle fxFlex [value]=false>{{ 'device-profile.device-profile' | translate }}</mat-button-toggle>
</mat-button-toggle-group>
</div>
<ng-container *ngIf="deviceInactivityTemplateForm.get('triggerConfig.filterByDevice').value; else deviceProfile">
<tb-entity-list
formControlName="devices"
subscriptSizing="dynamic"
labelText="{{ 'notification.devices' | translate }}"
placeholderText="{{ 'notification.devices' | translate }}"
labelText="{{ 'device.devices' | translate }}"
placeholderText="{{ 'device.devices' | translate }}"
hint="{{ 'notification.device-list-rule-hint' | translate }}"
[entityType]="entityType.DEVICE">
</tb-entity-list>
@ -177,8 +177,8 @@
<tb-entity-list
formControlName="deviceProfiles"
subscriptSizing="dynamic"
labelText="{{ 'notification.device-profiles' | translate }}"
placeholderText="{{ 'notification.device-profiles' | translate }}"
labelText="{{ 'device-profile.device-profiles' | translate }}"
placeholderText="{{ 'device-profile.device-profiles' | translate }}"
hint="{{ 'notification.device-profiles-list-rule-hint' | translate }}"
[entityType]="entityType.DEVICE_PROFILE">
</tb-entity-list>
@ -258,9 +258,14 @@
</mat-select>
</mat-form-field>
</fieldset>
<mat-slide-toggle formControlName="onlyUserComments" style="margin-bottom: 12px;">
{{ 'notification.notify-only-user-comments' | translate }}
</mat-slide-toggle>
<section fxLayout="column">
<mat-slide-toggle formControlName="onlyUserComments" style="margin-bottom: 12px;">
{{ 'notification.notify-only-user-comments' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="notifyOnCommentUpdate" style="margin-bottom: 12px;">
{{ 'notification.notify-on-comment-update' | translate }}
</mat-slide-toggle>
</section>
</section>
</form>
<form [formGroup]="ruleNotificationForm">
@ -306,9 +311,17 @@
</mat-select>
</mat-form-field>
</fieldset>
<mat-slide-toggle formControlName="notifyOnUnassign" style="margin-bottom: 12px;">
{{ 'notification.notify-on-unassign' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>notification.notify-on</mat-label>
<mat-select formControlName="notifyOn" multiple>
<mat-option *ngFor="let alarmAssignmentAction of alarmAssignmentActions" [value]="alarmAssignmentAction">
{{ alarmAssignmentActionTranslationMap.get(alarmAssignmentAction) | translate }}
</mat-option>
</mat-select>
<mat-error *ngIf="alarmAssignmentTemplateForm.get('triggerConfig.notifyOn').hasError('required')">
{{ 'notification.notify-on-required' | translate }}
</mat-error>
</mat-form-field>
</section>
</form>
<form [formGroup]="ruleNotificationForm">

View File

@ -17,6 +17,8 @@
import {
AlarmAction,
AlarmActionTranslationMap,
AlarmAssignmentAction,
AlarmAssignmentActionTranslationMap,
NotificationRule,
NotificationTarget,
TriggerType,
@ -94,6 +96,9 @@ export class RuleNotificationDialogComponent extends
alarmActions: AlarmAction[] = Object.values(AlarmAction);
alarmActionTranslationMap = AlarmActionTranslationMap;
alarmAssignmentActions: AlarmAssignmentAction[] = Object.values(AlarmAssignmentAction);
alarmAssignmentActionTranslationMap = AlarmAssignmentActionTranslationMap;
entityType = EntityType;
entityTypes: EntityType[] = Object.values(EntityType);
isAdd = true;
@ -207,7 +212,8 @@ export class RuleNotificationDialogComponent extends
alarmTypes: [null],
alarmSeverities: [[]],
alarmStatuses: [[]],
onlyUserComments: [false]
onlyUserComments: [false],
notifyOnCommentUpdate: [false]
})
});
@ -216,7 +222,7 @@ export class RuleNotificationDialogComponent extends
alarmTypes: [null],
alarmSeverities: [[]],
alarmStatuses: [[]],
notifyOnUnassign: [true]
notifyOn: [[AlarmAssignmentAction.ASSIGNED], Validators.required]
})
});

View File

@ -56,6 +56,38 @@
</mat-select>
</mat-form-field>
<ng-container [ngSwitch]="targetNotificationForm.get('configuration.usersFilter.type').value">
<ng-container *ngSwitchCase="notificationTargetConfigType.TENANT_ADMINISTRATORS">
<section *ngIf="isSysAdmin()">
<div fxFlex fxLayoutAlign="center center">
<mat-button-toggle-group class="tb-notification-tenant-group"
style="width: 280px;"
formControlName="filterByTenants">
<mat-button-toggle fxFlex [value]=true>{{ 'tenant.tenant' | translate }}</mat-button-toggle>
<mat-button-toggle fxFlex [value]=false>{{ 'tenant-profile.tenant-profile' | translate }}</mat-button-toggle>
</mat-button-toggle-group>
</div>
<ng-container *ngIf="targetNotificationForm.get('configuration.usersFilter.filterByTenants').value; else tenantProfiles">
<tb-entity-list
formControlName="tenantsIds"
subscriptSizing="dynamic"
labelText="{{ 'tenant.tenants' | translate }}"
placeholderText="{{ 'tenant.tenants' | translate }}"
hint="{{ 'notification.tenants-list-rule-hint' | translate }}"
[entityType]="entityType.TENANT">
</tb-entity-list>
</ng-container>
<ng-template #tenantProfiles>
<tb-entity-list
formControlName="tenantProfilesIds"
subscriptSizing="dynamic"
labelText="{{ 'tenant-profile.tenant-profiles' | translate }}"
placeholderText="{{ 'tenant-profile.tenant-profiles' | translate }}"
hint="{{ 'notification.tenant-profiles-list-rule-hint' | translate }}"
[entityType]="entityType.TENANT_PROFILE">
</tb-entity-list>
</ng-template>
</section>
</ng-container>
<ng-container *ngSwitchCase="notificationTargetConfigType.USER_LIST">
<tb-entity-list
required

View File

@ -56,4 +56,58 @@
flex-direction: column;
}
}
.mat-button-toggle-group.tb-notification-tenant-group {
&.mat-button-toggle-group-appearance-standard {
border: none;
border-radius: 18px;
margin-bottom: 14px;
.mat-button-toggle + .mat-button-toggle {
border-left: none;
}
}
.mat-button-toggle {
background: rgba(0, 0, 0, 0.06);
height: 36px;
align-items: center;
display: flex;
.mat-button-toggle-ripple {
top: 2px;
left: 2px;
right: 2px;
bottom: 2px;
border-radius: 18px;
}
}
.mat-button-toggle-button {
color: #959595;
}
.mat-button-toggle-focus-overlay {
border-radius: 18px;
margin: 2px;
}
.mat-button-toggle-checked .mat-button-toggle-button {
background-color: $tb-primary-color;
color: #fff;
border-radius: 18px;
margin-left: 2px;
margin-right: 2px;
}
.mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
line-height: 34px;
font-size: 16px;
font-weight: 500;
}
.mat-button-toggle-checked.mat-button-toggle-appearance-standard:not(.mat-button-toggle-disabled):hover .mat-button-toggle-focus-overlay {
opacity: .01;
}
}
}

View File

@ -32,7 +32,7 @@ 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 { deepTrim, isDefinedAndNotNull } from '@core/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Authority } from '@shared/models/authority.enum';
@ -61,7 +61,7 @@ export class TargetNotificationDialogComponent extends
notificationTargetTypes: NotificationTargetType[] = Object.values(NotificationTargetType);
notificationTargetTypeTranslationMap = NotificationTargetTypeTranslationMap;
notificationTargetConfigType = NotificationTargetConfigType;
notificationTargetConfigTypes: NotificationTargetConfigType[] = Object.values(NotificationTargetConfigType);
notificationTargetConfigTypes: NotificationTargetConfigType[] = this.allowNotificationTargetConfigTypes();
notificationTargetConfigTypeTranslateMap = NotificationTargetConfigTypeTranslateMap;
slackChanelTypes = Object.keys(SlackChanelType) as SlackChanelType[];
slackChanelTypesTranslateMap = SlackChanelTypesTranslateMap;
@ -79,7 +79,7 @@ export class TargetNotificationDialogComponent extends
private notificationService: NotificationService) {
super(store, router, dialogRef);
if (isDefined(data.isAdd)) {
if (isDefinedAndNotNull(data.isAdd)) {
this.isAdd = data.isAdd;
}
@ -88,9 +88,12 @@ export class TargetNotificationDialogComponent extends
configuration: this.fb.group({
type: [NotificationTargetType.PLATFORM_USERS],
usersFilter: this.fb.group({
type: [{value: NotificationTargetConfigType.ALL_USERS, disabled: !this.isTenantAdmin()}],
type: [NotificationTargetConfigType.ALL_USERS],
filterByTenants: [{value: true, disabled: true}],
tenantsIds: [{value: null, disabled: true}],
tenantProfilesIds: [{value: null, disabled: true}],
customerId: [{value: null, disabled: true}, Validators.required],
usersIds: [{value: null, disabled: true}, Validators.required],
customerId: [{value: null, disabled: true}, Validators.required]
}),
conversationType: [{value: SlackChanelType.PUBLIC_CHANNEL, disabled: true}],
conversation: [{value: '', disabled: true}, Validators.required],
@ -104,10 +107,8 @@ export class TargetNotificationDialogComponent extends
this.targetNotificationForm.get('configuration').disable({emitEvent: false});
switch (type) {
case NotificationTargetType.PLATFORM_USERS:
if (this.isTenantAdmin()) {
this.targetNotificationForm.get('configuration.usersFilter').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.usersFilter.type').updateValueAndValidity({onlySelf: true});
}
this.targetNotificationForm.get('configuration.usersFilter').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.usersFilter.type').updateValueAndValidity({onlySelf: true});
break;
case NotificationTargetType.SLACK:
this.targetNotificationForm.get('configuration.conversationType').enable({emitEvent: false});
@ -123,6 +124,11 @@ export class TargetNotificationDialogComponent extends
).subscribe((type: NotificationTargetConfigType) => {
this.targetNotificationForm.get('configuration.usersFilter').disable({emitEvent: false});
switch (type) {
case NotificationTargetConfigType.TENANT_ADMINISTRATORS:
if (this.isSysAdmin()) {
this.targetNotificationForm.get('configuration.usersFilter.filterByTenants').enable({onlySelf: true});
}
break;
case NotificationTargetConfigType.USER_LIST:
this.targetNotificationForm.get('configuration.usersFilter.usersIds').enable({emitEvent: false});
break;
@ -133,9 +139,25 @@ export class TargetNotificationDialogComponent extends
this.targetNotificationForm.get('configuration.usersFilter.type').enable({emitEvent: false});
});
if (isDefined(data.target)) {
this.targetNotificationForm.get('configuration.usersFilter.filterByTenants').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value: boolean) => {
if (value) {
this.targetNotificationForm.get('configuration.usersFilter.tenantsIds').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.usersFilter.tenantProfilesIds').disable({emitEvent: false});
} else {
this.targetNotificationForm.get('configuration.usersFilter.tenantsIds').disable({emitEvent: false});
this.targetNotificationForm.get('configuration.usersFilter.tenantProfilesIds').enable({emitEvent: false});
}
});
if (isDefinedAndNotNull(data.target)) {
this.targetNotificationForm.patchValue(data.target, {emitEvent: false});
this.targetNotificationForm.get('configuration.type').updateValueAndValidity({onlySelf: true});
if (this.isSysAdmin() && data.target.configuration.usersFilter.type === NotificationTargetConfigType.TENANT_ADMINISTRATORS) {
this.targetNotificationForm.get('configuration.usersFilter.filterByTenants')
.patchValue(!Array.isArray(this.data.target.configuration.usersFilter.tenantProfilesIds), {onlySelf: true});
}
}
}
@ -150,21 +172,27 @@ export class TargetNotificationDialogComponent extends
}
save() {
let formValue: NotificationTarget = deepTrim(this.targetNotificationForm.value);
if (isDefined(this.data.target)) {
let formValue = deepTrim(this.targetNotificationForm.value);
if (isDefinedAndNotNull(this.data.target)) {
formValue = Object.assign({}, this.data.target, formValue);
}
if (formValue.configuration.type === NotificationTargetType.PLATFORM_USERS && !formValue.configuration.usersFilter) {
formValue.configuration.usersFilter = {
type: NotificationTargetConfigType.ALL_USERS
}
if (this.isSysAdmin() && formValue.configuration.type === NotificationTargetType.PLATFORM_USERS &&
formValue.configuration.usersFilter.type === NotificationTargetConfigType.TENANT_ADMINISTRATORS) {
delete formValue.configuration.usersFilter.filterByTenants;
}
this.notificationService.saveNotificationTarget(formValue).subscribe(
(target) => this.dialogRef.close(target)
);
}
public isTenantAdmin(): boolean {
return this.authUser.authority === Authority.TENANT_ADMIN;
isSysAdmin(): boolean {
return this.authUser.authority === Authority.SYS_ADMIN;
}
private allowNotificationTargetConfigTypes(): NotificationTargetConfigType[] {
if (this.isSysAdmin()) {
return [NotificationTargetConfigType.ALL_USERS, NotificationTargetConfigType.TENANT_ADMINISTRATORS];
}
return Object.values(NotificationTargetConfigType);
}
}

View File

@ -138,15 +138,15 @@ export interface AlarmCommentNotificationRuleTriggerConfig {
alarmTypes?: Array<string>;
alarmSeverities?: Array<AlarmSeverity>;
alarmStatuses?: Array<AlarmSearchStatus>;
notifyOnUnassign?: boolean;
onlyUserComments?: boolean;
notifyOnCommentUpdate?: boolean;
}
export interface AlarmAssignmentNotificationRuleTriggerConfig {
alarmTypes?: Array<string>;
alarmSeverities?: Array<AlarmSeverity>;
alarmStatuses?: Array<AlarmSearchStatus>;
notifyOnUnassign?: boolean;
notifyOn: Array<AlarmAssignmentAction>;
}
export enum AlarmAction {
@ -163,6 +163,16 @@ export const AlarmActionTranslationMap = new Map<AlarmAction, string>([
[AlarmAction.CLEARED, 'notification.notify-alarm-action.cleared']
])
export enum AlarmAssignmentAction {
ASSIGNED = 'ASSIGNED',
UNASSIGNED = 'UNASSIGNED'
}
export const AlarmAssignmentActionTranslationMap = new Map<AlarmAssignmentAction, string>([
[AlarmAssignmentAction.ASSIGNED, 'notification.notify-alarm-action.assigned'],
[AlarmAssignmentAction.UNASSIGNED, 'notification.notify-alarm-action.unassigned']
])
export interface NotificationRuleRecipientConfig {
targets?: Array<string>;
escalationTable?: {[key: number]: Array<string>};
@ -187,18 +197,23 @@ export interface PlatformUsersNotificationTargetConfig {
}
export interface UsersFilter extends
Partial<UserListNotificationTargetConfig & CustomerUsersNotificationTargetConfig>{
Partial<UserListFilter & CustomerUsersFilter & TenantAdministratorsFilter>{
type: NotificationTargetConfigType;
}
interface UserListNotificationTargetConfig {
interface UserListFilter {
usersIds: Array<string>;
}
interface CustomerUsersNotificationTargetConfig {
interface CustomerUsersFilter {
customerId: string;
}
interface TenantAdministratorsFilter {
tenantsIds?: Array<string>;
tenantProfilesIds?: Array<string>;
}
export interface SlackNotificationTargetConfig {
conversationType: SlackChanelType;
conversation: SlackConversation;
@ -310,16 +325,20 @@ export const SlackChanelTypesTranslateMap = new Map<SlackChanelType, string>([
export enum NotificationTargetConfigType {
ALL_USERS = 'ALL_USERS',
USER_LIST = 'USER_LIST',
TENANT_ADMINISTRATORS = 'TENANT_ADMINISTRATORS',
CUSTOMER_USERS = 'CUSTOMER_USERS',
ORIGINATOR_ENTITY_OWNER_USERS = 'ORIGINATOR_ENTITY_OWNER_USERS'
USER_LIST = 'USER_LIST',
ORIGINATOR_ENTITY_OWNER_USERS = 'ORIGINATOR_ENTITY_OWNER_USERS',
ACTION_TARGET_USER = 'ACTION_TARGET_USER'
}
export const NotificationTargetConfigTypeTranslateMap = new Map<NotificationTargetConfigType, string>([
[NotificationTargetConfigType.ALL_USERS, 'notification.target-type.all-users'],
[NotificationTargetConfigType.USER_LIST, 'notification.target-type.user-list'],
[NotificationTargetConfigType.TENANT_ADMINISTRATORS, 'notification.target-type.tenant-administrators'],
[NotificationTargetConfigType.CUSTOMER_USERS, 'notification.target-type.customer-users'],
[NotificationTargetConfigType.ORIGINATOR_ENTITY_OWNER_USERS, 'notification.target-type.originator-entity-owner-users']
[NotificationTargetConfigType.USER_LIST, 'notification.target-type.user-list'],
[NotificationTargetConfigType.ORIGINATOR_ENTITY_OWNER_USERS, 'notification.target-type.originator-entity-owner-users'],
[NotificationTargetConfigType.ACTION_TARGET_USER, 'notification.target-type.action-target-user'],
]);
export enum NotificationType {

View File

@ -2768,13 +2768,9 @@
},
"delivery-methods": "Delivery methods",
"description": "Description",
"device": "Device",
"device-inactivity-trigger-settings": "Device inactive trigger settings",
"device-list-rule-hint": "If the field is empty, the trigger will be applied to all devices",
"device-profile": "Device profile",
"device-profiles": "Device profiles",
"device-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all device profiles",
"devices": "Devices",
"edit-notification-target": "Edit notification recipient",
"edit-notification-template": "Edit notification template",
"edit-rule": "Edit rule",
@ -2816,13 +2812,16 @@
"notification-target": "Notification recipient",
"notify": "notify",
"notify-alarm-action": {
"created": "Alarm created",
"severity-changed": "Alarm severity changed",
"acknowledged": "Alarm acknowledged",
"cleared": "Alarm cleared"
"assigned": "Alarm assigned",
"created": "Alarm created",
"severity-changed": "Alarm severity changed",
"acknowledged": "Alarm acknowledged",
"cleared": "Alarm cleared",
"unassigned": "Alarm unassigned"
},
"notify-again": "Notify again",
"notify-on": "Notify on",
"notify-on-comment-update": "Notify on comment update",
"notify-on-required": "Notify on is required",
"notify-only-user-comments": "Notify only user comments",
"notify-on-unassign": "Notify on unassign",
@ -2866,9 +2865,11 @@
"target-name": "Name",
"target-name-required": "Name is required",
"target-type": {
"action-target-user": "Action target user",
"all-users": "All users",
"customer-users": "Customer users",
"originator-entity-owner-users": "Users of originator entity owner",
"tenant-administrators": "Tenant administrators",
"user-filters": "User filter",
"user-list": "User list"
},
@ -2894,6 +2895,8 @@
"general": "General"
},
"templates": "Templates",
"tenants-list-rule-hint": "If the field is empty, the trigger will be applied to all tenants",
"tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles",
"time": "Time",
"trigger": {
"alarm": "Alarm",