From d772365b235b2dfeb5f685bf202e778d2f448561 Mon Sep 17 00:00:00 2001 From: Sofia Papacharalampous Date: Thu, 11 Apr 2024 16:43:10 +0300 Subject: [PATCH] added user notification preferences --- dmp-frontend/src/app/app.module.ts | 1 + .../src/app/ui/navbar/navbar.component.scss | 1 + .../user-profile/user-profile.component.html | 20 +++ .../ui/user-profile/user-profile.component.ts | 10 +- .../ui/user-profile/user-profile.module.ts | 4 +- dmp-frontend/src/assets/i18n/en.json | 33 ++++ .../core/enum/contact-info-type.enum.ts | 5 + .../core/enum/contact-type.enum.ts | 6 + .../core/enum/is-active.enum.ts | 4 + .../enum/notification-inapp-tracking.enum.ts | 4 + .../enum/notification-notify-state.enum.ts | 7 + .../notification-template-channel.enum.ts | 6 + .../enum/notification-template-kind.enum.ts | 6 + .../notification-tracking-process.enum.ts | 7 + .../enum/notification-tracking-state.enum.ts | 16 ++ .../core/enum/notification-type.enum.ts | 16 ++ .../core/enum/permission.enum.ts | 46 +++++ .../enum/tenant-configuration-type.enum.ts | 7 + .../core/formatting/enum-utils.service.ts | 120 +++++++++++++ .../core/formatting/formatting.module.ts | 33 ++++ .../formatting/pipes/is-active-type.pipe.ts | 11 ++ .../notification-inapp-tracking-type.pipe.ts | 11 ++ .../core/model/inapp-notification.model.ts | 17 ++ .../core/model/notification-template.model.ts | 48 ++++++ .../core/model/notification.model.ts | 30 ++++ .../core/model/principal.model.ts | 6 + .../core/model/tenant-configuration.model.ts | 92 ++++++++++ .../user-notification-preference.model.ts | 16 ++ .../core/model/user.model.ts | 36 ++++ .../core/query/notifier-list.lookup.ts | 11 ++ .../core/query/tenant-configuration.lookup.ts | 22 +++ .../user-notification-preference.lookup.ts | 20 +++ .../services/core-service.module.ts | 39 +++++ .../user-notification-preference.service.ts | 58 +++++++ ...rofile-notifier-list-editor.component.html | 37 ++++ ...rofile-notifier-list-editor.component.scss | 0 ...-profile-notifier-list-editor.component.ts | 158 ++++++++++++++++++ ...user-profile-notifier-list-editor.model.ts | 48 ++++++ ...ser-profile-notifier-list-editor.module.ts | 24 +++ dmp-frontend/tsconfig.json | 3 + 40 files changed, 1036 insertions(+), 3 deletions(-) create mode 100644 dmp-frontend/src/notification-service/core/enum/contact-info-type.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/contact-type.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/is-active.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-inapp-tracking.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-notify-state.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-template-channel.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-template-kind.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-tracking-process.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-tracking-state.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/notification-type.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/permission.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/enum/tenant-configuration-type.enum.ts create mode 100644 dmp-frontend/src/notification-service/core/formatting/enum-utils.service.ts create mode 100644 dmp-frontend/src/notification-service/core/formatting/formatting.module.ts create mode 100644 dmp-frontend/src/notification-service/core/formatting/pipes/is-active-type.pipe.ts create mode 100644 dmp-frontend/src/notification-service/core/formatting/pipes/notification-inapp-tracking-type.pipe.ts create mode 100644 dmp-frontend/src/notification-service/core/model/inapp-notification.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/notification-template.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/notification.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/principal.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/tenant-configuration.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/user-notification-preference.model.ts create mode 100644 dmp-frontend/src/notification-service/core/model/user.model.ts create mode 100644 dmp-frontend/src/notification-service/core/query/notifier-list.lookup.ts create mode 100644 dmp-frontend/src/notification-service/core/query/tenant-configuration.lookup.ts create mode 100644 dmp-frontend/src/notification-service/core/query/user-notification-preference.lookup.ts create mode 100644 dmp-frontend/src/notification-service/services/core-service.module.ts create mode 100644 dmp-frontend/src/notification-service/services/http/user-notification-preference.service.ts create mode 100644 dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.html create mode 100644 dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.scss create mode 100644 dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.ts create mode 100644 dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.model.ts create mode 100644 dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.module.ts diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index f94de1a24..d8bf2dffb 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -39,6 +39,7 @@ import { GuidedTourModule } from './library/guided-tour/guided-tour.module'; import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module'; import { OpenDMPCustomTranslationCompiler } from './utilities/translate/opendmp-custom-translation-compiler'; import { CoreAnnotationServiceModule } from 'annotation-service/services/core-service.module'; +import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module'; // AoT requires an exported function for factories export function HttpLoaderFactory(languageHttpService: LanguageHttpService) { diff --git a/dmp-frontend/src/app/ui/navbar/navbar.component.scss b/dmp-frontend/src/app/ui/navbar/navbar.component.scss index c2e3067e0..a976156c5 100644 --- a/dmp-frontend/src/app/ui/navbar/navbar.component.scss +++ b/dmp-frontend/src/app/ui/navbar/navbar.component.scss @@ -5,6 +5,7 @@ $mat-card-header-size: 40px !default; width: $mat-card-header-size; border-radius: 50%; flex-shrink: 0; + margin: 0; } .progress-bar { diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html index 37ce06a56..fa58ff9ac 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html @@ -208,6 +208,26 @@
+
+
+ + + + + {{ 'NOTIFICATION-SERVICE.USER-PROFILE.TITLE' | translate}} + + +
+
+ + + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts index 1562e8347..c7328eb27 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; @@ -56,7 +56,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes nestedCount = []; nestedIndex = 0; tenants: Observable>; - + expandedPreferences: boolean = false; + organisationsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { filterFn: this.filterOrganisations.bind(this), initialItems: (excludedItems: any[]) => this.filterOrganisations('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), @@ -447,4 +448,9 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes const returnUrl = '/profile'; this.authService.prepareAuthRequest(from(this.keycloakService.getToken()), {}).pipe(takeUntil(this._destroyed)).subscribe(() => this.authService.onAuthenticateSuccess(returnUrl), (error) => this.authService.onAuthenticateError(error)); } + + //Preferences + expandPreferences(): void { + this.expandedPreferences = true; + } } diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.module.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.module.ts index b8cd5f809..9a6cf89d2 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.module.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.module.ts @@ -10,6 +10,7 @@ import { AddAccountDialogComponent } from './add-account/add-account-dialog.comp import { AddAccountDialogModule } from './add-account/add-account-dialog.module'; import { UserProfileComponent } from './user-profile.component'; import { UserProfileRoutingModule } from './user-profile.routing'; +import { UserProfileNotifierListModule } from '@notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.module'; @NgModule({ imports: [ @@ -19,7 +20,8 @@ import { UserProfileRoutingModule } from './user-profile.routing'; UserProfileRoutingModule, AutoCompleteModule, AddAccountDialogModule, - FormValidationErrorsDialogModule + FormValidationErrorsDialogModule, + UserProfileNotifierListModule ], declarations: [ UserProfileComponent diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 5edc8abb5..4f728aec4 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1119,6 +1119,39 @@ "ACTIONS": { "DELETE": "Delete" } + }, + "TYPES": { + "CONTACT-TYPE": { + "EMAIL": "Email", + "IN-APP": "In App" + }, + "IS-ACTIVE": { + "ACTIVE": "Active", + "INACTIVE": "Inactive" + }, + "NOTIFICATION-TYPE": { + "REGISTRATION-INVITATION": "Registration Invitation", + "TOTP-OVERRIDE-USED": "TOPT Override Used", + "CREDENTIAL-RESET": "Credential Reset", + "DIRECT-LINK": "Direct Link", + "FORGET-ME-REQUEST": "Forget me Request", + "WHAT-YOU-KNOW-ABOUT-ME-REQUEST": "'What you Know about me' request", + "WHAT-YOU-KNOW-ABOUT-ME-REQUEST-COMPLETED": "Completion of 'what you Know about me' request", + "EMAIL-RESET-REQUEST": "Email reset request", + "EMAIL-RESET-AWARENESS": "Awareness of 'email reset' request", + "EMAIL-RESET-REMOVE": "Remove email reset request" + } + }, + "USER-PROFILE": { + "TITLE": "Notification Preferences", + "NOTIFIER-LIST-EDITOR": { + "ACTIONS": { + "SAVE": "Save" + }, + "MESSAGES": { + "ERROR": "Something went wrong while trying to load your notification preferences." + } + } } }, "DESCRIPTION-TEMPLATE-TYPE-EDITOR": { diff --git a/dmp-frontend/src/notification-service/core/enum/contact-info-type.enum.ts b/dmp-frontend/src/notification-service/core/enum/contact-info-type.enum.ts new file mode 100644 index 000000000..c7d02933f --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/contact-info-type.enum.ts @@ -0,0 +1,5 @@ +export enum ContactInfoType { + Email = 0, + MobilePhone = 1, + LandLinePhone = 2, +} diff --git a/dmp-frontend/src/notification-service/core/enum/contact-type.enum.ts b/dmp-frontend/src/notification-service/core/enum/contact-type.enum.ts new file mode 100644 index 000000000..9ca81fb54 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/contact-type.enum.ts @@ -0,0 +1,6 @@ +export enum ContactType { + Email = 0, + SlackBroadcast = 1, + Sms = 2, + InApp = 3, +} diff --git a/dmp-frontend/src/notification-service/core/enum/is-active.enum.ts b/dmp-frontend/src/notification-service/core/enum/is-active.enum.ts new file mode 100644 index 000000000..cdadd23b4 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/is-active.enum.ts @@ -0,0 +1,4 @@ +export enum IsActive { + Inactive = 0, + Active = 1 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-inapp-tracking.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-inapp-tracking.enum.ts new file mode 100644 index 000000000..92422833e --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-inapp-tracking.enum.ts @@ -0,0 +1,4 @@ +export enum NotificationInAppTracking { + Stored = 0, + Delivered = 1 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-notify-state.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-notify-state.enum.ts new file mode 100644 index 000000000..a6f151ecf --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-notify-state.enum.ts @@ -0,0 +1,7 @@ +export enum NotificationNotifyState { + Pending = 0, + Processing = 1, + Successful = 2, + Error = 3, + Omitted = 4 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-template-channel.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-template-channel.enum.ts new file mode 100644 index 000000000..b4a4e9384 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-template-channel.enum.ts @@ -0,0 +1,6 @@ +export enum NotificationTemplateChannel { + Email = 0, + SlackBroadcast = 1, + Sms = 2, + InApp = 3, +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-template-kind.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-template-kind.enum.ts new file mode 100644 index 000000000..ec0e8a47e --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-template-kind.enum.ts @@ -0,0 +1,6 @@ +export enum NotificationTemplateKind { + Draft = 0, + Secondary = 1, + Primary = 2, + Default = 3 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-tracking-process.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-tracking-process.enum.ts new file mode 100644 index 000000000..c932e91ac --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-tracking-process.enum.ts @@ -0,0 +1,7 @@ +export enum NotificationTrackingProcess { + Pending = 0, + Processing = 1, + Completed = 2, + Error = 3, + Omitted = 4 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-tracking-state.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-tracking-state.enum.ts new file mode 100644 index 000000000..cc40aee45 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-tracking-state.enum.ts @@ -0,0 +1,16 @@ +export enum NotificationTrackingState { + /// + /// Initial state + /// + Undefined = 0, + /// + /// Final for notifiers that do not provide any kind of tracking + /// + NA = 1, + Queued = 2, + Sent = 3, + Delivered = 4, + Undelivered = 5, + Failed = 6, + Unsent = 7 +} diff --git a/dmp-frontend/src/notification-service/core/enum/notification-type.enum.ts b/dmp-frontend/src/notification-service/core/enum/notification-type.enum.ts new file mode 100644 index 000000000..bf9861f19 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/notification-type.enum.ts @@ -0,0 +1,16 @@ +export enum NotificationType { + RegistrationInvitation = '4fdbfa80-7a71-4a69-b854-67cbb70648f1', + TotpOverrideUsed = '065deecd-21bb-44af-9983-e660fdf24bc4', + CredentialReset = '90db0b46-42de-bd89-aebf-6f27efeb256e', + DirectLink = 'bfe68845-cb05-4c5a-a03d-29161a7c9660', + ForgetMeRequest = '4542262a-22f8-4baa-9db6-1c8e70ac1dbb', + WhatYouKnowAboutMeRequest = 'd3cd55fe-8da2-42e7-a501-3795ee4f16d3', + WhatYouKnowAboutMeRequestCompleted = '55736f7a-83ab-4190-af43-9d031a6f9612', + EmailResetRequest = '223bb607-efa1-4ce7-99ec-4beabfef9a8b', + EmailResetAwareness = 'c9bc3f16-057e-4bba-8a5f-36bd835e5604', + EmailResetRemove = '1aeb49e8-c817-4088-8b45-08e0a5155796', + Unknown1 = "5b1d6c52-88f9-418b-9b8a-6f1f963d9ead", + Unknown2 = "b542b606-acc6-4629-adef-4d8ee2f01222", + Unknown3 = "33790bad-94d4-488a-8ee2-7f6295ca18ea", + Unknown4 = "4904dea2-5079-46d3-83be-3a19c9ab45dc", +} diff --git a/dmp-frontend/src/notification-service/core/enum/permission.enum.ts b/dmp-frontend/src/notification-service/core/enum/permission.enum.ts new file mode 100644 index 000000000..68bce36a8 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/permission.enum.ts @@ -0,0 +1,46 @@ +export enum NotificationServicePermission { + //Tenant + BrowseTenant = 'BrowseTenant', + EditTenant = 'EditTenant', + DeleteTenant = 'DeleteTenant', + //Tenant Configuration + BrowseTenantConfiguration = 'BrowseTenantConfiguration', + BrowseTenantConfigurationSlackBroadcast = 'BrowseTenantConfigurationSlackBroadcast', + BrowseTenantConfigurationSmsClient = 'BrowseTenantConfigurationSmsClient', + BrowseTenantConfigurationEmailClient = 'BrowseTenantConfigurationEmailClient', + BrowseTenantConfigurationUserLocale = 'BrowseTenantConfigurationUserLocale', + BrowseTenantAvailableNotifierList = 'BrowseTenantAvailableNotifierList', + BrowseTenantConfigurationNotifierList = 'BrowseTenantConfigurationNotifierList', + DeleteTenantConfiguration = 'DeleteTenantConfiguration', + EditTenantConfiguration = 'EditTenantConfiguration', + //User + BrowseUser = 'BrowseUser', + EditUser = 'EditUser', + DeleteUser = 'DeleteUser', + //User Notification Preference + BrowseUserNotificationPreference = 'BrowseUserNotificationPreference', + EditUserNotificationPreference = 'EditUserNotificationPreference', + DeleteUserNotificationPreference = 'DeleteUserNotificationPreference', + //Contact Info + BrowseContactInfo = 'BrowseContactInfo', + //Profile + BrowseUserProfile = 'BrowseUserProfile', + //Notifications + SubmitNotification = 'SubmitNotification', + BrowseNotification = 'BrowseNotification', + BrowseNotificationSensitive = 'BrowseNotificationSensitive', + DeleteNotification = 'DeleteNotification', + //Notification Templates + BrowseNotificationTemplate = 'BrowseNotificationTemplate', + SubmitNotificationTemplate = 'SubmitNotificationTemplate', + EditNotificationTemplate = 'EditNotificationTemplate', + DeleteNotificationTemplate = 'DeleteNotificationTemplate', + //ForgetMe + BrowseForgetMe = 'BrowseForgetMe', + EditForgetMe = 'EditForgetMe', + DeleteForgetMe = 'DeleteForgetMe', + //WhatYouKnowAboutMe + BrowseWhatYouKnowAboutMe = 'BrowseWhatYouKnowAboutMe', + EditWhatYouKnowAboutMe = 'EditWhatYouKnowAboutMe', + DeleteWhatYouKnowAboutMe = 'DeleteWhatYouKnowAboutMe', +} diff --git a/dmp-frontend/src/notification-service/core/enum/tenant-configuration-type.enum.ts b/dmp-frontend/src/notification-service/core/enum/tenant-configuration-type.enum.ts new file mode 100644 index 000000000..408f613de --- /dev/null +++ b/dmp-frontend/src/notification-service/core/enum/tenant-configuration-type.enum.ts @@ -0,0 +1,7 @@ +export enum TenantConfigurationType { + SlackBroadcast = 0, + EmailClientConfiguration = 1, + SmsClientConfiguration = 2, + DefaultUserLocale = 3, + NotifierList = 4 +} diff --git a/dmp-frontend/src/notification-service/core/formatting/enum-utils.service.ts b/dmp-frontend/src/notification-service/core/formatting/enum-utils.service.ts new file mode 100644 index 000000000..3df2f1c31 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/formatting/enum-utils.service.ts @@ -0,0 +1,120 @@ +import { Injectable } from '@angular/core'; +import { BaseEnumUtilsService } from '@common/base/base-enum-utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { NotificationInAppTracking } from '@notification-service/core/enum/notification-inapp-tracking.enum'; +import { NotificationNotifyState } from '@notification-service/core/enum/notification-notify-state.enum'; +import { NotificationTemplateChannel } from '@notification-service/core/enum/notification-template-channel.enum'; +import { NotificationTemplateKind } from '@notification-service/core/enum/notification-template-kind.enum'; +import { NotificationTrackingProcess } from '@notification-service/core/enum/notification-tracking-process.enum'; +import { NotificationTrackingState } from '@notification-service/core/enum/notification-tracking-state.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; + +@Injectable() +export class NotificationServiceEnumUtils extends BaseEnumUtilsService { + constructor(private language: TranslateService) { super(); } + + + public toIsActiveString(value: IsActive): string { + switch (value) { + case IsActive.Active: return this.language.instant('NOTIFICATION-SERVICE.TYPES.IS-ACTIVE.ACTIVE'); + case IsActive.Inactive: return this.language.instant('NOTIFICATION-SERVICE.TYPES.IS-ACTIVE.INACTIVE'); + default: return ''; + } + } + + public toNotificationTypeString(value: NotificationType): string { + switch (value) { + case NotificationType.RegistrationInvitation: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.REGISTRATION-INVITATION'); + case NotificationType.TotpOverrideUsed: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.TOTP-OVERRIDE-USED'); + case NotificationType.CredentialReset: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.CREDENTIAL-RESET'); + case NotificationType.DirectLink: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.DIRECT-LINK'); + case NotificationType.ForgetMeRequest: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.FORGET-ME-REQUEST'); + case NotificationType.WhatYouKnowAboutMeRequest: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.WHAT-YOU-KNOW-ABOUT-ME-REQUEST'); + case NotificationType.WhatYouKnowAboutMeRequestCompleted: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.WHAT-YOU-KNOW-ABOUT-ME-REQUEST-COMPLETED'); + case NotificationType.EmailResetRequest: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.EMAIL-RESET-REQUEST'); + case NotificationType.EmailResetAwareness: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.EMAIL-RESET-AWARENESS'); + case NotificationType.EmailResetRemove: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.EMAIL-RESET-REMOVE'); + case NotificationType.Unknown1: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.UNKOWN1'); + case NotificationType.Unknown2: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.UNKOWN2'); + case NotificationType.Unknown3: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.UNKOWN3'); + case NotificationType.Unknown4: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TYPE.UNKOWN4'); + default: return ''; + } + } + + public toContactTypeString(value: ContactType): string { + switch (value) { + case ContactType.Email: return this.language.instant('NOTIFICATION-SERVICE.TYPES.CONTACT-TYPE.EMAIL'); + case ContactType.SlackBroadcast: return this.language.instant('NOTIFICATION-SERVICE.TYPES.CONTACT-TYPE.SLACK-BROADCAST'); + case ContactType.Sms: return this.language.instant('NOTIFICATION-SERVICE.TYPES.CONTACT-TYPE.SMS'); + case ContactType.InApp: return this.language.instant('NOTIFICATION-SERVICE.TYPES.CONTACT-TYPE.IN-APP'); + default: return ''; + } + } + + public toNotificationNotifyStateString(value: NotificationNotifyState): string { + switch (value) { + case NotificationNotifyState.Pending: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-NOTIFY-STATE.PENDING'); + case NotificationNotifyState.Processing: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-NOTIFY-STATE.PROCESSING'); + case NotificationNotifyState.Successful: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-NOTIFY-STATE.SUCCESSFUL'); + case NotificationNotifyState.Error: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-NOTIFY-STATE.ERROR'); + case NotificationNotifyState.Omitted: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-NOTIFY-STATE.OMITTED'); + default: return ''; + } + } + + public toNotificationTrackingStateString(value: NotificationTrackingState): string { + switch (value) { + case NotificationTrackingState.Undefined: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.UNDEFINED'); + case NotificationTrackingState.NA: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.NA'); + case NotificationTrackingState.Queued: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.QUEUED'); + case NotificationTrackingState.Sent: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.SENT'); + case NotificationTrackingState.Delivered: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.DELIVERED'); + case NotificationTrackingState.Undelivered: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.UNDELIVERED'); + case NotificationTrackingState.Failed: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.FAILED'); + case NotificationTrackingState.Unsent: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-STATE.UNSENT'); + default: return ''; + } + } + + public toNotificationTrackingProcessString(value: NotificationTrackingProcess): string { + switch (value) { + case NotificationTrackingProcess.Pending: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-PROCESS.PENDING'); + case NotificationTrackingProcess.Processing: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-PROCESS.PROCESSING'); + case NotificationTrackingProcess.Completed: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-PROCESS.COMPLETED'); + case NotificationTrackingProcess.Error: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-PROCESS.ERROR'); + case NotificationTrackingProcess.Omitted: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TRACKING-PROCESS.OMITTED'); + default: return ''; + } + } + + public toNotificationInAppTrackingString(value: NotificationInAppTracking): string { + switch (value) { + case NotificationInAppTracking.Delivered: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-INAPP-TRACKING.DELIVERED'); + case NotificationInAppTracking.Stored: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-INAPP-TRACKING.STORED'); + default: return ''; + } + } + + public toNotificationChannelString(value: NotificationTemplateChannel): string { + switch (value) { + case NotificationTemplateChannel.Email: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-CHANNEL.EMAIL'); + case NotificationTemplateChannel.SlackBroadcast: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-CHANNEL.SLACK-BROADCAST'); + case NotificationTemplateChannel.Sms: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-CHANNEL.SMS'); + case NotificationTemplateChannel.InApp: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-CHANNEL.IN-APP'); + default: return ''; + } + } + + public toNotificationTemplateKindString(value: NotificationTemplateKind): string { + switch (value) { + case NotificationTemplateKind.Default: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-KIND.DEFAULT'); + case NotificationTemplateKind.Primary: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-KIND.PRIMARY'); + case NotificationTemplateKind.Secondary: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-KIND.SECONDARY'); + case NotificationTemplateKind.Draft: return this.language.instant('NOTIFICATION-SERVICE.TYPES.NOTIFICATION-TEMPLATE-KIND.DRAFT'); + default: return ''; + } + } +} diff --git a/dmp-frontend/src/notification-service/core/formatting/formatting.module.ts b/dmp-frontend/src/notification-service/core/formatting/formatting.module.ts new file mode 100644 index 000000000..07a97677d --- /dev/null +++ b/dmp-frontend/src/notification-service/core/formatting/formatting.module.ts @@ -0,0 +1,33 @@ +import { DatePipe } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CommonFormattingModule } from '@common/formatting/common-formatting.module'; +import { PipeService } from '@common/formatting/pipe.service'; +import { IsActiveTypePipe } from '@notification-service/core/formatting/pipes/is-active-type.pipe'; +import { NotificationInAppTrackingTypePipe } from '@notification-service/core/formatting/pipes/notification-inapp-tracking-type.pipe'; + +// +// +// This is shared module that provides all notification service formatting utils. Its imported only once. +// +// +@NgModule({ + imports: [ + CommonFormattingModule + ], + declarations: [ + IsActiveTypePipe, + NotificationInAppTrackingTypePipe + ], + exports: [ + CommonFormattingModule, + IsActiveTypePipe, + NotificationInAppTrackingTypePipe + ], + providers: [ + PipeService, + DatePipe, + IsActiveTypePipe, + NotificationInAppTrackingTypePipe + ] +}) +export class NotificationServiceFormattingModule { } diff --git a/dmp-frontend/src/notification-service/core/formatting/pipes/is-active-type.pipe.ts b/dmp-frontend/src/notification-service/core/formatting/pipes/is-active-type.pipe.ts new file mode 100644 index 000000000..30c3415d7 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/formatting/pipes/is-active-type.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service'; + +@Pipe({ name: 'IsActiveTypeFormat' }) +export class IsActiveTypePipe implements PipeTransform { + constructor(private enumUtils: NotificationServiceEnumUtils) { } + + public transform(value): any { + return this.enumUtils.toIsActiveString(value); + } +} diff --git a/dmp-frontend/src/notification-service/core/formatting/pipes/notification-inapp-tracking-type.pipe.ts b/dmp-frontend/src/notification-service/core/formatting/pipes/notification-inapp-tracking-type.pipe.ts new file mode 100644 index 000000000..048d71cce --- /dev/null +++ b/dmp-frontend/src/notification-service/core/formatting/pipes/notification-inapp-tracking-type.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service'; + +@Pipe({ name: 'NotificationInAppTrackingTypeFormat' }) +export class NotificationInAppTrackingTypePipe implements PipeTransform { + constructor(private enumUtils: NotificationServiceEnumUtils) { } + + public transform(value): any { + return this.enumUtils.toNotificationInAppTrackingString(value); + } +} diff --git a/dmp-frontend/src/notification-service/core/model/inapp-notification.model.ts b/dmp-frontend/src/notification-service/core/model/inapp-notification.model.ts new file mode 100644 index 000000000..a373bf6be --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/inapp-notification.model.ts @@ -0,0 +1,17 @@ +import { Guid } from '@common/types/guid'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { NotificationInAppTracking } from '@notification-service/core/enum/notification-inapp-tracking.enum'; +import { NotificationServiceUser } from '@notification-service/core/model/user.model'; + +export interface InAppNotification { + id: Guid; + user: NotificationServiceUser; + isActive: IsActive; + type: Guid; + trackingState: NotificationInAppTracking; + subject: string; + body: string; + createdAt: Date; + updatedAt: Date; + hash: string; +} diff --git a/dmp-frontend/src/notification-service/core/model/notification-template.model.ts b/dmp-frontend/src/notification-service/core/model/notification-template.model.ts new file mode 100644 index 000000000..c68e86ff3 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/notification-template.model.ts @@ -0,0 +1,48 @@ +import { Guid } from '@common/types/guid'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { NotificationTemplateChannel } from '@notification-service/core/enum/notification-template-channel.enum'; +import { NotificationTemplateKind } from '@notification-service/core/enum/notification-template-kind.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; + +export interface NotificationTemplate { + id: Guid; + channel: NotificationTemplateChannel; + notificationType: NotificationType; + kind: NotificationTemplateKind; + language: string; + description: string; + value: NotificationTemplateValue; + isActive: IsActive; + createdAt: Date; + updatedAt: Date; + hash: string; +} + +export interface NotificationTemplateValue { + subjectText: string; + subjectFieldOptions: NotificationFieldOptions; + bodyText: string; + bodyFieldOptions: NotificationFieldOptions; +} + +export interface NotificationFieldOptions { + mandatory?: string[]; + options?: NotificationFieldInfo[]; + formatting?: { [key: string]: string }; +} + +export interface NotificationFieldInfo { + key: string; + value: string; +} + +export interface NotificationTemplatePersist { + id?: Guid; + channel: NotificationTemplateChannel; + notificationType: NotificationType; + kind: NotificationTemplateKind; + language: string; + description: string; + value: NotificationTemplateValue; + hash?: string; +} diff --git a/dmp-frontend/src/notification-service/core/model/notification.model.ts b/dmp-frontend/src/notification-service/core/model/notification.model.ts new file mode 100644 index 000000000..7edaf49ec --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/notification.model.ts @@ -0,0 +1,30 @@ +import { Guid } from '@common/types/guid'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { NotificationNotifyState } from '@notification-service/core/enum/notification-notify-state.enum'; +import { NotificationTrackingProcess } from '@notification-service/core/enum/notification-tracking-process.enum'; +import { NotificationTrackingState } from '@notification-service/core/enum/notification-tracking-state.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; +import { NotificationServiceUser } from '@notification-service/core/model/user.model'; + +export interface Notification { + id: Guid; + user: NotificationServiceUser; + isActive: IsActive; + type: NotificationType; + contactType: ContactType; + contactHint: string; + data: string; + notifyState: NotificationNotifyState; + notifiedWith: ContactType; + notifiedAt: Date; + retryCount: number; + trackingState: NotificationTrackingState; + trackingProcess: NotificationTrackingProcess; + trackingData: string; + provenanceType: NotificationType; + provenanceRef: string; + createdAt: Date; + updatedAt: Date; + hash: string; +} diff --git a/dmp-frontend/src/notification-service/core/model/principal.model.ts b/dmp-frontend/src/notification-service/core/model/principal.model.ts new file mode 100644 index 000000000..bf43f3199 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/principal.model.ts @@ -0,0 +1,6 @@ +import { NotificationServicePermission } from '@notification-service/core/enum/permission.enum'; + +export interface NotificationServiceAccount { + isAuthenticated: boolean; + permissions: NotificationServicePermission[]; +} diff --git a/dmp-frontend/src/notification-service/core/model/tenant-configuration.model.ts b/dmp-frontend/src/notification-service/core/model/tenant-configuration.model.ts new file mode 100644 index 000000000..367faef74 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/tenant-configuration.model.ts @@ -0,0 +1,92 @@ +import { Guid } from '@common/types/guid'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { TenantConfigurationType } from '@notification-service/core/enum/tenant-configuration-type.enum'; + +export interface TenantConfiguration { + id?: Guid; + type: TenantConfigurationType; + value: string; + slackBroadcastData: SlackBroadcastDataContainer; + emailClientData: EmailClientConfigurationDataContainer; + smsClientData: SmsClientConfigurationDataContainer; + notifierListData: NotifierListConfigurationDataContainer; + createdAt?: Date; + updatedAt?: Date; + hash: string; +} + +// +// Slack Broadcast +// +export interface SlackBroadcastDataContainer { + webhooks: SlackBroadcastWebhookInfo[]; +} + +export interface TenantConfigurationSlackBroadcastPersist { + id?: Guid; + hash?: string; + webhooks: SlackBroadcastWebhookInfo[]; +} + +export interface SlackBroadcastWebhookInfo { + id: Guid; + name: string; + webhook: string; +} + +// +// Email Client Configuration +// +export interface EmailClientConfigurationDataContainer { + requiresCredentials: boolean; + enableSSL: boolean; + hostServer: string; + hostPortNo: number; + emailAddress: string; + emailUsername: string; + emailPassword: string; +} + +export interface TenantConfigurationEmailClientPersist { + id?: Guid; + hash?: string; + requiresCredentials: boolean; + enableSSL: boolean; + hostServer: string; + hostPortNo: number; + emailAddress: string; + emailUsername: string; + emailPassword: string; +} + +// +// Sms Client +// +export interface SmsClientConfigurationDataContainer { + applicationId: string; + applicationSecret: string; + sendAsName: string; +} + +export interface TenantConfigurationSmsClientPersist { + id?: Guid; + hash?: string; + applicationId: string; + applicationSecret: string; + sendAsName: string; +} + +// +// Notifier List +// +export interface NotifierListConfigurationDataContainer { + notifiers: { [key: string]: ContactType[] }; +} + +export interface TenantConfigurationNotifierListPersist { + id?: Guid; + hash?: string; + notifiers: { [key: string]: ContactType[] }; +} + + diff --git a/dmp-frontend/src/notification-service/core/model/user-notification-preference.model.ts b/dmp-frontend/src/notification-service/core/model/user-notification-preference.model.ts new file mode 100644 index 000000000..e49c1ad1b --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/user-notification-preference.model.ts @@ -0,0 +1,16 @@ +import { Guid } from '@common/types/guid'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; + +export interface UserNotificationPreference { + userId?: Guid; + type: NotificationType; + channel: ContactType; + ordinal: number; + createdAt?: Date; +} + +export interface UserNotificationPreferencePersist { + userId?: Guid; + notificationPreferences: { [key: string]: ContactType[] }; +} diff --git a/dmp-frontend/src/notification-service/core/model/user.model.ts b/dmp-frontend/src/notification-service/core/model/user.model.ts new file mode 100644 index 000000000..fac532e8f --- /dev/null +++ b/dmp-frontend/src/notification-service/core/model/user.model.ts @@ -0,0 +1,36 @@ +import { Guid } from '@common/types/guid'; +import { ContactInfoType } from '@notification-service/core/enum/contact-info-type.enum'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; + +export interface NotificationServiceUser { + id: Guid; + isActive?: IsActive; + createdAt?: Date; + updatedAt?: Date; + hash: string; + contacts: NotificationServiceContactInfo[]; + profile: NotificationServiceUserProfile; +} + +export interface NotificationServiceContactInfo { + id: Guid; + user: NotificationServiceUser; + type?: ContactInfoType; + isActive?: IsActive; + value: string; + ordinal: number; + createdAt?: Date; + updatedAt?: Date; + hash: string; +} + +export interface NotificationServiceUserProfile { + id: Guid; + timezone: string; + culture?: string; + language: string; + createdAt?: Date; + updatedAt?: Date; + hash: string; + users: NotificationServiceUser[]; +} diff --git a/dmp-frontend/src/notification-service/core/query/notifier-list.lookup.ts b/dmp-frontend/src/notification-service/core/query/notifier-list.lookup.ts new file mode 100644 index 000000000..4234958ae --- /dev/null +++ b/dmp-frontend/src/notification-service/core/query/notifier-list.lookup.ts @@ -0,0 +1,11 @@ +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; + +export class NotifierListLookup implements TenantConfigurationFilter { + notificationTypes?: NotificationType[]; + + constructor() { } +} + +export interface TenantConfigurationFilter { + notificationTypes?: NotificationType[]; +} diff --git a/dmp-frontend/src/notification-service/core/query/tenant-configuration.lookup.ts b/dmp-frontend/src/notification-service/core/query/tenant-configuration.lookup.ts new file mode 100644 index 000000000..5ae3688a8 --- /dev/null +++ b/dmp-frontend/src/notification-service/core/query/tenant-configuration.lookup.ts @@ -0,0 +1,22 @@ +import { Lookup } from '@common/model/lookup'; +import { Guid } from '@common/types/guid'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { TenantConfigurationType } from '@notification-service/core/enum/tenant-configuration-type.enum'; + +export class TenantConfigurationLookup extends Lookup implements TenantConfigurationFilter { + ids?: Guid[]; + excludedIds?: Guid[]; + isActive?: IsActive[]; + type?: TenantConfigurationType[]; + + constructor() { + super(); + } +} + +export interface TenantConfigurationFilter { + ids?: Guid[]; + excludedIds?: Guid[]; + isActive?: IsActive[]; + type?: TenantConfigurationType[]; +} diff --git a/dmp-frontend/src/notification-service/core/query/user-notification-preference.lookup.ts b/dmp-frontend/src/notification-service/core/query/user-notification-preference.lookup.ts new file mode 100644 index 000000000..0a1ceef9b --- /dev/null +++ b/dmp-frontend/src/notification-service/core/query/user-notification-preference.lookup.ts @@ -0,0 +1,20 @@ +import { Lookup } from '@common/model/lookup'; +import { Guid } from '@common/types/guid'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; + +export class UserNotificationPreferenceLookup extends Lookup implements UserNotificationPreferenceFilter { + userIds?: Guid[]; + type?: NotificationType[]; + channel?: ContactType[]; + + constructor() { + super(); + } +} + +export interface UserNotificationPreferenceFilter { + userIds?: Guid[]; + type?: NotificationType[]; + channel?: ContactType[]; +} diff --git a/dmp-frontend/src/notification-service/services/core-service.module.ts b/dmp-frontend/src/notification-service/services/core-service.module.ts new file mode 100644 index 000000000..7ca5f6c74 --- /dev/null +++ b/dmp-frontend/src/notification-service/services/core-service.module.ts @@ -0,0 +1,39 @@ +import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; +import { BaseHttpV2Service } from '@app/core/services/http/base-http-v2.service'; +import { FormService } from '@common/forms/form-service'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { UserNotificationPreferenceService } from '@notification-service/services/http/user-notification-preference.service'; +import { LoggingService } from '@app/core/services/logging/logging-service'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { PrincipalService } from '@app/core/services/http/principal.service'; +import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service'; + +// +// +// This is shared module that provides all notification service's services. Its imported only once on the AppModule. +// +// +@NgModule({}) +export class CoreNotificationServiceModule { + constructor(@Optional() @SkipSelf() parentModule: CoreNotificationServiceModule) { + if (parentModule) { + throw new Error( + 'CoreNotificationServiceModule is already loaded. Import it in the AppModule only'); + } + } + static forRoot(): ModuleWithProviders { + return { + ngModule: CoreNotificationServiceModule, + providers: [ + BaseHttpV2Service, + HttpErrorHandlingService, + NotificationServiceEnumUtils, + FormService, + FilterService, + LoggingService, + PrincipalService, + UserNotificationPreferenceService + ], + }; + } +} diff --git a/dmp-frontend/src/notification-service/services/http/user-notification-preference.service.ts b/dmp-frontend/src/notification-service/services/http/user-notification-preference.service.ts new file mode 100644 index 000000000..70159ce87 --- /dev/null +++ b/dmp-frontend/src/notification-service/services/http/user-notification-preference.service.ts @@ -0,0 +1,58 @@ +import { HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { BaseHttpV2Service } from '@app/core/services/http/base-http-v2.service'; +import { QueryResult } from '@common/model/query-result'; +import { Guid } from '@common/types/guid'; +import { NotifierListConfigurationDataContainer } from '@notification-service/core/model/tenant-configuration.model'; +import { UserNotificationPreference, UserNotificationPreferencePersist } from '@notification-service/core/model/user-notification-preference.model'; +import { NotifierListLookup } from '@notification-service/core/query/notifier-list.lookup'; +import { UserNotificationPreferenceLookup } from '@notification-service/core/query/user-notification-preference.lookup'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +@Injectable() +export class UserNotificationPreferenceService { + + private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}api/notification/notification-preference`; } + + constructor( + private configurationService: ConfigurationService, + private http: BaseHttpV2Service + ) { } + + query(q: UserNotificationPreferenceLookup): Observable> { + const url = `${this.apiBase}/query`; + return this.http + .post>(url, q).pipe( + catchError((error: any) => throwError(error))); + } + + current(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/user/${id}/current`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + getNotifierList(q: NotifierListLookup): Observable { + const url = `${this.apiBase}/notifier-list/available`; + + return this.http + .post(url, q).pipe( + catchError((error: any) => throwError(error))); + } + + persist(item: UserNotificationPreferencePersist, totp?: string): Observable { + const url = `${this.apiBase}/persist`; + let headers = new HttpHeaders(); + // if (totp) { headers = headers.set(this.configurationService.authTotpHeader, totp); } + + return this.http + .post(url, item, headers ? { headers: headers } : undefined).pipe( + catchError((error: any) => throwError(error))); + return; + } +} diff --git a/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.html b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.html new file mode 100644 index 000000000..82bbb9cd9 --- /dev/null +++ b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.html @@ -0,0 +1,37 @@ +
+
+
+ +

{{notificationServiceEnumUtils.toNotificationTypeString(notificationType)}}

+ +
+ unfold_more + {{notificationServiceEnumUtils.toContactTypeString(contactType)}} +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ {{ 'NOTIFICATION-SERVICE.USER-PROFILE.NOTIFIER-LIST-EDITOR.MESSAGES.ERROR' | translate}} +
+
+ diff --git a/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.scss b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.ts b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.ts new file mode 100644 index 000000000..2cd130b19 --- /dev/null +++ b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component.ts @@ -0,0 +1,158 @@ +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { LoggingService } from '@app/core/services/logging/logging-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { BaseComponent } from '@common/base/base.component'; +import { FormService } from '@common/forms/form-service'; +import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +// import { TotpService } from '@idp-service/ui/totp/totp.service'; +import { TranslateService } from '@ngx-translate/core'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { NotificationTrackingProcess } from '@notification-service/core/enum/notification-tracking-process.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; +import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service'; +import { UserNotificationPreference } from '@notification-service/core/model/user-notification-preference.model'; +import { NotifierListLookup } from '@notification-service/core/query/notifier-list.lookup'; +import { UserNotificationPreferenceService } from '@notification-service/services/http/user-notification-preference.service'; +import { UserProfileNotifierListEditorModel } from '@notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.model'; +import { delay, takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + selector: 'app-user-profile-notifier-list-editor', + templateUrl: './user-profile-notifier-list-editor.component.html', + styleUrls: ['./user-profile-notifier-list-editor.component.scss'] +}) +export class UserProfileNotifierListEditorComponent extends BaseComponent implements OnInit { + + formGroup: FormGroup; + editorModel: UserProfileNotifierListEditorModel; + availableNotifiers: { [key: string]: ContactType[] } = {}; + availableNotifiersKeys: NotificationType[]; + + notificationTrackingProcess: NotificationTrackingProcess = NotificationTrackingProcess.Pending; + + constructor( + private userNotificationPreferenceService: UserNotificationPreferenceService, + private uiNotificationService: UiNotificationService, + private language: TranslateService, + private httpErrorHandlingService: HttpErrorHandlingService, + private authService: AuthService, + private formService: FormService, + private logger: LoggingService, + public notificationServiceEnumUtils: NotificationServiceEnumUtils, + // private totpService: TotpService + ) { + super(); + } + + ngOnInit(): void { + this.getConfiguration(); + } + + getConfiguration() { + this.formGroup = null; + this.userNotificationPreferenceService.getNotifierList(new NotifierListLookup()) + .pipe(takeUntil(this._destroyed)) + .subscribe( + data => { + try { + this.availableNotifiers = data.notifiers; + this.availableNotifiersKeys = Object.keys(this.availableNotifiers) as NotificationType[]; + this.getExistingSelections(); + } catch { + this.notificationTrackingProcess = NotificationTrackingProcess.Error; + this.logger.error('Could not parse Dataset: ' + data); + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); + } + }, + error => this.onCallbackError(error) + ); + } + + getExistingSelections() { + this.userNotificationPreferenceService.current(this.authService.userId(), [ + nameof(x => x.userId), + nameof(x => x.type), + nameof(x => x.channel), + nameof(x => x.ordinal), + ]) + .pipe(takeUntil(this._destroyed)).subscribe( + data => { + try { + if (data.length > 0) { + this.orderAvailableItemsbasedOnExistingSelections(data); + } + this.notificationTrackingProcess = NotificationTrackingProcess.Completed; + } catch { + this.notificationTrackingProcess = NotificationTrackingProcess.Error; + this.logger.error('Could not parse Dataset: ' + data); + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); + } + }, + error => this.onCallbackError(error) + ); + } + + orderAvailableItemsbasedOnExistingSelections(notificationPreferences: UserNotificationPreference[]) { + if (!notificationPreferences || notificationPreferences.length === 0) { return; } + this.availableNotifiersKeys.forEach(key => { + const orderedList = []; + orderedList.push(...(notificationPreferences.filter(x => x.type === key && this.availableNotifiers[key].includes(x.channel)).sort((n1, n2) => n1.ordinal - n2.ordinal).map(x => x.channel))); // First push the selected ordered values. + orderedList.push(...this.availableNotifiers[key].filter(x => !orderedList.includes(x))); //Then push the rest items. + this.availableNotifiers[key] = orderedList; + }); + } + + formSubmit(): void { + this.persist(); + } + + private persist() { + const persistValue = { notificationPreferences: this.availableNotifiers, userId: this.authService.userId() }; + + this.userNotificationPreferenceService.persist(persistValue) + .pipe(takeUntil(this._destroyed)) + .subscribe( + response => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); + this.getConfiguration(); + } + + onCallbackError(errorResponse: HttpErrorResponse) { + this.notificationTrackingProcess = NotificationTrackingProcess.Error; + + const error: HttpError = this.httpErrorHandlingService.getError(errorResponse); + if (error.statusCode === 400) { + } else { + this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning); + } + } + + clearErrorModel() { + this.editorModel.validationErrorModel.clear(); + this.formService.validateAllFormFields(this.formGroup); + } + + dropped(event: CdkDragDrop, type: NotificationType) { + moveItemInArray(this.availableNotifiers[type], event.previousIndex, event.currentIndex); + } + + preferencesNotPending(): boolean { + return ! (this.notificationTrackingProcess === NotificationTrackingProcess.Pending); + } + preferencesNotCompleted(): boolean { + return ! (this.notificationTrackingProcess === NotificationTrackingProcess.Completed); + } + preferencesNotWithErrors(): boolean { + return ! (this.notificationTrackingProcess === NotificationTrackingProcess.Error); + } +} diff --git a/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.model.ts b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.model.ts new file mode 100644 index 000000000..5e4a717d9 --- /dev/null +++ b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.model.ts @@ -0,0 +1,48 @@ +import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { BackendErrorValidator } from '@common/forms/validation/custom-validator'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { Validation, ValidationContext } from '@common/forms/validation/validation-context'; +import { Guid } from '@common/types/guid'; +import { ContactType } from '@notification-service/core/enum/contact-type.enum'; +import { NotificationType } from '@notification-service/core/enum/notification-type.enum'; +import { UserNotificationPreferencePersist } from '@notification-service/core/model/user-notification-preference.model'; + +export class UserProfileNotifierListEditorModel implements UserNotificationPreferencePersist { + userId?: Guid; + notificationPreferences: { [key: string]: ContactType[] }; + + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + protected formBuilder: FormBuilder = new FormBuilder(); + + constructor() { } + + public fromModel(userId: Guid, notificationPreferences: { [key: string]: ContactType[] }): UserProfileNotifierListEditorModel { + this.userId = userId; + this.notificationPreferences = notificationPreferences; + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false, availableNotificationTypes: NotificationType[]): FormGroup { + if (context == null) { context = this.createValidationContext(); } + + const notificationPreferencesFormGroup = this.formBuilder.group({}); + availableNotificationTypes.forEach(type => { + notificationPreferencesFormGroup.addControl(type, new FormControl(this.notificationPreferences ? this.notificationPreferences[type] : undefined)); + }); + + return this.formBuilder.group({ + userId: [{ value: this.userId, disabled: disabled }, context.getValidation('userId').validators], + notificationPreferences: notificationPreferencesFormGroup, + }); + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + + baseValidationArray.push({ key: 'userId', validators: [BackendErrorValidator(this.validationErrorModel, 'UserId')] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} diff --git a/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.module.ts b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.module.ts new file mode 100644 index 000000000..8279617c7 --- /dev/null +++ b/dmp-frontend/src/notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.module.ts @@ -0,0 +1,24 @@ +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { NgModule } from '@angular/core'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module'; +// import { TotpModule } from '@idp-service/ui/totp/totp.module'; +import { UserProfileNotifierListEditorComponent } from '@notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + DragDropModule, + CoreNotificationServiceModule.forRoot(), + // TotpModule, + ], + declarations: [ + UserProfileNotifierListEditorComponent + ], + exports: [ + UserProfileNotifierListEditorComponent + ] +}) +export class UserProfileNotifierListModule { } diff --git a/dmp-frontend/tsconfig.json b/dmp-frontend/tsconfig.json index 1800d0668..caa1c02be 100644 --- a/dmp-frontend/tsconfig.json +++ b/dmp-frontend/tsconfig.json @@ -27,6 +27,9 @@ ], "@annotation-service/*":[ "./annotation-service/*" + ], + "@notification-service/*":[ + "./notification-service/*" ] }, "useDefineForClassFields": false