NeahStable/NOTIFICATIONS_DEEP_ANALYSIS.md
2026-01-16 00:12:15 +01:00

14 KiB

🔔 Analyse Approfondie du Système 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, RocketChat, Email) et les affiche dans un badge clignotant rouge dans la navbar.

Architecture actuelle :

  • Service Pattern : Singleton avec adapter pattern
  • 3 Adapters implémentés : Leantime, RocketChat, Email
  • Cache : Redis avec TTL de 30 secondes
  • Polling : 30 secondes via useUnifiedRefresh
  • Temps réel : Système hybride avec event-driven triggers

🏗️ Architecture Actuelle

1. Composants Frontend

components/notification-badge.tsx

  • Rôle : Affiche le badge de notification et le dropdown
  • Problèmes identifiés :
    • Logs de debug excessifs : 6 console.log au render
    • Double fetch : manualFetch() appelé à la fois dans useEffect et handleOpenChange
    • Pas de fonctionnalité "Mark as read" : Les notifications ne peuvent pas être marquées comme lues depuis le dropdown
    • Pas de pagination : Limite fixe à 10 notifications
    • Pas de tri/filtre : Toutes les notifications mélangées
    • Pas d'actions : Impossible d'interagir avec les notifications (ouvrir, marquer lu, supprimer)

hooks/use-notifications.ts

  • Rôle : Hook principal pour gérer les notifications
  • Problèmes identifiés :
    • ⚠️ Force refresh par défaut : fetchNotificationCount(true) au mount
    • ⚠️ Pas de fonction markAsRead : Aucune méthode pour marquer comme lu
    • ⚠️ Deduplication complexe : Utilise requestDeduplicator mais peut être simplifié
    • Unified refresh : Bien intégré avec useUnifiedRefresh

2. Backend Services

lib/services/notifications/notification-service.ts

  • Rôle : Service singleton qui agrège les notifications
  • Problèmes identifiés :
    • ⚠️ Pas de méthode markAsRead : Le service ne peut pas marquer les notifications comme lues
    • ⚠️ Background refresh inutilisé : scheduleBackgroundRefresh() existe mais n'est jamais appelé
    • Cache bien géré : Redis avec TTL approprié
    • Adapter pattern : Architecture extensible

Adapters

LeantimeAdapter (leantime-adapter.ts)

  • Fonctionnel : Récupère les notifications Leantime
  • Pas de markAsRead : Ne peut pas marquer les notifications comme lues
  • ⚠️ Cache complexe : Cache des user IDs avec TTL

RocketChatAdapter (rocketchat-adapter.ts)

  • Fonctionnel : Récupère les messages non lus
  • Pas de markAsRead : Ne peut pas marquer les messages comme lus
  • ⚠️ Logique de recherche utilisateur : Complexe, pourrait être optimisée

EmailAdapter (email-adapter.ts)

  • Fonctionnel : Récupère les emails non lus
  • Pas de markAsRead : Ne peut pas marquer les emails comme lus
  • ⚠️ Support Graph API : Gère Microsoft Graph mais logique complexe

3. API Routes

/api/notifications/count

  • Fonctionnel : Retourne le nombre de notifications non lues
  • Support force refresh : ?force=true pour bypasser le cache
  • Bien implémenté

/api/notifications

  • Fonctionnel : Retourne la liste des notifications
  • Pagination : Support page et limit
  • Bien implémenté

/api/notifications/[id]/read MANQUANT

  • Problème critique : Aucun endpoint pour marquer les notifications comme lues
  • Impact : Les utilisateurs ne peuvent pas marquer les notifications comme lues depuis le dropdown

🐛 Problèmes Critiques Identifiés

1. Pas de fonctionnalité "Mark as Read"

Problème :

  • Aucun endpoint API pour marquer les notifications comme lues
  • Aucune méthode dans le service ou les adapters
  • Les utilisateurs ne peuvent pas interagir avec les notifications

Impact :

  • UX dégradée : les notifications restent "non lues" indéfiniment
  • Badge rouge persiste même après avoir vu les notifications
  • Pas de moyen de gérer les notifications

Solution nécessaire :

  1. Créer /api/notifications/[id]/read endpoint
  2. Ajouter markAsRead() dans NotificationAdapter interface
  3. Implémenter dans chaque adapter (Leantime, RocketChat, Email)
  4. Ajouter bouton "Mark as read" dans le dropdown
  5. Mettre à jour le count après marquage

2. Logs de debug excessifs

Problème :

  • notification-badge.tsx contient 6 console.log au render
  • Logs dans plusieurs fichiers (adapters, service, hooks)
  • Pollution de la console en production

Impact :

  • Performance dégradée (logs à chaque render)
  • Console difficile à déboguer
  • Informations sensibles potentiellement exposées

Solution nécessaire :

  • Utiliser logger.debug() au lieu de console.log
  • Retirer les logs de production
  • Garder uniquement les logs d'erreur

3. ⚠️ Double fetch dans notification-badge

Problème :

// Ligne 66-70 : Fetch quand dropdown s'ouvre
useEffect(() => {
  if (isOpen && status === 'authenticated') {
    manualFetch();
  }
}, [isOpen, status]);

// Ligne 73-78 : Fetch au mount
useEffect(() => {
  if (status === 'authenticated') {
    manualFetch();
  }
}, [status]);

// Ligne 85-89 : Fetch dans handleOpenChange
const handleOpenChange = (open: boolean) => {
  setIsOpen(open);
  if (open && status === 'authenticated') {
    manualFetch();
  }
};

Impact :

  • Appels API redondants
  • Charge serveur inutile
  • Expérience utilisateur dégradée (loading multiple)

Solution nécessaire :

  • Unifier la logique de fetch
  • Utiliser un seul point d'entrée
  • Éviter les appels multiples

4. ⚠️ Force refresh par défaut

Problème :

  • use-notifications.ts ligne 155 : fetchNotificationCount(true) au mount
  • Bypasse le cache à chaque chargement initial
  • Augmente la charge serveur

Impact :

  • Latence accrue au chargement
  • Charge serveur inutile
  • Cache Redis sous-utilisé

Solution nécessaire :

  • Utiliser le cache par défaut au mount
  • Force refresh uniquement pour refresh manuel
  • Aligner avec le pattern des autres widgets

5. ⚠️ Pas de pagination dans le dropdown

Problème :

  • Limite fixe à 10 notifications (ligne 81 de notification-badge.tsx)
  • Pas de "Load more" ou scroll infini
  • Utilisateurs avec beaucoup de notifications ne voient pas tout

Impact :

  • UX limitée pour les utilisateurs actifs
  • Notifications importantes peuvent être cachées
  • Pas de moyen de voir l'historique complet

Solution nécessaire :

  • Implémenter pagination ou scroll infini
  • Bouton "Load more" ou auto-load au scroll
  • Afficher le total de notifications

6. ⚠️ Pas de tri/filtre

Problème :

  • Toutes les notifications mélangées (Leantime, RocketChat, Email)
  • Pas de tri par date, source, type
  • Pas de filtre par source ou statut (lu/non lu)

Impact :

  • Difficile de trouver des notifications spécifiques
  • UX dégradée pour les utilisateurs avec beaucoup de notifications
  • Pas de moyen de gérer efficacement les notifications

Solution nécessaire :

  • Ajouter tri par date (déjà fait côté service mais pas exposé)
  • Ajouter filtres par source (Leantime, RocketChat, Email)
  • Ajouter filtre lu/non lu
  • Grouper par source ou date

7. ⚠️ Pas d'actions sur les notifications

Problème :

  • Impossible d'interagir avec les notifications depuis le dropdown
  • Pas de bouton "Mark as read"
  • Pas de bouton "Delete" ou "Dismiss"
  • Pas de lien direct vers la source (sauf via notification.link)

Impact :

  • UX limitée
  • Utilisateurs doivent aller dans chaque service pour gérer les notifications
  • Pas de gestion centralisée

Solution nécessaire :

  • Ajouter bouton "Mark as read" sur chaque notification
  • Ajouter bouton "Mark all as read"
  • Ajouter bouton "Dismiss" pour les notifications non actionnables
  • Améliorer les liens vers les sources

8. ⚠️ Background refresh inutilisé

Problème :

  • scheduleBackgroundRefresh() existe dans notification-service.ts (ligne 362)
  • Jamais appelé
  • Code mort

Impact :

  • Code inutile qui complique la maintenance
  • Potentiel de confusion pour les développeurs

Solution nécessaire :

  • Soit implémenter le background refresh
  • Soit supprimer le code mort
  • Le système de refresh unifié remplace cette fonctionnalité

9. ⚠️ Gestion d'erreurs incomplète

Problème :

  • Erreurs génériques affichées
  • Pas de retry automatique
  • Pas de distinction entre erreurs temporaires/permanentes
  • Pas de fallback si un adapter échoue

Impact :

  • UX dégradée en cas d'erreur
  • Utilisateurs ne comprennent pas les erreurs
  • Pas de résilience si un service est down

Solution nécessaire :

  • Améliorer les messages d'erreur
  • Implémenter retry avec backoff exponentiel
  • Distinguer erreurs temporaires/permanentes
  • Fallback gracieux si un adapter échoue (afficher les autres)

10. ⚠️ Performance et optimisation

Problèmes :

  • Pas de virtualisation pour les longues listes
  • Re-renders potentiels excessifs
  • Pas de memoization des composants de notification
  • Logs à chaque render

Impact :

  • Performance dégradée avec beaucoup de notifications
  • Expérience utilisateur ralentie
  • Consommation mémoire élevée

Solution nécessaire :

  • Implémenter virtualisation (react-window ou react-virtual)
  • Memoizer les composants de notification
  • Optimiser les re-renders
  • Retirer les logs de production

📊 État Actuel vs État Idéal

État Actuel

Utilisateur ouvre dropdown
  └─> Fetch notifications (force refresh)
      └─> Affiche 10 notifications
          └─> ❌ Pas d'action possible
              └─> Utilisateur doit aller dans chaque service
                  └─> Badge reste rouge même après avoir vu

État Idéal

Utilisateur ouvre dropdown
  └─> Fetch notifications (cache par défaut)
      └─> Affiche notifications avec pagination
          └─> ✅ Actions disponibles :
              ├─> Mark as read (individuel)
              ├─> Mark all as read
              ├─> Dismiss
              └─> Ouvrir dans source
          └─> Badge mis à jour immédiatement
              └─> Cache invalidé
                  └─> Count rafraîchi

🎯 Plan d'Action Recommandé

Phase 1 : Corrections Critiques (Priorité Haute)

  1. Créer endpoint /api/notifications/[id]/read

    • POST pour marquer comme lu
    • Support pour tous les adapters
    • Invalidation du cache après marquage
  2. Ajouter markAsRead() dans les adapters

    • Implémenter dans LeantimeAdapter
    • Implémenter dans RocketChatAdapter (marquer message comme lu)
    • Implémenter dans EmailAdapter (marquer email comme lu)
  3. Ajouter bouton "Mark as read" dans le dropdown

    • Sur chaque notification
    • Bouton "Mark all as read"
    • Mise à jour optimiste de l'UI
  4. Nettoyer les logs de debug

    • Retirer tous les console.log de production
    • Utiliser logger.debug() uniquement
    • Garder uniquement les logs d'erreur

Phase 2 : Améliorations UX (Priorité Moyenne)

  1. Corriger le double fetch

    • Unifier la logique de fetch
    • Un seul point d'entrée
    • Éviter les appels multiples
  2. Utiliser le cache par défaut

    • Retirer force=true au mount
    • Utiliser cache pour initial load
    • Force refresh uniquement pour refresh manuel
  3. Implémenter pagination

    • Scroll infini ou "Load more"
    • Afficher le total
    • Gérer le loading state
  4. Ajouter tri/filtre

    • Tri par date (déjà fait côté service)
    • Filtre par source
    • Filtre lu/non lu
    • Grouper par source ou date

Phase 3 : Optimisations (Priorité Basse)

  1. Améliorer la gestion d'erreurs

    • Messages d'erreur plus clairs
    • Retry automatique
    • Fallback gracieux
  2. Optimiser les performances

    • Virtualisation pour longues listes
    • Memoization des composants
    • Réduire les re-renders
  3. Nettoyer le code mort

    • Supprimer scheduleBackgroundRefresh() si inutilisé
    • Simplifier la deduplication si possible

📝 Fichiers à Modifier

Backend

  1. app/api/notifications/[id]/read/route.ts - NOUVEAU
  2. lib/services/notifications/notification-adapter.interface.ts - Ajouter markAsRead()
  3. lib/services/notifications/notification-service.ts - Ajouter méthode markAsRead()
  4. lib/services/notifications/leantime-adapter.ts - Implémenter markAsRead()
  5. lib/services/notifications/rocketchat-adapter.ts - Implémenter markAsRead()
  6. lib/services/notifications/email-adapter.ts - Implémenter markAsRead()

Frontend

  1. components/notification-badge.tsx - Nettoyer logs, ajouter actions, pagination
  2. hooks/use-notifications.ts - Ajouter markAsRead(), retirer force refresh par défaut
  3. lib/types/notification.ts - Vérifier si besoin d'ajouter des champs

🔍 Points d'Attention

  1. Synchronisation avec les sources : Quand on marque une notification comme lue, il faut aussi la marquer dans la source (Leantime, RocketChat, Email)

  2. Cache invalidation : Après markAsRead(), invalider le cache pour que le count se mette à jour immédiatement

  3. Optimistic updates : Mettre à jour l'UI immédiatement avant la confirmation serveur pour une meilleure UX

  4. Gestion des erreurs : Si le marquage échoue, rollback l'update optimiste

  5. Multi-sources : Chaque adapter a sa propre logique pour marquer comme lu, il faut gérer les différences


Résumé

Problèmes critiques : 3

  • Pas de fonctionnalité "Mark as Read"
  • Logs de debug excessifs
  • Double fetch

Problèmes importants : 4

  • Force refresh par défaut
  • Pas de pagination
  • Pas de tri/filtre
  • Pas d'actions

Optimisations : 3

  • Background refresh inutilisé
  • Gestion d'erreurs incomplète
  • Performance

Total : 10 problèmes identifiés nécessitant des corrections


Recommandation : Commencer par la Phase 1 (corrections critiques) qui résoudra les problèmes les plus impactants pour l'UX.