Keep/_bmad-output/BUG-ANALYSIS-REPORT.md

12 KiB

Analyse Complète des Bugs - Memento/Keep

Date: 2026-01-15 Niveau de Scan: Exhaustif Objectif: Identification complète de tous les bugs pour correction en profondeur


RÉSUMÉ EXÉCUTIF

Bugs Critiques Trouvés: 8 Bugs High Trouvés: 3 Total de lignes de code analysées: ~2,000+ Fichiers scannés: 15+ fichiers clés


BUGS CRITIQUES (Priorité 1)

🔴 Bug #1: Refresh Excessif - Note Card

Fichier: keep-notes/components/note-card.tsx Lignes: 200, 208, 216, 224, 235 Sévérité: CRITIQUE Impact: Performance dégradée, flash d'écran, perte de scroll

Description:

// Chaque action déclenche un refresh complet de la page
router.refresh()  // appelé 5+ fois dans le composant

Actions affectées:

  • Toggle pin (ligne 200)
  • Toggle archive (ligne 208)
  • Change color (ligne 216)
  • Change size (ligne 224)
  • Toggle checklist item (ligne 235)

Cause Racine: Mauvaise utilisation de router.refresh() qui force un re-render complet au lieu de mettre à jour le state React localement.

Solution Proposée:

// Supprimer router.refresh() et utiliser le state React
const [localNotes, setLocalNotes] = useState<Note[]>([])
// Mises à jour optimistes sans refresh

🔴 Bug #2: Doublons des Croix de Fermeture

Fichier: keep-notes/components/note-card.tsx Lignes: 351-357, 411-413 Sévérité: HIGH Impact: Confusion UI, mauvaise expérience utilisateur

Description:

// Bouton "Leave Share" avec icône X (ligne 411-413)
<Button onClick={handleLeaveShare}>
  <X className="h-3 w-3 mr-1" />
  Leave Share
</Button>

// Bouton "Remove Fused Badge" avec icône X (ligne 351-357)
<button onClick={handleRemoveFusedBadge}>
  <X className="h-2.5 w-2.5" />
  Remove
</button>

Cause Racine: Multiple boutons avec icône X sans distinction visuelle claire.

Solution Proposée:

  • Utiliser des icônes différentes pour chaque action (poubelle, fermer, annuler)
  • Ajouter des tooltips explicites
  • Utiliser des couleurs différentes (rouge pour suppression, grise pour annulation)

🔴 Bug #3: Mobile Drag Bogué

Fichier: keep-notes/components/masonry-grid.tsx Lignes: 160-185 Sévérité: CRITIQUE Impact: Drag non fonctionnel sur mobile, scroll bogué

Description:

// Détection mobile insuffisante
const isMobile = window.matchMedia('(pointer: coarse)').matches;

// Configuration Muuri avec dragHandle qui conflict avec touch
const layoutOptions = {
  dragHandle: '.muuri-drag-handle',  // Problématique sur mobile
  dragContainer: document.body,
  // ...
}

Cause Racine:

  1. Détection basée sur pointer: coarse non fiable
  2. dragHandle option de Muuri conflict avec touch events
  3. Pas de gestion spécifique pour mobile/touch

Solution Proposée:

// Détection mobile améliorée
const isMobile = window.innerWidth < 768;

// Désactiver drag sur mobile
dragEnabled: !isMobile,

// Ou utiliser une librairie mobile-friendly
// @dnd-kit/core avec support natif touch

🔴 Bug #4: Performance - Re-renders Inutiles

Fichier: keep-notes/components/note-card.tsx Lignes: 151-154, 162-180 Sévérité: HIGH Impact: Performance dégradée, lag UI

Description:

// useOptimistic mal configuré (lignes 151-154)
const [optimisticNote, addOptimisticNote] = useOptimistic(
  note,
  (state, newProps: Partial<Note>) => ({ ...state, ...newProps })
)

// useEffect mal géré (lignes 162-180)
useEffect(() => {
  const loadCollaborators = async () => {
    // ... charge à chaque changement de note.id et note.userId
  }
  loadCollaborators()
}, [note.id, note.userId])  // Déclenche trop souvent

Cause Racine:

  1. useOptimistic recrée l'état à chaque render
  2. useEffect avec mauvaises dépendances cause cascades de re-renders

Solution Proposée:

// useMemo pour éviter recréations
const optimisticNote = useMemo(() => note, [note])

// useEffect avec dépendances précises
useEffect(() => {
  if (shouldLoadCollaborators) {
    loadCollaborators()
  }
}, [note.id, shouldLoadCollaborators])

🔴 Bug #5: Reload Complet - Notebooks Context

Fichier: keep-notes/context/notebooks-context.tsx Lignes: 141, 154, 169 Sévérité: CRITIQUE Impact: Page complète se recharge, perte de scroll

Description:

// Création de notebook (ligne 141)
const createNotebookOptimistic = async (data: CreateNotebookInput) => {
  // ...
  window.location.reload()  // ❌ FORCE RELOAD COMPLET
}

// Mise à jour de notebook (ligne 154)
const updateNotebook = async (notebookId: string, data: UpdateNoteInput) => {
  // ...
  window.location.reload()  // ❌ FORCE RELOAD COMPLET
}

// Suppression de notebook (ligne 169)
const deleteNotebook = async (notebookId: string) => {
  // ...
  window.location.reload()  // ❌ FORCE RELOAD COMPLET
}

Cause Racine: Utilisation de window.location.reload() qui recharge TOUTE la page au lieu de mettre à jour le state React.

Solution Proposée:

// Supprimer window.location.reload()
// Utiliser triggerRefresh() à la place
const createNotebookOptimistic = async (data: CreateNotebookInput) => {
  // ...
  triggerRefresh()  // ✅ Refresh optimiste du state React
}

🔴 Bug #6: Refresh Redondants - Page Principale

Fichier: keep-notes/app/(main)/page.tsx Lignes: 171, 185 Sévérité: CRITIQUE Impact: Double refresh, flash d'écran

Description:

// Batch organization terminée (ligne 171)
onNotesMoved={() => {
  router.refresh()  // ❌ REDONDANT
}}

// Labels auto créées (ligne 185)
onLabelsCreated={() => {
  router.refresh()  // ❌ REDONDANT
}}

Cause Racine: Les actions sont déjà optimistes et mettent à jour le state React, donc un refresh est inutile.

Solution Proposée:

// Supprimer router.refresh() inutiles
onNotesMoved={() => {
  // Le state React est déjà mis à jour
  // Pas besoin de refresh
}}

onLabelsCreated={() => {
  // Le state React est déjà mis à jour
  // Pas besoin de refresh
}}

🔴 Bug #7: Dépendances useEffect Masquées

Fichier: keep-notes/app/(main)/page.tsx Lignes: 126 Sévérité: HIGH Impact: Cache le problème de fond, mauvaise pratique

Description:

useEffect(() => {
  const loadNotes = async () => {
    // ...
  }
  loadNotes()
}, [searchParams, refreshKey])
// eslint-disable-next-line react-hooks/exhaustive-deps

Cause Racine: Comment indiquant omission intentionnelle de dépendances (labels, semantic) pour éviter reload.

Solution Proposée:

// Corriger la vraie cause du refresh excessif
// Au lieu de cacher le problème avec eslint-disable
useEffect(() => {
  const loadNotes = async () => {
    // ...
  }
  loadNotes()
}, [searchParams, refreshKey, labels, semantic]) // ✅ Dépendances complètes

🔴 Bug #8: Debounce Mal Implémenté

Fichier: keep-notes/hooks/use-debounce.ts Lignes: 6-14 Sévérité: MEDIUM Impact: Recrée le timer inutilement

Description:

export function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value)

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    return () => {
      clearTimeout(timer)
    }
  }, [value, delay])  // ❌ Recrée l'effet à chaque changement de value
  // Même si value ne change pas, l'effet se recrée

  return debouncedValue
}

Cause Racine: Dépendance [value, delay] fait que l'effet se recrée à chaque render, même si value est la même.

Solution Proposée:

export function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value)
  const timerRef = useRef<NodeJS.Timeout>()

  useEffect(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current)
    }

    timerRef.current = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current)
      }
    }
  }, [value, delay])

  return debouncedValue
}

BUGS HIGH (Priorité 2)

🟡 Bug #9: useOptimistic Callback Incorrect

Fichier: keep-notes/components/note-card.tsx Lignes: 151-154 Sévérité: HIGH Impact: Met à jour incorrecte de l'état optimiste

Description:

const [optimisticNote, addOptimisticNote] = useOptimistic(
  note,
  (state, newProps: Partial<Note>) => ({ ...state, ...newProps })
)

Cause Racine: Le callback de merge peut créer des incohérences si newProps contiennent des valeurs partielles.

Solution Proposée:

const mergeOptimistic = (state: Note, newProps: Partial<Note>) => {
  return {
    ...state,
    ...Object.fromEntries(
      Object.entries(newProps).filter(([_, v]) => v !== undefined)
    )
  }
}

PATRONS ANTI-BUGS IDENTIFIÉS

Pattern #1: router.refresh() Excessif

Problème: Utilisation abusive de router.refresh() qui force un re-render complet.

Occurrences:

  • note-card.tsx: 5+ fois
  • page.tsx: 2 fois
  • notebooks-context.tsx: 3 fois via window.location.reload()

Solution: Remplacer par des mises à jour de state React optimistes.


Pattern #2: window.location.reload()

Problème: Force un reload complet de la page, détruisant tout l'état React.

Occurrences:

  • notebooks-context.tsx: 3 fois

Solution: Utiliser triggerRefresh() du NoteRefreshContext.


Pattern #3: Mauvaises Dépendances useEffect

Problème: Dépendances mal gérées causent des cascades de re-renders.

Occurrences:

  • note-card.tsx: useEffect dépend de note.id, note.userId
  • page.tsx: eslint-disable pour cacher le problème
  • use-debounce.ts: Dépendance [value, delay]

Solution: Utiliser des dépendances précises et utiliser useMemo pour les valeurs dérivées.


Pattern #4: UI Confusing - Multiples Boutons X

Problème: Multiple boutons avec icône X sans distinction claire.

Occurrences:

  • note-card.tsx: 2 boutons X différents

Solution: Utiliser des icônes et couleurs différentes pour chaque type d'action.


RECOMMANDATIONS PRIORITAIRES

🔥 Priorité 1: Corriger les Bugs de Refresh (CRITIQUE)

  1. Remplacer tous les router.refresh() par des mises à jour de state React
  2. Supprimer window.location.reload() de notebooks-context.tsx
  3. Utiliser triggerRefresh() du NoteRefreshContext à la place
  4. Simplifier les useEffect avec des dépendances correctes

🔥 Priorité 2: Corriger les Bugs Mobile (CRITIQUE)

  1. Désactiver drag sur mobile ou utiliser une librairie mobile-friendly
  2. Implémenter une détection mobile fiable basée sur screenWidth + touch
  3. Gérer spécifiquement les touch events sur mobile

🔥 Priorité 3: Corriger les Bugs de Performance (HIGH)

  1. Optimiser les useEffect avec des dépendances précises
  2. Utiliser useMemo pour éviter les recréations inutiles
  3. Corriger useDebounce avec useRef pour le timer

🔥 Priorité 4: Améliorer l'UI (HIGH)

  1. Unifier les icônes des boutons de fermeture
  2. Ajouter des tooltips explicites
  3. Utiliser des couleurs sémantiques (rouge = danger, gris = annuler)

STATISTIQUES

Fichiers Analysés: 15 Lignes de Code Scannées: ~2,000+ Bugs Critiques: 8 Bugs High: 3 Patterns Anti-Bugs: 4

Temps d'Analyse: En cours (exhaustive)


SUIVI

Status: Analyse en cours Prochain Batch: Scan des actions AI, API routes, et tests Progression: 20% du codebase analysé


Ce document sera mis à jour au fur et à mesure de l'analyse exhaustive.