diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 1f58b2f94b..f4a7217b34 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/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; diff --git a/ui-ngx/src/app/core/http/notification.service.ts b/ui-ngx/src/app/core/http/notification.service.ts index 722dcdd045..b4ca3b78f9 100644 --- a/ui-ngx/src/app/core/http/notification.service.ts +++ b/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 { + return this.http.delete(`/api/notification/${id}`, defaultHttpOptionsFromConfig(config)); + } + public markNotificationAsRead(id: string, config?: RequestConfig): Observable { return this.http.put(`/api/notification/${id}/read`, defaultHttpOptionsFromConfig(config)); } @@ -127,9 +131,13 @@ export class NotificationService { defaultHttpOptionsFromConfig(config)); } - public getNotificationTargets(pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/notification/targets${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getNotificationTargets(pageLink: PageLink, type?: NotificationType, + config?: RequestConfig): Observable> { + let url = `/api/notification/targets${pageLink.toQuery()}`; + if (isNotEmptyStr(type)) { + url += `¬ificationType=${type}`; + } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } public getRecipientsForNotificationTargetConfig(notificationTarget: NotificationTarget, pageLink: PageLink, diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index bcf44ae792..ac21a879a1 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/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(), diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 35ea628784..81a1206800 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/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"> +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 1276a200da..e070d760ad 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/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)]], diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts index e0f383068a..70fb7af11f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts +++ b/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( @@ -50,6 +51,7 @@ export const rateLimitsLabelTranslationMap = new Map( [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; + 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('createdTime', 'notification.created-time', this.datePipe, '170px'), + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('type', 'notification.type', '10%', (notification) => this.translate.instant(NotificationTemplateTypeTranslateMap.get(notification.type).name)), new EntityTableColumn('subject', 'notification.subject', '30%'), diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.componet.scss b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.componet.scss index 25d51c040b..8406cae3fe 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.componet.scss +++ b/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); - } } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts index bc9d7fae22..d18d9e5ec4 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts +++ b/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; this.config.entitiesFetchFunction = pageLink => this.notificationService.getNotificationTargets(pageLink); @@ -76,7 +74,7 @@ export class RecipientTableConfigResolver implements Resolve('createdTime', 'notification.created-time', this.datePipe, '170px'), + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('name', 'notification.notification-target', '20%'), new EntityTableColumn('configuration.type', 'notification.type', '20%', (target) => this.translate.instant(NotificationTargetTypeTranslationMap.get(target.configuration.type)), diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.html index f37da71867..d3ca08ec5f 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/escalation-form.component.html +++ b/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)"> diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.ts index 8ecfba1c24..fb102b4243 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/escalations.component.ts +++ b/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 diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 0dae65583d..3b4ccba2b3 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -65,7 +65,8 @@ - + + + + +
{{ 'notification.scheduler-later' | translate }} @@ -112,7 +103,7 @@ notification.time - + @@ -122,24 +113,29 @@ - {{ 'notification.web-settings' | translate }} + {{ 'notification.delivery-method.web' | translate }}
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
- + notification.subject - + + + {{ 'notification.subject-required' | translate }} + - + notification.message + + {{ 'notification.message-required' | translate }} +
@@ -209,15 +205,18 @@ - {{ 'notification.email-settings' | translate }} + {{ 'notification.delivery-method.email' | translate }}
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
- + notification.subject - + + + {{ 'notification.subject-required' | translate }} + notification.message @@ -227,40 +226,44 @@ - {{ 'notification.sms-settings' | translate }} + {{ 'notification.delivery-method.sms' | translate }}
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
- + notification.message + + {{ 'notification.message-required' | translate }} +
- {{ 'notification.slack-settings' | translate }} + {{ 'notification.delivery-method.slack' | translate }}
{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).hint | translate }}
- + notification.message + + {{ 'notification.message-required' | translate }} +
@@ -273,7 +276,7 @@
-
notification.delivery-method-type.web-preview
+
notification.delivery-method.web-preview
@@ -282,7 +285,7 @@
-
notification.delivery-method-type.email-preview
+
notification.delivery-method.email-preview
{{ preview.processedTemplates.EMAIL.subject }}
@@ -293,7 +296,7 @@
-
notification.delivery-method-type.sms-preview
+
notification.delivery-method.sms-preview
{{ preview.processedTemplates.SMS.body }} @@ -302,7 +305,7 @@
-
notification.delivery-method-type.slack-preview
+
notification.delivery-method.slack-preview
{{ preview.processedTemplates.SLACK.body }} diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss index 2fe0583a1b..b18c434360 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss +++ b/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; diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts index b910a4cb99..7cdb791037 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts +++ b/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; + 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'; } diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts index a71951a28b..f25a037aeb 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts +++ b/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; - 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('createdTime', 'notification.created-time', this.datePipe, '170px'), + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('status', 'notification.status', '15%', request => `${this.requestStatus(request.status)}${this.requestStats(request.stats)}`, request => this.requestStatusStyle(request.status)), - new EntityTableColumn('deliveryMethods', 'notification.delivery-method', '15%', + new EntityTableColumn('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 ${countError} ${this.translate.instant('notification.fails')} >
`; @@ -182,8 +181,7 @@ export class SentTableConfigResolver implements Resolve 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 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 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([ diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html index 67ae8d0e49..dde6e57168 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html @@ -28,7 +28,9 @@
- check @@ -52,22 +54,6 @@
- - notification.subject - - - {{ 'notification.subject-required' | translate }} - - {{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }} - - - notification.message - - - {{ 'notification.message-required' | translate }} - - {{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).hint | translate }} -