Browse Source

Merge remote-tracking branch 'origin/feature/notification-system' into feature/notification-system

pull/7911/head
ViacheslavKlimov 4 years ago
parent
commit
3dc0ec5845
  1. 3
      ui-ngx/src/app/core/http/entity.service.ts
  2. 16
      ui-ngx/src/app/core/http/notification.service.ts
  3. 28
      ui-ngx/src/app/core/services/menu.service.ts
  4. 5
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html
  5. 1
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts
  6. 5
      ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts
  7. 18
      ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts
  8. 6
      ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.componet.scss
  9. 10
      ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts
  10. 3
      ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.html
  11. 7
      ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.ts
  12. 2
      ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.html
  13. 2
      ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.ts
  14. 7
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
  15. 10
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
  16. 8
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.ts
  17. 121
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html
  18. 2
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss
  19. 26
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts
  20. 24
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts
  21. 14
      ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts
  22. 66
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html
  23. 2
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss
  24. 12
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts
  25. 18
      ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts
  26. 3
      ui-ngx/src/app/shared/components/notification/notification.component.html
  27. 21
      ui-ngx/src/app/shared/models/entity-type.models.ts
  28. 16
      ui-ngx/src/app/shared/models/notification.models.ts
  29. 1
      ui-ngx/src/app/shared/models/tenant.model.ts
  30. 69
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
ui-ngx/src/app/core/http/entity.service.ts

@ -90,6 +90,7 @@ 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';
import { NotificationType } from '@shared/models/notification.models';
@Injectable({
providedIn: 'root'
@ -418,7 +419,7 @@ export class EntityService {
break;
case EntityType.NOTIFICATION_TARGET:
pageLink.sortOrder.property = 'name';
entitiesObservable = this.notificationService.getNotificationTargets(pageLink, config);
entitiesObservable = this.notificationService.getNotificationTargets(pageLink, subType as NotificationType, config);
break;
}
return entitiesObservable;

16
ui-ngx/src/app/core/http/notification.service.ts

@ -34,7 +34,7 @@ import {
SlackConversation
} from '@shared/models/notification.models';
import { User } from '@shared/models/user.model';
import { isDefinedAndNotNull } from '@core/utils';
import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils';
@Injectable({
providedIn: 'root'
@ -51,6 +51,10 @@ export class NotificationService {
defaultHttpOptionsFromConfig(config));
}
public deleteNotification(id: string, config?: RequestConfig): Observable<void> {
return this.http.delete<void>(`/api/notification/${id}`, defaultHttpOptionsFromConfig(config));
}
public markNotificationAsRead(id: string, config?: RequestConfig): Observable<void> {
return this.http.put<void>(`/api/notification/${id}/read`, defaultHttpOptionsFromConfig(config));
}
@ -127,9 +131,13 @@ export class NotificationService {
defaultHttpOptionsFromConfig(config));
}
public getNotificationTargets(pageLink: PageLink, config?: RequestConfig): Observable<PageData<NotificationTarget>> {
return this.http.get<PageData<NotificationTarget>>(`/api/notification/targets${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
public getNotificationTargets(pageLink: PageLink, type?: NotificationType,
config?: RequestConfig): Observable<PageData<NotificationTarget>> {
let url = `/api/notification/targets${pageLink.toQuery()}`;
if (isNotEmptyStr(type)) {
url += `&notificationType=${type}`;
}
return this.http.get<PageData<NotificationTarget>>(url, defaultHttpOptionsFromConfig(config));
}
public getRecipientsForNotificationTargetConfig(notificationTarget: NotificationTarget, pageLink: PageLink,

28
ui-ngx/src/app/core/services/menu.service.ts

@ -160,18 +160,18 @@ export class MenuService {
},
{
id: guid(),
name: 'notification.templates',
name: 'notification.recipients',
type: 'link',
path: '/notification/templates',
icon: 'mdi:message-draw',
isMdiIcon: true
path: '/notification/recipients',
icon: 'contacts'
},
{
id: guid(),
name: 'notification.recipients',
name: 'notification.templates',
type: 'link',
path: '/notification/recipients',
icon: 'contacts'
path: '/notification/templates',
icon: 'mdi:message-draw',
isMdiIcon: true
},
{
id: guid(),
@ -529,18 +529,18 @@ export class MenuService {
},
{
id: guid(),
name: 'notification.templates',
name: 'notification.recipients',
type: 'link',
path: '/notification/templates',
icon: 'mdi:message-draw',
isMdiIcon: true
path: '/notification/recipients',
icon: 'contacts'
},
{
id: guid(),
name: 'notification.recipients',
name: 'notification.templates',
type: 'link',
path: '/notification/recipients',
icon: 'contacts'
path: '/notification/templates',
icon: 'mdi:message-draw',
isMdiIcon: true
},
{
id: guid(),

5
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html

@ -476,6 +476,11 @@
[type]="rateLimitsType.CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION">
</tb-rate-limits>
</div>
<div fxFlex fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="16px">
<tb-rate-limits fxFlex="50" formControlName="tenantNotificationRequestsRateLimit"
[type]="rateLimitsType.TENANT_NOTIFICATION_REQUEST_RATE_LIMIT">
</tb-rate-limits>
</div>
</ng-template>
</mat-expansion-panel>
</fieldset>

1
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts

@ -72,6 +72,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
transportDeviceTelemetryDataPointsRateLimit: [null, []],
tenantEntityExportRateLimit: [null, []],
tenantEntityImportRateLimit: [null, []],
tenantNotificationRequestsRateLimit: [null, []],
maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
maxREExecutions: [null, [Validators.required, Validators.min(0)]],

5
ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts

@ -33,7 +33,8 @@ export enum RateLimitsType {
WS_UPDATE_PER_SESSION_RATE_LIMIT = 'WS_UPDATE_PER_SESSION_RATE_LIMIT',
CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION = 'CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION',
TENANT_ENTITY_EXPORT_RATE_LIMIT = 'TENANT_ENTITY_EXPORT_RATE_LIMIT',
TENANT_ENTITY_IMPORT_RATE_LIMIT = 'TENANT_ENTITY_IMPORT_RATE_LIMIT'
TENANT_ENTITY_IMPORT_RATE_LIMIT = 'TENANT_ENTITY_IMPORT_RATE_LIMIT',
TENANT_NOTIFICATION_REQUEST_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUEST_RATE_LIMIT'
}
export const rateLimitsLabelTranslationMap = new Map<RateLimitsType, string>(
@ -50,6 +51,7 @@ export const rateLimitsLabelTranslationMap = new Map<RateLimitsType, string>(
[RateLimitsType.CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION, 'tenant-profile.cassandra-tenant-limits-configuration'],
[RateLimitsType.TENANT_ENTITY_EXPORT_RATE_LIMIT, 'tenant-profile.tenant-entity-export-rate-limit'],
[RateLimitsType.TENANT_ENTITY_IMPORT_RATE_LIMIT, 'tenant-profile.tenant-entity-import-rate-limit'],
[RateLimitsType.TENANT_NOTIFICATION_REQUEST_RATE_LIMIT, 'tenant-profile.tenant-notification-request-rate-limit'],
]
);
@ -67,6 +69,7 @@ export const rateLimitsDialogTitleTranslationMap = new Map<RateLimitsType, strin
[RateLimitsType.CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION, 'tenant-profile.rate-limits.edit-cassandra-tenant-limits-configuration-title'],
[RateLimitsType.TENANT_ENTITY_EXPORT_RATE_LIMIT, 'tenant-profile.rate-limits.edit-tenant-entity-export-rate-limit-title'],
[RateLimitsType.TENANT_ENTITY_IMPORT_RATE_LIMIT, 'tenant-profile.rate-limits.edit-tenant-entity-import-rate-limit-title'],
[RateLimitsType.TENANT_NOTIFICATION_REQUEST_RATE_LIMIT, 'tenant-profile.rate-limits.edit-tenant-notification-request-rate-limit-title'],
]
);

18
ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
import { DatePipe } from '@angular/common';
import { Direction } from '@shared/models/page/sort-order';
import {
@ -50,17 +50,19 @@ export class InboxTableConfigResolver implements Resolve<EntityTableConfig<Notif
private dialog: MatDialog,
private datePipe: DatePipe) {
this.config.entityType = EntityType.NOTIFICATION;
this.config.detailsPanelEnabled = false;
this.config.selectionEnabled = false;
this.config.addEnabled = false;
this.config.entitiesDeleteEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = {
noEntities: 'notification.no-inbox-notification',
search: 'notification.search-notification'
};
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION);
this.config.entityResources = {} as EntityTypeResource<Notification>;
this.config.deleteEntityTitle = () => this.translate.instant('notification.delete-notification-title');
this.config.deleteEntityContent = () => this.translate.instant('notification.delete-notification-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('notification.delete-notifications-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('notification.delete-notifications-text');
this.config.deleteEntity = id => this.notificationService.deleteNotification(id.id);
this.config.entitiesFetchFunction = pageLink =>
this.notificationService.getNotifications(pageLink, this.config.componentsData.unreadOnly);
@ -87,7 +89,7 @@ export class InboxTableConfigResolver implements Resolve<EntityTableConfig<Notif
}];
this.config.columns.push(
new DateEntityTableColumn<Notification>('createdTime', 'notification.created-time', this.datePipe, '170px'),
new DateEntityTableColumn<Notification>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<Notification>('type', 'notification.type', '10%', (notification) =>
this.translate.instant(NotificationTemplateTypeTranslateMap.get(notification.type).name)),
new EntityTableColumn<Notification>('subject', 'notification.subject', '30%'),

6
ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.componet.scss

@ -42,12 +42,8 @@
.mat-mdc-radio-button {
flex: 1 1 100%;
padding: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
&.mat-mdc-radio-checked {
border-color: map_get($tb-accent, 500);
}
}
}

10
ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
import { Direction } from '@shared/models/page/sort-order';
import { NotificationTarget, NotificationTargetTypeTranslationMap } from '@shared/models/notification.models';
import { NotificationService } from '@core/http/notification.service';
@ -46,15 +46,13 @@ export class RecipientTableConfigResolver implements Resolve<EntityTableConfig<N
private dialog: MatDialog,
private datePipe: DatePipe) {
this.config.entityType = EntityType.NOTIFICATION_TARGET;
this.config.detailsPanelEnabled = false;
this.config.selectionEnabled = false;
this.config.addEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = {
noEntities: 'notification.no-targets-notification',
search: 'notification.search-targets'
};
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TARGET);
this.config.entityResources = {} as EntityTypeResource<NotificationTarget>;
this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTargets(pageLink);
@ -76,7 +74,7 @@ export class RecipientTableConfigResolver implements Resolve<EntityTableConfig<N
};
this.config.columns.push(
new DateEntityTableColumn<NotificationTarget>('createdTime', 'notification.created-time', this.datePipe, '170px'),
new DateEntityTableColumn<NotificationTarget>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<NotificationTarget>('name', 'notification.notification-target', '20%'),
new EntityTableColumn<NotificationTarget>('configuration.type', 'notification.type', '20%',
(target) => this.translate.instant(NotificationTargetTypeTranslationMap.get(target.configuration.type)),

3
ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.html

@ -35,7 +35,8 @@
fxFlex="60"
formControlName="targets"
subscriptSizing="dynamic"
[entityType]="entityType.NOTIFICATION_TARGET"
entityType="{{ entityType.NOTIFICATION_TARGET }}"
subType="{{ notificationType.ALARM }}"
[placeholderText]="escalationFormGroup.get('targets').value ? '' : ('notification.add-target' | translate)">
<button #createTargetButton
mat-button [fxHide]="disabled" matSuffix

7
ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.ts

@ -27,7 +27,11 @@ import {
} from '@angular/forms';
import { isDefinedAndNotNull } from '@core/utils';
import { Subject } from 'rxjs';
import { NonConfirmedNotificationEscalation, NotificationTarget } from '@shared/models/notification.models';
import {
NonConfirmedNotificationEscalation,
NotificationTarget,
NotificationType
} from '@shared/models/notification.models';
import { EntityType } from '@shared/models/entity-type.models';
import { takeUntil } from 'rxjs/operators';
import {
@ -65,6 +69,7 @@ export class EscalationFormComponent implements ControlValueAccessor, OnInit, On
escalationFormGroup: FormGroup;
entityType = EntityType;
notificationType = NotificationType;
private modelValue;
private propagateChange = null;

2
ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.html

@ -42,7 +42,7 @@
<button mat-raised-button color="primary"
type="button"
(click)="addEscalation()">
<span translate>notification.add-rule</span>
<span translate>notification.add-stage</span>
</button>
</div>
</div>

2
ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.ts

@ -133,7 +133,7 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
(this.escalationsFormGroup.get('escalations') as FormArray).removeAt(index);
}
public addEscalation(delay = 60000) {
public addEscalation(delay = 3600000) {
const escalation = {
delayInSec: delay,
targets: null

7
ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html

@ -65,7 +65,8 @@
<tb-entity-list
required
formControlName="targets"
[entityType]="entityType.NOTIFICATION_TARGET"
subType="{{ ruleNotificationForm.get('triggerType').value }}"
entityType="{{ entityType.NOTIFICATION_TARGET }}"
placeholderText="{{ 'notification.target' | translate }}">
<button #createTargetButton
mat-button color="primary" matSuffix
@ -76,7 +77,7 @@
</section>
<ng-template #alarmTargesConfig>
<fieldset class="fields-group tb-hierarchy" formGroupName="recipientsConfig">
<legend translate>notification.notification-chain</legend>
<legend translate>notification.escalation-chain</legend>
<div class="mat-body-2" translate>notification.targets</div>
<tb-escalations-component formControlName="escalationTable"></tb-escalations-component>
<ng-container *ngTemplateOutlet="clearRules"></ng-container>
@ -87,7 +88,7 @@
<form [formGroup]="alarmTemplateForm">
<section formGroupName="triggerConfig">
<section formGroupName="clearRule">
<div class="mat-body-2" translate>notification.clear-rule</div>
<div class="mat-body-2" translate>notification.stop-escalation-alarm-status-become</div>
<mat-form-field fxFlex class="mat-block" floatLabel="always" style="margin-right: 48px">
<mat-label translate>alarm.alarm-status-list</mat-label>
<mat-select formControlName="alarmStatuses" multiple

10
ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
import { Direction } from '@shared/models/page/sort-order';
import { NotificationRule, TriggerTypeTranslationMap } from '@shared/models/notification.models';
import { NotificationService } from '@core/http/notification.service';
@ -46,15 +46,13 @@ export class RuleTableConfigResolver implements Resolve<EntityTableConfig<Notifi
private dialog: MatDialog,
private datePipe: DatePipe) {
this.config.entityType = EntityType.NOTIFICATION_RULE;
this.config.detailsPanelEnabled = false;
this.config.selectionEnabled = false;
this.config.addEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = {
noEntities: 'notification.no-rules-notification',
search: 'notification.search-rules'
};
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_RULE);
this.config.entityResources = {} as EntityTypeResource<NotificationRule>;
this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationRules(pageLink);
@ -75,7 +73,7 @@ export class RuleTableConfigResolver implements Resolve<EntityTableConfig<Notifi
};
this.config.columns.push(
new DateEntityTableColumn<NotificationRule>('createdTime', 'notification.created-time', this.datePipe, '170px'),
new DateEntityTableColumn<NotificationRule>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<NotificationRule>('name', 'notification.rule-name', '30%'),
new EntityTableColumn<NotificationRule>('templateName', 'notification.template', '20%'),
new EntityTableColumn<NotificationRule>('triggerType', 'notification.trigger.trigger', '20%',

8
ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.ts

@ -36,10 +36,10 @@ export class SentErrorDialogComponent extends DialogComponent<SentErrorDialogCom
errorStats: { [key in NotificationDeliveryMethod]: {[errorKey in string]: string}};
notificationDeliveryMethodErrorTranslateMap = new Map<NotificationDeliveryMethod, string>([
[NotificationDeliveryMethod.WEB, 'notification.delivery-method-type.web-failed-sent'],
[NotificationDeliveryMethod.SMS, 'notification.delivery-method-type.sms-failed-sent'],
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method-type.email-failed-sent'],
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method-type.slack-failed-sent'],
[NotificationDeliveryMethod.WEB, 'notification.delivery-method.web-failed-sent'],
[NotificationDeliveryMethod.SMS, 'notification.delivery-method.sms-failed-sent'],
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method.email-failed-sent'],
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method.slack-failed-sent'],
]);
constructor(protected store: Store<AppState>,

121
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html

@ -28,7 +28,8 @@
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper [linear]="true" #createNotification
<mat-horizontal-stepper linear #createNotification
[labelPosition]="(stepperLabelPosition | async)"
[orientation]="(stepperOrientation | async)"
(selectionChange)="changeStep($event)">
<ng-template matStepperIcon="edit">
@ -45,32 +46,19 @@
<mat-button-toggle fxFlex [value]=true>{{ 'notification.use-template' | translate }}</mat-button-toggle>
</mat-button-toggle-group>
</div>
<tb-template-autocomplete
*ngIf="notificationRequestForm.get('useTemplate').value; else scratchTemplate"
required
allowCreate
formControlName="templateId"
[notificationTypes]="notificationType.GENERAL">
</tb-template-autocomplete>
<div *ngIf="notificationRequestForm.get('useTemplate').value; else scratchTemplate">
<tb-template-autocomplete
required
allowCreate
formControlName="templateId"
[notificationTypes]="notificationType.GENERAL">
</tb-template-autocomplete>
<ng-container *ngTemplateOutlet="recipientsList"></ng-container>
</div>
<ng-template #scratchTemplate>
<ng-container *ngTemplateOutlet="recipientsList"></ng-container>
<section formGroupName="template">
<section formGroupName="configuration">
<mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="notificationSubject" required>
<mat-error *ngIf="notificationRequestForm.get('template.configuration.notificationSubject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
<mat-hint>{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}</mat-hint>
</mat-form-field>
<mat-form-field class="mat-block tb-form-fields" subscriptSizing="dynamic">
<mat-label translate>notification.message</mat-label>
<input matInput formControlName="defaultTextTemplate" required>
<mat-error *ngIf="notificationRequestForm.get('template.configuration.defaultTextTemplate').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
<mat-hint>{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}</mat-hint>
</mat-form-field>
<label class="tb-title tb-required"
[ngClass]="{'tb-error': notificationRequestForm.get('template.configuration.deliveryMethodsTemplates').hasError('atLeastOne')}">
{{ "notification.delivery-methods" | translate }}
@ -87,19 +75,22 @@
</section>
</section>
</ng-template>
<tb-entity-list
required
formControlName="targets"
labelText="{{ 'notification.targets' | translate }}"
placeholderText="{{ 'notification.target' | translate }}"
requiredText="{{ 'notification.targets-required' | translate }}"
[entityType]="entityType.NOTIFICATION_TARGET">
<button #createTargetButton
mat-button color="primary" matSuffix
(click)="createTarget($event, createTargetButton)">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
</button>
</tb-entity-list>
<ng-template #recipientsList>
<tb-entity-list
required
formControlName="targets"
labelText="{{ 'notification.targets' | translate }}"
placeholderText="{{ 'notification.target' | translate }}"
requiredText="{{ 'notification.targets-required' | translate }}"
entityType="{{ entityType.NOTIFICATION_TARGET }}"
subType="{{ notificationType.GENERAL }}">
<button #createTargetButton
mat-button color="primary" matSuffix
(click)="createTarget($event, createTargetButton)">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
</button>
</tb-entity-list>
</ng-template>
<section formGroupName="additionalConfig" class="additional-config-group">
<mat-slide-toggle formControlName="enabled" class="toggle">
{{ 'notification.scheduler-later' | translate }}
@ -112,7 +103,7 @@
<mat-form-field>
<mat-label translate>notification.time</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker type="datetime" openOnFocus="true" timeInterval="1"></mat-datetimepicker>
<mat-datetimepicker #startTimePicker type="datetime" openOnFocus="true" [timeInterval]="1"></mat-datetimepicker>
<input required matInput fxFlex formControlName="time" [min]="minDate()" [max]="maxDate()" [matDatetimepicker]="startTimePicker">
</mat-form-field>
</div>
@ -122,24 +113,29 @@
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.WEB.enabled').value"
[stepControl]="webTemplateForm">
<ng-template matStepLabel>{{ 'notification.web-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.web' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
</div>
<form [formGroup]="webTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject" [placeholder]="notificationRequestForm.get('template.configuration.notificationSubject').value">
<input matInput formControlName="subject">
<mat-error *ngIf="webTemplateForm.get('subject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="notificationRequestForm.get('template.configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="webTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<section formGroupName="additionalConfig">
<section formGroupName="icon" class="additional-config-group">
@ -209,15 +205,18 @@
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.EMAIL.enabled').value"
[stepControl]="emailTemplateForm">
<ng-template matStepLabel>{{ 'notification.email-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.email' | translate }}</ng-template>
<ng-template matStepContent>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
</div>
<form [formGroup]="emailTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject" [placeholder]="notificationRequestForm.get('template.configuration.notificationSubject').value">
<input matInput formControlName="subject">
<mat-error *ngIf="emailTemplateForm.get('subject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
</mat-form-field>
<mat-label class="tb-title" translate>notification.message</mat-label>
<editor [init]="tinyMceOptions" formControlName="body"></editor>
@ -227,40 +226,44 @@
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.SMS.enabled').value"
[stepControl]="smsTemplateForm">
<ng-template matStepLabel>{{ 'notification.sms-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.sms' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
</div>
<form [formGroup]="smsTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="notificationRequestForm.get('template.configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="smsTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
</form>
</mat-step>
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.SLACK.enabled').value"
[stepControl]="slackTemplateForm">
<ng-template matStepLabel>{{ 'notification.slack-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.slack' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
</div>
<form [formGroup]="slackTemplateForm" fxLayoutGap="8px">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="notificationRequestForm.get('template.configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="slackTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
</form>
</mat-step>
@ -273,7 +276,7 @@
<section class="preview-group notification" *ngIf="preview.processedTemplates.WEB?.enabled">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18" svgIcon="mdi:bell-badge"></mat-icon>
<div class="group-title" translate>notification.delivery-method-type.web-preview</div>
<div class="group-title" translate>notification.delivery-method.web-preview</div>
</div>
<div class="web-preview">
<tb-notification preview [notification]="preview.processedTemplates.WEB"></tb-notification>
@ -282,7 +285,7 @@
<section class="preview-group notification" *ngIf="preview.processedTemplates.EMAIL?.enabled">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18" svgIcon="mdi:email"></mat-icon>
<div class="group-title" translate>notification.delivery-method-type.email-preview</div>
<div class="group-title" translate>notification.delivery-method.email-preview</div>
</div>
<div class="notification-content">
<div class="subject">{{ preview.processedTemplates.EMAIL.subject }}</div>
@ -293,7 +296,7 @@
<section class="preview-group notification" *ngIf="preview.processedTemplates.SMS?.enabled">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18" svgIcon="mdi:message-processing"></mat-icon>
<div class="group-title" translate>notification.delivery-method-type.sms-preview</div>
<div class="group-title" translate>notification.delivery-method.sms-preview</div>
</div>
<div class="notification-content">
{{ preview.processedTemplates.SMS.body }}
@ -302,7 +305,7 @@
<section class="preview-group notification" *ngIf="preview.processedTemplates.SLACK?.enabled">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18" svgIcon="mdi:slack"></mat-icon>
<div class="group-title" translate>notification.delivery-method-type.slack-preview</div>
<div class="group-title" translate>notification.delivery-method.slack-preview</div>
</div>
<div class="notification-content">
{{ preview.processedTemplates.SLACK.body }}

2
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss

@ -17,7 +17,7 @@
@import "../../../../../../theme";
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
width: 800px;
width: 820px;
height: 100%;
max-width: 100%;
max-height: 100vh;

26
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts

@ -29,7 +29,7 @@ import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NotificationService } from '@core/http/notification.service';
import { deepTrim, guid } from '@core/utils';
import { deepTrim, guid, isDefinedAndNotNull } from '@core/utils';
import { Observable } from 'rxjs';
import { EntityType } from '@shared/models/entity-type.models';
import { BreakpointObserver } from '@angular/cdk/layout';
@ -60,6 +60,8 @@ export class SentNotificationDialogComponent extends
@ViewChild('createNotification', {static: true}) createNotification: MatStepper;
stepperOrientation: Observable<StepperOrientation>;
stepperLabelPosition: Observable<'bottom' | 'end'>;
isAdd = true;
entityType = EntityType;
notificationType = NotificationType;
@ -87,6 +89,9 @@ export class SentNotificationDialogComponent extends
this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm'])
.pipe(map(({matches}) => matches ? 'horizontal' : 'vertical'));
this.stepperLabelPosition = this.breakpointObserver.observe(MediaBreakpoints['gt-md'])
.pipe(map(({matches}) => matches ? 'end' : 'bottom'));
this.notificationRequestForm = this.fb.group({
useTemplate: [false],
templateId: [{value: null, disabled: true}, Validators.required],
@ -129,7 +134,18 @@ export class SentNotificationDialogComponent extends
this.dialogTitle = 'notification.new-notification';
}
if (data.request) {
this.notificationRequestForm.reset({}, {emitEvent: false});
this.notificationRequestForm.patchValue(this.data.request, {emitEvent: false});
let useTemplate = true;
if (isDefinedAndNotNull(this.data.request.template)) {
useTemplate = false;
// eslint-disable-next-line guard-for-in
for (const method in this.data.request.template.configuration.deliveryMethodsTemplates) {
this.deliveryMethodFormsMap.get(NotificationDeliveryMethod[method])
.patchValue(this.data.request.template.configuration.deliveryMethodsTemplates[method]);
}
}
this.notificationRequestForm.get('useTemplate').setValue(useTemplate, {onlySelf : true});
}
}
@ -142,12 +158,8 @@ export class SentNotificationDialogComponent extends
}
nextStepLabel(): string {
if (this.selectedIndex !== 0) {
if (this.selectedIndex >= this.maxStepperIndex) {
return 'action.send';
} else if (this.createNotification.selected.stepControl.pristine) {
return 'action.skip';
}
if (this.selectedIndex >= this.maxStepperIndex) {
return 'action.send';
}
return 'action.next';
}

24
ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts

@ -32,7 +32,7 @@ import {
import { NotificationService } from '@core/http/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
import { Direction } from '@shared/models/page/sort-order';
import { DatePipe } from '@angular/common';
import { EntityAction } from '@home/models/entity/entity-component.models';
@ -60,21 +60,20 @@ export class SentTableConfigResolver implements Resolve<EntityTableConfig<Notifi
private dialog: MatDialog,
private datePipe: DatePipe) {
this.config.entityType = EntityType.NOTIFICATION_REQUEST;
this.config.detailsPanelEnabled = false;
this.config.selectionEnabled = false;
this.config.addEnabled = false;
this.config.searchEnabled = false;
this.config.entityTranslations = {
noEntities: 'notification.no-notification-request',
};
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_REQUEST);
this.config.entityResources = {} as EntityTypeResource<NotificationRequest>;
this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationRequests(pageLink);
this.config.deleteEnabled = (request) => request.status === NotificationRequestStatus.SCHEDULED;
this.config.deleteEntityTitle = () => this.translate.instant('notification.delete-request-title');
this.config.deleteEntityContent = () => this.translate.instant('notification.delete-request-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('notification.delete-requests-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('notification.delete-requests-text');
this.config.deleteEntity = id => this.notificationService.deleteNotificationRequest(id.id);
this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationRequests(pageLink);
this.config.cellActionDescriptors = this.configureCellActions();
@ -91,11 +90,11 @@ export class SentTableConfigResolver implements Resolve<EntityTableConfig<Notifi
this.config.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.config.columns.push(
new DateEntityTableColumn<NotificationRequestInfo>('createdTime', 'notification.created-time', this.datePipe, '170px'),
new DateEntityTableColumn<NotificationRequestInfo>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<NotificationRequestInfo>('status', 'notification.status', '15%',
request => `<span style="display: flex;">${this.requestStatus(request.status)}${this.requestStats(request.stats)}</span>`,
request => this.requestStatusStyle(request.status)),
new EntityTableColumn<NotificationRequest>('deliveryMethods', 'notification.delivery-method', '15%',
new EntityTableColumn<NotificationRequest>('deliveryMethods', 'notification.delivery-method.delivery-method', '15%',
(request) => request.deliveryMethods
.map((deliveryMethod) => this.translate.instant(NotificationDeliveryMethodTranslateMap.get(deliveryMethod))).join(', '),
() => ({}), false),
@ -173,7 +172,7 @@ export class SentTableConfigResolver implements Resolve<EntityTableConfig<Notifi
if (countError === 0) {
return '';
}
return `<div style="border-radius: 12px; height: 24px; line-height: 24px; padding: 0 10px; width: max-content;
return `<div style="border-radius: 12px; height: 24px; line-height: 24px; padding: 0 10px; width: max-content; cursor: pointer;
background-color: #D12730; color: #fff; font-weight: 500; margin-left: 8px" class="stats">
${countError} ${this.translate.instant('notification.fails')} >
</div>`;
@ -182,8 +181,7 @@ export class SentTableConfigResolver implements Resolve<EntityTableConfig<Notifi
private requestStatusStyle(status: NotificationRequestStatus): object {
const styleObj = {
fontSize: '14px',
color: '#198038',
cursor: 'pointer'
color: '#198038'
};
switch (status) {
case NotificationRequestStatus.SCHEDULED:

14
ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts

@ -79,8 +79,6 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
name: ['', Validators.required],
notificationType: [NotificationType.GENERAL],
configuration: this.fb.group({
notificationSubject: ['', Validators.required],
defaultTextTemplate: ['', Validators.required],
deliveryMethodsTemplates: this.fb.group({}, {validators: this.atLeastOne()})
})
});
@ -91,8 +89,8 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
});
this.webTemplateForm = this.fb.group({
subject: [''],
body: [''],
subject: ['', Validators.required],
body: ['', Validators.required],
additionalConfig: this.fb.group({
icon: this.fb.group({
enabled: [false],
@ -153,16 +151,16 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
});
this.emailTemplateForm = this.fb.group({
subject: [''],
body: ['']
subject: ['', Validators.required],
body: ['', Validators.required]
});
this.smsTemplateForm = this.fb.group({
body: ['']
body: ['', Validators.required]
});
this.slackTemplateForm = this.fb.group({
body: ['']
body: ['', Validators.required]
});
this.deliveryMethodFormsMap = new Map<NotificationDeliveryMethod, FormGroup>([

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

@ -28,7 +28,9 @@
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper linear labelPosition="end" #notificationTemplateStepper [orientation]="(stepperOrientation | async)"
<mat-horizontal-stepper linear #notificationTemplateStepper
[labelPosition]="(stepperLabelPosition | async)"
[orientation]="(stepperOrientation | async)"
(selectionChange)="changeStep($event)">
<ng-template matStepperIcon="edit">
<mat-icon>check</mat-icon>
@ -52,22 +54,6 @@
</mat-select>
</mat-form-field>
<section formGroupName="configuration">
<mat-form-field class="mat-block" subscriptSizing="dynamic">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="notificationSubject" required>
<mat-error *ngIf="templateNotificationForm.get('configuration.notificationSubject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
<mat-hint>{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}</mat-hint>
</mat-form-field>
<mat-form-field class="mat-block tb-form-fields" subscriptSizing="dynamic">
<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-hint>{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}</mat-hint>
</mat-form-field>
<label class="tb-title tb-required"
[ngClass]="{'tb-error': templateNotificationForm.get('configuration.deliveryMethodsTemplates').hasError('atLeastOne')}">
{{ "notification.delivery-methods" | translate }}
@ -86,24 +72,29 @@
</mat-step>
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.WEB.enabled').value"
[stepControl]="webTemplateForm">
<ng-template matStepLabel>{{ 'notification.web-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.web' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}
</div>
<form [formGroup]="webTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject" [placeholder]="templateNotificationForm.get('configuration.notificationSubject').value">
<input matInput formControlName="subject">
<mat-error *ngIf="webTemplateForm.get('subject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="templateNotificationForm.get('configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="webTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<section formGroupName="additionalConfig">
<section formGroupName="icon" class="additional-config-group">
@ -172,15 +163,18 @@
</mat-step>
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.EMAIL.enabled').value"
[stepControl]="emailTemplateForm">
<ng-template matStepLabel>{{ 'notification.email-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.email' | translate }}</ng-template>
<ng-template matStepContent>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}
</div>
<form [formGroup]="emailTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject" [placeholder]="templateNotificationForm.get('configuration.notificationSubject').value">
<input matInput formControlName="subject">
<mat-error *ngIf="emailTemplateForm.get('subject').hasError('required')">
{{ 'notification.subject-required' | translate }}
</mat-error>
</mat-form-field>
<mat-label class="tb-title" translate>notification.message</mat-label>
<editor [init]="tinyMceOptions" formControlName="body"></editor>
@ -189,39 +183,43 @@
</mat-step>
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.SMS.enabled').value"
[stepControl]="smsTemplateForm">
<ng-template matStepLabel>{{ 'notification.sms-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.sms' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}
</div>
<form [formGroup]="smsTemplateForm">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="templateNotificationForm.get('configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="smsTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
</form>
</mat-step>
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.SLACK.enabled').value"
[stepControl]="slackTemplateForm">
<ng-template matStepLabel>{{ 'notification.slack-settings' | translate }}</ng-template>
<ng-template matStepLabel>{{ 'notification.delivery-method.slack' | translate }}</ng-template>
<div class="tb-hint tb-hint-available-params">
{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }}
</div>
<form [formGroup]="slackTemplateForm" fxLayoutGap="8px">
<mat-form-field class="mat-block" floatLabel="always">
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body"
[placeholder]="templateNotificationForm.get('configuration.defaultTextTemplate').value">
formControlName="body">
</textarea>
<mat-error *ngIf="slackTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
</form>
</mat-step>

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

@ -17,7 +17,7 @@
@import "../../../../../../theme";
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
width: 930px;
width: 800px;
height: 100%;
max-width: 100%;
max-height: 100vh;

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

@ -54,6 +54,7 @@ export class TemplateNotificationDialogComponent
@ViewChild('notificationTemplateStepper', {static: true}) notificationTemplateStepper: MatStepper;
stepperOrientation: Observable<StepperOrientation>;
stepperLabelPosition: Observable<'bottom' | 'end'>;
dialogTitle = 'notification.edit-notification-template';
@ -81,6 +82,9 @@ export class TemplateNotificationDialogComponent
this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm'])
.pipe(map(({matches}) => matches ? 'horizontal' : 'vertical'));
this.stepperLabelPosition = this.breakpointObserver.observe(MediaBreakpoints['gt-md'])
.pipe(map(({matches}) => matches ? 'end' : 'bottom'));
if (isDefinedAndNotNull(this.data?.predefinedType)) {
this.hideSelectType = true;
this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvents: false});
@ -132,12 +136,8 @@ export class TemplateNotificationDialogComponent
}
nextStepLabel(): string {
if (this.selectedIndex !== 0) {
if (this.selectedIndex >= this.maxStepperIndex) {
return (this.data.isAdd || this.data.isCopy) ? 'action.add' : 'action.save';
} else if (this.notificationTemplateStepper.selected.stepControl.pristine) {
return 'action.skip';
}
if (this.selectedIndex >= this.maxStepperIndex) {
return (this.data.isAdd || this.data.isCopy) ? 'action.add' : 'action.save';
}
return 'action.next';
}

18
ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { EntityTypeResource } from '@shared/models/entity-type.models';
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
import { Direction } from '@shared/models/page/sort-order';
import { NotificationTemplate, NotificationTemplateTypeTranslateMap } from '@shared/models/notification.models';
import { NotificationService } from '@core/http/notification.service';
@ -46,15 +46,13 @@ export class TemplateTableConfigResolver implements Resolve<EntityTableConfig<No
private dialog: MatDialog,
private datePipe: DatePipe) {
this.config.entityType = EntityType.NOTIFICATION_TEMPLATE;
this.config.detailsPanelEnabled = false;
this.config.selectionEnabled = false;
this.config.addEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = {
noEntities: 'notification.no-notification-templates',
search: 'notification.search-templates'
};
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TEMPLATE);
this.config.entityResources = {} as EntityTypeResource<NotificationTemplate>;
this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTemplates(pageLink);
@ -76,14 +74,10 @@ export class TemplateTableConfigResolver implements Resolve<EntityTableConfig<No
};
this.config.columns.push(
new DateEntityTableColumn<NotificationTemplate>('createdTime', 'notification.created-time', this.datePipe, '170px'),
new EntityTableColumn<NotificationTemplate>('notificationType', 'notification.type', '15%',
new DateEntityTableColumn<NotificationTemplate>('createdTime', 'common.created-time', this.datePipe, '170px'),
new EntityTableColumn<NotificationTemplate>('notificationType', 'notification.type', '20%',
(template) => this.translate.instant(NotificationTemplateTypeTranslateMap.get(template.notificationType).name)),
new EntityTableColumn<NotificationTemplate>('name', 'notification.template', '25%'),
new EntityTableColumn<NotificationTemplate>('configuration.notificationSubject', 'notification.subject', '25%',
(template) => template.configuration.notificationSubject, () => ({}), false),
new EntityTableColumn<NotificationTemplate>('configuration.defaultTextTemplate', 'notification.message', '35%',
(template) => template.configuration.defaultTextTemplate, () => ({}), false)
new EntityTableColumn<NotificationTemplate>('name', 'notification.template', '80%')
);
}

3
ui-ngx/src/app/shared/components/notification/notification.component.html

@ -38,7 +38,8 @@
<div class="description" fxLayout="column" fxLayoutAlign="start end">
<div fxLayout="row" fxLayoutAlign="end center" class="mark-read">
<span class="time">{{ notification.createdTime ?? currentDate | dateAgo }}</span>
<button mat-icon-button class="tb-mat-28" (click)="markRead($event)" *ngIf="!hideMarkAsReadButton">
<button mat-icon-button class="tb-mat-28" (click)="markRead($event)" *ngIf="!hideMarkAsReadButton"
matTooltip="{{ 'notification.mark-as-read' | translate }}" matTooltipPosition="above">
<mat-icon>check_circle_outline</mat-icon>
</button>
</div>

21
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -350,31 +350,42 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
[
EntityType.NOTIFICATION,
{
type: 'entity.type-notification'
type: 'entity.type-notification',
noEntities: 'notification.no-inbox-notification',
search: 'notification.search-notification',
selectedEntities: 'notification.selected-notifications'
}
],
[
EntityType.NOTIFICATION_REQUEST,
{
type: 'entity.type-notification-request'
type: 'entity.type-notification-request',
noEntities: 'notification.no-notification-request',
selectedEntities: 'notification.selected-requests'
}
],
[
EntityType.NOTIFICATION_RULE,
{
type: 'entity.type-notification-rule'
type: 'entity.type-notification-rule',
noEntities: 'notification.no-rules-notification',
search: 'notification.search-rules'
}
],
[
EntityType.NOTIFICATION_TARGET,
{
type: 'entity.type-notification-target'
type: 'entity.type-notification-target',
noEntities: 'notification.no-targets-notification',
search: 'notification.search-targets'
}
],
[
EntityType.NOTIFICATION_TEMPLATE,
{
type: 'entity.type-notification-template'
type: 'entity.type-notification-template',
noEntities: 'notification.no-notification-templates',
search: 'notification.search-templates'
}
]
]

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

@ -253,7 +253,7 @@ export enum NotificationTargetType {
export const NotificationTargetTypeTranslationMap = new Map<NotificationTargetType, string>([
[NotificationTargetType.PLATFORM_USERS, 'notification.platform-users'],
[NotificationTargetType.SLACK, 'notification.slack']
[NotificationTargetType.SLACK, 'notification.delivery-method.slack']
]);
export interface NotificationTemplate extends Omit<BaseData<NotificationTemplateId>, 'label'>{
@ -263,8 +263,6 @@ export interface NotificationTemplate extends Omit<BaseData<NotificationTemplate
}
interface NotificationTemplateConfig {
defaultTextTemplate: string;
notificationSubject: string;
deliveryMethodsTemplates: {
[key in NotificationDeliveryMethod]: DeliveryMethodNotificationTemplate
};
@ -321,10 +319,10 @@ export enum NotificationDeliveryMethod {
}
export const NotificationDeliveryMethodTranslateMap = new Map<NotificationDeliveryMethod, string>([
[NotificationDeliveryMethod.WEB, 'notification.delivery-method-type.web'],
[NotificationDeliveryMethod.SMS, 'notification.delivery-method-type.sms'],
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method-type.email'],
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method-type.slack']
[NotificationDeliveryMethod.WEB, 'notification.delivery-method.web'],
[NotificationDeliveryMethod.SMS, 'notification.delivery-method.sms'],
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method.email'],
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method.slack']
]);
export enum NotificationRequestStatus {
@ -357,7 +355,7 @@ export enum NotificationTargetConfigType {
CUSTOMER_USERS = 'CUSTOMER_USERS',
USER_LIST = 'USER_LIST',
ORIGINATOR_ENTITY_OWNER_USERS = 'ORIGINATOR_ENTITY_OWNER_USERS',
ACTION_TARGET_USER = 'ACTION_TARGET_USER'
AFFECTED_USER = 'AFFECTED_USER'
}
interface NotificationTargetConfigTypeInfo {
@ -392,7 +390,7 @@ export const NotificationTargetConfigTypeInfoMap = new Map<NotificationTargetCon
hint: 'notification.target-type.users-entity-owner-hint'
}
],
[NotificationTargetConfigType.ACTION_TARGET_USER,
[NotificationTargetConfigType.AFFECTED_USER,
{
name: 'notification.target-type.affected-user',
hint: 'notification.target-type.affected-user-hint'

1
ui-ngx/src/app/shared/models/tenant.model.ts

@ -43,6 +43,7 @@ export interface DefaultTenantProfileConfiguration {
tenantEntityExportRateLimit?: string;
tenantEntityImportRateLimit?: string;
tenantNotificationRequestsRateLimit?: string;
maxTransportMessages: number;
maxTransportDataPoints: number;

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

@ -2732,6 +2732,7 @@
"add-notification-target": "Add notification recipients group",
"add-notification-template": "Add notification template",
"add-rule": "Add rule",
"add-stage": "Add stage",
"add-target": "Add recipients",
"add-template": "Add template",
"after": "After",
@ -2743,7 +2744,6 @@
"basic-settings": "Basic settings",
"button-text": "Button text",
"button-text-required": "Button text is required",
"clear-rule": "Clear rule",
"compose": "Compose",
"conversation": "Conversation",
"conversation-required": "Conversation is required",
@ -2753,9 +2753,14 @@
"create-new": "Create new",
"create-target": "Create recipient",
"created": "Created",
"created-time": "Created time",
"delete-notification-text": "Be careful, after the confirmation the notification will become unrecoverable.",
"delete-notification-title": "Are you sure you want to delete the notification?",
"delete-notifications-text": "Be careful, after the confirmation the notifications will become unrecoverable.",
"delete-notifications-title": "Are you sure you want to delete { count, plural, =1 {1 notification} other {# notifications} }?",
"delete-request-text": "Be careful, after the confirmation the notification request will become unrecoverable.",
"delete-request-title": "Are you sure you want to delete the notification request?",
"delete-requests-text": "Be careful, after the confirmation the notification requests will become unrecoverable.",
"delete-requests-title": "Are you sure you want to delete { count, plural, =1 {1 request} other {# requests} }?",
"delete-rule-text": "Be careful, after the confirmation the notification rule will become unrecoverable.",
"delete-rule-title": "Are you sure you want to delete the notification rule '{{ruleName}}'?",
"delete-target-text": "Be careful, after the confirmation the notification recipient will become unrecoverable.",
@ -2763,20 +2768,20 @@
"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}}'?",
"deleted": "Deleted",
"delivery-method": "Delivery method",
"delivery-method-type": {
"delivery-method": {
"delivery-method": "Delivery method",
"email": "Email",
"email-failed-sent": "Email messages failed sent",
"email-preview": "Email notification preview",
"web": "Web",
"web-failed-sent": "Web messages failed sent",
"web-preview": "Web notification preview",
"slack": "Slack",
"slack-failed-sent": "Slack messages failed sent",
"slack-preview": "Slack notification preview",
"sms": "SMS",
"sms-failed-sent": "SMS messages failed sent",
"sms-preview": "SMS notification preview"
"sms-preview": "SMS notification preview",
"web": "Web",
"web-failed-sent": "Web messages failed sent",
"web-preview": "Web notification preview"
},
"delivery-methods": "Delivery methods",
"description": "Description",
@ -2787,10 +2792,10 @@
"edit-notification-template": "Edit notification template",
"edit-rule": "Edit rule",
"edit-template": "Edit template",
"email-settings": "Email settings",
"entities-limit-trigger-settings": "Entities limit trigger settings",
"entity-action-trigger-settings": "Entity action trigger settings",
"entity-type": "Entity type",
"escalation-chain": "Escalation chain",
"failed-send": "Failed send",
"fails": "Fails",
"filter": "Filter",
@ -2799,8 +2804,8 @@
"link": "Link",
"link-required": "Link is required",
"link-type": {
"link": "Open URL link",
"dashboard": "Open dashboard"
"dashboard": "Open dashboard",
"link": "Open URL link"
},
"management": "Notification management",
"mark-all-as-read": "Mark all as read",
@ -2821,28 +2826,30 @@
"not-found-slack-recipient": "Slack recipient not found",
"notification": "Notification",
"notification-center": "Notification center",
"notification-chain": "Notification chain",
"notification-target": "Recipient group",
"notify": "notify",
"notify-again": "Notify again",
"notify-alarm-action": {
"acknowledged": "Alarm acknowledged",
"assigned": "Alarm assigned",
"cleared": "Alarm cleared",
"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",
"notify-only-user-comments": "Notify only user comments",
"only-rule-chain-lifecycle-failures": "Only rule chain lifecycle failures",
"only-rule-node-lifecycle-failures": "Only rule node lifecycle failures",
"platform-users": "Platform users",
"recipient": "Recipient",
"recipients": "Recipients",
"recipient-type": "Recipient type",
"recipients": "Recipients",
"recipients-count": "{ count, plural, =1 {1 recipient} other {# recipients} }",
"request-search": "Request search",
"request-status": {
"processing": "Processing",
"scheduled": "Scheduled",
@ -2862,21 +2869,20 @@
"search-rules": "Search rules",
"search-targets": "Search recipients",
"search-templates": "Search templates",
"selected-notifications": "{ count, plural, =1 {1 notification} other {# notifications} } selected",
"selected-requests": "{ count, plural, =1 {1 request} other {# requests} } selected",
"send-notification": "Send notification",
"sent": "Sent",
"settings": "Notification settings",
"set-entity-from-notification": "Set entity from notification to dashboard state",
"slack": "Slack",
"slack-chanel-type": "Slack channel type",
"slack-chanel-types": {
"direct": "Direct message",
"private-channel": "Private channel",
"public-channel": "Public channel"
},
"slack-settings": "Slack settings",
"sms-settings": "SMS settings",
"start-from-scratch": "Start from scratch",
"status": "Status",
"stop-escalation-alarm-status-become": "Stop the escalation on the alarm status become:",
"subject": "Subject",
"subject-required": "Subject is required",
"target": "Recipient",
@ -2887,11 +2893,11 @@
"affected-user-hint": "Affected user hint",
"all-users": "All users",
"customer-users": "Customer users",
"users-entity-owner": "Users of the entity owner",
"users-entity-owner-hint": "Users of the entity owner hint",
"tenant-administrators": "Tenant administrators",
"user-filters": "User filter",
"user-list": "User list"
"user-list": "User list",
"users-entity-owner": "Users of the entity owner",
"users-entity-owner-hint": "Users of the entity owner hint"
},
"targets": "Recipients",
"targets-required": "Recipients is required",
@ -2919,11 +2925,11 @@
"rule-engine-lifecycle-event": "Rule engine lifecycle event"
},
"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",
"tenants-list-rule-hint": "If the field is empty, the trigger will be applied to all tenants",
"threshold": "Threshold",
"time": "Time",
"track-rule-node-events": "Track rule node events",
"threshold": "Threshold",
"trigger": {
"alarm": "Alarm",
"alarm-assignment": "Alarm assignment",
@ -2931,18 +2937,15 @@
"device-inactivity": "Device inactivity",
"entities-limit": "Entities limit",
"entity-action": "Entity action",
"rule-engine-lifecycle-event": "Rule engine lifecycle event",
"trigger": "Trigger",
"trigger-required": "Trigger is required",
"rule-engine-lifecycle-event": "Rule engine lifecycle event"
"trigger-required": "Trigger is required"
},
"type": "Type",
"unread": "Unread",
"updated": "Updated",
"use-template": "Use template",
"view-all": "View all",
"web-settings": "Web settings",
"only-rule-chain-lifecycle-failures": "Only rule chain lifecycle failures",
"only-rule-node-lifecycle-failures": "Only rule node lifecycle failures"
"view-all": "View all"
},
"ota-update": {
"add": "Add package",
@ -3525,6 +3528,7 @@
"transport-device-telemetry-data-points-rate-limit": "Transport device telemetry data points",
"tenant-entity-export-rate-limit": "Entity version creation",
"tenant-entity-import-rate-limit": "Entity version load",
"tenant-notification-request-rate-limit": "Notification requests",
"max-transport-messages": "Transport messages maximum number",
"max-transport-messages-required": "Transport messages maximum number is required.",
"max-transport-messages-range": "Transport messages maximum number can't be negative",
@ -3597,6 +3601,7 @@
"edit-cassandra-tenant-limits-configuration-title": "Edit Cassandra query for tenant rate limits",
"edit-tenant-entity-export-rate-limit-title": "Edit entity version creation rate limits",
"edit-tenant-entity-import-rate-limit-title": "Edit entity version load rate limits",
"edit-tenant-notification-request-rate-limit-title": "Edit notification requests rate limits",
"messages-per": "messages per",
"not-set": "Not set",
"number-of-messages": "Number of messages",

Loading…
Cancel
Save