npm package: @flusys/ng-notification
Description: Real-time notification module with Socket.IO for FLUSYS Angular applications
Latest version: 4.1.1

# @flusys/ng-notification

> Real-time notification library for the FLUSYS Angular platform — Socket.IO WebSocket client, notification state management, bell widget, and layout integration.

[![npm version](https://img.shields.io/npm/v/@flusys/ng-notification.svg)](https://www.npmjs.com/package/@flusys/ng-notification)
[![Angular](https://img.shields.io/badge/Angular-21-red.svg)](https://angular.io)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org)
[![Socket.IO](https://img.shields.io/badge/Socket.IO-4.x-black.svg)](https://socket.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Compatibility](#compatibility)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Module Registration](#module-registration)
- [WebSocket Connection](#websocket-connection)
  - [Authentication](#authentication)
  - [WebSocket Events](#websocket-events)
- [Services](#services)
  - [NotificationSocketService](#notificationsocketservice)
  - [NotificationStateService](#notificationstateservice)
- [Components](#components)
  - [NotificationBellComponent](#notificationbellcomponent)
  - [NotificationListComponent](#notificationlistcomponent)
- [Cross-Module Adapter](#cross-module-adapter)
- [API Endpoints](#api-endpoints)
- [Configuration Reference](#configuration-reference)
- [Troubleshooting](#troubleshooting)
- [License](#license)

---

## Overview

`@flusys/ng-notification` provides real-time push notifications for FLUSYS applications via Socket.IO WebSocket. The client connects to the `nestjs-notification` WebSocket gateway, authenticates with a JWT token, and receives live notification events.

The notification bell is injected into the layout topbar through the `LAYOUT_NOTIFICATION_BELL` token — no layout dependency on `ng-notification`.

---

## Features

- ✅ Socket.IO WebSocket client with auto-reconnect
- ✅ JWT authentication on connection
- ✅ `NotificationStateService` — signal-based notification state
- ✅ Notification bell widget with unread badge counter
- ✅ Company-room broadcasting (all users in a company receive company-wide notifications)
- ✅ `provideNotificationProviders()` — registers `LAYOUT_NOTIFICATION_BELL`
- ✅ Mark as read / mark all as read
- ✅ Notification history list with pagination

---

## Compatibility

| Package | Version |
|---------|---------|
| Angular | 21+ |
| @flusys/ng-core | 4.x |
| @flusys/ng-shared | 4.x |
| socket.io-client | 4.x |

---

## Installation

```bash
npm install @flusys/ng-notification @flusys/ng-core @flusys/ng-shared socket.io-client
```

---

## Quick Start

### 1. Enable Notification in Config

```typescript
// environments/environment.ts
export const environment = {
  services: {
    notification: { enabled: true },
  },
};
```

### 2. Register Notification Providers

```typescript
// app.config.ts
import { provideNotificationProviders } from '@flusys/ng-notification';

export const appConfig: ApplicationConfig = {
  providers: [
    ...provideNotificationProviders(),
    // Provides: LAYOUT_NOTIFICATION_BELL (renders bell in AppTopbar automatically)
  ],
};
```

### 3. Connect WebSocket After Login

The WebSocket connects automatically when `NotificationSocketService` is injected and the user is authenticated. The service listens for auth state changes via `USER_PROVIDER`.

```typescript
// No manual connection needed — NotificationSocketService auto-connects
// when the user logs in (detected via USER_PROVIDER signal)
```

---

## Module Registration

### `provideNotificationProviders()`

```typescript
import { provideNotificationProviders } from '@flusys/ng-notification';

providers: [
  ...provideNotificationProviders(),
]
```

Registers:
- `LAYOUT_NOTIFICATION_BELL` — `INotificationBellProvider` implementation

This token is consumed by `AppTopbar` (from `ng-layout`) to render the bell widget without any direct dependency on `ng-notification`.

---

## WebSocket Connection

### Connection URL

The WebSocket connects to the FLUSYS backend notification gateway:

```
ws://{apiBaseUrl}/notifications
```

The namespace `/notifications` is the Socket.IO namespace on the `nestjs-notification` gateway.

### Authentication

The JWT access token is sent as a handshake auth parameter on connection:

```typescript
// Internal connection (handled automatically)
const socket = io(`${apiBaseUrl}/notifications`, {
  auth: {
    token: accessToken,  // Bearer token from AuthStateService
  },
  transports: ['websocket'],
});
```

On the backend, the gateway validates the token and joins the user to their personal room and company room.

### WebSocket Events

**Events the client listens for:**

| Event | Direction | Payload | Description |
|-------|-----------|---------|-------------|
| `notification` | Server → Client | `INotification` | New notification pushed to user |
| `company-notification` | Server → Client | `INotification` | Notification to all company users |
| `connect` | System | — | Socket connected |
| `disconnect` | System | `reason` | Socket disconnected |
| `connect_error` | System | `Error` | Connection failed |

**Events the client emits:**

| Event | Direction | Payload | Description |
|-------|-----------|---------|-------------|
| `mark-read` | Client → Server | `{ id: string }` | Mark notification as read |
| `mark-all-read` | Client → Server | — | Mark all as read |

---

## Services

### NotificationSocketService

Manages the Socket.IO connection lifecycle and emits socket events:

```typescript
import { NotificationSocketService } from '@flusys/ng-notification';

@Injectable({ providedIn: 'root' })
export class MyService {
  private socketService = inject(NotificationSocketService);

  // Connection state signals
  isConnected = this.socketService.isConnected;     // Signal<boolean>
  connectionError = this.socketService.connectionError; // Signal<string | null>

  // Manually connect/disconnect (usually auto-managed)
  connect(): void { this.socketService.connect(); }
  disconnect(): void { this.socketService.disconnect(); }

  // Emit events
  markRead(notificationId: string): void {
    this.socketService.emit('mark-read', { id: notificationId });
  }
}
```

**NotificationSocketService auto-connects** when:
1. The user becomes authenticated (via `USER_PROVIDER` signal)
2. The access token is available in `AuthStateService`

**Auto-disconnects** when:
1. User logs out (auth state clears)
2. Component/service is destroyed

### NotificationStateService

Signal-based service managing the notification list and unread count:

```typescript
import { NotificationStateService } from '@flusys/ng-notification';

@Component({ ... })
export class MyComponent {
  private notifState = inject(NotificationStateService);

  // Signals
  notifications = this.notifState.notifications;   // Signal<INotification[]>
  unreadCount = this.notifState.unreadCount;        // Signal<number>
  isLoading = this.notifState.isLoading;            // Signal<boolean>
  hasMore = this.notifState.hasMore;                // Signal<boolean>

  // Actions
  loadMore(): void { this.notifState.loadMore(); }
  markRead(id: string): void { this.notifState.markRead(id); }
  markAllRead(): void { this.notifState.markAllRead(); }
  refresh(): void { this.notifState.refresh(); }
}
```

**Notification Model:**

```typescript
interface INotification {
  id: string;
  title: string;
  message: string;
  type: 'INFO' | 'SUCCESS' | 'WARNING' | 'ERROR';
  isRead: boolean;
  link?: string;          // Optional navigation URL
  metadata?: Record<string, unknown>;
  createdAt: string;
  readAt?: string;
}
```

---

## Components

### NotificationBellComponent

Topbar bell icon with animated unread badge. Registered via `LAYOUT_NOTIFICATION_BELL` and rendered automatically in `AppTopbar`.

```html
<!-- Rendered automatically in AppTopbar when provideNotificationProviders() is called -->
<!-- Manual embedding: -->
<flusys-notification-bell />
```

Features:
- Animated shake on new notification
- Unread count badge (99+ capped display)
- Click opens notification panel

### NotificationListComponent

Scrollable notification history list with real-time updates:

```html
<flusys-notification-list
  [maxHeight]="'400px'"
  [showMarkAllRead]="true"
  (notificationClicked)="onNotificationClicked($event)"
/>
```

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `maxHeight` | `string` | `'500px'` | Panel max height |
| `showMarkAllRead` | `boolean` | `true` | Show "Mark all read" button |

| Output | Type | Description |
|--------|------|-------------|
| `notificationClicked` | `INotification` | User clicked a notification |

---

## Cross-Module Adapter

`NOTIFICATION_ADAPTER` (defined in `ng-shared`) allows other packages to send notifications without importing `ng-notification` directly.

```typescript
// In ng-event-manager — sends notification when event is created
import { NOTIFICATION_ADAPTER, INotificationAdapter } from '@flusys/ng-shared';

@Injectable({ providedIn: 'root' })
export class EventService {
  private notifAdapter = inject<INotificationAdapter>(NOTIFICATION_ADAPTER, { optional: true });

  afterEventCreated(event: ICalendarEvent): void {
    this.notifAdapter?.notify({
      userId: event.createdById,
      title: 'Event Created',
      message: `"${event.title}" has been scheduled.`,
      type: 'SUCCESS',
    });
  }
}
```

```typescript
// INotificationAdapter interface (defined in ng-shared)
interface INotificationAdapter {
  notify(payload: INotifyPayload): Observable<void>;
  notifyCompany(companyId: string, payload: INotifyPayload): Observable<void>;
}
```

---

## API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/notification/get-all` | List notifications (paginated) |
| POST | `/notification/get/:id` | Get notification by ID |
| POST | `/notification/mark-read` | Mark notification as read |
| POST | `/notification/mark-all-read` | Mark all as read |
| POST | `/notification/delete` | Delete notification |
| POST | `/notification/unread-count` | Get unread count |

---

## Configuration Reference

| Config Key | Type | Default | Description |
|------------|------|---------|-------------|
| `services.notification.enabled` | `boolean` | `false` | Enable notification module |

---

## Troubleshooting

**WebSocket doesn't connect**

Check:
1. Backend `nestjs-notification` is running and WebSocket gateway is active
2. `apiBaseUrl` in `APP_CONFIG` is correct (use `ws://` or `wss://` automatically via socket.io)
3. JWT token is valid — check `NotificationSocketService.connectionError` signal
4. CORS is configured on the backend WebSocket gateway

**Unread count doesn't update in real-time**

The socket must be connected. Check `NotificationSocketService.isConnected` signal. If false, the auto-connect may have failed silently — inspect browser console for WebSocket errors.

**Bell icon not appearing in topbar**

Ensure `provideNotificationProviders()` is called in `app.config.ts` and `provideAuthLayoutIntegration()` is also called (layout must be integrated with auth).

**Company-wide notifications not received**

The user must be in a company room. Company room joining requires `COMPANY_PROVIDER` to have a company selected. Ensure company selection is complete before expecting company notifications.

**`No provider for LAYOUT_NOTIFICATION_BELL`**

Register `provideNotificationProviders()` in `app.config.ts`.

---

## License

MIT © FLUSYS