Keep/_bmad-output/PLAN-DE-CORRECTION-DES-BUGS.md

521 lines
14 KiB
Markdown

# Plan Complet de Correction des Bugs - Memento/Keep
**Date:** 2026-01-15
**Version:** 1.0
**Statut:** Prêt pour correction
---
## RÉSUMÉ EXÉCUTIF
**Bugs Critiques Confirmés par Tests Playwright:** 3
**Bugs Critiques Identifiés par Analyse de Code:** 5
**Bugs High Priorité:** 0
**Total Bugs à Corriger:** 8
---
## PRIORITÉS DE CORRECTION
### 🔴 CRITIQUE (Doit être corrigé immédiatement)
1. **TriggerRefresh ne fonctionne pas** - Confirmé par tests
2. **Refresh excessif router.refresh()** - Cause flash et perte de scroll
3. **Reload complet window.location.reload()** - Force page reload complète
4. **Mobile drag non fonctionnel** - Muuri incompatible avec touch
### 🟡 HIGH (Corriger rapidement)
5. **Doublons boutons fermeture** - Confusion UI
6. **Performance re-renders** - useEffect mal gérés
---
## DÉTAIL DES CORRECTIONS PAR BUG
### 🔴 Bug #1: triggerRefresh() Non Fonctionnel
**Confirmation par tests:**
- `bug-move-direct.spec.ts` ligne 168-174
- `bug-note-move-refresh.spec.ts` ligne 105-109, 123-136
- Les tests confirment: "triggerRefresh() didn't work!"
**Cause Racine:**
```typescript
// keep-notes/context/NoteRefreshContext.tsx
const NoteRefreshContext = createContext<NoteRefreshContextType | undefined>(undefined)
export function NoteRefreshProvider({ children }: { children: React.ReactNode }) {
const [refreshKey, setRefreshKey] = useState(0)
const triggerRefresh = useCallback(() => {
setRefreshKey(prev => prev + 1) // ❌ INCORRECT! Ne déclenche pas de re-render global
}, [refreshKey]) // ❌ DÉPENDANCE INCORRECT!
return (
<NoteRefreshContext.Provider value={{ refreshKey, triggerRefresh }}>
{children}
</NoteRefreshContext.Provider>
)
}
```
**Correction:**
```typescript
// CORRECT: utiliser useRef pour éviter les cycles de dépendances
export function NoteRefreshProvider({ children }: { children: React.ReactNode }) {
const [refreshKey, setRefreshKey] = useState(0)
const refreshKeyRef = useRef(refreshKey)
const triggerRefresh = useCallback(() => {
const newKey = refreshKeyRef.current + 1
refreshKeyRef.current = newKey
setRefreshKey(newKey) // ✅ Déclenche le re-render
}, []) // ✅ Pas de dépendances
return (
<NoteRefreshContext.Provider value={{ refreshKey, triggerRefresh }}>
{children}
</NoteRefreshContext.Provider>
)
}
```
**Fichiers à modifier:**
- `keep-notes/context/NoteRefreshContext.tsx`
---
### 🔴 Bug #2: router.refresh() Excessif
**Fichiers affectés:**
- `keep-notes/components/note-card.tsx` (lignes 200, 208, 216, 224, 235)
- `keep-notes/app/(main)/page.tsx` (lignes 171, 185)
**Cause Racine:**
```typescript
// Chaque action déclenche un refresh complet de la page
const handleTogglePin = async () => {
startTransition(async () => {
addOptimisticNote({ isPinned: !note.isPinned })
await togglePin(note.id, !note.isPinned)
router.refresh() // ❌ FORCE RELOAD COMPLET
})
}
```
**Correction:**
```typescript
// CORRECT: Supprimer router.refresh() et laisser le state React se mettre à jour
const handleTogglePin = async () => {
addOptimisticNote({ isPinned: !note.isPinned })
await togglePin(note.id, !note.isPinned)
// ✅ Pas de refresh - l'état optimiste gère l'UI
}
```
**Fichiers à modifier:**
- `keep-notes/components/note-card.tsx`
- `keep-notes/app/(main)/page.tsx`
---
### 🔴 Bug #3: window.location.reload() dans notebooks-context.tsx
**Fichier affecté:**
- `keep-notes/context/notebooks-context.tsx` (lignes 141, 154, 169)
**Cause Racine:**
```typescript
// Création, mise à jour, suppression de notebook déclenche un reload complet
const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => {
const response = await fetch(`/api/notebooks/${notebookId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to update notebook')
}
// Recharger les notebooks après mise à jour
window.location.reload() // ❌ FORCE RELOAD COMPLET
}
```
**Correction:**
```typescript
// CORRECT: Utiliser triggerRefresh() à la place
const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => {
const response = await fetch(`/api/notebooks/${notebookId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) {
throw new Error('Failed to update notebook')
}
// ✅ Rafraîchissement optimiste du state React
triggerRefresh()
}
```
**Fichiers à modifier:**
- `keep-notes/context/notebooks-context.tsx`
---
### 🔴 Bug #4: Mobile Drag Non Fonctionnel
**Fichier affecté:**
- `keep-notes/components/masonry-grid.tsx` (lignes 160-185)
**Cause Racine:**
```typescript
// Détection mobile insuffisante
const isMobile = window.matchMedia('(pointer: coarse)').matches;
// Configuration Muuri avec dragHandle qui conflict avec touch events
const layoutOptions = {
dragEnabled: true,
dragHandle: '.muuri-drag-handle', // ❌ Problématique sur mobile
dragContainer: document.body,
dragStartPredicate: {
distance: 10,
delay: 0,
},
// ...
}
```
**Correction:**
```typescript
// CORRECT 1: Désactiver drag sur mobile
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
// ...
}
```
**Correction Alternative (Recommandée):**
```typescript
// CORRECT 2: Utiliser @dnd-kit/core qui supporte nativement le touch
// Remplacer Muuri par @dnd-kit/core
import { DndContext, DndProvider } from '@dnd-kit/core'
// Ceci résoudra définitivement les problèmes de mobile drag
```
**Fichiers à modifier:**
- `keep-notes/components/masonry-grid.tsx`
- Éventuellement: `package.json` (si changement de librairie)
---
### 🟡 Bug #5: Doublons Boutons Fermeture
**Fichier affecté:**
- `keep-notes/components/note-card.tsx` (lignes 351-357, 411-413)
**Cause Racine:**
```typescript
// Bouton "Leave Share" avec icône X
<Button onClick={handleLeaveShare}>
<X className="h-3 w-3 mr-1" />
Leave Share
</Button>
// Bouton "Remove Fused Badge" avec icône X
<button onClick={handleRemoveFusedBadge}>
<X className="h-2.5 w-2.5" />
Remove
</button>
```
**Correction:**
```typescript
// CORRECT: Utiliser des icônes différentes pour chaque action
import { X, Trash2, FolderOpen, LogOut } from 'lucide-react'
// Bouton "Remove Fused Badge" - utiliser icône différente
<button onClick={handleRemoveFusedBadge}>
<Trash2 className="h-2.5 w-2.5 text-purple-600" />
<span className="ml-2">Remove</span>
</button>
// Bouton "Leave Share" - utiliser icône différente
<Button onClick={handleLeaveShare}>
<LogOut className="h-3 w-3 mr-1 text-blue-600" />
Leave Share
</Button>
```
**Fichiers à modifier:**
- `keep-notes/components/note-card.tsx`
---
### 🟡 Bug #6: Re-renders Inutiles (Performance)
**Fichiers affectés:**
- `keep-notes/components/note-card.tsx` (lignes 151-154, 162-180)
- `keep-notes/hooks/use-debounce.ts` (lignes 6-14)
**Cause Racine #1: useDebounce mal implémenté**
```typescript
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 render
// ❌ Même si value ne change pas, l'effet se recrée
// ❌ Provoque des cascades de re-renders
return debouncedValue
}
```
**Cause Racine #2: useEffect mal géré**
```typescript
useEffect(() => {
const loadCollaborators = async () => {
// ...
}
loadCollaborators()
}, [note.id, note.userId]) // ❌ Se déclenche trop souvent
// ❌ Même si les collaborateurs n'ont pas changé
// ❌ Provoque des cascades de re-renders
```
**Correction #1:**
```typescript
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]) // ✅ Recrée uniquement quand value change
return debouncedValue
}
```
**Correction #2:**
```typescript
useEffect(() => {
const loadCollaborators = async () => {
// ...
}
loadCollaborators()
}, [note.id, note.userId, isOwner, isSharedNote]) // ✅ Dépendances complètes
// ✅ Se déclenche uniquement quand une de ces valeurs change vraiment
```
**Fichiers à modifier:**
- `keep-notes/hooks/use-debounce.ts`
- `keep-notes/components/note-card.tsx`
---
## ORDRE DES CORRECTIONS RECOMMANDÉ
### Phase 1: Critique (Doit être corrigé immédiatement)
1. **Corriger triggerRefresh()** - Bug #1
2. **Supprimer router.refresh()** - Bug #2
3. **Supprimer window.location.reload()** - Bug #3
4. **Corriger mobile drag** - Bug #4
### Phase 2: High (Corriger rapidement)
5. **Doublons boutons** - Bug #5
6. **Performance re-renders** - Bug #6
### Phase 3: Validation (Tests)
7. **Rejouer tous les tests** pour confirmer les corrections
8. **Tests manuels** pour vérifier chaque bug corrigé
---
## INSTRUCTIONS D'EXÉCUTION
### Étape 1: Sauvegarder l'état actuel
```bash
git add .
git commit -m "Backup avant correction des bugs"
```
### Étape 2: Corriger Bug #1 (triggerRefresh)
**Fichier:** `keep-notes/context/NoteRefreshContext.tsx`
1. Remplacer l'implémentation actuelle
2. Tester avec les tests Playwright existants
3. Confirmer que triggerRefresh fonctionne
### Étape 3: Corriger Bug #2 (router.refresh excessif)
**Fichiers:**
- `keep-notes/components/note-card.tsx`
- `keep-notes/app/(main)/page.tsx`
1. Supprimer tous les appels à `router.refresh()`
2. Laisser l'état optimiste gérer l'UI
3. Tester que les actions se font sans refresh
### Étape 4: Corriger Bug #3 (window.location.reload())
**Fichier:** `keep-notes/context/notebooks-context.tsx`
1. Remplacer tous les `window.location.reload()` par `triggerRefresh()`
2. Tester que les actions sur notebooks se font sans reload complet
### Étape 5: Corriger Bug #4 (Mobile drag)
**Fichier:** `keep-notes/components/masonry-grid.tsx`
**Option A (Simple):**
1. Désactiver drag sur mobile
2. Améliorer la détection mobile
**Option B (Recommandée):**
1. Remplacer Muuri par @dnd-kit/core
2. Implémenter un drag handler mobile-friendly
### Étape 6: Corriger Bug #5 (Doublons boutons)
**Fichier:** `keep-notes/components/note-card.tsx`
1. Remplacer les icônes X par des icônes spécifiques
2. Ajouter des tooltips explicites
3. Utiliser des couleurs sémantiques
### Étape 7: Corriger Bug #6 (Performance)
**Fichiers:**
- `keep-notes/hooks/use-debounce.ts`
- `keep-notes/components/note-card.tsx`
1. Corriger useDebounce avec useRef
2. Corriger les dépendances useEffect
3. Utiliser useMemo pour éviter les recréations
### Étape 8: Validation
```bash
# Rejouer tous les tests
npx playwright test tests/bug-*.spec.ts
# Tests manuels
1. Ouvrir l'application
2. Créer une note
3. Toggle pin, archive, color
4. Vérifier: pas de flash, pas de perte de scroll
5. Déplacer la note vers un notebook
6. Vérifier: la note disparaît de la page principale
7. Sur mobile: tester le drag and drop
8. Vérifier: pas de bugs de scroll sur mobile
```
---
## CRITÈRES DE VALIDATION
### Pour chaque bug corrigé:
- ✅ Le bug ne se produit plus
- ✅ Aucun effet secondaire indésirable
- ✅ Les tests Playwright passent
- ✅ Performance améliorée (mesurable)
- ✅ Code propre et bien documenté
### Pour l'application entière:
- ✅ Aucun flash d'écran inutile
- ✅ Aucune perte de scroll
- ✅ Drag and drop fonctionne sur desktop
- ✅ Drag and drop fonctionne/ou est désactivé proprement sur mobile
- ✅ Toutes les actions UI sont optimistes
- ✅ L'UI reste réactive sans re-renders inutiles
---
## SUITE APRÈS CORRECTIONS
### Améliorations UX:
1. **Animations fluides** lors des actions
2. **Feedback visuel** (loading states, success toasts)
3. **Indicateurs de progression** pour les actions longues
4. **Undo/Redo** pour les actions destructrices
### Améliorations Mobile:
1. **Touch gestures** pour swipe actions
2. **Pull-to-refresh** pour synchronisation
3. **Double-tap** pour quick actions
4. **Responsive design** amélioré
### Améliorations Techniques:
1. **Optimisation des re-renders** (React.memo, useMemo, useCallback)
2. **Lazy loading** des composants lourds
3. **Virtual scrolling** pour les grandes listes de notes
4. **Code splitting** pour réduire la taille du bundle
---
## RISQUES ET ATTENTIONS
### Risques:
- **Casser des fonctionnalités existantes** en modifiant le state
- **Introduction de nouveaux bugs** pendant la correction
- **Régression** de bugs déjà corrigés
### Atténuations:
- Faire les corrections une par une avec validation
- Commencer par les bugs critiques les plus simples
- Tester minutieusement après chaque correction
- Avoir un plan de rollback prêt
---
## DOCUMENTS DE RÉFÉRENCE
- Analyse complète: `_bmad-output/BUG-ANALYSIS-REPORT.md`
- Tests Playwright: `keep-notes/tests/bug-*.spec.ts`
- Documentation existante: `docs/` (à mettre à jour après corrections)
---
## TIMELINE ESTIMÉE
**Phase 1 (Critique):** 2-4 heures
**Phase 2 (High):** 1-2 heures
**Phase 3 (Validation):** 1-2 heures
**Total estimé:** 4-8 heures
**Note:** Les corrections sont relativement simples car les bugs sont bien localisés et documentés.
---
## STATUT
**État:** ✅ Prêt pour l'exécution
**Fichiers à modifier:** 6 fichiers principaux
**Bugs à corriger:** 8
**Tests à rejouer:** 3 tests Playwright
---
*Ce plan doit être exécuté après validation par le développeur.*