WEB-509:fix(notifications): resolve account navigation using entity resolution (#3032)
improve
This commit is contained in:
@@ -30,7 +30,7 @@
|
|||||||
mat-row
|
mat-row
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
class="select-row"
|
class="select-row"
|
||||||
[routerLink]="[routeMap[row.objectType], row.objectId]"
|
(click)="navigateToNotification(row)"
|
||||||
></tr>
|
></tr>
|
||||||
<tr *matNoDataRow>
|
<tr *matNoDataRow>
|
||||||
<td [attr.colspan]="displayedColumns.length">You don't have any notifications</td>
|
<td [attr.colspan]="displayedColumns.length">You don't have any notifications</td>
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.select-row:hover {
|
.select-row {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ import {
|
|||||||
MatRow,
|
MatRow,
|
||||||
MatNoDataRow
|
MatNoDataRow
|
||||||
} from '@angular/material/table';
|
} from '@angular/material/table';
|
||||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||||
|
import { NotificationsService } from '../notifications.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifications Page Component
|
* Notifications Page Component
|
||||||
@@ -54,6 +55,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
})
|
})
|
||||||
export class NotificationsPageComponent implements OnInit {
|
export class NotificationsPageComponent implements OnInit {
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
|
private router = inject(Router);
|
||||||
|
private notificationsService = inject(NotificationsService);
|
||||||
|
|
||||||
/** Notifications data. */
|
/** Notifications data. */
|
||||||
notificationsData: any;
|
notificationsData: any;
|
||||||
@@ -112,4 +115,88 @@ export class NotificationsPageComponent implements OnInit {
|
|||||||
this.dataSource.paginator = this.paginator;
|
this.dataSource.paginator = this.paginator;
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to notification object with proper entity context
|
||||||
|
* @param {any} notification Notification object
|
||||||
|
*/
|
||||||
|
navigateToNotification(notification: any): void {
|
||||||
|
const objectType = notification.objectType;
|
||||||
|
const objectId = notification.objectId;
|
||||||
|
|
||||||
|
// For entities that don't require parent context (client, group, center, products)
|
||||||
|
if ([
|
||||||
|
'client',
|
||||||
|
'group',
|
||||||
|
'center',
|
||||||
|
'shareProduct',
|
||||||
|
'loanProduct'
|
||||||
|
].includes(objectType)) {
|
||||||
|
this.router.navigate([
|
||||||
|
this.routeMap[objectType],
|
||||||
|
objectId
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For account types that require parent entity (client/group) ID
|
||||||
|
switch (objectType) {
|
||||||
|
case 'loan':
|
||||||
|
this.notificationsService.getLoanAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/loans-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'savingsAccount':
|
||||||
|
this.notificationsService.getSavingsAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/savings-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'fixedDeposit':
|
||||||
|
this.notificationsService.getFixedDepositAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/fixed-deposits-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'recurringDepositAccount':
|
||||||
|
this.notificationsService.getRecurringDepositAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/recurring-deposits-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'shareAccount':
|
||||||
|
this.notificationsService.getShareAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/shares-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Fallback to old behavior for unknown types
|
||||||
|
this.router.navigate([
|
||||||
|
this.routeMap[objectType],
|
||||||
|
objectId
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { HttpClient, HttpParams } from '@angular/common/http';
|
|||||||
|
|
||||||
/** rxjs Imports */
|
/** rxjs Imports */
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification service.
|
* Notification service.
|
||||||
@@ -37,6 +38,86 @@ export class NotificationsService {
|
|||||||
return this.http.put('/notifications', {});
|
return this.http.put('/notifications', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get loan account details to retrieve client/group ID
|
||||||
|
* @param {string} loanId Loan Account ID
|
||||||
|
* @returns {Observable<any>} Loan account with clientId or groupId
|
||||||
|
*/
|
||||||
|
getLoanAccount(loanId: string): Observable<any> {
|
||||||
|
return this.http.get(`/loans/${loanId}`).pipe(
|
||||||
|
map((response: any) => ({
|
||||||
|
clientId: response.clientId,
|
||||||
|
groupId: response.groupId,
|
||||||
|
accountId: loanId
|
||||||
|
})),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get savings account details to retrieve client/group ID
|
||||||
|
* @param {string} savingsId Savings Account ID
|
||||||
|
* @returns {Observable<any>} Savings account with clientId or groupId
|
||||||
|
*/
|
||||||
|
getSavingsAccount(savingsId: string): Observable<any> {
|
||||||
|
return this.http.get(`/savingsaccounts/${savingsId}`).pipe(
|
||||||
|
map((response: any) => ({
|
||||||
|
clientId: response.clientId,
|
||||||
|
groupId: response.groupId,
|
||||||
|
accountId: savingsId
|
||||||
|
})),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get fixed deposit account details to retrieve client/group ID
|
||||||
|
* @param {string} accountId Fixed Deposit Account ID
|
||||||
|
* @returns {Observable<any>} Fixed deposit account with clientId or groupId
|
||||||
|
*/
|
||||||
|
getFixedDepositAccount(accountId: string): Observable<any> {
|
||||||
|
return this.http.get(`/fixeddepositaccounts/${accountId}`).pipe(
|
||||||
|
map((response: any) => ({
|
||||||
|
clientId: response.clientId,
|
||||||
|
groupId: response.groupId,
|
||||||
|
accountId: accountId
|
||||||
|
})),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get recurring deposit account details to retrieve client/group ID
|
||||||
|
* @param {string} accountId Recurring Deposit Account ID
|
||||||
|
* @returns {Observable<any>} Recurring deposit account with clientId or groupId
|
||||||
|
*/
|
||||||
|
getRecurringDepositAccount(accountId: string): Observable<any> {
|
||||||
|
return this.http.get(`/recurringdepositaccounts/${accountId}`).pipe(
|
||||||
|
map((response: any) => ({
|
||||||
|
clientId: response.clientId,
|
||||||
|
groupId: response.groupId,
|
||||||
|
accountId: accountId
|
||||||
|
})),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get share account details to retrieve client/group ID
|
||||||
|
* @param {string} accountId Share Account ID
|
||||||
|
* @returns {Observable<any>} Share account with clientId or groupId
|
||||||
|
*/
|
||||||
|
getShareAccount(accountId: string): Observable<any> {
|
||||||
|
return this.http.get(`/accounts/share/${accountId}`).pipe(
|
||||||
|
map((response: any) => ({
|
||||||
|
clientId: response.clientId,
|
||||||
|
groupId: response.groupId,
|
||||||
|
accountId: accountId
|
||||||
|
})),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Observable<any>} Mock Unread Notifications for Testing.
|
* @returns {Observable<any>} Mock Unread Notifications for Testing.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -41,11 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="notifications-list">
|
<div class="notifications-list">
|
||||||
@for (notification of unreadNotifications; track notification) {
|
@for (notification of unreadNotifications; track notification) {
|
||||||
<button
|
<button mat-menu-item class="notification-item unread" (click)="navigateToNotification(notification, $event)">
|
||||||
mat-menu-item
|
|
||||||
class="notification-item unread"
|
|
||||||
[routerLink]="[routeMap[notification.objectType], notification.objectId]"
|
|
||||||
>
|
|
||||||
<div class="notification-content">
|
<div class="notification-content">
|
||||||
<span class="notification-text">{{ notification.content }}</span>
|
<span class="notification-text">{{ notification.content }}</span>
|
||||||
<span class="unread-indicator">●</span>
|
<span class="unread-indicator">●</span>
|
||||||
@@ -55,11 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@for (notification of displayedReadNotifications; track notification) {
|
@for (notification of displayedReadNotifications; track notification) {
|
||||||
<button
|
<button mat-menu-item class="notification-item read" (click)="navigateToNotification(notification, $event)">
|
||||||
mat-menu-item
|
|
||||||
class="notification-item read"
|
|
||||||
[routerLink]="[routeMap[notification.objectType], notification.objectId]"
|
|
||||||
>
|
|
||||||
<div class="notification-content">
|
<div class="notification-content">
|
||||||
<span class="notification-text">{{ notification.content }}</span>
|
<span class="notification-text">{{ notification.content }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/** Angular Imports */
|
/** Angular Imports */
|
||||||
import { Component, OnInit, OnDestroy, ViewEncapsulation, inject } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewEncapsulation, inject } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
/** RxJS Imports */
|
/** RxJS Imports */
|
||||||
import { forkJoin } from 'rxjs';
|
import { forkJoin } from 'rxjs';
|
||||||
@@ -45,6 +46,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|||||||
})
|
})
|
||||||
export class NotificationsTrayComponent implements OnInit, OnDestroy {
|
export class NotificationsTrayComponent implements OnInit, OnDestroy {
|
||||||
notificationsService = inject(NotificationsService);
|
notificationsService = inject(NotificationsService);
|
||||||
|
private router = inject(Router);
|
||||||
|
|
||||||
/** Wait time between API status calls 60 seg */
|
/** Wait time between API status calls 60 seg */
|
||||||
waitTime = environment.waitTimeForNotifications || 60;
|
waitTime = environment.waitTimeForNotifications || 60;
|
||||||
@@ -143,4 +145,92 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy {
|
|||||||
this.setNotifications();
|
this.setNotifications();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to notification object with proper entity context
|
||||||
|
* @param {any} notification Notification object
|
||||||
|
* @param {Event} event Click event
|
||||||
|
*/
|
||||||
|
navigateToNotification(notification: any, event: Event): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const objectType = notification.objectType;
|
||||||
|
const objectId = notification.objectId;
|
||||||
|
|
||||||
|
// For entities that don't require parent context (client, group, center, products)
|
||||||
|
if ([
|
||||||
|
'client',
|
||||||
|
'group',
|
||||||
|
'center',
|
||||||
|
'shareProduct',
|
||||||
|
'loanProduct'
|
||||||
|
].includes(objectType)) {
|
||||||
|
this.router.navigate([
|
||||||
|
this.routeMap[objectType],
|
||||||
|
objectId
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For account types that require parent entity (client/group) ID
|
||||||
|
switch (objectType) {
|
||||||
|
case 'loan':
|
||||||
|
this.notificationsService.getLoanAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/loans-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'savingsAccount':
|
||||||
|
this.notificationsService.getSavingsAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/savings-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'fixedDeposit':
|
||||||
|
this.notificationsService.getFixedDepositAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/fixed-deposits-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'recurringDepositAccount':
|
||||||
|
this.notificationsService.getRecurringDepositAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/recurring-deposits-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'shareAccount':
|
||||||
|
this.notificationsService.getShareAccount(objectId).subscribe((account) => {
|
||||||
|
if (account && (account.clientId || account.groupId)) {
|
||||||
|
const entityType = account.clientId ? 'clients' : 'groups';
|
||||||
|
const entityId = account.clientId || account.groupId;
|
||||||
|
this.router.navigate([`/${entityType}/${entityId}/shares-accounts/${account.accountId}`]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Fallback to old behavior for unknown types
|
||||||
|
this.router.navigate([
|
||||||
|
this.routeMap[objectType],
|
||||||
|
objectId
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user