# 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):**
```typescript
const triggerRefresh = useCallback(() => {
setRefreshKey(prev => prev + 1) // ❌ INCORRECT
}, [refreshKey]) // ❌ Cycle de dépendances
```
- **Approche corrigée:**
```typescript
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):**
```typescript
const handleTogglePin = async () => {
startTransition(async () => {
addOptimisticNote({ isPinned: !note.isPinned })
await togglePin(note.id, !note.isPinned)
router.refresh() // ❌ FORCE RELOAD COMPLET
})
}
```
- **Approche corrigée:**
```typescript
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):**
```typescript
onNotesMoved={() => {
router.refresh() // ❌ REDONDANT - déjà optimiste
}}
onLabelsCreated={() => {
router.refresh() // ❌ REDONDANT - déjà optimiste
}}
```
- **Approche corrigée:**
```typescript
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):**
```typescript
const updateNotebook = async (notebookId: string, data: UpdateNotebookInput) => {
// ... API call ...
window.location.reload() // ❌ FORCE RELOAD COMPLET
}
```
- **Approche corrigée:**
```typescript
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):**
```typescript
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):**
```typescript
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):**
```typescript
// Bouton "Remove Fused Badge" avec icône X
// Bouton "Leave Share" avec icône X
```
- **Approche corrigée:**
```typescript
import { Trash2, LogOut, X } from 'lucide-react'
// Bouton "Remove Fused Badge" - icône de poubelle
// Bouton "Leave Share" - icône de déconnexion
```
**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):**
```typescript
export function useDebounce(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(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:**
```typescript
export function useDebounce(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value)
const timerRef = useRef() // ✅ 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):**
```typescript
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:**
```typescript
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:**
```typescript
// 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.*