# 🔔 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 && ( {notificationCount.unread > 99 ? '99+' : notificationCount.unread} )} ``` ### 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": , "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 {/* Liste des notifications */} ``` ### É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; } ``` --- ## 🚀 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).