From 30a354c153f6073d4470c3426750cfff194996ce Mon Sep 17 00:00:00 2001 From: Karan Takalkar Date: Tue, 4 Aug 2020 17:39:11 +0530 Subject: [PATCH] feat: add notifications tray component (#1172) Fixes: #1171 --- src/app/core/core.module.ts | 2 + .../core/shell/toolbar/toolbar.component.html | 9 +- .../notifications-page.component.html | 2 +- .../notifications-page.component.scss | 3 + .../notifications-page.component.ts | 17 +++ .../notifications-tray.component.html | 23 ++++ .../notifications-tray.component.scss | 21 ++++ .../notifications-tray.component.spec.ts | 25 ++++ .../notifications-tray.component.ts | 109 ++++++++++++++++++ src/app/notifications/notifications.module.ts | 7 +- .../notifications/notifications.service.ts | 31 ++++- src/app/shared/material.module.ts | 6 +- 12 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 src/app/notifications/notifications-tray/notifications-tray.component.html create mode 100644 src/app/notifications/notifications-tray/notifications-tray.component.scss create mode 100644 src/app/notifications/notifications-tray/notifications-tray.component.spec.ts create mode 100644 src/app/notifications/notifications-tray/notifications-tray.component.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 94881acc3..8b019b05f 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -28,6 +28,7 @@ import { RouteReusableStrategy } from './route/route-reusable-strategy'; /** Custom Modules */ import { SharedModule } from '../shared/shared.module'; +import { NotificationsModule } from '../notifications/notifications.module'; /** Custom Components */ import { ShellComponent } from './shell/shell.component'; @@ -44,6 +45,7 @@ import { ContentComponent } from './shell/content/content.component'; @NgModule({ imports: [ SharedModule, + NotificationsModule, HttpClientModule, TranslateModule, RouterModule diff --git a/src/app/core/shell/toolbar/toolbar.component.html b/src/app/core/shell/toolbar/toolbar.component.html index 2a733e8cc..32e1e470c 100644 --- a/src/app/core/shell/toolbar/toolbar.component.html +++ b/src/app/core/shell/toolbar/toolbar.component.html @@ -61,14 +61,7 @@ - - + + + + + + + + + + + diff --git a/src/app/notifications/notifications-tray/notifications-tray.component.scss b/src/app/notifications/notifications-tray/notifications-tray.component.scss new file mode 100644 index 000000000..0ca616d20 --- /dev/null +++ b/src/app/notifications/notifications-tray/notifications-tray.component.scss @@ -0,0 +1,21 @@ +.mifosx-notifications-menu { + + max-height: 20rem !important; + width: 12.5rem !important; + + .mat-menu-content { + padding: 0 !important; + } + + [mat-menu-item] { + height: 5rem; + line-height: 2.2rem; + .time{ + font-size: small; + } + &.unread, &.view-all { + background-color: #E0F7FF !important; + } + } + +} diff --git a/src/app/notifications/notifications-tray/notifications-tray.component.spec.ts b/src/app/notifications/notifications-tray/notifications-tray.component.spec.ts new file mode 100644 index 000000000..8aa1b36fc --- /dev/null +++ b/src/app/notifications/notifications-tray/notifications-tray.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotificationsTrayComponent } from './notifications-tray.component'; + +describe('NotificationsTrayComponent', () => { + let component: NotificationsTrayComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ NotificationsTrayComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NotificationsTrayComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/notifications/notifications-tray/notifications-tray.component.ts b/src/app/notifications/notifications-tray/notifications-tray.component.ts new file mode 100644 index 000000000..b953cfce0 --- /dev/null +++ b/src/app/notifications/notifications-tray/notifications-tray.component.ts @@ -0,0 +1,109 @@ +/** Angular Imports */ +import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; + +/** RxJS Imports */ +import { forkJoin } from 'rxjs'; + +/** Custom Services */ +import { NotificationsService } from '../notifications.service'; + +/** + * Notifications Tray Component + */ +@Component({ + selector: 'mifosx-notifications-tray', + templateUrl: './notifications-tray.component.html', + styleUrls: ['./notifications-tray.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class NotificationsTrayComponent implements OnInit, OnDestroy { + + /** Read Notifications */ + readNotifications: any[] = []; + /** Displayed Read Notifications */ + displayedReadNotifications: any[] = []; + /** Unread Notifications */ + unreadNotifications: any[] = []; + /** Timer to refetch notifications every 60 seconds */ + timer: any; + + /** + * Gets router link prefix from notification's objectType attribute + * Shares, Savings, Deposits, Loans routes inaccessible because of dependency on entity ID. + */ + routeMap: any = { + 'client' : '/clients/', + 'group' : '/groups/', + 'loan' : '/loans/', + 'center' : '/centers/', + 'shareAccount' : '/sharesaccounts/', + 'fixedDeposit' : '/fixed-deposits-accounts/', + 'recurringDepositAccount': '/recurringdeposits/', + 'savingsAccount' : '/savingsaccounts/', + 'shareProduct': '/products/share-products/', + 'loanProduct' : '/products/loan-products/' + }; + + /** + * @param {NotificationsService} notificationsService Notifications Service + */ + constructor(public notificationsService: NotificationsService) { + forkJoin([this.notificationsService.getNotifications(true), this.notificationsService.getNotifications(false)]) + .subscribe((response: any[]) => { + this.readNotifications = response[0].pageItems; + this.unreadNotifications = response[1].pageItems; + this.setNotifications(); + }); + } + + ngOnInit() { + setTimeout(() => { this.fetchUnreadNotifications(); }, 60000); + } + + ngOnDestroy() { + clearTimeout(this.timer); + } + + /** + * Restructures displayed read notifications vis-a-vis unread notifications. + */ + setNotifications() { + const length = this.unreadNotifications.length; + this.displayedReadNotifications = length < 9 ? this.readNotifications.slice(0, 9 - length) : []; + } + + /** + * Recursively fetch unread notifications. + */ + fetchUnreadNotifications() { + this.notificationsService.getNotifications(false).subscribe((response: any) => { + this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); + this.setNotifications(); + }); + // this.mockNotifications(); // Uncomment for Testing. + this.timer = setTimeout(() => { this.fetchUnreadNotifications(); }, 60000); + } + + /** + * Update read/unread notifications. + */ + menuClosed() { + // Update the server for read notifications. + this.notificationsService.updateNotifications().subscribe(() => {}); + // Update locally for read notifications. + this.readNotifications = this.unreadNotifications.concat(this.readNotifications); + this.unreadNotifications = []; + this.setNotifications(); + } + + /** + * Function to test notifications in case of faulty backend. + */ + mockNotifications() { + this.notificationsService.getMockUnreadNotification().subscribe((response: any) => { + this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); + this.setNotifications(); + }); + } + +} diff --git a/src/app/notifications/notifications.module.ts b/src/app/notifications/notifications.module.ts index bd5859a73..55fbdc935 100644 --- a/src/app/notifications/notifications.module.ts +++ b/src/app/notifications/notifications.module.ts @@ -8,18 +8,23 @@ import { NotificationsRoutingModule } from './notifications-routing.module'; /** Custom Components */ import { NotificationsPageComponent } from './notifications-page/notifications-page.component'; +import { NotificationsTrayComponent } from './notifications-tray/notifications-tray.component'; /** * Notifications Module */ @NgModule({ declarations: [ - NotificationsPageComponent + NotificationsPageComponent, + NotificationsTrayComponent ], imports: [ SharedModule, PipesModule, NotificationsRoutingModule + ], + exports: [ + NotificationsTrayComponent ] }) export class NotificationsModule { } diff --git a/src/app/notifications/notifications.service.ts b/src/app/notifications/notifications.service.ts index 9137310e9..9f837c55d 100644 --- a/src/app/notifications/notifications.service.ts +++ b/src/app/notifications/notifications.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; /** rxjs Imports */ -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; /** * Notification service. @@ -26,4 +26,33 @@ export class NotificationsService { return this.http.get('/notifications', { params: httpParams }); } + /** + * @returns {Observable} Notifications. + */ + updateNotifications(): Observable { + return this.http.put('/notifications', { }); + } + + /** + * @returns {Observable} Mock Unread Notifications for Testing. + */ + getMockUnreadNotification(): Observable { + const date = new Date(); + return of({ + 'totalFilteredRecords': 1, + 'pageItems': [ + { 'id': Math.floor(Math.random() * 100), + 'objectType': 'client', + 'objectId': Math.floor(Math.random() * 10), + 'action': 'clientCreated', + 'actorId': 2, + 'content': 'Client Created', + 'isRead': false, + 'isSystemGenerated': false, + 'createdAt': `${date.toLocaleDateString()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}` + } + ] + }); + } + } diff --git a/src/app/shared/material.module.ts b/src/app/shared/material.module.ts index 7093f8481..25b7fd88d 100644 --- a/src/app/shared/material.module.ts +++ b/src/app/shared/material.module.ts @@ -37,7 +37,8 @@ import { MatTabsModule, MatToolbarModule, MatTooltipModule, - MatTreeModule + MatTreeModule, + MatBadgeModule } from '@angular/material'; /** @@ -81,7 +82,8 @@ import { MatTabsModule, MatToolbarModule, MatTooltipModule, - MatTreeModule + MatTreeModule, + MatBadgeModule ], providers: [ {