WIP: Améliorations UX et corrections de bugs avant création des épiques
This commit is contained in:
456
_bmad-output/BUG-ANALYSIS-REPORT.md
Normal file
456
_bmad-output/BUG-ANALYSIS-REPORT.md
Normal file
@@ -0,0 +1,456 @@
|
||||
# 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
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:**
|
||||
```typescript
|
||||
// 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:**
|
||||
```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 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:**
|
||||
```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])
|
||||
|
||||
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:**
|
||||
```typescript
|
||||
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:**
|
||||
```typescript
|
||||
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.*
|
||||
Reference in New Issue
Block a user