[Library | Trunk]: Notifications for development
git-svn-id: https://svn.driver.research-infrastructures.eu/driver/dnet40/modules/uoa-services-library/trunk/ng-openaire-library/src/app@60572 d315682c-612b-4755-9ff5-7f18f6832af3
This commit is contained in:
parent
2b77657e80
commit
391388f503
|
@ -37,7 +37,7 @@ export class PageContentFormComponent implements OnInit {
|
||||||
public errorMessage: string = '';
|
public errorMessage: string = '';
|
||||||
@Input() updateErrorMessage: string = '';
|
@Input() updateErrorMessage: string = '';
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
private pageHelpContent: PageHelpContent;
|
public pageHelpContent: PageHelpContent;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private _router: Router, private _fb: FormBuilder, private _helpContentService: HelpContentService) {
|
constructor(private route: ActivatedRoute, private _router: Router, private _fb: FormBuilder, private _helpContentService: HelpContentService) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {Role, Session, User} from "../../../login/utils/helper.class";
|
||||||
import {UserManagementService} from "../../../services/user-management.service";
|
import {UserManagementService} from "../../../services/user-management.service";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {StringUtils} from "../../../utils/string-utils.class";
|
import {StringUtils} from "../../../utils/string-utils.class";
|
||||||
|
import {NotificationService} from "../../../notifications/notification.service";
|
||||||
|
|
||||||
declare var UIkit;
|
declare var UIkit;
|
||||||
|
|
||||||
|
@ -54,15 +55,16 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
|
|
||||||
constructor(private userRegistryService: UserRegistryService,
|
constructor(private userRegistryService: UserRegistryService,
|
||||||
private userManagementService: UserManagementService,
|
private userManagementService: UserManagementService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private fb: FormBuilder) {
|
private fb: FormBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.updateLists();
|
this.updateLists();
|
||||||
this.userManagementService.getUserInfo().subscribe(user => {
|
this.userManagementService.getUserInfo().subscribe(user => {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
@ -207,6 +209,21 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
if (!this.pending.includes(this.invited.value)) {
|
if (!this.pending.includes(this.invited.value)) {
|
||||||
this.pending.push(this.invited.value);
|
this.pending.push(this.invited.value);
|
||||||
}
|
}
|
||||||
|
if(this.notificationFn) {
|
||||||
|
this.subs.push(this.notificationService.sendNotification(this.notificationFn(this.name, this.invited.value, this.role, invitation)).subscribe(notification => {
|
||||||
|
UIkit.notification('A notification has been <b>sent</b> successfully', {
|
||||||
|
status: 'success',
|
||||||
|
timeout: 6000,
|
||||||
|
pos: 'bottom-right'
|
||||||
|
});
|
||||||
|
}, error => {
|
||||||
|
UIkit.notification('An error has occurred. Please try again later', {
|
||||||
|
status: 'danger',
|
||||||
|
timeout: 6000,
|
||||||
|
pos: 'bottom-right'
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
UIkit.notification(StringUtils.capitalize(this.role) + ' invitation to ' + this.selectedUser + ' has been <b>sent</b>', {
|
UIkit.notification(StringUtils.capitalize(this.role) + ' invitation to ' + this.selectedUser + ' has been <b>sent</b>', {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
timeout: 6000,
|
timeout: 6000,
|
||||||
|
|
|
@ -10,9 +10,10 @@ import {IconsModule} from "../../../utils/icons/icons.module";
|
||||||
import {InputModule} from "../../../sharedComponents/input/input.module";
|
import {InputModule} from "../../../sharedComponents/input/input.module";
|
||||||
import {PageContentModule} from "../../sharedComponents/page-content/page-content.module";
|
import {PageContentModule} from "../../sharedComponents/page-content/page-content.module";
|
||||||
import {SafeHtmlPipeModule} from "../../../utils/pipes/safeHTMLPipe.module";
|
import {SafeHtmlPipeModule} from "../../../utils/pipes/safeHTMLPipe.module";
|
||||||
|
import {NotifyFormModule} from "../../../notifications/notify-form/notify-form.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, AlertModalModule, ReactiveFormsModule, LoadingModule, IconsModule, InputModule, PageContentModule, SafeHtmlPipeModule],
|
imports: [CommonModule, AlertModalModule, ReactiveFormsModule, LoadingModule, IconsModule, InputModule, PageContentModule, SafeHtmlPipeModule, NotifyFormModule],
|
||||||
declarations: [RoleUsersComponent],
|
declarations: [RoleUsersComponent],
|
||||||
exports: [RoleUsersComponent]
|
exports: [RoleUsersComponent]
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
svg:not(.outlined) circle{
|
||||||
|
fill: currentColor;
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg:not(.outlined) text {
|
||||||
|
fill: white;
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
svg.outlined circle{
|
||||||
|
fill: white;
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.outlined text {
|
||||||
|
fill: currentColor;
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
user-select: none;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {Component, Input, OnInit} from "@angular/core";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'notification-user',
|
||||||
|
template: `
|
||||||
|
<svg *ngIf="firstLetters" height="44" width="44" [ngClass]="colorClass" [class.outlined]="outline">
|
||||||
|
<circle cx="22" cy="22" r="20" stroke-width="2"></circle>
|
||||||
|
<text x="50%" y="50%" text-anchor="middle" dy=".4em" font-size="16">
|
||||||
|
{{firstLetters}}
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
styleUrls: ['notification-user.component.css']
|
||||||
|
})
|
||||||
|
export class NotificationUserComponent implements OnInit{
|
||||||
|
@Input()
|
||||||
|
public name: string;
|
||||||
|
@Input()
|
||||||
|
public surname: string;
|
||||||
|
@Input()
|
||||||
|
public colorClass = 'portal-color';
|
||||||
|
@Input()
|
||||||
|
public outline: boolean = false;
|
||||||
|
public firstLetters: string;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if(this.name && this.surname) {
|
||||||
|
this.firstLetters = this.name.charAt(0) + this.surname.charAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {NotificationUserComponent} from "./notification-user.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [NotificationUserComponent],
|
||||||
|
exports: [NotificationUserComponent]
|
||||||
|
})
|
||||||
|
export class NotificationUserModule {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {Notification} from "./notifications";
|
||||||
|
import {HelperFunctions} from "../utils/HelperFunctions.class";
|
||||||
|
import {Composer} from "../utils/email/composer";
|
||||||
|
|
||||||
|
export class NotificationUtils {
|
||||||
|
public static CREATE_STAKEHOLDER: Notification = new Notification('CREATE', ['monitor'], 'User ((__user__)) has created a new profile', 'stakeholder')
|
||||||
|
public static EDIT_STAKEHOLDER: Notification = new Notification('EDIT', ['monitor'], 'User ((__user__)) has updated ((__stakeholder__)) profile', 'stakeholder')
|
||||||
|
public static CREATE_INDICATOR: Notification = new Notification('CREATE', ['monitor'], 'User ((__user__)) has created a new indicator in ((__stakeholder__)) profile', 'indicator');
|
||||||
|
public static EDIT_INDICATOR: Notification = new Notification('EDIT', ['monitor'], 'User ((__user__)) has updated an indicator in ((__stakeholder__)) profile', 'indicator');
|
||||||
|
public static DELETE_INDICATOR: Notification = new Notification('DELETE', ['monitor'], 'User ((__user__)) has deleted an indicator in ((__stakeholder__)) profile', 'indicator');
|
||||||
|
public static INVITE_MONITOR_MANAGER: Notification = new Notification('INVITE_MANAGER', ['monitor'], null, 'user');
|
||||||
|
public static INVITE_MONITOR_MEMBER: Notification = new Notification('INVITE_MEMBER', ['monitor'], null, 'user');
|
||||||
|
|
||||||
|
public static createStakeholder(user: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.CREATE_STAKEHOLDER);
|
||||||
|
notification.message = notification.message.replace('((__user__))', user);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static editStakeholder(user: string, stakeholder: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.EDIT_STAKEHOLDER);
|
||||||
|
notification.message = notification.message.replace('((__user__))', user);
|
||||||
|
notification.message = notification.message.replace('((__stakeholder__))', stakeholder);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createIndicator(user: string, stakeholder: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.CREATE_INDICATOR);
|
||||||
|
notification.message = notification.message.replace('((__user__))', user);
|
||||||
|
notification.message = notification.message.replace('((__stakeholder__))', stakeholder);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static editIndicator(user: string, stakeholder: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.EDIT_INDICATOR);
|
||||||
|
notification.message = notification.message.replace('((__user__))', user);
|
||||||
|
notification.message = notification.message.replace('((__stakeholder__))', stakeholder);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static deleteIndicator(user: string, stakeholder: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.DELETE_INDICATOR);
|
||||||
|
notification.message = notification.message.replace('((__user__))', user);
|
||||||
|
notification.message = notification.message.replace('((__stakeholder__))', stakeholder);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static invite(name: string,role: "manager" | "member", user, invitation: string): Notification {
|
||||||
|
let notification: Notification = HelperFunctions.copy(this.INVITE_MONITOR_MANAGER);
|
||||||
|
if(role === "member") {
|
||||||
|
notification = HelperFunctions.copy(this.INVITE_MONITOR_MEMBER);
|
||||||
|
}
|
||||||
|
notification.message = Composer.composeMessageForMonitorDashboard(name, role, user, invitation);
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import {Injectable} from "@angular/core";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {properties} from "../../../environments/environment";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {Notification, NotificationUser} from "./notifications";
|
||||||
|
import {CustomOptions} from "../services/servicesUtils/customOptions.class";
|
||||||
|
import {not} from "rxjs/internal-compatibility";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: "root"
|
||||||
|
})
|
||||||
|
export class NotificationService {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAllNotifications(): Observable<Notification[]> {
|
||||||
|
return this.httpClient.get<Notification[]>(properties.notificationsAPIURL + 'all', CustomOptions.registryOptions()).pipe(map(notifications => {
|
||||||
|
notifications.forEach(notification => {
|
||||||
|
this.formatNotification(notification);
|
||||||
|
})
|
||||||
|
return notifications;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNotifications(service: string): Observable<Notification[]> {
|
||||||
|
return this.httpClient.get<Notification[]>(properties.notificationsAPIURL + encodeURIComponent(service), CustomOptions.registryOptions()).pipe(map(notifications => {
|
||||||
|
notifications.forEach(notification => {
|
||||||
|
this.formatNotification(notification);
|
||||||
|
})
|
||||||
|
return notifications;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendNotification(notification: Notification): Observable<Notification> {
|
||||||
|
return this.httpClient.post<Notification>(properties.notificationsAPIURL + 'save', notification, CustomOptions.registryOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public readNotification(id: string): Observable<NotificationUser> {
|
||||||
|
return this.httpClient.put<NotificationUser>(properties.notificationsAPIURL + encodeURIComponent(id), null, CustomOptions.registryOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatNotification(notification: Notification): Notification {
|
||||||
|
if (notification.title) {
|
||||||
|
notification.preview = notification.title;
|
||||||
|
} else {
|
||||||
|
notification.preview = notification.message.replace(/<[^>]*>/g, '');
|
||||||
|
notification.preview = notification.preview.replace(/(\r\n|\n|\r| +(?= ))|\s\s+/gm, " ");
|
||||||
|
}
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
#notifications-switcher {
|
||||||
|
top: 250px !important;
|
||||||
|
position: fixed;
|
||||||
|
height: 36px;
|
||||||
|
background-color: var(--portal-main-color);
|
||||||
|
color: var(--portal-main-contrast);
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
box-shadow: -2px 2px 5px rgba(0, 0, 0, .26);
|
||||||
|
box-sizing: border-box;
|
||||||
|
right: 0;
|
||||||
|
z-index: 980;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications-switcher #notifications-count {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar {
|
||||||
|
background-color: white;
|
||||||
|
color: #1a1a1a;
|
||||||
|
font-size: 14px;
|
||||||
|
top: 100px;
|
||||||
|
padding: 45px 0;
|
||||||
|
border: 1px solid var(--portal-main-color);
|
||||||
|
width: 550px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-flip .uk-offcanvas-bar {
|
||||||
|
right: -550px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar .text-small {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar h5, #notifications .uk-offcanvas-bar h6 {
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications button.notification-close, #notifications button.notification-close:focus {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #F0F0F0;
|
||||||
|
color: #76706B;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications button.notification-close:hover, #notifications button.notification-close:focus {
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notification-list {
|
||||||
|
padding: 0 0 0 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notification-list:not(:last-child) {
|
||||||
|
height: 500px;
|
||||||
|
border-bottom: 1px solid #E4E4E4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notification-list ul {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px 45px 10px 0;
|
||||||
|
height: calc(100% - 66px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notification-list ul > li:nth-child(n+2){
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notify {
|
||||||
|
padding: 20px 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .notification {
|
||||||
|
padding: 0 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar a {
|
||||||
|
color: #2D72D6 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar a:hover {
|
||||||
|
color: var(--portal-main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar .uk-button-secondary {
|
||||||
|
background-color: #4687e6;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar .uk-button-secondary:focus, #notifications .uk-offcanvas-bar .uk-button-secondary:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #4687e6;
|
||||||
|
border-color: #4687e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications .uk-offcanvas-bar .uk-button-secondary:disabled {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #bfbfbf;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
background-image: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
import {Component, Input, OnDestroy, OnInit, ViewEncapsulation} from "@angular/core";
|
||||||
|
import {Notification} from "../notifications";
|
||||||
|
import {NotificationService} from "../notification.service";
|
||||||
|
import {Subscription} from "rxjs";
|
||||||
|
import {User} from "../../login/utils/helper.class";
|
||||||
|
import {Dates} from "../../utils/string-utils.class";
|
||||||
|
import {Option} from "../../sharedComponents/input/input.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'notification-sidebar',
|
||||||
|
template: `
|
||||||
|
<div id="notifications-switcher" uk-toggle href="#notifications">
|
||||||
|
<icon name="mail"></icon>
|
||||||
|
<span [class.uk-hidden]="unreadCount === 0" id="notifications-count">
|
||||||
|
{{unreadCount}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="notifications" uk-offcanvas="flip: true; bg-close: false;">
|
||||||
|
<div class="uk-offcanvas-bar">
|
||||||
|
<button class="uk-offcanvas-close notification-close" type="button">
|
||||||
|
<icon name="close"></icon>
|
||||||
|
</button>
|
||||||
|
<ng-template [ngIf]="!notification">
|
||||||
|
<div class="notification-list uk-position-relative">
|
||||||
|
<h5 class="uk-text-bold">Notifications</h5>
|
||||||
|
<h6 *ngIf="notifications.length == 0" class="uk-position-center uk-margin-remove">No notifications</h6>
|
||||||
|
<ul *ngIf="notifications.length > 0" class="uk-list">
|
||||||
|
<li *ngFor="let notification of notifications; let i=index" class="clickable" (click)="select(notification)">
|
||||||
|
<div class="uk-grid uk-grid-small" uk-grid>
|
||||||
|
<notification-user [name]="user.firstname" [surname]="user.lastname" [outline]="true"
|
||||||
|
colorClass="uk-text-secondary"></notification-user>
|
||||||
|
<div class="uk-width-expand">
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-middle">
|
||||||
|
<div class="uk-width-expand multi-line-ellipsis lines-2">
|
||||||
|
<p class="uk-margin-remove">
|
||||||
|
{{notification.preview}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-margin-left uk-flex uk-flex-center uk-text-secondary">
|
||||||
|
<icon *ngIf="!notification.read" name="bullet" ratio="0.6"></icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="uk-text-secondary text-small">{{getDate(notification.date)}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="availableGroups.length > 0" [availableGroups]="availableGroups" [service]="service" notify-form class="notify"></div>
|
||||||
|
</ng-template>
|
||||||
|
<div *ngIf="notification" class="notification">
|
||||||
|
<div class="uk-flex uk-flex-middle uk-margin-medium-bottom">
|
||||||
|
<span class="uk-text-secondary clickable" (click)="back($event)">
|
||||||
|
<icon ratio="1.5" name="arrow_left"></icon>
|
||||||
|
</span>
|
||||||
|
<h5 *ngIf="notification.title" class="uk-text-bold uk-margin-left">{{notification.title}}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="uk-flex uk-flex-middle uk-margin-medium-bottom">
|
||||||
|
<notification-user [name]="notification.name" [surname]="notification.surname" colorClass="uk-text-secondary" [outline]="true"></notification-user>
|
||||||
|
<div class="uk-margin-left">
|
||||||
|
{{notification.name + ' ' + notification.surname}}<br>
|
||||||
|
<span style="opacity: 0.8;" class="text-small uk-margin-small-top">
|
||||||
|
{{notification.date | date:'medium'}} ({{getDate(notification.date)}})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div [innerHTML]="notification.message | safeHtml">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styleUrls: ['notification-sidebar.component.css'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class NotificationsSidebarComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
public user: User;
|
||||||
|
public notifications: Notification[] = [];
|
||||||
|
@Input()
|
||||||
|
public availableGroups: Option[] = [];
|
||||||
|
@Input()
|
||||||
|
public service: string;
|
||||||
|
public notification: Notification;
|
||||||
|
private subscriptions: any[] = [];
|
||||||
|
|
||||||
|
constructor(private notificationService: NotificationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.subscriptions.push(this.notificationService.getNotifications(this.service).subscribe(notifications => {
|
||||||
|
this.notifications = notifications;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subscriptions.forEach(subscription => {
|
||||||
|
if (subscription instanceof Subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get unreadCount(): number {
|
||||||
|
return this.notifications.filter(notification => !notification.read).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDate(date: Date): string {
|
||||||
|
return Dates.timeSince(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(notification: Notification) {
|
||||||
|
this.notificationService.readNotification(notification._id).subscribe(user => {
|
||||||
|
notification.read = true;
|
||||||
|
this.notification = notification;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
back(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.notification = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {NotificationsSidebarComponent} from "./notifications-sidebar.component";
|
||||||
|
import {IconsModule} from "../../utils/icons/icons.module";
|
||||||
|
import {IconsService} from "../../utils/icons/icons.service";
|
||||||
|
import {arrow_left, bullet, close, mail} from "../../utils/icons/icons";
|
||||||
|
import {NotificationUserModule} from "../notification-user/notification-user.module";
|
||||||
|
import {NotifyFormModule} from "../notify-form/notify-form.module";
|
||||||
|
import {SafeHtmlPipeModule} from "../../utils/pipes/safeHTMLPipe.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, IconsModule, NotificationUserModule, NotifyFormModule, SafeHtmlPipeModule],
|
||||||
|
declarations: [NotificationsSidebarComponent],
|
||||||
|
exports: [NotificationsSidebarComponent]
|
||||||
|
})
|
||||||
|
export class NotificationsSidebarModule {
|
||||||
|
constructor(private iconsService: IconsService) {
|
||||||
|
this.iconsService.registerIcons([mail, close, bullet, arrow_left]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
export class Notification {
|
||||||
|
actionType: string;
|
||||||
|
services: string[];
|
||||||
|
user: string;
|
||||||
|
name: string;
|
||||||
|
surname: string;
|
||||||
|
title?: string;
|
||||||
|
preview: string;
|
||||||
|
message: string;
|
||||||
|
stakeholder: string;
|
||||||
|
stakeholderType: string;
|
||||||
|
entity: string;
|
||||||
|
entityType: string;
|
||||||
|
date: Date;
|
||||||
|
groups: string[];
|
||||||
|
read: boolean = false;
|
||||||
|
_id: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(actionType: string, services: string[], message: string, entityType: string) {
|
||||||
|
this.actionType = actionType;
|
||||||
|
this.services = services;
|
||||||
|
this.message = message;
|
||||||
|
this.entityType = entityType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotificationUser {
|
||||||
|
_id: string,
|
||||||
|
read: string[]
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
import {ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||||
|
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||||
|
import {User} from "../../login/utils/helper.class";
|
||||||
|
import {UserManagementService} from "../../services/user-management.service";
|
||||||
|
import {of, Subscription} from "rxjs";
|
||||||
|
import {NotificationService} from "../notification.service";
|
||||||
|
import {Notification} from "../notifications";
|
||||||
|
import {InputComponent, Option} from "../../sharedComponents/input/input.component";
|
||||||
|
import {properties} from "../../../../environments/environment";
|
||||||
|
|
||||||
|
declare var UIkit;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: '[notify-form]',
|
||||||
|
template: `
|
||||||
|
<form *ngIf="user && form" [formGroup]="form">
|
||||||
|
<ng-template [ngIf]="form.get('notify')">
|
||||||
|
<mat-checkbox formControlName="notify" class="uk-text-small">{{label}}</mat-checkbox>
|
||||||
|
<div [class.uk-hidden]="!form.get('notify').value" class="uk-grid uk-grid-small uk-margin-top" uk-grid>
|
||||||
|
<div style="margin-left: -5px;">
|
||||||
|
<notification-user [name]="user.firstname" [surname]="user.lastname"></notification-user>
|
||||||
|
</div>
|
||||||
|
<div dashboard-input [formInput]="form.get('message')"
|
||||||
|
rows="3" placeholder="Send a notification"
|
||||||
|
type="textarea" class="uk-width-expand"></div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="form.get('groups') && availableGroups">
|
||||||
|
<div class="uk-grid uk-grid-small" uk-grid>
|
||||||
|
<span style="opacity: 0.5;" class="uk-text-bold uk-margin-small-top">Send to: </span>
|
||||||
|
<div [class.uk-hidden]="focused" class="uk-width-expand uk-margin-small" (click)="focus($event)">
|
||||||
|
<span *ngIf="groups.length === 0" class="placeholder">Add a recipient</span>
|
||||||
|
<span *ngIf="groups.length > 0" [attr.uk-tooltip]="(groups.length > 2)?groups.join(', '):null">
|
||||||
|
{{groups.slice(0, 2).join(', ')}}
|
||||||
|
<span *ngIf="groups.length > 2" style="opacity: 0.5; margin-left: 4px">+ {{groups.length - 2}} more</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div #recipients dashboard-input type="chips" [options]="availableGroups" [class.uk-hidden]="!focused"
|
||||||
|
panelClass="uk-text-small" [showOptionsOnEmpty]="false"
|
||||||
|
inputClass="input-borderless" class="uk-width-expand" (focusEmitter)="onFocus($event)" [panelWidth]="400"
|
||||||
|
[smallChip]="true" [gridSmall]="true" [formInput]="form.get('groups')">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-grid uk-grid-small uk-margin-top" uk-grid>
|
||||||
|
<div>
|
||||||
|
<notification-user [name]="user.firstname" [surname]="user.lastname"></notification-user>
|
||||||
|
</div>
|
||||||
|
<div dashboard-input [formInput]="form.get('message')"
|
||||||
|
[rows]="4" placeholder="Send a notification"
|
||||||
|
type="textarea" class="uk-width-expand">
|
||||||
|
<div options class="uk-margin-top uk-width-1-1 uk-flex uk-flex-right">
|
||||||
|
<button *ngIf="!sending && message" (click)="sendNotification()"
|
||||||
|
class="uk-button uk-button-small uk-button-secondary">Send</button>
|
||||||
|
<button *ngIf="sending || !message" (click)="sendNotification()"
|
||||||
|
class="uk-button uk-button-small uk-button-secondary" disabled>Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</form>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class NotifyFormComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
public label: string = 'Notify Managers';
|
||||||
|
public form: FormGroup;
|
||||||
|
@Input()
|
||||||
|
public availableGroups: Option[] = null;
|
||||||
|
@Input() service: string;
|
||||||
|
public user: User;
|
||||||
|
public focused: boolean = false;
|
||||||
|
public groups: string[] = [];
|
||||||
|
@ViewChild('recipients') recipients: InputComponent;
|
||||||
|
private notification: Notification;
|
||||||
|
private subscriptions: any[] = [];
|
||||||
|
public sending: boolean = false;
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private userManagementService: UserManagementService,
|
||||||
|
private notificationService: NotificationService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.reset();
|
||||||
|
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
|
||||||
|
this.user = user;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subscriptions.forEach(subscription => {
|
||||||
|
if (subscription instanceof Subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(message: string = null) {
|
||||||
|
if (!this.availableGroups) {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
notify: this.fb.control(properties.environment === 'development'),
|
||||||
|
message: this.fb.control(message)
|
||||||
|
});
|
||||||
|
this.subscriptions.push(this.form.get('notify').valueChanges.subscribe(value => {
|
||||||
|
if (value) {
|
||||||
|
this.form.get('message').markAsUntouched();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
groups: this.fb.array([]),
|
||||||
|
message: this.fb.control(message)
|
||||||
|
});
|
||||||
|
this.groups = [];
|
||||||
|
this.subscriptions.push(this.form.get('groups').valueChanges.subscribe(value => {
|
||||||
|
this.groups = [];
|
||||||
|
value.forEach(group => {
|
||||||
|
this.groups.push(this.availableGroups.find(available => available.value === group).label);
|
||||||
|
});
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendNotification(notification: Notification = null) {
|
||||||
|
if (this.message) {
|
||||||
|
if(notification === null) {
|
||||||
|
notification = new Notification('CUSTOM', [this.service], null, null);
|
||||||
|
notification.groups = this.groupsAsFromArray.value;
|
||||||
|
this.sending = true;
|
||||||
|
}
|
||||||
|
this.notification = notification;
|
||||||
|
this.notification.message = this.form.value.message;
|
||||||
|
// TODO remove
|
||||||
|
this.notification.name = this.user.firstname;
|
||||||
|
this.notification.surname = this.user.lastname;
|
||||||
|
this.subscriptions.push(this.notificationService.sendNotification(this.notification).subscribe(notification => {
|
||||||
|
this.sending = false;
|
||||||
|
UIkit.notification('A notification has been <b>sent</b> successfully', {
|
||||||
|
status: 'success',
|
||||||
|
timeout: 6000,
|
||||||
|
pos: 'bottom-right'
|
||||||
|
});
|
||||||
|
this.reset();
|
||||||
|
}, error => {
|
||||||
|
this.sending = false;
|
||||||
|
UIkit.notification('An error has occurred. Please try again later', {
|
||||||
|
status: 'danger',
|
||||||
|
timeout: 6000,
|
||||||
|
pos: 'bottom-right'
|
||||||
|
});
|
||||||
|
this.reset();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get groupsAsFromArray(): FormArray {
|
||||||
|
return this.form.get('groups')?(<FormArray>this.form.get('groups')):null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get message(): string {
|
||||||
|
if ((this.form.get('notify') && !this.form.get('notify').value) || (this.groupsAsFromArray && this.groupsAsFromArray.length === 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.form.get('message').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus(event: boolean) {
|
||||||
|
this.focused = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(event) {
|
||||||
|
this.focused = true;
|
||||||
|
event.stopPropagation();
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.recipients.searchInput.nativeElement.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {NotifyFormComponent} from "./notify-form.component";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||||
|
import {ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||||
|
import {NotificationUserModule} from "../notification-user/notification-user.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, MatCheckboxModule, ReactiveFormsModule, InputModule, NotificationUserModule],
|
||||||
|
declarations: [NotifyFormComponent],
|
||||||
|
exports: [NotifyFormComponent]
|
||||||
|
})
|
||||||
|
export class NotifyFormModule {
|
||||||
|
|
||||||
|
}
|
|
@ -5,10 +5,12 @@ import {AlertModalModule} from "../utils/modal/alertModal.module";
|
||||||
import {ReactiveFormsModule} from "@angular/forms";
|
import {ReactiveFormsModule} from "@angular/forms";
|
||||||
import {LoadingModule} from "../utils/loading/loading.module";
|
import {LoadingModule} from "../utils/loading/loading.module";
|
||||||
import {InputModule} from "../sharedComponents/input/input.module";
|
import {InputModule} from "../sharedComponents/input/input.module";
|
||||||
|
import {EmailService} from "../utils/email/email.service";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, AlertModalModule, ReactiveFormsModule, LoadingModule, InputModule],
|
imports: [CommonModule, AlertModalModule, ReactiveFormsModule, LoadingModule, InputModule],
|
||||||
declarations: [RoleVerificationComponent],
|
declarations: [RoleVerificationComponent],
|
||||||
exports: [RoleVerificationComponent]
|
exports: [RoleVerificationComponent],
|
||||||
|
providers: [EmailService]
|
||||||
})
|
})
|
||||||
export class RoleVerificationModule {}
|
export class RoleVerificationModule {}
|
||||||
|
|
Loading…
Reference in New Issue