398 lines
10 KiB
Markdown
398 lines
10 KiB
Markdown
# 🔔 Analyse du Flow de Notifications
|
|
|
|
## 📋 Vue d'ensemble
|
|
|
|
Le système de notifications est un système **multi-sources** qui agrège les notifications de plusieurs services externes (Leantime, Nextcloud, etc.) et les affiche dans un badge clignotant rouge dans la navbar.
|
|
|
|
---
|
|
|
|
## 🎯 Déclenchement du Badge Rouge Clignotant
|
|
|
|
### Condition d'affichage
|
|
|
|
Le badge rouge apparaît lorsque :
|
|
```typescript
|
|
hasUnread = notificationCount.unread > 0
|
|
```
|
|
|
|
**Fichier :** `components/notification-badge.tsx:26`
|
|
|
|
```tsx
|
|
const hasUnread = notificationCount.unread > 0;
|
|
{hasUnread && (
|
|
<Badge variant="notification" size="notification" className="absolute -top-2 -right-2 z-50">
|
|
{notificationCount.unread > 99 ? '99+' : notificationCount.unread}
|
|
</Badge>
|
|
)}
|
|
```
|
|
|
|
### Style du badge
|
|
|
|
**Fichier :** `components/ui/badge.tsx:18-19`
|
|
|
|
```typescript
|
|
notification: "border-transparent bg-red-500 text-white hover:bg-red-600 absolute -top-1 -right-1 px-1.5 py-0.5 min-w-[1.25rem] h-5 flex items-center justify-center"
|
|
```
|
|
|
|
Le badge est **rouge** (`bg-red-500`) et positionné en haut à droite de l'icône cloche.
|
|
|
|
---
|
|
|
|
## 🔄 Flow Complet de Notifications
|
|
|
|
### 1. **Initialisation** (Au chargement de la page)
|
|
|
|
```
|
|
MainNav (navbar)
|
|
└─> NotificationBadge (composant)
|
|
└─> useNotifications() (hook)
|
|
└─> useEffect() [status === 'authenticated']
|
|
├─> fetchNotificationCount(true) // Force refresh
|
|
└─> fetchNotifications(1, 20) // Charge les 20 premières
|
|
```
|
|
|
|
**Fichiers :**
|
|
- `components/main-nav.tsx` - Affiche le badge
|
|
- `components/notification-badge.tsx:86-91` - Fetch initial
|
|
- `hooks/use-notifications.ts:265-277` - Initialisation
|
|
|
|
---
|
|
|
|
### 2. **Rafraîchissement Automatique** (Polling)
|
|
|
|
Le système utilise un **système de rafraîchissement unifié** qui poll les notifications toutes les **30 secondes**.
|
|
|
|
```
|
|
useUnifiedRefresh({
|
|
resource: 'notifications-count',
|
|
interval: 30000, // 30 secondes
|
|
priority: 'high',
|
|
onRefresh: fetchNotificationCount(true)
|
|
})
|
|
```
|
|
|
|
**Fichiers :**
|
|
- `hooks/use-notifications.ts:253-262` - Configuration du refresh
|
|
- `lib/constants/refresh-intervals.ts:12` - Interval défini
|
|
- `lib/services/refresh-manager.ts` - Gestionnaire centralisé
|
|
|
|
**Interval de rafraîchissement :**
|
|
- **Notifications Count** : `30 secondes` (priorité haute)
|
|
- **Notifications List** : `30 secondes` (priorité haute)
|
|
|
|
---
|
|
|
|
### 3. **Récupération des Notifications** (API Calls)
|
|
|
|
#### A. Fetch du Count (Badge)
|
|
|
|
```
|
|
GET /api/notifications/count
|
|
└─> NotificationService.getInstance()
|
|
└─> getNotificationCount(userId)
|
|
├─> Check Redis Cache (TTL: 30s)
|
|
└─> Si pas en cache:
|
|
├─> LeantimeAdapter.getNotificationCount()
|
|
│ └─> API Leantime: getAllNotifications(limit: 1000)
|
|
│ └─> Compte les notifications avec read=0
|
|
└─> Autres adapters (futurs)
|
|
└─> Cache dans Redis (30s)
|
|
```
|
|
|
|
**Fichiers :**
|
|
- `app/api/notifications/count/route.ts`
|
|
- `lib/services/notifications/notification-service.ts:182-310`
|
|
- `lib/services/notifications/leantime-adapter.ts:150-280`
|
|
|
|
#### B. Fetch de la Liste
|
|
|
|
```
|
|
GET /api/notifications?page=1&limit=20
|
|
└─> NotificationService.getInstance()
|
|
└─> getNotifications(userId, page, limit)
|
|
├─> Check Redis Cache (TTL: 30s)
|
|
└─> Si pas en cache:
|
|
├─> LeantimeAdapter.getNotifications()
|
|
│ └─> API Leantime: getAllNotifications()
|
|
│ └─> Transforme en Notification[]
|
|
└─> Autres adapters (futurs)
|
|
└─> Trie par timestamp (newest first)
|
|
└─> Cache dans Redis (30s)
|
|
```
|
|
|
|
**Fichiers :**
|
|
- `app/api/notifications/route.ts`
|
|
- `lib/services/notifications/notification-service.ts:61-177`
|
|
- `lib/services/notifications/leantime-adapter.ts:57-148`
|
|
|
|
---
|
|
|
|
### 4. **Sources de Notifications** (Adapters)
|
|
|
|
Actuellement, **un seul adapter** est actif :
|
|
|
|
#### LeantimeAdapter
|
|
|
|
**Source :** Leantime (Agilité - `agilite.slm-lab.net`)
|
|
|
|
**Méthode API :**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "leantime.rpc.Notifications.Notifications.getAllNotifications",
|
|
"params": {
|
|
"userId": <leantime_user_id>,
|
|
"showNewOnly": 0,
|
|
"limitStart": 0,
|
|
"limitEnd": 1000
|
|
}
|
|
}
|
|
```
|
|
|
|
**Types de notifications Leantime :**
|
|
- Tâches assignées
|
|
- Commentaires
|
|
- Mentions
|
|
- Changements de statut
|
|
- Dates d'échéance
|
|
|
|
**Fichier :** `lib/services/notifications/leantime-adapter.ts`
|
|
|
|
**Futurs adapters (non implémentés) :**
|
|
- NextcloudAdapter
|
|
- GiteaAdapter
|
|
- DolibarrAdapter
|
|
- MoodleAdapter
|
|
|
|
---
|
|
|
|
### 5. **Cache Redis** (Performance)
|
|
|
|
Le système utilise **Redis** pour mettre en cache les notifications et éviter les appels API répétés.
|
|
|
|
**Clés de cache :**
|
|
- `notifications:count:{userId}` - TTL: 30 secondes
|
|
- `notifications:list:{userId}:{page}:{limit}` - TTL: 30 secondes
|
|
|
|
**Stratégie :**
|
|
- Cache-first avec fallback API
|
|
- Background refresh si TTL < 50%
|
|
- Invalidation automatique après 30s
|
|
|
|
**Fichier :** `lib/services/notifications/notification-service.ts:11-18`
|
|
|
|
---
|
|
|
|
### 6. **Déduplication des Requêtes**
|
|
|
|
Le système utilise un **request deduplicator** pour éviter les appels API en double.
|
|
|
|
**Window de déduplication :** `2000ms` (2 secondes)
|
|
|
|
**Fichier :** `hooks/use-notifications.ts:39-59`
|
|
|
|
```typescript
|
|
const requestKey = `notifications-count-${session.user.id}`;
|
|
const data = await requestDeduplicator.execute(
|
|
requestKey,
|
|
async () => { /* fetch */ },
|
|
2000 // 2 secondes
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 Affichage du Badge
|
|
|
|
### Composant NotificationBadge
|
|
|
|
**Localisation :** `components/notification-badge.tsx`
|
|
|
|
**Structure :**
|
|
```tsx
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger>
|
|
<Button>
|
|
<Bell /> {/* Icône cloche */}
|
|
{hasUnread && (
|
|
<Badge variant="notification">
|
|
{count > 99 ? '99+' : count}
|
|
</Badge>
|
|
)}
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent>
|
|
{/* Liste des notifications */}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
```
|
|
|
|
### États du Badge
|
|
|
|
| État | Condition | Affichage |
|
|
|------|-----------|-----------|
|
|
| **Visible** | `notificationCount.unread > 0` | Badge rouge avec nombre |
|
|
| **Caché** | `notificationCount.unread === 0` | Pas de badge |
|
|
| **99+** | `notificationCount.unread > 99` | Affiche "99+" |
|
|
|
|
---
|
|
|
|
## 🔍 Déclencheurs du Badge Rouge
|
|
|
|
### 1. **Notifications Leantime**
|
|
|
|
Les notifications sont créées dans **Leantime** lorsque :
|
|
- Une tâche vous est assignée
|
|
- Quelqu'un commente sur une tâche
|
|
- Vous êtes mentionné
|
|
- Une date d'échéance approche
|
|
- Un statut change
|
|
|
|
**Flow :**
|
|
```
|
|
Action dans Leantime
|
|
└─> Leantime crée notification (read=0)
|
|
└─> Polling toutes les 30s
|
|
└─> LeantimeAdapter récupère
|
|
└─> NotificationService agrège
|
|
└─> API retourne count
|
|
└─> Badge apparaît si unread > 0
|
|
```
|
|
|
|
### 2. **Rafraîchissement Automatique**
|
|
|
|
Le badge se met à jour automatiquement via :
|
|
- **Polling** : Toutes les 30 secondes
|
|
- **Ouverture du dropdown** : Fetch immédiat
|
|
- **Mount du composant** : Fetch initial
|
|
|
|
**Fichier :** `hooks/use-notifications.ts:253-262`
|
|
|
|
### 3. **Marquer comme lu**
|
|
|
|
Quand l'utilisateur marque une notification comme lue :
|
|
|
|
```
|
|
Clic sur "Mark as read"
|
|
└─> POST /api/notifications/{id}/read
|
|
└─> LeantimeAdapter.markAsRead()
|
|
└─> API Leantime: markNotificationRead()
|
|
└─> Update local state (optimistic)
|
|
└─> Refresh count (polling)
|
|
└─> Badge disparaît si unread === 0
|
|
```
|
|
|
|
**Fichier :** `hooks/use-notifications.ts:123-189`
|
|
|
|
---
|
|
|
|
## 📊 Structure des Données
|
|
|
|
### NotificationCount
|
|
|
|
```typescript
|
|
interface NotificationCount {
|
|
total: number; // Total de notifications
|
|
unread: number; // Nombre de non lues (TRIGGER DU BADGE)
|
|
sources: {
|
|
leantime: {
|
|
total: number;
|
|
unread: number;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Notification
|
|
|
|
```typescript
|
|
interface Notification {
|
|
id: string;
|
|
source: 'leantime' | 'nextcloud' | ...;
|
|
sourceId: string;
|
|
type: string;
|
|
title: string;
|
|
message: string;
|
|
link?: string;
|
|
isRead: boolean; // false = non lue
|
|
timestamp: Date;
|
|
priority: 'low' | 'normal' | 'high';
|
|
user: { id: string; name?: string; };
|
|
metadata?: Record<string, any>;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Points d'Entrée (Triggers)
|
|
|
|
### 1. **Au chargement de l'app**
|
|
- `NotificationBadge` monte
|
|
- `useNotifications` s'initialise
|
|
- Fetch immédiat du count et de la liste
|
|
|
|
### 2. **Polling automatique**
|
|
- Toutes les 30 secondes
|
|
- Via `useUnifiedRefresh`
|
|
- Priorité haute
|
|
|
|
### 3. **Ouverture du dropdown**
|
|
- Fetch immédiat des notifications
|
|
- Rafraîchissement du count
|
|
|
|
### 4. **Actions utilisateur**
|
|
- Marquer comme lu → Update count
|
|
- Marquer tout comme lu → unread = 0
|
|
|
|
---
|
|
|
|
## 🔧 Configuration
|
|
|
|
### Intervalles de rafraîchissement
|
|
|
|
**Fichier :** `lib/constants/refresh-intervals.ts`
|
|
|
|
```typescript
|
|
NOTIFICATIONS_COUNT: 30000 // 30 secondes
|
|
NOTIFICATIONS: 30000 // 30 secondes
|
|
```
|
|
|
|
### Cache TTL
|
|
|
|
**Fichier :** `lib/services/notifications/notification-service.ts:15-16`
|
|
|
|
```typescript
|
|
COUNT_CACHE_TTL = 30; // 30 secondes
|
|
LIST_CACHE_TTL = 30; // 30 secondes
|
|
```
|
|
|
|
---
|
|
|
|
## 🐛 Debugging
|
|
|
|
### Logs disponibles
|
|
|
|
Le système logge abondamment :
|
|
- `[NOTIFICATION_BADGE]` - Actions du composant
|
|
- `[useNotifications]` - Actions du hook
|
|
- `[NOTIFICATION_SERVICE]` - Service backend
|
|
- `[LEANTIME_ADAPTER]` - Appels API Leantime
|
|
|
|
### Endpoints de debug
|
|
|
|
- `GET /api/debug/notifications` - État du système
|
|
- `GET /api/debug/leantime-methods` - Méthodes Leantime disponibles
|
|
|
|
---
|
|
|
|
## 📝 Résumé : Ce qui déclenche le badge rouge
|
|
|
|
1. **Condition :** `notificationCount.unread > 0`
|
|
2. **Source principale :** Leantime (notifications non lues)
|
|
3. **Rafraîchissement :** Toutes les 30 secondes automatiquement
|
|
4. **Cache :** Redis (30s TTL) pour performance
|
|
5. **Déduplication :** 2 secondes pour éviter les doublons
|
|
6. **Affichage :** Badge rouge avec nombre (ou "99+")
|
|
|
|
Le badge apparaît dès qu'il y a **au moins une notification non lue** dans Leantime (ou autres sources futures).
|