14 KiB
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)
- TriggerRefresh ne fonctionne pas - Confirmé par tests
- Refresh excessif router.refresh() - Cause flash et perte de scroll
- Reload complet window.location.reload() - Force page reload complète
- Mobile drag non fonctionnel - Muuri incompatible avec touch
🟡 HIGH (Corriger rapidement)
- Doublons boutons fermeture - Confusion UI
- 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.tsligne 168-174bug-note-move-refresh.spec.tsligne 105-109, 123-136- Les tests confirment: "triggerRefresh() didn't work!"
Cause Racine:
// 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:
// 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:
// 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:
// 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.tsxkeep-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:
// 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:
// 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:
// 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:
// 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):
// 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:
// 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:
// 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é
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é
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:
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:
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.tskeep-notes/components/note-card.tsx
ORDRE DES CORRECTIONS RECOMMANDÉ
Phase 1: Critique (Doit être corrigé immédiatement)
- Corriger triggerRefresh() - Bug #1
- Supprimer router.refresh() - Bug #2
- Supprimer window.location.reload() - Bug #3
- Corriger mobile drag - Bug #4
Phase 2: High (Corriger rapidement)
- Doublons boutons - Bug #5
- Performance re-renders - Bug #6
Phase 3: Validation (Tests)
- Rejouer tous les tests pour confirmer les corrections
- Tests manuels pour vérifier chaque bug corrigé
INSTRUCTIONS D'EXÉCUTION
Étape 1: Sauvegarder l'état actuel
git add .
git commit -m "Backup avant correction des bugs"
Étape 2: Corriger Bug #1 (triggerRefresh)
Fichier: keep-notes/context/NoteRefreshContext.tsx
- Remplacer l'implémentation actuelle
- Tester avec les tests Playwright existants
- Confirmer que triggerRefresh fonctionne
Étape 3: Corriger Bug #2 (router.refresh excessif)
Fichiers:
keep-notes/components/note-card.tsxkeep-notes/app/(main)/page.tsx
- Supprimer tous les appels à
router.refresh() - Laisser l'état optimiste gérer l'UI
- Tester que les actions se font sans refresh
Étape 4: Corriger Bug #3 (window.location.reload())
Fichier: keep-notes/context/notebooks-context.tsx
- Remplacer tous les
window.location.reload()partriggerRefresh() - 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):
- Désactiver drag sur mobile
- Améliorer la détection mobile
Option B (Recommandée):
- Remplacer Muuri par @dnd-kit/core
- Implémenter un drag handler mobile-friendly
Étape 6: Corriger Bug #5 (Doublons boutons)
Fichier: keep-notes/components/note-card.tsx
- Remplacer les icônes X par des icônes spécifiques
- Ajouter des tooltips explicites
- Utiliser des couleurs sémantiques
Étape 7: Corriger Bug #6 (Performance)
Fichiers:
keep-notes/hooks/use-debounce.tskeep-notes/components/note-card.tsx
- Corriger useDebounce avec useRef
- Corriger les dépendances useEffect
- Utiliser useMemo pour éviter les recréations
Étape 8: Validation
# 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:
- Animations fluides lors des actions
- Feedback visuel (loading states, success toasts)
- Indicateurs de progression pour les actions longues
- Undo/Redo pour les actions destructrices
Améliorations Mobile:
- Touch gestures pour swipe actions
- Pull-to-refresh pour synchronisation
- Double-tap pour quick actions
- Responsive design amélioré
Améliorations Techniques:
- Optimisation des re-renders (React.memo, useMemo, useCallback)
- Lazy loading des composants lourds
- Virtual scrolling pour les grandes listes de notes
- 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.