Keep/_bmad-output/planning-artifacts/sprint-1-bug-fixes.md

38 KiB

Sprint #1: Correction des Bugs Critiques et High Priority

Métadonnées

Propriété Valeur
Nom du Sprint Correction des Bugs Critiques UI et Performance
ID du Sprint sprint-1-bug-fixes
Date de début 2026-01-15
Durée prévue 2 semaines (10 jours ouvrés)
Statut 🟡 Prêt à démarrer
Priorité 🔴 Critique (P0)
Capacité 8 stories
Lead Frontend Engineer

🎯 Goal (Objectif du Sprint)

Objectif principal: Éliminer tous les problèmes de refresh, re-render excessifs, et bugs UI qui affectent l'expérience utilisateur quotidienne.

Métriques de succès:

  • Aucun flash d'écran inutile
  • Aucune perte de position de scroll
  • Toutes les actions UI sont instantanées (optimistes)
  • Drag and drop fonctionne correctement sur desktop
  • Drag est désactivé proprement sur mobile
  • Performance améliorée (moins de re-renders)
  • Toutes les corrections sont validées par tests

📋 Backlog (Stories du Sprint)

🔴 CRITIQUE (Doit être complété avant fin du Sprint)

Story #1: Corriger triggerRefresh() dans NoteRefreshContext.tsx

Priorité: P0 (Critique)
Estimation: 2 heures Complexité: Faible

En tant que: Développeur Frontend Je veux: Corriger la fonction triggerRefresh() dans le Provider de contexte pour qu'elle force un re-render global de tous les composants consommateurs.

Afin de: Permettre aux composants de se rafraîchir sans utiliser router.refresh() ou window.location.reload(), éliminant ainsi les flashs d'écran et la perte de scroll.

Critères d'acceptation:

  • L'appel à triggerRefresh() force immédiatement un re-render de tous les composants qui utilisent useNoteRefresh()
  • Le re-render ne crée pas de cycles de dépendances
  • Aucun appel à router.refresh() ou window.location.reload() dans la fonction elle-même
  • Les tests Playwright existants passent (confirmant que les bugs de refresh sont résolus)

Contexte technique:

  • Fichier affecté: keep-notes/context/NoteRefreshContext.tsx
  • Fonction cible: triggerRefresh()
  • Approche actuelle (bug):
    const triggerRefresh = useCallback(() => {
      setRefreshKey(prev => prev + 1)  // ❌ INCORRECT
    }, [refreshKey])  // ❌ Cycle de dépendances
    
  • Approche corrigée:
    const refreshKeyRef = useRef(refreshKey)
    const triggerRefresh = useCallback(() => {
      const newKey = refreshKeyRef.current + 1
      refreshKeyRef.current = newKey
      setRefreshKey(newKey)  // ✅ Force le re-render
    }, [])  // ✅ Pas de dépendances
    

Tests:

  • Jouer les tests Playwright existants (bug-move-direct.spec.ts, bug-note-move-refresh.spec.ts)
  • Manuellement: Créer une note, toggle pin, vérifier que l'UI se met à jour sans flash
  • Cross-browser: Tester sur Chrome, Firefox, Safari

Points techniques:

  • Utiliser useRef pour stocker la valeur actuelle de la clé
  • Utiliser useCallback sans dépendances
  • Éviter les cycles de dépendances entre state et callback
  • Documenter pourquoi l'ancienne approche ne fonctionnait pas

Effets secondaires:

  • Tous les composants utilisant triggerRefresh() bénéficieront immédiatement
  • Réduit le nombre de re-renders dans l'application entière

Risques:

  • Si mal implémenté, peut créer des re-renders infinis
  • Peut affecter d'autres fonctionnalités qui dépendent de refreshKey

Mitigation:

  • Tests approfondis avant de merge
  • Code review attentif
  • Tester avec des scénarios limites (beaucoup de composants, beaucoup de triggers)

Story #2: Supprimer router.refresh() dans note-card.tsx

Priorité: P0 (Critique)
Estimation: 1 heure Complexité: Faible

En tant que: Développeur Frontend Je veux: Supprimer tous les appels inutiles à router.refresh() dans le composant NoteCard pour laisser l'état optimiste gérer l'UI.

Afin de: Éliminer les refreshs de page complets lors des actions courantes (pin, archive, color, size, toggle checklist), réduisant les flashs d'écran.

Critères d'acceptation:

  • Aucun appel à router.refresh() dans handleTogglePin()
  • Aucun appel à router.refresh() dans handleToggleArchive()
  • Aucun appel à router.refresh() dans handleColorChange()
  • Aucun appel à router.refresh() dans handleSizeChange()
  • Aucun appel à router.refresh() dans handleCheckItem()
  • L'état optimiste (useOptimistic) met à jour l'UI instantanément
  • Les tests de la Story #1 passent (car le triggerRefresh fonctionne maintenant)

Contexte technique:

  • Fichier affecté: keep-notes/components/note-card.tsx
  • Lignes à modifier: 200, 208, 216, 224, 235
  • Approche actuelle (bug):
    const handleTogglePin = async () => {
      startTransition(async () => {
        addOptimisticNote({ isPinned: !note.isPinned })
        await togglePin(note.id, !note.isPinned)
        router.refresh()  // ❌ FORCE RELOAD COMPLET
      })
    }
    
  • Approche corrigée:
    const handleTogglePin = async () => {
      addOptimisticNote({ isPinned: !note.isPinned })
      await togglePin(note.id, !note.isPinned)
      // ✅ Pas de refresh - l'état optimiste gère l'UI
    }
    

Tests:

  • Jouer les tests Playwright existants
  • Manuellement: Créer 10 notes, toggle pin sur chacune, vérifier que le temps total < 2 secondes
  • Vérifier qu'il n'y a pas de flash d'écran

Points techniques:

  • Supprimer les appels à router.refresh() dans les handlers d'actions
  • Conserver addOptimisticNote pour l'UI instantanée
  • Laisser startTransition pour la mise à jour asynchrone du serveur
  • Documenter que le refresh est maintenant géré par triggerRefresh()

Effets secondaires:

  • Réduction significative des refreshs de page
  • Amélioration de la réactivité perçue par l'utilisateur

Risques:

  • Si une action échoue côté serveur, l'UI peut être désynchronisée
  • L'état optimiste peut ne pas correspondre à la réalité serveur

Mitigation:

  • Gérer les erreurs serveur avec startTransition et rollback de l'état optimiste
  • Ajouter des notifications de succès/erreur pour informer l'utilisateur
  • Tester les scénarios d'échec réseau

Story #3: Supprimer router.refresh() dans page.tsx

Priorité: P0 (Critique)
Estimation: 30 minutes Complexité: Faible

En tant que: Développeur Frontend Je veux: Supprimer les appels redondants à router.refresh() dans la page principale (page.tsx) après les actions de batch organization et auto-labeling.

Afin de: Éviter les double refreshs inutiles qui causent des flashs d'écran et une mauvaise expérience utilisateur.

Critères d'acceptation:

  • Aucun appel à router.refresh() dans onNotesMoved() (ligne 171)
  • Aucun appel à router.refresh() dans onLabelsCreated() (ligne 185)
  • Les actions de batch et auto-labeling fonctionnent sans refresh
  • Les tests de la Story #1 passent

Contexte technique:

  • Fichier affecté: keep-notes/app/(main)/page.tsx
  • Lignes à modifier: 171, 185
  • Approche actuelle (bug):
    onNotesMoved={() => {
      router.refresh()  // ❌ REDONDANT - déjà optimiste
    }}
    
    onLabelsCreated={() => {
      router.refresh()  // ❌ REDONDANT - déjà optimiste
    }}
    
  • Approche corrigée:
    onNotesMoved={() => {
      // ✅ Le state React est déjà mis à jour de manière optimiste
      // Pas besoin de refresh
    }}
    
    onLabelsCreated={() => {
      // ✅ Le state React est déjà mis à jour de manière optimiste
      // Pas besoin de refresh
    }}
    

Tests:

  • Jouer les tests Playwright existants
  • Manuellement: Organiser des notes en batch, vérifier qu'il n'y a pas de refresh
  • Manuellement: Créer des labels automatiques, vérifier qu'il n'y a pas de refresh

Points techniques:

  • Supprimer les appels à router.refresh() dans les callbacks
  • Comprendre que l'état React local (notes, labels) est déjà mis à jour
  • Le refresh global sera déclenché par triggerRefresh() si nécessaire
  • Documenter pourquoi les refreshs étaient redondants

Effets secondaires:

  • Élimination des double refreshs
  • Amélioration de la performance
  • Expérience utilisateur plus fluide

Risques:

  • Si le state React local n'est pas correctement synchronisé, l'utilisateur peut voir des données obsolètes
  • Les tests doivent être mis à jour pour refléter l'absence de refresh

Mitigation:

  • S'assurer que les handlers de batch et auto-labeling mettent à jour le state React correctement
  • Ajouter des logs de debugging pour tracer les mises à jour de state
  • Documenter les changements dans le README

Story #4: Remplacer window.location.reload() dans notebooks-context.tsx

Priorité: P0 (Critique)
Estimation: 1 heure Complexité: Faible

En tant que: Développeur Frontend Je veux: Remplacer tous les appels à window.location.reload() dans notebooks-context.tsx par des appels à triggerRefresh() pour éviter les reloads complets de la page.

Afin de: Permettre aux actions sur les notebooks (création, mise à jour, suppression, réordonnancement) de rafraîchir l'UI sans recharger toute la page.

Critères d'acceptation:

  • Remplacer window.location.reload() par triggerRefresh() dans createNotebookOptimistic() (ligne 141)
  • Remplacer window.location.reload() par triggerRefresh() dans updateNotebook() (ligne 154)
  • Remplacer window.location.reload() par triggerRefresh() dans deleteNotebook() (ligne 169)
  • Remplacer window.location.reload() par triggerRefresh() dans updateNotebookOrderOptimistic() (ligne 169)
  • Les tests de la Story #1 passent (car le triggerRefresh fonctionne maintenant)

Contexte technique:

  • Fichier affecté: keep-notes/context/notebooks-context.tsx
  • Lignes à modifier: 141, 154, 169
  • Approche actuelle (bug):
    const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => {
      // ... API call ...
      window.location.reload()  // ❌ FORCE RELOAD COMPLET
    }
    
  • Approche corrigée:
    const { triggerRefresh } = useNoteRefresh()
    
    const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => {
      // ... API call ...
      triggerRefresh()  // ✅ Rafraîchissement optimiste du state React
    }
    

Tests:

  • Jouer les tests Playwright existants
  • Manuellement: Créer un notebook, modifier son nom, vérifier qu'il n'y a pas de reload complet
  • Manuellement: Supprimer un notebook, vérifier que la page ne se recharge pas
  • Manuellement: Réordonner des notebooks, vérifier que l'ordre se met à jour sans reload

Points techniques:

  • Importer useNoteRefresh depuis le Contexte
  • Remplacer tous les appels à window.location.reload() par triggerRefresh()
  • S'assurer que les callbacks sont async/await
  • Tester que l'état des notebooks se met à jour dans les autres composants

Effets secondaires:

  • Élimination des reloads complets de la page
  • Préservation de la position de scroll
  • Meilleure expérience utilisateur (pas de flash blanc)
  • Conservation de l'état de l'application

Risques:

  • Si triggerRefresh() ne fonctionne pas (Bug #1 non résolu), toutes les actions sur notebooks échoueront
  • Peut créer des incohérences d'état si d'autres composants modifient aussi les notebooks

Mitigation:

  • Corriger d'abord la Story #1 (triggerRefresh) AVANT de modifier les actions sur notebooks
  • Tester soigneusement chaque action sur notebooks après correction
  • Ajouter des logs pour tracer les appels à triggerRefresh()
  • Avoir un plan de rollback prêt

Dépendance: Story #1 doit être complétée AVANT cette story


🟡 HIGH (Compléter avant fin du Sprint si possible)

Story #5: Désactiver Drag sur Mobile

Priorité: P1 (High)
Estimation: 2 heures Complexité: Moyenne

En tant que: Développeur Frontend Je veux: Désactiver correctement le drag and drop sur les appareils mobiles (smartphones, tablets) pour éviter les conflits avec les touch events et les bugs de scroll.

Afin de: Permettre aux utilisateurs mobiles d'utiliser l'application sans que le drag and drop interfère avec le scroll et les touch events, améliorant significativement l'expérience mobile.

Critères d'acceptation:

  • Le drag est désactivé sur mobile (écran < 768px)
  • Le drag reste activé sur desktop
  • Aucun conflit avec les touch events sur mobile
  • Le scroll fonctionne normalement sur mobile
  • Les tests mobile passent (ou sont créés pour valider)
  • Aucune erreur console liée au drag sur mobile

Contexte technique:

  • Fichier affecté: keep-notes/components/masonry-grid.tsx
  • Lignes à modifier: 160, 165 (options de layout)
  • Approche actuelle (bug):
    const isMobile = window.matchMedia('(pointer: coarse)').matches; // ❌ Non fiable
    const layoutOptions = {
      dragEnabled: true, // ❌ Problématique sur mobile
      dragHandle: '.muuri-drag-handle', // ❌ Conflict avec touch
      dragContainer: document.body,
      // ...
    }
    
  • Approche corrigée Option A (Simple):
    const isMobile = window.innerWidth < 768; // ✅ Détection fiable
    const isTouchDevice = 'ontouchstart' in window; // ✅ Détection fiable
    
    const layoutOptions = {
      dragEnabled: !isMobile && !isTouchDevice, // ✅ Désactiver sur mobile
      dragHandle: isMobile ? undefined : '.muuri-drag-handle', // ✅ Pas de handle sur mobile
      dragContainer: document.body,
      // ...
    }
    
  • Approche corrigée Option B (Recommandée - Plus complexe): Remplacer Muuri par @dnd-kit/core qui supporte nativement le touch

Tests:

  • Tests Playwright existants (si tests mobile existent)
  • Manuellement: Tester sur desktop - drag fonctionne
  • Manuellement: Tester sur mobile - drag désactivé, scroll fonctionne
  • Cross-device: Tester sur iPhone, Android, iPad, Desktop
  • Playwright en mode mobile (viewports mobiles)

Points techniques:

  • Utiliser window.innerWidth pour une détection fiable
  • Utiliser 'ontouchstart' in window pour détecter les appareils tactiles
  • Désactiver dragEnabled pour mobile et touch devices
  • Désactiver dragHandle pour mobile et touch devices
  • Conserver les options Muuri restantes pour desktop
  • Optionnellement: Remplacer Muuri par @dnd-kit/core (recommandé pour mobile)

Effets secondaires:

  • Expérience mobile fluide sans conflits
  • Scroll fonctionnel sur mobile
  • Amélioration de la performance sur mobile (moins de calculs de layout)
  • Drag and drop reste fonctionnel sur desktop

Risques:

  • Les breakpoints de détection mobile (768px) peuvent ne pas correspondre à tous les devices
  • Certains tablets peuvent être traités comme mobiles alors qu'ils supportent le drag
  • La détection mobile côté client peut différer de la détection Playwright

Mitigation:

  • Utiliser des breakpoints standard et bien documentés
  • Tester sur une variété de devices réels (pas uniquement iPhone)
  • Considérer une taille d'écran intermédiaire (768-1024px) pour les tablets
  • Ajouter un flag utilisateur pour forcer le mode mobile si nécessaire

Story #6: Corriger les Doublons de Boutons de Fermeture

Priorité: P1 (High)
Estimation: 1 heure Complexité: Faible

En tant que: Développeur Frontend + UX Designer Je veux: Remplacer les boutons multiples avec icône X par des boutons avec des icônes et couleurs sémantiques distinctes pour chaque type d'action (poubelle, fermer, annuler), améliorant ainsi la clarté de l'UI et réduisant la confusion.

Afin de: Permettre aux utilisateurs de distinguer visuellement et rapidement les différentes actions disponibles sur une note (quitter le partage, supprimer le badge de fusion, etc.), améliorant l'expérience utilisateur et réduisant les erreurs.

Critères d'acceptation:

  • Le bouton "Remove Fused Badge" utilise une icône de poubelle (Trash2) et couleur violette
  • Le bouton "Leave Share" utilise une icône de déconnexion (LogOut) et couleur bleue
  • Les deux boutons sont visuellement distincts
  • Des tooltips explicites décrivent l'action
  • Les couleurs sémantiques sont cohérentes (rouge = danger, gris/bleu = annuler)
  • L'UX est améliorée avec moins de confusion

Contexte technique:

  • Fichier affecté: keep-notes/components/note-card.tsx
  • Lignes à modifier: 351-357 (remove fused badge), 411-413 (leave share)
  • Approche actuelle (bug):
    // Bouton "Remove Fused Badge" avec icône X
    <button onClick={handleRemoveFusedBadge}>
      <X className="h-2.5 w-2.5" />
      <span>Remove</span>
    </button>
    
    // Bouton "Leave Share" avec icône X
    <Button onClick={handleLeaveShare}>
      <X className="h-3 w-3 mr-1" />
      Leave Share
    </Button>
    
  • Approche corrigée:
    import { Trash2, LogOut, X } from 'lucide-react'
    
    // Bouton "Remove Fused Badge" - icône de poubelle
    <button onClick={handleRemoveFusedBadge} className="...">
      <Trash2 className="h-2.5 w-2.5 text-purple-600" />
      <span className="ml-2">Remove Fused Badge</span>
    </button>
    
    // Bouton "Leave Share" - icône de déconnexion
    <Button onClick={handleLeaveShare} className="...">
      <LogOut className="h-3 w-3 mr-1 text-blue-600" />
      Leave Share
    </Button>
    

Tests:

  • Manuellement: Ouvrir une note avec badge fusionné, tester le bouton de suppression
  • Manuellement: Ouvrir une note partagée, tester le bouton de quitter
  • Tests d'accessibilité: Vérifier les attributs ARIA
  • Tests visuels: Screenshot avant/après pour vérifier les icônes et couleurs

Points techniques:

  • Importer les nouvelles icônes depuis lucide-react
  • Appliquer des classes Tailwind pour les couleurs
  • Ajouter des tooltips explicites (title ou aria-label)
  • Garder les classes existantes pour la disposition et le padding
  • S'assurer que les boutons sont accessibles au clavier et à l'écran

Effets secondaires:

  • Réduction significative de la confusion utilisateur
  • Amélioration de l'accessibilité (icônes sémantiques)
  • Expérience utilisateur plus claire et intuitive
  • Moins d'erreurs (supprimer au lieu de quitter, etc.)

Risques:

  • Si les nouvelles icônes ne sont pas cohérentes avec le reste de l'UI, peut créer de la confusion
  • Changement visuel peut surprendre les utilisateurs habitués
  • Les tooltips doivent être traduits correctement

Mitigation:

  • Avoir une review UX pour valider les nouvelles icônes et couleurs
  • Traduire les tooltips dans toutes les langues supportées
  • Ajouter une note dans le changelog expliquant les nouvelles icônes
  • Considérer une période de transition ou un flag utilisateur pour revenir à l'ancien design

Dépendances: Aucune (peut être fait en parallèle)


Story #7: Corriger useDebounce Hook

Priorité: P1 (High)
Estimation: 30 minutes Complexité: Faible

En tant que: Développeur Frontend Je veux: Corriger l'implémentation du hook useDebounce qui recrée le timer à chaque render au lieu de réutiliser le timer existant, causant ainsi des cascades de re-renders inutiles et une dégradation des performances.

Afin de: Optimiser le hook en réutilisant le timer existant via useRef, éliminant les recréations inutiles et améliorant ainsi la performance globale de l'application.

Critères d'acceptation:

  • Le timer est stocké dans un useRef au lieu d'être recréé
  • Le hook ne recrée pas le timer si la valeur ne change pas
  • Les re-renders inutiles sont éliminés
  • La performance mesurable (temps de render) est améliorée
  • Le hook reste fonctionnellement identique pour l'utilisateur

Contexte technique:

  • Fichier affecté: keep-notes/hooks/use-debounce.ts
  • Approche actuelle (bug):
    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 le timer à chaque render
      // ❌ Même si value ne change pas, l'effet se recrée
    
      return debouncedValue
    }
    
  • Approche corrigée:
    export function useDebounce<T>(value: T, delay: number): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value)
      const timerRef = useRef<NodeJS.Timeout>()  // ✅ Référence persistante
    
      useEffect(() => {
        if (timerRef.current) {
          clearTimeout(timerRef.current)
        }
    
        timerRef.current = setTimeout(() => {
          setDebouncedValue(value)
        }, delay)
    
        return () => {
          if (timerRef.current) {
            clearTimeout(timerRef.current)
          }
        }
      }, [value, delay])  // ✅ Recrée uniquement quand value change
    
      return debouncedValue
    }
    

Tests:

  • Tests de performance: Mesurer le nombre de renders avant/après correction
  • Tests fonctionnels: Vérifier que le debounce fonctionne toujours correctement
  • Tests d'intégration: Vérifier que les composants utilisant useDebounce fonctionnent toujours
  • Tests de stress: Tester avec des mises à jour très fréquentes

Points techniques:

  • Utiliser useRef pour stocker la référence du timer
  • Vérifier si timerRef.current existe avant de créer un nouveau timer
  • Nettoyer le timer existant avant d'en créer un nouveau
  • Conserver la même API publique (ne pas changer la signature du hook)

Effets secondaires:

  • Réduction des cascades de re-renders
  • Amélioration de la performance globale
  • Moins d'utilisation CPU
  • Meilleure fluidité de l'UI

Risques:

  • Si mal implémenté, le timer peut ne jamais être nettoyé
  • Peut introduire des bugs subtiles de timing
  • Peut affecter tous les composants utilisant useDebounce

Mitigation:

  • Tests approfondis avec différents scénarios de mise à jour
  • Code review attentif de la logique du timer
  • Tests de performance pour comparer avant/après
  • Avoir un plan de rollback prêt

Dépendances: Aucune (peut être fait en parallèle)


Story #8: Corriger useEffect Mal Géré dans note-card.tsx

Priorité: P1 (High)
Estimation: 30 minutes Complexité: Moyenne

En tant que: Développeur Frontend Je veux: Corriger le useEffect qui charge les collaborateurs à chaque changement de note.id et note.userId en ajoutant les dépendances manquantes (isOwner, isSharedNote) pour éviter les re-renders inutiles et améliorer les performances.

Afin de: Éliminer les chargements inutiles de collaborateurs et les re-renders associés, améliorant ainsi la performance du composant NoteCard et de l'application globale.

Critères d'acceptation:

  • Le useEffect ne se déclenche pas uniquement quand note.id ou note.userId changent
  • Les dépendances complètes (isOwner, isSharedNote) sont incluses
  • Les re-renders inutiles sont éliminés
  • Les collaborateurs sont toujours correctement chargés
  • La performance du composant est améliorée

Contexte technique:

  • Fichier affecté: keep-notes/components/note-card.tsx
  • Lignes à modifier: 162-180
  • Approche actuelle (bug):
    useEffect(() => {
      const loadCollaborators = async () => {
        // ... chargement des collaborateurs
      }
    
      loadCollaborators()
    }, [note.id, note.userId])  // ❌ Se déclenche trop souvent
    // ❌ Se déclenche même si isOwner et isSharedNote changent
    
  • Approche corrigée:
    useEffect(() => {
      const loadCollaborators = async () => {
        // ... chargement des collaborateurs
      }
    
      loadCollaborators()
    }, [note.id, note.userId, isOwner, isSharedNote])  // ✅ Dépendances complètes
    // ✅ Se déclenche uniquement quand une de ces valeurs change vraiment
    

Tests:

  • Tests de performance: Mesurer le nombre de renders du composant
  • Tests fonctionnels: Vérifier que les collaborateurs sont toujours chargés correctement
  • Tests d'intégration: Vérifier que les modifications de note ne causent pas de re-renders inutiles
  • Tests de limites: Cas limites avec beaucoup de notes

Points techniques:

  • Ajouter isOwner et isSharedNote aux dépendances du useEffect
  • Comprendre quand chaque valeur change vraiment
  • Éviter les re-renders en cascade
  • S'assurer que les calculs de isOwner et isSharedNote sont optimisés

Effets secondaires:

  • Réduction des re-renders du composant NoteCard
  • Amélioration de la performance globale
  • Moins d'appels API inutiles
  • Meilleure expérience utilisateur (pas de lag lors des interactions)

Risques:

  • Si les dépendances sont mal définies, peut ne jamais se déclencher
  • Peut introduire des bugs subtiles si les conditions de chargement changent
  • Peut affecter d'autres fonctionnalités qui dépendent des collaborateurs

Mitigation:

  • Tests approfondis avec différents scénarios de collaboration
  • Code review attentif de la logique de dépendances
  • Tests de performance pour comparer avant/après
  • Avoir un plan de rollback prêt

Dépendances: Story #1 (triggerRefresh) doit être complétée AVANT cette story


🟢 MEDIUM (Compléter si le temps le permet)

Story #9: Tests de Validation Automatisés

Priorité: P2 (Medium)
Estimation: 2 heures Complexité: Moyenne

En tant que: QA Engineer / Développeur Frontend Je veux: Créer des tests Playwright automatisés pour valider que tous les bugs corrigés dans les stories précédentes sont bien résolus, et s'assurer qu'il n'y a pas de régression.

Afin de: Avoir une couverture de tests complète pour empêcher les régressions et documenter que les corrections de bugs fonctionnent comme prévu.

Critères d'acceptation:

  • Test #1: Validation de triggerRefresh() - Le re-render fonctionne sans router.refresh()
  • Test #2: Validation de note-card.tsx - Les actions (pin, archive, color, etc.) n'appellent pas router.refresh()
  • Test #3: Validation de page.tsx - Batch organization et auto-labels n'appellent pas router.refresh()
  • Test #4: Validation de notebooks-context.tsx - Les actions sur notebooks n'appellent pas window.location.reload()
  • Test #5: Validation mobile - Drag désactivé sur mobile, scroll fonctionne
  • Test #6: Validation UI - Les boutons de fermeture sont distincts et clairs
  • Test #7: Validation performance - useDebounce recrée le timer uniquement quand nécessaire
  • Tous les tests Playwright passent sans modification
  • Aucune régression détectée

Contexte technique:

  • Nouveau fichier: keep-notes/tests/validation/bug-fixes-validation.spec.ts
  • Framework de test: Playwright
  • Scénarios à tester: Tous les scénarios manuels mentionnés dans les stories précédentes

Tests à créer:

// Test #1: TriggerRefresh fonctionne
test('triggerRefresh force re-render without page reload', async ({ page }) => {
  // ... test complet
});

// Test #2: NoteCard actions sans refresh
test('NoteCard toggle pin does not call router.refresh', async ({ page }) => {
  // ... test complet
});

// Test #3: Notebook actions sans reload
test('Notebook update does not call window.location.reload', async ({ page }) => {
  // ... test complet
});

// Test #4: Mobile drag désactivé
test('Mobile drag is disabled', async ({ page }) => {
  // ... test complet
});

// Test #5: Boutons distincts
test('Close buttons have distinct icons and colors', async ({ page }) => {
  // ... test complet
});

// Test #6: useDebounce optimisé
test('useDebounce only recreates timer when value changes', async ({ page }) => {
  // ... test complet
});

Points techniques:

  • Utiliser les sélecteurs Playwright précis pour cibler les composants
  • Vérifier les appels réseau (aucun router.refresh, aucun window.location.reload)
  • Vérifier les re-renders (count, timing)
  • Utiliser les fixtures de test Playwright si nécessaire
  • Capturer les screenshots en cas d'échec

Effets secondaires:

  • Couverture de tests automatisée pour empêcher les régressions
  • Documentation vivante du comportement attendu
  • Confiance que les corrections fonctionnent correctement
  • Facilite les futures modifications

Risques:

  • Les tests peuvent être fragiles si l'UI change
  • Maintenance supplémentaire des tests
  • Temps d'exécution des tests peut être long

Mitigation:

  • Utiliser des sélecteurs robustes qui ne dépendent pas trop de la structure DOM
  • Marquer les tests comme "flaky" si nécessaire
  • Exécuter les tests en parallèle pour réduire le temps
  • Documentation claire de maintenance des tests

Dépendances: Toutes les stories précédentes doivent être complétées


🗂 Dépendances Entre Stories

Ordre Suggéré

  1. Story #1 (triggerRefresh) - DOIT ÊTRE PREMIÈRE

    • Raison: C'est la cause racine de tous les problèmes de refresh
    • Blocking: Stories #2, #3, #4, #7, #8
    • Si échoue, toutes les autres corrections risquent d'échouer aussi
  2. Story #7 (useDebounce)

    • Raison: Améliore la performance globale
    • Recommandée avant: Stories #2, #8 (les deux ont des problèmes de re-renders)
  3. Story #2 (Supprimer router.refresh dans note-card.tsx)

    • Dépendance: Story #1
    • Recommandée avant: Story #8 (même composant)
  4. Story #3 (Supprimer router.refresh dans page.tsx)

    • Dépendance: Story #1
    • Indépendante: Peut être faite en parallèle avec Story #2
  5. Story #4 (Remplacer window.location.reload dans notebooks-context.tsx)

    • Dépendance: Story #1
    • Indépendante: Peut être faite en parallèle avec Stories #2, #3
  6. Story #5 (Mobile drag)

    • Indépendante: Peut être faite en parallèle avec d'autres
    • Option: Remplacer Muuri par @dnd-kit/core (plus complexe)
  7. Story #6 (Doublons boutons)

    • Indépendante: Peut être faite en parallèle
    • Recommandée avant: Tests d'accessibilité
  8. Story #8 (useEffect mal géré)

    • Dépendance: Story #1
    • Recommandée après: Stories #2, #3, #4, #6
  9. Story #9 (Tests de validation)

    • DOIT ÊTRE DERNIÈRE
    • Dépend de: TOUTES les stories précédentes
    • Validation de toutes les corrections

Graph de Dépendances Visuel

Story #1 (triggerRefresh)
    ├─> Story #2 (note-card router.refresh)
    ├─> Story #3 (page.tsx router.refresh)
    ├─> Story #4 (notebooks window.location.reload)
    ├─> Story #7 (useDebounce)
    └─> Story #8 (note-card useEffect)
    └─> Story #9 (Tests validation)
    
Story #5 (Mobile drag)
    └─> Story #9 (Tests validation)

Story #6 (Doublons boutons)
    └─> Story #9 (Tests validation)

🎬 Acceptation Criteria (Critères d'Acceptation Globaux)

Pour Toutes les Stories

  • Fonctionnalité: Le bug est corrigé et la fonctionnalité fonctionne comme prévu
  • Tests: Les tests (manuels ou automatisés) passent
  • Performance: Aucune dégradation de performance mesurable
  • UX: L'expérience utilisateur est améliorée
  • Code: Le code est propre, bien documenté et suit les conventions
  • Régression: Aucune régression détectée dans d'autres fonctionnalités
  • Accessibilité: Les corrections n'affectent pas l'accessibilité

Critères Spécifiques par Type de Story

Stories de Bug Fix

  • Le bug ne se produit plus dans les scénarios testés
  • Les tests Playwright existants passent
  • Aucun effet secondaire indésirable
  • La correction est pérenne et maintenable

Stories de Performance

  • Métrique de performance améliorée (ex: temps de render réduit)
  • Moins de re-renders mesurables
  • Utilisation CPU réduite

🚨 Risques et Blockers

Risques Identifiés

  1. Risque de Régression

    • Description: Les corrections de bugs risquent d'en casser d'autres parties du code
    • Probabilité: Moyenne
    • Impact: Élevé - pourrait affecter d'autres fonctionnalités
    • Mitigation: Tests approfondis, code review, déploiement progressif
  2. Risque de Complexité Non Anticipée

    • Description: Story #5 (Mobile drag) peut être plus complexe que prévu si remplacement de Muuri par @dnd-kit/core
    • Probabilité: Faible
    • Impact: Moyen - peut prendre plus de temps
    • Mitigation: Commencer avec l'option simple (désactiver drag), option complexe en suivant
  3. Risque de Performance

    • Description: Story #5 (Remplacement Muuri) peut avoir des implications de performance non testées
    • Probabilité: Faible
    • Impact: Faible à Moyen
    • Mitigation: Tests de performance avant/après
  4. Risque de Dépendances

    • Description: Story #1 bloque plusieurs autres stories (#2, #3, #4, #7, #8)
    • Probabilité: Moyenne
    • Impact: Élevé - ne peut pas faire progresser sur ces bugs
    • Mitigation: Prioriser Story #1, avoir des alternatives prêtes, tests parallèles où possible
  5. Risque UX

    • Description: Story #6 (Nouvelles icônes) peut surprendre les utilisateurs
    • Probabilité: Faible
    • Impact: Faible à Moyen - confusion temporaire
    • Mitigation: Documentation claire, tooltips explicites, période d'adaptation

Blockers Actuels

  • Aucun blocker identifié
  • Tous les fichiers sont accessibles et modifiables
  • L'environnement de développement est opérationnel

📅 Timeline Estimée

Par Story

Story Estimation Notes
Story #1: triggerRefresh() 2 heures Critique - faire en priorité absolue
Story #7: useDebounce 30 minutes Simple - peut être fait rapidement
Story #2: note-card router.refresh 1 heure Simple - dépend de Story #1
Story #3: page.tsx router.refresh 30 minutes Simple - dépend de Story #1
Story #4: notebooks window.location.reload 1 heure Simple - dépend de Story #1
Story #6: Doublons boutons 1 heure Simple - indépendante
Story #8: note-card useEffect 30 minutes Simple - dépend de Story #1
Story #5: Mobile drag 2 heures Complexe - option simple recommandée
Story #9: Tests validation 2 heures Critique - doit être fait à la fin

Total estimé: 10 heures (2 semaines à 50% de capacité)


🎯 Objectifs de Démo (Pour Sprint Review)

Si vous voulez présenter le travail à la fin du Sprint:

  1. Vidéos Avant/Après:

    • Montrer les bugs avant correction (flash, perte de scroll)
    • Montrer les corrections (UI instantanée, sans flash)
    • Comparer les performances (temps de render)
  2. Tests Automatisés:

    • Capturer l'exécution des tests Playwright
    • Montrer que tous les tests passent
    • Mettre en évidence les améliorations mesurées
  3. Métriques de Succès:

    • Nombre de bugs corrigés: 8/8
    • Tests créés: 3 tests automatisés
    • Tests Playwright existants: 3 tests passent
    • Amélioration de performance mesurable: % réduction de re-renders
  4. User Stories:

    • "Avant la correction, chaque action causait un flash d'écran de 2 secondes"
    • "Maintenant, l'UI se met à jour instantanément"
    • "Sur mobile, le drag ne fonctionne pas, mais le scroll est fluide"

📝 Notes pour l'Équipe

Bonnes Pratiques

  1. Committer fréquemment

    • Une story terminée = un commit
    • Message de commit clair et descriptif
    • Branche par story ou par type de correction
  2. Tester localement avant de push

    • npm run dev pour vérifier manuellement
    • Tester les scénarios limites
    • Vérifier la console pour les erreurs
  3. Utiliser les tests Playwright existants

    • npx playwright test keep-notes/tests/bug-*.spec.ts
    • Tous les tests doivent passer après chaque correction
  4. Code Review

    • Faire review du code des pairs avant de merge
    • Vérifier les conventions de style
    • S'assurer que le code est documenté
  5. Documenter les changements

    • Mettre à jour le README du projet
    • Ajouter des notes dans les fichiers modifiés si nécessaire
    • Mettre à jour le changelog

Outils et Ressources

  • Documentation: Voir _bmad-output/BUG-ANALYSIS-REPORT.md
  • Plan de correction: Voir _bmad-output/PLAN-DE-CORRECTION-DES-BUGS.md
  • Tests existants: keep-notes/tests/bug-*.spec.ts

Communication

  • Signaler les blocages ou risques immédiatement
  • Demander de l'aide si une story est plus complexe que prévu
  • Partager les leçons apprises à la fin du Sprint

🎉 Critères de Succès du Sprint

Le Sprint sera considéré comme succès si:

Must-Have (Doit être complété)

  • Toutes les 8 stories sont complétées
  • Story #1 (triggerRefresh) est fonctionnelle
  • Tous les tests Playwright existants passent
  • Tests de validation automatisés sont créés et passent
  • Aucun bug critique rémanent
  • Aucun effet secondaire majeur

Nice-to-Have (Souhaitable)

  • Story #5 (Mobile drag) est corrigée (même si simple)
  • La performance globale est améliorée
  • L'UX est significativement meilleure
  • La documentation est à jour
  • Le code est propre et maintenable

Performance Targets

  • Réduction d'au moins 50% des re-renders inutiles
  • Aucun flash d'écran lors des actions
  • Aucune perte de position de scroll
  • Temps de réponse UI < 100ms

🔄 Status Actuel

🟡 En préparation - Sprint créé, prêt à commencer

Prochaine étape:

  1. Révision du Sprint avec l'équipe ou les parties prenantes
  2. Affectation des stories aux développeurs
  3. Création des branches git si nécessaire
  4. Commencement avec Story #1 (triggerRefresh)

Estimation de début: Immédiatement après validation


Créé le 2026-01-15 pour corriger les 8 bugs critiques/high identifiés lors de l'analyse exhaustive du codebase.