Browse Source

UI: Add UI to configuration notification in Microsoft team

pull/8843/head
Vladyslav_Prykhodko 3 years ago
parent
commit
93e4070e51
  1. 27
      ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html
  2. 60
      ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss
  3. 9
      ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts
  4. 1
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss
  5. 1
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.scss
  6. 229
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html
  7. 104
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss
  8. 30
      ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts
  9. 207
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html
  10. 51
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss
  11. 39
      ui-ngx/src/app/shared/models/notification.models.ts
  12. 10
      ui-ngx/src/assets/locale/locale.constant-en_US.json

27
ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html

@ -61,12 +61,11 @@
<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>
<tb-toggle-select class="tb-notification-tenant-group" appearance="fill"
formControlName="filterByTenants">
<tb-toggle-option [value]="true">{{ 'tenant.tenant' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="false">{{ 'tenant-profile.tenant-profile' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<ng-container *ngIf="targetNotificationForm.get('configuration.usersFilter.filterByTenants').value; else tenantProfiles">
<tb-entity-list
@ -121,6 +120,22 @@
[slackChanelType]="targetNotificationForm.get('configuration.conversationType').value">
</tb-slack-conversation-autocomplete>
</section>
<section *ngIf="targetNotificationForm.get('configuration.type').value === notificationTargetType.MICROSOFT_TEAMS">
<mat-form-field class="mat-block">
<mat-label translate>notification.webhook-url</mat-label>
<input matInput formControlName="webhookUrl">
<mat-error *ngIf="targetNotificationForm.get('configuration.webhookUrl').hasError('required')">
{{ 'notification.webhook-url-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>notification.channel-name</mat-label>
<input matInput formControlName="channelName">
<mat-error *ngIf="targetNotificationForm.get('configuration.channelName').hasError('required')">
{{ 'notification.channel-name-required' | translate }}
</mat-error>
</mat-form-field>
</section>
<mat-form-field class="mat-block">
<mat-label translate>notification.description</mat-label>
<input matInput formControlName="description">

60
ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss

@ -20,6 +20,7 @@
form.tb-dialog-container {
min-width: 600px;
max-height: 100vh;
color: rgba(0, 0, 0, 0.87);
}
.mat-dialog-content {
@ -30,6 +31,11 @@
display: block;
padding-bottom: 6px;
}
.tb-notification-tenant-group {
width: 280px;
margin-bottom: 16px;
}
}
:host ::ng-deep {
@ -52,58 +58,4 @@
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;
}
}
}

9
ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts

@ -39,7 +39,6 @@ import { Authority } from '@shared/models/authority.enum';
import { AuthState } from '@core/auth/auth.models';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { AuthUser } from '@shared/models/user.model';
import { control } from 'leaflet';
export interface RecipientNotificationDialogData {
target?: NotificationTarget;
@ -99,6 +98,8 @@ export class RecipientNotificationDialogComponent extends
}),
conversationType: [{value: SlackChanelType.PUBLIC_CHANNEL, disabled: true}],
conversation: [{value: '', disabled: true}, Validators.required],
webhookUrl: [{value: '', disabled: true}, Validators.required],
channelName: [{value: '', disabled: true}, Validators.required],
description: [null]
})
});
@ -116,6 +117,10 @@ export class RecipientNotificationDialogComponent extends
this.targetNotificationForm.get('configuration.conversationType').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.conversation').enable({emitEvent: false});
break;
case NotificationTargetType.MICROSOFT_TEAMS:
this.targetNotificationForm.get('configuration.webhookUrl').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.channelName').enable({emitEvent: false});
break;
}
this.targetNotificationForm.get('configuration.type').enable({emitEvent: false});
this.targetNotificationForm.get('configuration.description').enable({emitEvent: false});
@ -160,7 +165,7 @@ export class RecipientNotificationDialogComponent extends
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) {
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});
}

1
ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss

@ -97,6 +97,7 @@
flex-direction: column;
height: 100%;
padding: 0 !important;
color: rgba(0, 0, 0, 0.87);
.mat-stepper-horizontal {
display: flex;

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

@ -17,6 +17,7 @@
display: block;
width: 600px;
max-width: 100%;
color: rgba(0, 0, 0, 0.87);
h6 {
font-weight: 500;

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

@ -27,7 +27,6 @@
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper linear #createNotification
[labelPosition]="(stepperLabelPosition | async)"
@ -40,12 +39,11 @@
<ng-template matStepLabel>{{ 'notification.compose' | translate }}</ng-template>
<form [formGroup]="notificationRequestForm">
<div fxLayout="row" fxLayoutAlign="center">
<mat-button-toggle-group class="tb-notification-use-template-toggle-group"
style="width: 320px;"
formControlName="useTemplate">
<mat-button-toggle fxFlex [value]=false>{{ 'notification.start-from-scratch' | translate }}</mat-button-toggle>
<mat-button-toggle fxFlex [value]=true>{{ 'notification.use-template' | translate }}</mat-button-toggle>
</mat-button-toggle-group>
<tb-toggle-select class="tb-notification-use-template-toggle-group" appearance="fill"
formControlName="useTemplate">
<tb-toggle-option [value]="false">{{ 'notification.start-from-scratch' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="true">{{ 'notification.use-template' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<div *ngIf="notificationRequestForm.get('useTemplate').value; else scratchTemplate">
<tb-template-autocomplete
@ -172,73 +170,82 @@
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<section formGroupName="additionalConfig">
<section formGroupName="icon" class="additional-config-group">
<mat-slide-toggle formControlName="enabled" class="toggle">
<section formGroupName="additionalConfig" class="tb-form-panel no-padding no-border">
<div class="tb-form-row space-between" formGroupName="icon">
<mat-slide-toggle formControlName="enabled" class="mat-slide">
{{ 'icon.icon' | translate }}
</mat-slide-toggle>
<div *ngIf="webTemplateForm.get('additionalConfig.icon.enabled').value"
fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<tb-material-icon-select formControlName="icon" required fxFlex>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="webTemplateForm.get('additionalConfig.icon.color').value"
formControlName="icon">
</tb-material-icon-select>
<tb-color-input formControlName="color" fxFlex>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</section>
<section formGroupName="actionButtonConfig" class="additional-config-group">
<mat-slide-toggle formControlName="enabled" class="toggle">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
<div *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').value">
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="text" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field fxFlex="30" fxFlex.xs="100">
<mat-label translate>notification.action-type</mat-label>
<mat-select formControlName="linkType">
<mat-option *ngFor="let actionButtonLinkType of actionButtonLinkTypes" [value]="actionButtonLinkType">
{{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.LINK; else dashboardSelector">
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="link" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.link').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
<ng-template #dashboardSelector>
<tb-dashboard-autocomplete
fxFlex="35" fxFlex.xs="100"
required
formControlName="dashboardId">
</tb-dashboard-autocomplete>
<tb-dashboard-state-autocomplete fxFlex="35" fxFlex.xs="100"
[dashboardId]="webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').value"
formControlName="dashboardState">
</tb-dashboard-state-autocomplete>
</ng-template>
</div>
<mat-slide-toggle formControlName="setEntityIdInState" class="toggle"
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.DASHBOARD">
{{ 'notification.set-entity-from-notification' | translate }}
</mat-slide-toggle>
</div>
</section>
</div>
<div class="tb-form-panel tb-slide-toggle stroked" formGroupName="actionButtonConfig">
<mat-expansion-panel class="tb-settings" [expanded]="webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').value">
<mat-expansion-panel-header fxLayout="row wrap" class="fill-width">
<mat-panel-title fxFlex="60">
<mat-slide-toggle class="mat-slide" formControlName="enabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent class="tb-extension-panel">
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="text" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field fxFlex="30" fxFlex.xs="100">
<mat-label translate>notification.action-type</mat-label>
<mat-select formControlName="linkType">
<mat-option *ngFor="let actionButtonLinkType of actionButtonLinkTypes" [value]="actionButtonLinkType">
{{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.LINK; else dashboardSelector">
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="link" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.link').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
<ng-template #dashboardSelector>
<tb-dashboard-autocomplete
fxFlex="35" fxFlex.xs="100"
required
formControlName="dashboardId">
</tb-dashboard-autocomplete>
<tb-dashboard-state-autocomplete fxFlex="35" fxFlex.xs="100"
[dashboardId]="webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').value"
formControlName="dashboardState">
</tb-dashboard-state-autocomplete>
</ng-template>
</div>
<mat-slide-toggle formControlName="setEntityIdInState" class="toggle"
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.DASHBOARD">
{{ 'notification.set-entity-from-notification' | translate }}
</mat-slide-toggle>
</ng-template>
</mat-expansion-panel>
</div>
</section>
</form>
</mat-step>
@ -333,6 +340,78 @@
</mat-form-field>
</form>
</mat-step>
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.MICROSOFT_TEAMS.enabled').value"
[stepControl]="microsoftTeamsTemplateForm">
<ng-template matStepLabel>{{ 'notification.delivery-method.microsoft-teams' | translate }}</ng-template>
<div class="tb-hint-available-params mat-body-2">
<span class="content">{{ 'notification.input-fields-support-templatization' | translate}}</span>
<span tb-help-popup="{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).helpId }}"
tb-help-popup-placement="bottom"
trigger-style="letter-spacing:0.25px"
[tb-help-popup-style]="{maxWidth: '800px'}"
trigger-text="{{ 'notification.see-documentation' | translate }}"></span>
</div>
<form [formGroup]="microsoftTeamsTemplateForm">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body">
</textarea>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<div class="tb-form-panel no-padding no-border">
<div class="tb-form-row space-between">
<div translate>notification.theme-color</div>
<tb-color-input asBoxInput formControlName="themeColor"></tb-color-input>
</div>
<div class="tb-form-panel tb-slide-toggle stroked" formGroupName="button">
<mat-expansion-panel class="tb-settings" [expanded]="microsoftTeamsTemplateForm.get('button.enabled').value">
<mat-expansion-panel-header fxLayout="row wrap" class="fill-width">
<mat-panel-title fxFlex="60">
<mat-slide-toggle class="mat-slide" formControlName="enabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.name').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.name').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: microsoftTeamsTemplateForm.get('button.name').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="uri" required>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.uri').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
</div>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>{{ 'notification.review' | translate }}</ng-template>
<mat-progress-spinner color="warn" mode="indeterminate"
@ -377,6 +456,16 @@
{{ preview.processedTemplates.SLACK.body }}
</div>
</section>
<section class="preview-group notification" *ngIf="preview.processedTemplates.MICROSOFT_TEAMS?.enabled">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18" svgIcon="mdi:microsoft-teams"></mat-icon>
<div class="group-title" translate>notification.delivery-method.microsoft-teams-preview</div>
</div>
<div class="notification-content mini">
<div class="subject">{{ preview.processedTemplates.MICROSOFT_TEAMS.subject }}</div>
{{ preview.processedTemplates.MICROSOFT_TEAMS.body }}
</div>
</section>
<section class="preview-group">
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
<mat-icon class="tb-mat-18">supervisor_account</mat-icon>
@ -399,7 +488,7 @@
</mat-horizontal-stepper>
</div>
<mat-divider></mat-divider>
<div mat-dialog-actions fxLayout="row">
<div mat-dialog-actions class="tb-dialog-actions">
<button mat-stroked-button *ngIf="selectedIndex > 0"
(click)="backStep()">{{ 'action.back' | translate }}</button>
<span fxFlex></span>

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

@ -16,13 +16,33 @@
@import "../../../../../../scss/constants";
@import "../../../../../../theme";
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
:host {
width: 820px;
height: 100%;
max-width: 100%;
max-height: 100vh;
display: flex;
flex-direction: column;
display: grid;
grid-template-rows: min-content 4px minmax(auto, 1fr) min-content min-content;
}
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
.mat-mdc-dialog-content {
grid-row: 3;
display: flex;
flex-direction: column;
height: 100%;
padding: 0;
color: rgba(0, 0, 0, 0.87);
}
.tb-dialog-actions {
grid-row: 5;
display: flex;
}
.mat-divider {
grid-row: 4;
}
.tb-title {
font-size: 16px;
@ -76,6 +96,7 @@
.delivery-method-container {
display: inline-flex;
flex: 1 1 calc(50% - 8px);
max-width: calc(50% - 8px);
padding: 16px 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
@ -178,17 +199,28 @@
line-height: 20px;
overflow-x: auto;
}
&.mini {
font-size: 12px;
line-height: 1.25;
.subject {
font-size: 14px;
line-height: 1.5;
padding-bottom: 4px;
}
}
}
}
.tb-notification-use-template-toggle-group {
margin-bottom: 24px;
width: 320px;
}
}
:host ::ng-deep {
.mat-mdc-dialog-content {
display: flex;
flex-direction: column;
height: 100%;
padding: 0 !important;
.mat-stepper-horizontal {
display: flex;
height: 100%;
@ -209,59 +241,9 @@
}
}
}
}
.mat-button-toggle-group.tb-notification-use-template-toggle-group {
&.mat-button-toggle-group-appearance-standard {
border: none;
border-radius: 18px;
margin-bottom: 24px;
.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;
.tb-form-panel .mat-expansion-panel.tb-settings > .mat-expansion-panel-content > .mat-expansion-panel-body {
gap: 0;
}
}
@ -273,9 +255,7 @@
}
}
}
}
:host ::ng-deep {
.delivery-methods-container {
.delivery-method-container {
&.interact * {

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

@ -43,6 +43,7 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
emailTemplateForm: FormGroup;
smsTemplateForm: FormGroup;
slackTemplateForm: FormGroup;
microsoftTeamsTemplateForm: FormGroup;
notificationDeliveryMethods = Object.keys(NotificationDeliveryMethod) as NotificationDeliveryMethod[];
notificationDeliveryMethodTranslateMap = NotificationDeliveryMethodTranslateMap;
@ -95,7 +96,7 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
icon: this.fb.group({
enabled: [false],
icon: [{value: 'notifications', disabled: true}, Validators.required],
color: ['#757575']
color: [{value: '#757575', disabled: true}]
}),
actionButtonConfig: this.fb.group({
enabled: [false],
@ -114,8 +115,10 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
).subscribe((value) => {
if (value) {
this.webTemplateForm.get('additionalConfig.icon.icon').enable({emitEvent: false});
this.webTemplateForm.get('additionalConfig.icon.color').enable({emitEvent: false});
} else {
this.webTemplateForm.get('additionalConfig.icon.icon').disable({emitEvent: false});
this.webTemplateForm.get('additionalConfig.icon.color').disable({emitEvent: false});
}
});
@ -163,11 +166,34 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
body: ['', Validators.required]
});
this.microsoftTeamsTemplateForm = this.fb.group({
subject: [''],
body: ['', Validators.required],
themeColor: [''],
button: this.fb.group({
enabled: [false],
name: [{value: '', disabled: true}, [Validators.required, Validators.maxLength(50)]],
uri: [{value: '', disabled: true}, Validators.required],
}),
});
this.microsoftTeamsTemplateForm.get('button.enabled').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
if (value) {
this.microsoftTeamsTemplateForm.get('button').enable({emitEvent: false});
} else {
this.microsoftTeamsTemplateForm.get('button').disable({emitEvent: false});
this.microsoftTeamsTemplateForm.get('button.enabled').enable({emitEvent: false});
}
});
this.deliveryMethodFormsMap = new Map<NotificationDeliveryMethod, FormGroup>([
[NotificationDeliveryMethod.WEB, this.webTemplateForm],
[NotificationDeliveryMethod.EMAIL, this.emailTemplateForm],
[NotificationDeliveryMethod.SMS, this.smsTemplateForm],
[NotificationDeliveryMethod.SLACK, this.slackTemplateForm]
[NotificationDeliveryMethod.SLACK, this.slackTemplateForm],
[NotificationDeliveryMethod.MICROSOFT_TEAMS, this.microsoftTeamsTemplateForm]
]);
}

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

@ -27,7 +27,6 @@
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper linear #notificationTemplateStepper
[labelPosition]="(stepperLabelPosition | async)"
@ -101,73 +100,82 @@
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<section formGroupName="additionalConfig">
<section formGroupName="icon" class="additional-config-group">
<mat-slide-toggle formControlName="enabled" class="toggle">
<section formGroupName="additionalConfig" class="tb-form-panel no-padding no-border">
<div class="tb-form-row space-between" formGroupName="icon">
<mat-slide-toggle formControlName="enabled" class="mat-slide">
{{ 'icon.icon' | translate }}
</mat-slide-toggle>
<div *ngIf="webTemplateForm.get('additionalConfig.icon.enabled').value"
fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<tb-material-icon-select formControlName="icon" required fxFlex>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-material-icon-select asBoxInput
[color]="webTemplateForm.get('additionalConfig.icon.color').value"
formControlName="icon">
</tb-material-icon-select>
<tb-color-input formControlName="color" fxFlex>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</section>
<section formGroupName="actionButtonConfig" class="additional-config-group">
<mat-slide-toggle formControlName="enabled" class="toggle">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
<div *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').value">
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="text" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field fxFlex="30" fxFlex.xs="100">
<mat-label translate>notification.action-type</mat-label>
<mat-select formControlName="linkType">
<mat-option *ngFor="let actionButtonLinkType of actionButtonLinkTypes" [value]="actionButtonLinkType">
{{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.LINK; else dashboardSelector">
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="link" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.link').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
<ng-template #dashboardSelector>
<tb-dashboard-autocomplete
fxFlex="35" fxFlex.xs="100"
required
formControlName="dashboardId">
</tb-dashboard-autocomplete>
<tb-dashboard-state-autocomplete fxFlex="35" fxFlex.xs="100"
[dashboardId]="webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').value"
formControlName="dashboardState">
</tb-dashboard-state-autocomplete>
</ng-template>
</div>
<mat-slide-toggle formControlName="setEntityIdInState" class="toggle"
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.DASHBOARD">
{{ 'notification.set-entity-from-notification' | translate }}
</mat-slide-toggle>
</div>
</section>
</div>
<div class="tb-form-panel tb-slide-toggle stroked" formGroupName="actionButtonConfig">
<mat-expansion-panel class="tb-settings" [expanded]="webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').value">
<mat-expansion-panel-header fxLayout="row wrap" class="fill-width">
<mat-panel-title fxFlex="60">
<mat-slide-toggle class="mat-slide" formControlName="enabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent class="tb-extension-panel">
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="text" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.text').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field fxFlex="30" fxFlex.xs="100">
<mat-label translate>notification.action-type</mat-label>
<mat-select formControlName="linkType">
<mat-option *ngFor="let actionButtonLinkType of actionButtonLinkTypes" [value]="actionButtonLinkType">
{{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.LINK; else dashboardSelector">
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="link" required>
<mat-error *ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.link').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
<ng-template #dashboardSelector>
<tb-dashboard-autocomplete
fxFlex="35" fxFlex.xs="100"
required
formControlName="dashboardId">
</tb-dashboard-autocomplete>
<tb-dashboard-state-autocomplete fxFlex="35" fxFlex.xs="100"
[dashboardId]="webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').value"
formControlName="dashboardState">
</tb-dashboard-state-autocomplete>
</ng-template>
</div>
<mat-slide-toggle formControlName="setEntityIdInState" class="toggle"
*ngIf="webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').value === actionButtonLinkType.DASHBOARD">
{{ 'notification.set-entity-from-notification' | translate }}
</mat-slide-toggle>
</ng-template>
</mat-expansion-panel>
</div>
</section>
</form>
</mat-step>
@ -259,10 +267,81 @@
</mat-form-field>
</form>
</mat-step>
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.MICROSOFT_TEAMS.enabled').value"
[stepControl]="microsoftTeamsTemplateForm">
<ng-template matStepLabel>{{ 'notification.delivery-method.microsoft-teams' | translate }}</ng-template>
<div class="tb-hint-available-params mat-body-2">
<span class="content">{{ 'notification.input-fields-support-templatization' | translate}}</span>
<span tb-help-popup="{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).helpId }}"
tb-help-popup-placement="bottom"
trigger-style="letter-spacing:0.25px"
[tb-help-popup-style]="{maxWidth: '800px'}"
trigger-text="{{ 'notification.see-documentation' | translate }}"></span>
</div>
<form [formGroup]="microsoftTeamsTemplateForm">
<mat-form-field class="mat-block">
<mat-label translate>notification.subject</mat-label>
<input matInput formControlName="subject">
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>notification.message</mat-label>
<textarea matInput
cdkTextareaAutosize
cols="1"
cdkAutosizeMinRows="1"
formControlName="body">
</textarea>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('body').hasError('required')">
{{ 'notification.message-required' | translate }}
</mat-error>
</mat-form-field>
<div class="tb-form-panel no-padding no-border">
<div class="tb-form-row space-between">
<div translate>notification.theme-color</div>
<tb-color-input asBoxInput formControlName="themeColor"></tb-color-input>
</div>
<div class="tb-form-panel tb-slide-toggle stroked" formGroupName="button">
<mat-expansion-panel class="tb-settings" [expanded]="microsoftTeamsTemplateForm.get('button.enabled').value">
<mat-expansion-panel-header fxLayout="row wrap" class="fill-width">
<mat-panel-title fxFlex="60">
<mat-slide-toggle class="mat-slide" formControlName="enabled" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'notification.action-button' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>notification.button-text</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.name').hasError('required')">
{{ 'notification.button-text-required' | translate }}
</mat-error>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.name').hasError('maxlength')">
{{ 'notification.button-text-max-length' | translate :
{length: microsoftTeamsTemplateForm.get('button.name').getError('maxlength').requiredLength}
}}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>notification.link</mat-label>
<input matInput formControlName="uri" required>
<mat-error *ngIf="microsoftTeamsTemplateForm.get('button.uri').hasError('required')">
{{ 'notification.link-required' | translate }}
</mat-error>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
</div>
</div>
</form>
</mat-step>
</mat-horizontal-stepper>
</div>
<mat-divider></mat-divider>
<div mat-dialog-actions fxLayout="row">
<div mat-dialog-actions class="tb-dialog-actions">
<button mat-stroked-button *ngIf="selectedIndex > 0"
(click)="backStep()">{{ 'action.back' | translate }}</button>
<span fxFlex></span>

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

@ -16,13 +16,33 @@
@import "../../../../../../scss/constants";
@import "../../../../../../theme";
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
width: 800px;
:host {
width: 840px;
height: 100%;
max-width: 100%;
max-height: 100vh;
display: flex;
flex-direction: column;
display: grid;
grid-template-rows: min-content 4px minmax(auto, 1fr) min-content min-content;
}
:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) {
.mat-mdc-dialog-content {
grid-row: 3;
display: flex;
flex-direction: column;
height: 100%;
padding: 0;
color: rgba(0, 0, 0, 0.87);
}
.tb-dialog-actions {
grid-row: 5;
display: flex;
}
.mat-divider {
grid-row: 4;
}
.tb-title {
font-size: 16px;
@ -70,6 +90,7 @@
.delivery-method-container {
display: inline-flex;
flex: 1 1 calc(50% - 8px);
max-width: calc(50% - 8px);
padding: 16px 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
@ -80,28 +101,10 @@
}
}
}
.additional-config-group {
padding: 16px 16px 4px;
margin-bottom: 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
width: 100%;
height: 100%;
.toggle {
margin-bottom: 12px;
}
}
}
:host ::ng-deep {
.mat-mdc-dialog-content {
display: flex;
flex-direction: column;
height: 100%;
padding: 0 !important;
.mat-stepper-horizontal {
display: flex;
height: 100%;
@ -122,5 +125,9 @@
}
}
}
.tb-form-panel .mat-expansion-panel.tb-settings > .mat-expansion-panel-content > .mat-expansion-panel-body {
gap: 0;
}
}
}

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

@ -246,7 +246,9 @@ export interface NotificationTarget extends Omit<BaseData<NotificationTargetId>,
configuration: NotificationTargetConfig;
}
export interface NotificationTargetConfig extends Partial<PlatformUsersNotificationTargetConfig & SlackNotificationTargetConfig> {
export interface NotificationTargetConfig extends Partial<PlatformUsersNotificationTargetConfig
& SlackNotificationTargetConfig
& MicrosoftTeamsNotificationTargetConfig> {
description?: string;
type: NotificationTargetType;
}
@ -276,14 +278,21 @@ export interface SlackNotificationTargetConfig {
conversationType: SlackChanelType;
conversation: SlackConversation;
}
export interface MicrosoftTeamsNotificationTargetConfig {
webhookUrl: string;
channelName: string;
}
export enum NotificationTargetType {
PLATFORM_USERS = 'PLATFORM_USERS',
SLACK = 'SLACK'
SLACK = 'SLACK',
MICROSOFT_TEAMS = 'MICROSOFT_TEAMS'
}
export const NotificationTargetTypeTranslationMap = new Map<NotificationTargetType, string>([
[NotificationTargetType.PLATFORM_USERS, 'notification.platform-users'],
[NotificationTargetType.SLACK, 'notification.delivery-method.slack']
[NotificationTargetType.SLACK, 'notification.delivery-method.slack'],
[NotificationTargetType.MICROSOFT_TEAMS, 'notification.delivery-method.microsoft-teams'],
]);
export interface NotificationTemplate extends Omit<BaseData<NotificationTemplateId>, 'label'>, ExportableEntity<NotificationTemplateId> {
@ -299,14 +308,17 @@ interface NotificationTemplateConfig {
}
export interface DeliveryMethodNotificationTemplate extends
Partial<WebDeliveryMethodNotificationTemplate & EmailDeliveryMethodNotificationTemplate & SlackDeliveryMethodNotificationTemplate>{
body?: string;
Partial<WebDeliveryMethodNotificationTemplate
& EmailDeliveryMethodNotificationTemplate
& SlackDeliveryMethodNotificationTemplate
& MicrosoftTeamsDeliveryMethodNotificationTemplate>{
body: string;
enabled: boolean;
method: NotificationDeliveryMethod;
}
interface WebDeliveryMethodNotificationTemplate {
subject?: string;
subject: string;
additionalConfig: WebDeliveryMethodAdditionalConfig;
}
@ -336,6 +348,15 @@ interface SlackDeliveryMethodNotificationTemplate {
conversationId: string;
}
interface MicrosoftTeamsDeliveryMethodNotificationTemplate {
subject?: string;
button: {
enabled: boolean;
name?: string;
uri?: string;
};
}
export enum NotificationStatus {
SENT = 'SENT',
READ = 'READ'
@ -345,14 +366,16 @@ export enum NotificationDeliveryMethod {
WEB = 'WEB',
SMS = 'SMS',
EMAIL = 'EMAIL',
SLACK = 'SLACK'
SLACK = 'SLACK',
MICROSOFT_TEAMS = 'MICROSOFT_TEAMS'
}
export const NotificationDeliveryMethodTranslateMap = new Map<NotificationDeliveryMethod, string>([
[NotificationDeliveryMethod.WEB, 'notification.delivery-method.web'],
[NotificationDeliveryMethod.SMS, 'notification.delivery-method.sms'],
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method.email'],
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method.slack']
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method.slack'],
[NotificationDeliveryMethod.MICROSOFT_TEAMS, 'notification.delivery-method.microsoft-teams'],
]);
export enum NotificationRequestStatus {

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

@ -2914,6 +2914,8 @@
"email-preview": "Email notification preview",
"slack": "Slack",
"slack-preview": "Slack notification preview",
"microsoft-teams": "Microsoft teams",
"microsoft-teams-preview": "Microsoft teams notification preview",
"sms": "SMS",
"sms-preview": "SMS notification preview",
"web": "Web",
@ -3079,6 +3081,7 @@
"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",
"theme-color": "Theme color",
"time": "Time",
"track-rule-node-events": "Track rule node events",
"trigger": {
@ -3100,7 +3103,12 @@
"updated": "Updated",
"use-template": "Use template",
"view-all": "View all",
"warning": "Warning"
"warning": "Warning",
"webhook-url": "Webhook URL",
"webhook-url-required": "Webhook URL is required",
"channel-name": "Chanel name",
"channel-name-required": "Chanel name is required"
},
"ota-update": {
"add": "Add package",

Loading…
Cancel
Save