浏览代码

Notification enhancements

tigger 1 年之前
父节点
当前提交
9a7a52bd3e

+ 2 - 2
src/app/app.component.html

@@ -58,7 +58,7 @@
             <mat-icon id="{{item.key}}-icon">{{item.icon}}</mat-icon>
             <mat-label>{{(item.key|tr:item.title)}}</mat-label>
             @if (item.key === 'notifs' && notificationCount > 0) {
-              <div class="count">{{notificationCount}}</div>
+              <div class="notif-count">{{notificationCount}}</div>
             }
           </div>
         }
@@ -158,6 +158,6 @@
   }
 </mat-menu>
 
-<mat-menu #notifications="matMenu">
+<mat-menu class="notification" #notifications="matMenu">
   <notification (click)="$event.stopPropagation()"/>
 </mat-menu>

+ 26 - 2
src/app/app.component.scss

@@ -74,12 +74,27 @@
         line-height: 9pt;
         font-size: 8pt;
     }
+    position: relative;
 }
 
 .toolbar-item:hover {
     background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
 }
 
+.toolbar-item .notif-count {
+    position: absolute;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    right: 5%;
+    width:15px;
+    height: 15px;
+    font-size: 8pt;
+    background: yellow;
+    border-radius: 50%;
+    color: $text-color-light;
+}
+
 .profile-pic {
     height: 25px;
     width: 25px;
@@ -132,6 +147,10 @@
     bottom: 0;
 }
 
+.notification {
+    width: 400px;
+}
+
 footer {
     color:#868686;
     font-size: 8pt;
@@ -164,8 +183,13 @@ footer {
         display: flex;
     }
 
-    #notifications .count {
+    .toolbar-item .notif-count {
         right: 35%;
-        top: -3px;
+    }
+}
+
+@media screen and (pointer: none), (pointer: coarse) and (orientation: landscape) {
+    .toolbar-item .notif-count {
+        right: 43%;
     }
 }

+ 20 - 3
src/app/app.component.ts

@@ -3,7 +3,7 @@ import { ActivatedRoute, Router, RouterModule, RouterOutlet, RoutesRecognized }
 import { MatModule } from '../dependencies/angularlib/mat.module';
 import { Angularlib } from 'angularlib/angularlib.module';
 import { BaseComponent, untilDestroy } from 'angularlib/base.component';
-import { Subject, filter, map, repeat, takeUntil, timer } from 'rxjs';
+import { Subject, Subscription, filter, map, repeat, takeUntil, timer } from 'rxjs';
 import { Title } from '@angular/platform-browser';
 import { LoginService } from 'angularlib/login/login.service';
 import { CommonModule, DatePipe } from '@angular/common';
@@ -18,6 +18,9 @@ import config from '../config/config.json';
 import { UIAuthActions } from 'angularlib/login/state/login.actions';
 import { Platform } from '@angular/cdk/platform';
 import { AppSettingsAction } from 'angularlib/ui.state/ui.state.actions';
+import { LeaveNotificationDecorator } from './decorators/leave.notification.decorator';
+import { FISMessaging } from 'fis/index';
+import { BusinessDataModule } from 'fis/business.data/business.data.module';
 
 @Component({
   selector: 'app-root',
@@ -28,9 +31,10 @@ import { AppSettingsAction } from 'angularlib/ui.state/ui.state.actions';
     MatModule, 
     RouterModule,
     Angularlib,
-    NotificationModule
+    NotificationModule,
+    BusinessDataModule
   ],
-  providers:[DatePipe],
+  providers:[DatePipe,FISMessaging],
   templateUrl: './app.component.html',
   styleUrls: [
     './app.component.scss'
@@ -64,6 +68,9 @@ export class AppComponent extends BaseComponent implements OnInit {
   /**number of notifications */
   protected notificationCount: number = 0;
 
+  private leaveNotification: LeaveNotificationDecorator<any>;
+  private leaveNotificationSubscription: Subscription;
+
   constructor(
     private router: Router,
     private store: Store,
@@ -75,6 +82,7 @@ export class AppComponent extends BaseComponent implements OnInit {
     private platform: Platform
   ) {
     super(store,cs);
+    this.leaveNotification = new LeaveNotificationDecorator(this,{serviceId:'09 - Leave Application'});
   }
 
   @HostListener('window:mousedown')
@@ -101,6 +109,13 @@ export class AppComponent extends BaseComponent implements OnInit {
     this.loginService.user$.pipe(untilDestroy(this)).subscribe(user => {
       if (user) {
         this.timeout.pipe(untilDestroy(this),takeUntil(this.loginService.loggedOut)).subscribe();
+        if (!this.leaveNotificationSubscription) {
+          this.leaveNotificationSubscription = this.leaveNotification.subscribe({
+            next: async notif => {
+              
+            }
+          });
+        }
       }
     });
 
@@ -110,6 +125,8 @@ export class AppComponent extends BaseComponent implements OnInit {
       }
     });
 
+    
+
     if (this.platform.ANDROID || this.platform.IOS) this.pwaPrompt();
   }
 

+ 2 - 0
src/app/app.config.ts

@@ -9,6 +9,7 @@ import { Angularlib } from 'angularlib/angularlib.module';
 import { provideStore } from '@ngxs/store';
 import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin';
 import { withNgxsStoragePlugin } from '@ngxs/storage-plugin';
+import { provideServiceWorker} from '@angular/service-worker';
 import { LoginModule } from 'angularlib/login';
 
 export const appConfig: ApplicationConfig = {
@@ -19,6 +20,7 @@ export const appConfig: ApplicationConfig = {
     importProvidersFrom(DpModule),
     importProvidersFrom(Angularlib),
     importProvidersFrom(LoginModule),
+    provideServiceWorker('ngsw-worker.js'),
     provideRouter(routes,withHashLocation(),withRouterConfig({onSameUrlNavigation:'reload'})),
     provideHttpClient()
   ]

+ 108 - 0
src/app/decorators/leave.notification.decorator.ts

@@ -0,0 +1,108 @@
+import { formatDate } from "@angular/common";
+import { inject } from "@angular/core";
+import { untilDestroy } from "angularlib/base.component";
+import { NotificationService } from "angularlib/notification/notification.service";
+import { DecoratorSubject } from "fis-commons/decorator";
+import { debounceNotification } from "fis-commons/notification/operators";
+import { skipUndefined } from "fis-commons/observable/operators";
+import { adjustISOTimezoneOffset } from "fis/fis.date";
+import { FISMessaging } from "fis/index";
+import { NotificationRequest } from "fis/tm/tm.i";
+import { firstValueFrom } from "rxjs";
+
+export class LeaveNotificationDecorator<T> extends DecoratorSubject<T> {
+    messaging = inject(FISMessaging);
+    notification = inject(NotificationService);
+    subscriptionId;
+    empRoleId;
+
+    constructor(
+        private component: any,
+        request?: NotificationRequest
+    ) {
+        super();
+        this.empRoleId = component.loginService.user?.fisInfo?.defaultEmpRoleId;
+        this.messaging.Notification(request).pipe(untilDestroy(component)).subscribe({
+            next: (notif:any) => {
+                if (!this.subscriptionId) this.subscriptionId = notif?.data?.SubscriptionData?.subscriptionId;
+                this.onReceiveNotification(notif)
+                this.next(notif);
+            },
+            error: error => this.error(error),
+            complete: () => this.complete()
+        });
+    }
+
+    private async onReceiveNotification(notif) {
+        const OPERATION = notif.data?.data?.NotificationMicroserviceData?.uiMessage?.NotificationData?.Operation;
+        const DOCID = +notif.data?.data?.NotificationMicroserviceData?.uiMessage?.NotificationData?.ID;
+        const CODE = notif.data?.data?.NotificationMicroserviceData?.uiMessage?.NotificationData?.Code;
+        let leave;
+        let applicant;
+        await firstValueFrom(this.messaging.GetData({
+        serviceId: 'Leave Application Data',
+        parameter: `__clearSearchValue__=true,caching=false,code=${CODE}`
+        },{liveResponsesOnly:true}).pipe(skipUndefined(),debounceNotification(100))).then(res => {
+            leave = res.GenericFisData?.data?.DataService?.rows?.row[0]?.column;
+        }).catch(error => console.error(error));
+        if (leave) {
+        await firstValueFrom(this.messaging.GetData({
+            serviceId: 'Employee Role Data',
+            parameter: `roleId=${leave?.ps_doc_leave_emp_role_id},__clearSearchValue__=true,caching=false`
+        }).pipe(skipUndefined())).then(res => {
+            applicant = res.GenericFisData?.data?.DataService?.rows?.row[0]?.column || res.data?.GenericFisData?.data?.DataService?.rows?.row[0]?.column;
+        }).catch(error => console.error(error));
+        switch (OPERATION?.toLowerCase()) {
+            case 'new': {
+                this.notification.notify({
+                    message:{
+                    title:{key:'new_leave_applied',default:'New Leave Applied'},
+                    desc: `${applicant?.pers_name}`,
+                    timestamp: new Date(),
+                    },
+                    action: () => {this.component.cs.navigate('/leave/approval')}
+                });
+                break;
+            }
+            case 'modify': {
+                this.notification.notify({
+                    message:{
+                    title: {key:'leave_modified',default:'Leave Modified'},
+                    desc: `${applicant?.pers_name}`,
+                    timestamp: new Date(),
+                    },
+                    action: () => {this.component.cs.navigate('/leave/approval')}
+                });
+                break;
+            }
+            case 'post': {
+                if (leave?.ps_doc_leave_emp_role_id !== this.empRoleId) break;
+                const DATE_FROM = formatDate(adjustISOTimezoneOffset(leave.ps_doc_leave_ps_dt_from),'yyyy-MM-dd EEE',this.component.cs.appSettings.locale);
+                this.notification.notify({
+                    message:{
+                    title: {key:'leave_approved',default:'Leave Approved'},
+                    desc: {key:`#{leave_date_from}: ${DATE_FROM}`,default:''},
+                    timestamp: new Date(),
+                    },
+                    action: () => {this.component.cs.navigate('/leave',{type:'view',detailsTabIndex:1})}
+                });
+                break;
+            }
+            case 'cancel': {
+                if (leave?.ps_doc_leave_emp_role_id !== this.empRoleId) break;
+                const DATE_FROM = formatDate(adjustISOTimezoneOffset(leave.ps_doc_leave_ps_dt_from),'yyyy-MM-dd EEE',this.component.cs.appSettings.locale);
+                this.notification.notify({
+                    message:{
+                    title: {key:'leave_cancelled',default:'Leave Cancelled'},
+                    desc: {key:`#{leave_date_from}: ${DATE_FROM}`,default:''},
+                    timestamp: new Date(),
+                    },
+                    action: () => {this.component.cs.navigate('/leave',{type:'view',detailsTabIndex:2})}
+                });
+                break;
+            }
+            default: break;
+        }
+        }
+    }
+}

+ 1 - 1
src/dependencies/fis

@@ -1 +1 @@
-Subproject commit 58bc6d40ff2f59edf0f9615cfc5a292971c5deb1
+Subproject commit c7f0d75ab5ec915e779c15cd74b8ea81edc18c37

+ 12 - 0
src/src.quotation/config/config.mafrica.json

@@ -0,0 +1,12 @@
+{
+    "connection": {
+        "uacp": "https://ebid.mafrica.com.my:8081",
+        "uacp_ws": "https://ebid.mafrica.com.my/ws",
+        "uacpEmulation": "off",
+        "auth": {
+            "google": "https://api.swopt.com/auth/google",
+            "origin": "https://swopt.com:4205"
+        }
+    },
+    "sessionTimeoutDuration":1800000
+}