fix: improve note interactions and markdown LaTeX support
## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
This commit is contained in:
43
_bmad-output/planning-artifacts/bmm-workflow-status.yaml
Normal file
43
_bmad-output/planning-artifacts/bmm-workflow-status.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
# Workflow Status Template
|
||||
|
||||
# This tracks progress through BMM methodology Analysis, Planning, and Solutioning phases.
|
||||
# Implementation phase is tracked separately in sprint-status.yaml
|
||||
|
||||
# STATUS DEFINITIONS:
|
||||
# ==================
|
||||
# Initial Status (before completion):
|
||||
# - required: Must be completed to progress
|
||||
# - optional: Can be completed but not required
|
||||
# - recommended: Strongly suggested but not required
|
||||
# - conditional: Required only if certain conditions met (e.g., if_has_ui)
|
||||
#
|
||||
# Completion Status:
|
||||
# - {file-path}: File created/found (e.g., "docs/product-brief.md")
|
||||
# - skipped: Optional/conditional workflow that was skipped
|
||||
|
||||
generated: "2026-01-09"
|
||||
project: "Memento"
|
||||
project_type: "intermediate"
|
||||
selected_track: "bmad-method"
|
||||
field_type: "brownfield"
|
||||
workflow_path: "_bmad/bmm/workflows/workflow-status/paths/method-brownfield.yaml"
|
||||
workflow_status:
|
||||
# Phase 0: Documentation (Prerequisite for brownfield)
|
||||
document-project: docs/index.md
|
||||
|
||||
# Phase 1: Analysis (Optional)
|
||||
brainstorm-project: optional
|
||||
research: optional
|
||||
|
||||
# Phase 2: Planning
|
||||
prd: _bmad-output/planning-artifacts/prd.md
|
||||
create-ux-design: conditional
|
||||
|
||||
# Phase 3: Solutioning
|
||||
create-architecture: required
|
||||
create-epics-and-stories: _bmad-output/planning-artifacts/epics.md
|
||||
test-design: optional
|
||||
implementation-readiness: _bmad-output/planning-artifacts/implementation-readiness-report-2026-01-09.md
|
||||
|
||||
# Phase 4: Implementation
|
||||
sprint-planning: required
|
||||
337
_bmad-output/planning-artifacts/epic-collaborators.md
Normal file
337
_bmad-output/planning-artifacts/epic-collaborators.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Epic: Implémentation Complète de la Fonctionnalité Collaborateurs
|
||||
|
||||
**Epic ID:** EPIC-COLLABORATORS
|
||||
**Status:** Draft
|
||||
**Priority:** High
|
||||
**Created:** 2026-01-09
|
||||
**Owner:** Development Team
|
||||
**Type:** Feature Implementation
|
||||
|
||||
---
|
||||
|
||||
## Description du Problème
|
||||
|
||||
### Symptôme
|
||||
Le bouton "Collaborator" (icône UserPlus) est **grisé et désactivé** dans note-input, et ne fonctionne pas non plus sur les notes existantes.
|
||||
|
||||
### Contexte
|
||||
- L'utilisateur veut pouvoir ajouter des collaborateurs à ses notes
|
||||
- Actuellement: bouton grisé dans note-input, fonctionnalité non testée sur notes existantes
|
||||
- Les tests de la collaborator dialog n'ont pas été faits
|
||||
|
||||
---
|
||||
|
||||
## User Stories
|
||||
|
||||
### Story 1: Sélectionner des Collaborateurs lors de la Création de Note
|
||||
|
||||
**ID:** COLLAB-1
|
||||
**Title:** Permettre d'ajouter des collaborateurs pendant la création d'une note
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 3h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** pouvoir sélectionner des collaborateurs AVANT de créer ma note
|
||||
**Afin que:** la note soit partagée dès sa création avec les bonnes personnes
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une nouvelle note en cours de création (note-input)
|
||||
2. **When** je clique sur le bouton collaborateur (UserPlus)
|
||||
3. **Then** une boîte de dialogue s'ouvre
|
||||
4. **And** je peux chercher des utilisateurs par email
|
||||
5. **And** je peux ajouter plusieurs collaborateurs
|
||||
6. **Given** que j'ai sélectionné des collaborateurs
|
||||
7. **When** je crée la note (bouton "Add")
|
||||
8. **Then** la note est créée avec les collaborateurs déjà assignés
|
||||
9. **And** les collaborateurs reçoivent une notification (si implémenté)
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-input.tsx` - Ajouter état `collaborators: string[]`
|
||||
- `keep-notes/components/note-input.tsx` - Rendre le bouton collaborateur actif
|
||||
- `keep-notes/components/note-input.tsx` - Intégrer CollaboratorDialog
|
||||
- `keep-notes/app/actions/notes.ts` - Modifier `createNote` pour accepter `sharedWith`
|
||||
|
||||
**Implémentation:**
|
||||
```typescript
|
||||
// Dans note-input.tsx
|
||||
const [collaborators, setCollaborators] = useState<string[]>([])
|
||||
const [showCollaboratorDialog, setShowCollaboratorDialog] = useState(false)
|
||||
|
||||
// Dans handleSubmit
|
||||
await createNote({
|
||||
// ... autres champs
|
||||
sharedWith: collaborators.length > 0 ? collaborators : undefined,
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Story 2: Vérifier le Fonctionnement sur Notes Existantes
|
||||
|
||||
**ID:** COLLAB-2
|
||||
**Title:** Tester et corriger l'ajout de collaborateurs sur les notes existantes
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 2h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** pouvoir partager une note existante avec d'autres utilisateurs
|
||||
**Afin que:** nous puissions collaborer sur une note déjà créée
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note existante affichée
|
||||
2. **When** je clique sur les trois points (⋮) → "Share with collaborators"
|
||||
3. **Then** la boîte de dialogue CollaboratorDialog s'ouvre
|
||||
4. **And** je vois la liste des collaborateurs actuels
|
||||
5. **Given** la boîte de dialogue ouverte
|
||||
6. **When** j'entre un email et clique "Invite"
|
||||
7. **Then** l'utilisateur est ajouté aux collaborateurs
|
||||
8. **And** il apparaît dans la liste avec son nom/avatar
|
||||
9. **And** je peux le retirer avec le bouton X
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-card.tsx` - Déjà intégré, à tester
|
||||
- `keep-notes/components/collaborator-dialog.tsx` - Déjà créé, à tester
|
||||
- `keep-notes/app/actions/notes.ts` - Actions déjà créées, à tester
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test E2E: Ouvrir une note → Menu → Share → Ajouter collaborateur
|
||||
- Test E2E: Vérifier que le collaborateur apparaît dans la liste
|
||||
- Test E2E: Vérifier qu'on peut retirer un collaborateur
|
||||
|
||||
---
|
||||
|
||||
### Story 3: Afficher les Collaborateurs sur la Note Card
|
||||
|
||||
**ID:** COLLAB-3
|
||||
**Title:** Afficher les avatars des collaborateurs sur les notes partagées
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 2h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** voir quels collaborateurs ont accès à une note
|
||||
**Afin que:** je sache qui peut voir et éditer mes notes
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note qui a des collaborateurs
|
||||
2. **When** la note est affichée
|
||||
3. **Then** je vois les avatars des collaborateurs en bas de la note
|
||||
4. **And** les avatars sont petits (20-24px) et disposés horizontalement
|
||||
5. **Given** que je survole un avatar
|
||||
6. **When** je passe la souris dessus
|
||||
7. **Then** le nom complet de l'utilisateur apparaît en tooltip
|
||||
8. **And** un badge "Owner" distingue le propriétaire
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-card.tsx` - Afficher les avatars
|
||||
- `keep-notes/components/note-card.tsx` - Récupérer `sharedWith` depuis la note
|
||||
|
||||
**Implémentation:**
|
||||
```typescript
|
||||
// Dans note-card.tsx, après les labels:
|
||||
{note.sharedWith && note.sharedWith.length > 0 && (
|
||||
<div className="flex items-center gap-1 mt-2">
|
||||
{note.sharedWith.map(userId => (
|
||||
<CollaboratorAvatar key={userId} userId={userId} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Story 4: Voir les Notes Partagées avec Moi
|
||||
|
||||
**ID:** COLLAB-4
|
||||
**Title:** Afficher une liste de notes que d'autres utilisateurs ont partagées avec moi
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 3h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** voir les notes que d'autres personnes ont partagées avec moi
|
||||
**Afin que:** je puisse accéder aux notes collaboratives
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** que des utilisateurs m'ont partagé des notes
|
||||
2. **When** j'accède à la page principale
|
||||
3. **Then** les notes partagées apparaissent mélangées avec mes notes
|
||||
4. **And** un badge "Shared by X" indique le propriétaire
|
||||
5. **Given** une note partagée
|
||||
6. **When** je la regarde
|
||||
7. **Then** je peux voir qui m'a partagé cette note
|
||||
8. **And** l'avatar du propriétaire est visible
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - `getAllNotes()` existe déjà
|
||||
- `keep-notes/app/(main)/page.tsx` - Utiliser `getAllNotes()` au lieu de `getNotes()`
|
||||
|
||||
**Note:** L'action `getAllNotes()` existe déjà et combine notes propres + notes partagées !
|
||||
|
||||
---
|
||||
|
||||
### Story 5: Gérer les Permissions - Lecture vs Écriture
|
||||
|
||||
**ID:** COLLAB-5
|
||||
**Title:** Implémenter des permissions de lecture et d'édition
|
||||
**Priority:** Could Have (Future)
|
||||
**Estimation:** 4h
|
||||
|
||||
**En tant que:** propriétaire d'une note
|
||||
**Je veux:** choisir si les collaborateurs peuvent seulement voir ou aussi éditer
|
||||
**Afin que:** je puisse contrôler qui peut modifier mes notes
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note avec des collaborateurs
|
||||
2. **When** j'ajoute un collaborateur
|
||||
3. **Then** je peux choisir le permission: "Can view" ou "Can edit"
|
||||
4. **Given** un collaborateur avec "Can view"
|
||||
5. **When** il ouvre la note
|
||||
6. **Then** il peut voir le contenu mais PAS modifier
|
||||
7. **Given** un collaborateur avec "Can edit"
|
||||
8. **When** il modifie la note
|
||||
9. **Then** les modifications sont sauvegardées
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/prisma/schema.prisma` - Ajouter table `NoteCollaborator` avec permissions
|
||||
- `keep-notes/app/actions/notes.ts` - Vérifier les permissions avant update
|
||||
- `keep-notes/components/collaborator-dialog.tsx` - Ajouter sélecteur de permission
|
||||
|
||||
**Note:** Story à implémenter plus tard, complexité élevée.
|
||||
|
||||
---
|
||||
|
||||
### Story 6: Notification quand On Partage une Note
|
||||
|
||||
**ID:** COLLAB-6
|
||||
**Title:** Envoyer une notification (email/IN-APP) quand on est ajouté comme collaborateur
|
||||
**Priority:** Could Have
|
||||
**Estimation:** 3h
|
||||
|
||||
**En tant que:** collaborateur
|
||||
**Je veux:** recevoir une notification quand quelqu'un partage une note avec moi
|
||||
**Afin que:** je sois au courant que j'ai accès à de nouvelles notes
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** qu'un utilisateur partage une note avec moi
|
||||
2. **When** la note est partagée
|
||||
3. **Then** je reçois une notification email
|
||||
4. **And** l'email contient: le titre de la note, le propriétaire, un lien
|
||||
5. **Given** que je suis connecté à l'application
|
||||
6. **When** on partage une note avec moi
|
||||
7. **Then** une notification in-app apparaît
|
||||
8. **And** je peux cliquer pour voir la note
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Envoyer email après `addCollaborator()`
|
||||
- `keep-notes/lib/mail.ts` - Template email pour partage
|
||||
- `keep-notes/components/notifications.tsx` - Système de notifications in-app (nouveau)
|
||||
|
||||
---
|
||||
|
||||
### Story 7: Filtrer/Afficher Seulement les Notes Partagées
|
||||
|
||||
**ID:** COLLAB-7
|
||||
**Title:** Ajouter une vue "Shared with me" pour voir uniquement les notes collaboratives
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 2h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** pouvoir filtrer pour voir uniquement les notes partagées avec moi
|
||||
**Afin que:** je puisse me concentrer sur la collaboration
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** que j'ai des notes partagées
|
||||
2. **When** je clique sur un filtre "Shared with me"
|
||||
3. **Then** seules les notes partagées par d'autres s'affichent
|
||||
4. **And** mes notes personnelles sont masquées
|
||||
5. **Given** le filtre actif
|
||||
6. **When** je le désactive
|
||||
7. **Then** toutes les notes réapparaissent
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/sidebar.tsx` - Ajouter "Shared with me"
|
||||
- `keep-notes/app/actions/notes.ts` - Créer `getSharedNotesOnly()`
|
||||
|
||||
---
|
||||
|
||||
### Story 8: Tests E2E Complets pour Collaborateurs
|
||||
|
||||
**ID:** COLLAB-8
|
||||
**Title:** Créer une suite de tests E2E pour valider le système de collaboration
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 4h
|
||||
|
||||
**En tant que:** QA / Développeur
|
||||
**Je veux:** des tests automatisés pour valider toutes les fonctionnalités de collaboration
|
||||
**Afin que:** nous puissions détecter les régressions
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. Tests pour ajouter collaborateur lors de la création
|
||||
2. Tests pour ajouter collaborateur sur note existante
|
||||
3. Tests pour retirer un collaborateur
|
||||
4. Tests pour voir les notes partagées
|
||||
5. Tests pour vérifier que les non-collaborateurs ne peuvent pas accéder
|
||||
6. Tests pour les permissions (si implémenté)
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/tests/collaboration.spec.ts` - Nouveau fichier
|
||||
|
||||
---
|
||||
|
||||
## Ordre d'Implémentation
|
||||
|
||||
**Sprint 1** (Fonctionnalités de base - AUJOURD'HUI):
|
||||
1. ✅ **COLLAB-1:** Permettre la sélection lors de la création (Must Have)
|
||||
2. ✅ **COLLAB-2:** Tester et corriger sur notes existantes (Must Have)
|
||||
|
||||
**Sprint 2** (Améliorations UX):
|
||||
3. **COLLAB-3:** Afficher les avatars sur les notes
|
||||
4. **COLLAB-4:** Afficher les notes partagées (déjà fait avec `getAllNotes()`)
|
||||
|
||||
**Sprint 3** (Futures):
|
||||
5. **COLLAB-5:** Permissions lecture/écriture
|
||||
6. **COLLAB-6:** Notifications
|
||||
7. **COLLAB-7:** Filtre "Shared with me"
|
||||
8. **COLLAB-8:** Tests E2E
|
||||
|
||||
---
|
||||
|
||||
## Fichers à Modifier
|
||||
|
||||
### Critiques
|
||||
1. `keep-notes/components/note-input.tsx` - Activer le bouton et gérer les collaborateurs
|
||||
2. `keep-notes/components/note-card.tsx` - Tester la dialog
|
||||
3. `keep-notes/components/collaborator-dialog.tsx` - Tester le composant
|
||||
|
||||
### Secondaires
|
||||
4. `keep-notes/app/actions/notes.ts` - `createNote` pour accepter `sharedWith`
|
||||
5. `keep-notes/lib/types.ts` - Assurer que Note a bien `sharedWith`
|
||||
|
||||
---
|
||||
|
||||
## Tests de Validation
|
||||
|
||||
### Scénario 1: Création avec Collaborateurs
|
||||
```
|
||||
1. Cliquer sur "Take a note..."
|
||||
2. Taper du contenu
|
||||
3. Cliquer sur le bouton collaborateur (UserPlus)
|
||||
4. Entrer un email existant
|
||||
5. Cliquer "Invite"
|
||||
6. Vérifier que l'utilisateur apparaît dans la liste
|
||||
7. Cliquer "Add" pour créer la note
|
||||
8. Vérifier que la note est créée avec le collaborateur
|
||||
```
|
||||
|
||||
### Scénario 2: Note Existante
|
||||
```
|
||||
1. Ouvrir une note existante
|
||||
2. Cliquer sur (⋮) → "Share with collaborators"
|
||||
3. Ajouter un collaborateur
|
||||
4. Vérifier qu'il peut voir la note
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2026-01-09
|
||||
**Priority:** High - Bouton grisé à corriger URGENTEMENT
|
||||
691
_bmad-output/planning-artifacts/epic-ghost-tags-fix.md
Normal file
691
_bmad-output/planning-artifacts/epic-ghost-tags-fix.md
Normal file
@@ -0,0 +1,691 @@
|
||||
# Epic: Correction Bug Ghost Tags - Fermeture Intempestive
|
||||
|
||||
**Epic ID:** EPIC-GHOST-TAGS-FIX
|
||||
**Status:** Draft
|
||||
**Priority:** High (Bug critique)
|
||||
**Created:** 2026-01-09
|
||||
**Owner:** Development Team
|
||||
**Type:** Bug Fix
|
||||
|
||||
---
|
||||
|
||||
## Description du Bug
|
||||
|
||||
### Symptôme
|
||||
Lorsqu'un utilisateur clique sur un **tag fantôme** (ghost tag) suggéré par l'IA pour l'ajouter à sa note:
|
||||
1. ❌ **La fenêtre d'édition de la note se ferme immédiatement et de manière inattendue**
|
||||
2. ❌ **Un toast de confirmation apparaît en haut à droite**
|
||||
3. ❌ **L'utilisateur perd son contexte d'édition**
|
||||
|
||||
### Conditions de Reproduction
|
||||
|
||||
1. Créer une nouvelle note ou éditer une note existante
|
||||
2. Ajouter du contenu texte qui déclenche l'analyse IA
|
||||
3. Attendre que les suggestions de tags IA apparaissent (tags fantômes)
|
||||
4. Cliquer sur un tag fantôme pour l'ajouter
|
||||
5. **Résultat attendu:** Le tag est ajouté, la note reste ouverte
|
||||
6. **Résultat actuel (BUG):** La note se ferme, toast apparaît
|
||||
|
||||
### Impact Utilisateur
|
||||
|
||||
- **Frustration élevée:** L'utilisateur perd sa place dans l'édition
|
||||
- **Interruption du workflow:** Obligation de rouvrir la note pour continuer
|
||||
- **Perte de confiance:** Les fonctionnalités IA deviennent agaçantes
|
||||
- **Contourner le bug:** Les utilisateurs n'utilisent plus les tags suggérés
|
||||
|
||||
---
|
||||
|
||||
## Analyse des Causes Racines
|
||||
|
||||
Après analyse du code dans:
|
||||
- `keep-notes/components/ghost-tags.tsx` (lignes 56-84)
|
||||
- `keep-notes/components/note-input.tsx` (lignes 94-112)
|
||||
- `keep-notes/components/note-editor.tsx` (lignes 77-95)
|
||||
|
||||
### Causes Identifiées
|
||||
|
||||
1. **Propagation d'événements:** Le clic sur le bouton du tag fantôme pourrait propager à un élément parent qui ferme la note
|
||||
2. **Appel asynchrone `addLabel()`:** L'appel API pour créer le label pourrait déclencher un rafraîchissement
|
||||
3. **Pas de prévention du comportement par défaut:** Le formulaire pourrait se soumettre implicitement
|
||||
4. **Problème de focus:** Le clic pourrait déclencher une perte de focus qui ferme la note
|
||||
5. **Toast trop intrusif:** Le toast de confirmation apparaît mais ne devrait pas interrompre
|
||||
|
||||
### Code Problématique
|
||||
|
||||
Dans `ghost-tags.tsx` lignes 56-68:
|
||||
```typescript
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onSelectTag(suggestion.tag);
|
||||
}}
|
||||
className={...}
|
||||
>
|
||||
```
|
||||
|
||||
Le `e.preventDefault()` et `e.stopPropagation()` sont présents, mais **ne suffisent pas** à empêcher la fermeture de la note.
|
||||
|
||||
---
|
||||
|
||||
## User Stories
|
||||
|
||||
### Story 1: Prévenir la Fermeture de la Note lors du Clic sur Tag Fantôme
|
||||
|
||||
**ID:** GHOST-TAGS-1
|
||||
**Title:** Empêcher la fermeture intempestive de la note lors de l'ajout d'un tag fantôme
|
||||
**Priority:** Must Have (Bug critique)
|
||||
**Estimation:** 2h
|
||||
**Type:** Bug Fix
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** cliquer sur un tag fantôme suggéré par l'IA sans que ma note se ferme
|
||||
**Afin que:** je puisse continuer à éditer ma note sans interruption
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note en cours d'édition avec des tags fantômes affichés
|
||||
2. **When** je clique sur un tag fantôme pour l'ajouter
|
||||
3. **Then** le tag est ajouté à la note
|
||||
4. **And** la note reste OUVERTE et le focus reste sur la zone d'édition
|
||||
5. **And** un toast de confirmation apparaît en haut à droite (non-intrusif)
|
||||
6. **Given** que je clique sur le tag fantôme
|
||||
7. **When** le tag est ajouté
|
||||
8. **Then** le tag fantôme disparaît de la liste des suggestions
|
||||
9. **And** il apparaît dans la liste des tags sélectionnés avec une coche de validation
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/ghost-tags.tsx` - Améliorer la prévention de propagation
|
||||
- `keep-notes/components/note-input.tsx` - Vérifier qu'aucun événement parent ne ferme
|
||||
- `keep-notes/components/note-editor.tsx` - Vérifier qu'aucun événement parent ne ferme
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- **Test E2E Playwright:**
|
||||
```
|
||||
1. Créer une nouvelle note
|
||||
2. Taper du contenu texte qui déclenche l'IA
|
||||
3. Attendre l'apparition des tags fantômes
|
||||
4. Cliquer sur un tag fantôme
|
||||
5. Vérifier que la note est toujours ouverte
|
||||
6. Vérifier que le tag est ajouté
|
||||
7. Vérifier que le toast apparaît
|
||||
```
|
||||
|
||||
**Risques:**
|
||||
- Risque de casser d'autres fonctionnalités de clic
|
||||
- Nécessite de tester scénario par scénario
|
||||
|
||||
---
|
||||
|
||||
### Story 2: Gestion Asynchrone de l'Ajout de Tag sans Interrompre l'UI
|
||||
|
||||
**ID:** GHOST-TAGS-2
|
||||
**Title:** Rendre l'ajout de tag non-bloquant et transparent pour l'utilisateur
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 3h
|
||||
**Type:** UX Improvement
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** que l'ajout d'un tag suggéré soit instantané et ne bloque pas mon travail
|
||||
**Afin que:** je puisse continuer à éditer sans attendre
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** un tag fantôme sur lequel je clique
|
||||
2. **When** je clique
|
||||
3. **Then** le tag est **immédiatement** ajouté à l'état local (optimistic update)
|
||||
4. **And** l'appel API `addLabel()` se fait en arrière-plan (async)
|
||||
5. **And** si l'appel API échoue, le tag est retiré et une erreur est affichée
|
||||
6. **Given** que l'appel API est en cours
|
||||
7. **When** je continue à éditer
|
||||
8. **Then** je ne vois aucun indicateur de chargement bloquant
|
||||
9. **And** le tag apparaît comme "ajouté" avec un badge visuel
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-input.tsx` - Modifier `handleSelectGhostTag` pour optimistic update
|
||||
- `keep-notes/components/note-editor.tsx` - Modifier `handleSelectGhostTag` pour optimistic update
|
||||
- `keep-notes/context/LabelContext.tsx` - Ajouter gestion d'erreur optimistic
|
||||
|
||||
**Implémentation Proposée:**
|
||||
```typescript
|
||||
const handleSelectGhostTag = async (tag: string) => {
|
||||
// Optimistic update immédiat
|
||||
setSelectedLabels(prev => [...prev, tag])
|
||||
|
||||
try {
|
||||
// Appel API en arrière-plan
|
||||
const globalExists = globalLabels.some(l => l.toLowerCase() === tag.toLowerCase())
|
||||
if (!globalExists) {
|
||||
await addLabel(tag)
|
||||
}
|
||||
addToast(`Tag "${tag}" added`, 'success')
|
||||
} catch (error) {
|
||||
// Rollback en cas d'erreur
|
||||
setSelectedLabels(prev => prev.filter(l => l !== tag))
|
||||
addToast(`Failed to add tag "${tag}"`, 'error')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test unitaire du optimistic update
|
||||
- Test E2E: vérifier que le tag apparaît immédiatement
|
||||
- Test E2E: simuler une erreur API et vérifier le rollback
|
||||
|
||||
**Risques:**
|
||||
- Si l'API échoue souvent, les utilisateurs pourraient avoir des tags inconsistants
|
||||
- Nécessite une bonne gestion des erreurs
|
||||
|
||||
---
|
||||
|
||||
### Story 3: Améliorer la Feedback Visuel des Tags Fantômes
|
||||
|
||||
**ID:** GHOST-TAGS-3
|
||||
**Title:** Rendre les tags fantômes plus visiblement interactifs et éviter les clics accidentels
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 2h
|
||||
**Type:** UX Improvement
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** voir clairement que les tags fantômes sont cliquables
|
||||
**Afin que:** je comprenne comment interagir avec eux sans erreur
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** des tags fantômes affichés
|
||||
2. **When** je survole le tag avec la souris
|
||||
3. **Then** un changement visuel clair apparaît (curseur pointer, surbrillance, animation)
|
||||
4. **And** une tooltip explicite apparaît: "Click to add this tag"
|
||||
5. **Given** que je clique sur le tag
|
||||
6. **When** le clic est en cours
|
||||
7. **Then** un indicateur de chargement subtil apparaît (spinner ou animation)
|
||||
8. **And** le tag devient non-cliquable pendant le traitement
|
||||
9. **Given** le tag ajouté
|
||||
10. **When** il est confirmé
|
||||
11. **Then** une coche verte ou un badge "✓ Added" apparaît
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/ghost-tags.tsx` - Ajouter états hover, loading, success
|
||||
- `keep-notes/components/ghost-tags.tsx` - Améliorer les tooltips et indicateurs visuels
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test visuel: vérifier les états hover
|
||||
- Test E2E: survoler et vérifier la tooltip
|
||||
- Test E2E: cliquer et vérifier l'indicateur de chargement
|
||||
|
||||
**Risques:**
|
||||
- Trop d'animations pourraient être distrayants
|
||||
- Surcharge visuelle si trop d'indicateurs
|
||||
|
||||
---
|
||||
|
||||
### Story 4: Supprimer ou Rendre le Toast Optionnel
|
||||
|
||||
**ID:** GHOST-TAGS-4
|
||||
**Title:** Ne pas afficher de toast intrusif lors de l'ajout d'un tag fantôme
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 1h
|
||||
**Type:** UX Polish
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** ne pas être interrompu par un toast quand j'ajoute un tag suggéré
|
||||
**Afin que:** je puisse me concentrer sur mon édition
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** que j'ajoute un tag fantôme
|
||||
2. **When** le tag est ajouté avec succès
|
||||
3. **Then** aucun toast n'apparaît
|
||||
4. **And** le tag simplement apparaît dans la liste des tags sélectionnés
|
||||
5. **Given** que l'ajout du tag échoue
|
||||
6. **When** une erreur se produit
|
||||
7. **Then** un toast d'erreur apparaît (pour les erreurs uniquement)
|
||||
8. **And** le tag n'est pas ajouté à la liste
|
||||
|
||||
**Alternative proposée:**
|
||||
- Remplacer le toast par un indicateur visuel SUBTIL sur le tag lui-même
|
||||
- Ou: ajouter une petite animation de "succès" sur le tag ajouté
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-input.tsx` - Retirer le `addToast` de succès
|
||||
- `keep-notes/components/note-editor.tsx` - Retirer le `addToast` de succès
|
||||
- Garder le toast uniquement pour les erreurs
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test E2E: ajouter un tag et vérifier qu'aucun toast n'apparaît
|
||||
- Test E2E: simuler une erreur et vérifier qu'un toast d'erreur apparaît
|
||||
|
||||
**Risques:**
|
||||
- Les utilisateurs pourraient ne pas savoir si le tag a été ajouté
|
||||
- Nécessite un autre indicateur visuel de succès
|
||||
|
||||
---
|
||||
|
||||
### Story 5: Prévenir les Fermetures Accidentelles de Note
|
||||
|
||||
**ID:** GHOST-TAGS-5
|
||||
**Title:** Ajouter une protection contre la fermeture accidentelle lors de l'interaction avec les tags
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 2h
|
||||
**Type:** Bug Fix
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** que ma note ne se ferme pas accidentellement quand j'interagis avec les tags
|
||||
**Afin que:** je ne perde pas mon travail
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note ouverte en cours d'édition
|
||||
2. **When** j'interagis avec n'importe quel élément de l'UI (tags, boutons, etc.)
|
||||
3. **Then** la note ne se ferme PAS sauf si je clique explicitement sur:
|
||||
- Le bouton "Close" / "X"
|
||||
- Le bouton "Add" (après création)
|
||||
- La touche Escape
|
||||
4. **Given** un clic sur un tag fantôme
|
||||
5. **When** le clic se produit
|
||||
6. **Then** l'événement est complètement isolé et ne propage JAMAIS à un gestionnaire de fermeture
|
||||
7. **And** un `e.preventDefault()` supplémentaire est ajouté sur tous les boutons interactifs dans la note
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/components/note-input.tsx` - Vérifier tous les gestionnaires d'événements
|
||||
- `keep-notes/components/note-editor.tsx` - Vérifier tous les gestionnaires d'événements
|
||||
- `keep-notes/components/ghost-tags.tsx` - Renforcer la prévention de propagation
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test E2E complet: parcourir tous les éléments interactifs et vérifier que la note reste ouverte
|
||||
- Test régression: s'assurer que les boutons de fermeture fonctionnent toujours
|
||||
|
||||
**Risques:**
|
||||
- Pourrait casser d'autres fonctionnalités de clic
|
||||
- Nécessite une revue complète de tous les événements
|
||||
|
||||
---
|
||||
|
||||
### Story 6: Mode "Silencieux" pour les Tags Fantômes
|
||||
|
||||
**ID:** GHOST-TAGS-6
|
||||
**Title:** Ajouter une option pour désactiver les toasts de succès pour les tags
|
||||
**Priority:** Could Have
|
||||
**Estimation:** 2h
|
||||
**Type:** Feature Enhancement
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** pouvoir choisir de ne pas voir les toasts quand j'ajoute des tags
|
||||
**Afin que:** je ne sois pas interrompu dans mon workflow
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** un paramètre utilisateur "Show toast for tag actions"
|
||||
2. **When** ce paramètre est désactivé
|
||||
3. **Then** aucun toast n'apparaît quand j'ajoute un tag (succès ou erreur)
|
||||
4. **And** un indicateur visuel subtil remplace le toast
|
||||
5. **Given** le paramètre activé (défaut)
|
||||
6. **When** j'ajoute un tag
|
||||
7. **Then** le comportement actuel est conservé (toast visible)
|
||||
8. **And** ce paramètre est configurable dans les paramètres utilisateur
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/lib/config.ts` - Ajouter `SHOW_TAG_TOASTS` dans SystemConfig
|
||||
- `keep-notes/components/note-input.tsx` - Conditionner les toasts sur ce paramètre
|
||||
- `keep-notes/components/note-editor.tsx` - Conditionner les toasts sur ce paramètre
|
||||
- `keep-notes/app/(main)/settings/page.tsx` - Ajouter l'option dans les paramètres
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test E2E: désactiver l'option et vérifier qu'aucun toast n'apparaît
|
||||
- Test E2E: activer l'option et vérifier que les toasts apparaissent
|
||||
- Test unitaire: vérifier la logique de condition
|
||||
|
||||
**Risques:**
|
||||
- Complexité supplémentaire dans la configuration
|
||||
- Pourrait créer de la confusion si mal documenté
|
||||
|
||||
---
|
||||
|
||||
### Story 7: Tests E2E pour le Workflow Complet des Tags Fantômes
|
||||
|
||||
**ID:** GHOST-TAGS-7
|
||||
**Title:** Créer une suite de tests E2E pour valider le workflow des tags fantômes
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 4h
|
||||
**Type:** QA
|
||||
|
||||
**En tant que:** QA / Développeur
|
||||
**Je veux:** des tests E2E automatisés pour valider que le bug ne revient pas
|
||||
**Afin que:** nous ayons confiance dans les corrections apportées
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une suite de tests E2E pour les tags fantômes
|
||||
2. **When** les tests sont exécutés
|
||||
3. **Then** ils couvrent tous les scénarios suivants:
|
||||
- Création de note + ajout de tag fantôme
|
||||
- Édition de note existante + ajout de tag fantôme
|
||||
- Ajout multiple de tags fantômes
|
||||
- Rejet de tags fantômes
|
||||
- Échec de l'API lors de l'ajout
|
||||
- Interaction avec d'autres éléments UI simultanément
|
||||
4. **And** chaque scénario a des assertions claires
|
||||
5. **And** les tests sont documentés pour maintenance future
|
||||
|
||||
**Tests à Créer:**
|
||||
|
||||
```typescript
|
||||
// keep-notes/tests/ghost-tags-workflow.spec.ts
|
||||
|
||||
test('should add ghost tag without closing note (note-input)', async ({ page }) => {
|
||||
// 1. Navigate to app
|
||||
// 2. Click on note input
|
||||
// 3. Type content that triggers AI
|
||||
// 4. Wait for ghost tags
|
||||
// 5. Click on ghost tag
|
||||
// 6. Assert: note is still open
|
||||
// 7. Assert: tag is in selected labels
|
||||
// 8. Assert: no toast interruption (or minimal)
|
||||
})
|
||||
|
||||
test('should add ghost tag without closing note (note-editor)', async ({ page }) => {
|
||||
// Similar for note-editor
|
||||
})
|
||||
|
||||
test('should handle multiple ghost tag clicks', async ({ page }) => {
|
||||
// Test adding multiple ghost tags in sequence
|
||||
})
|
||||
|
||||
test('should dismiss ghost tag without closing note', async ({ page }) => {
|
||||
// Test clicking X to dismiss
|
||||
})
|
||||
|
||||
test('should handle API error gracefully when adding ghost tag', async ({ page }) => {
|
||||
// Mock API error
|
||||
// Verify rollback happens
|
||||
// Verify error toast appears
|
||||
})
|
||||
```
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/tests/ghost-tags-workflow.spec.ts` - Nouveau fichier de tests
|
||||
- `keep-notes/playwright.config.ts` - Configuration si nécessaire
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Exécuter les tests après correction
|
||||
- S'assurer qu'ils passent tous
|
||||
- Les intégrer au CI/CD
|
||||
|
||||
**Risques:**
|
||||
- Les tests E2E peuvent être "flaky" (instables)
|
||||
- Nécessite une maintenance continue
|
||||
|
||||
---
|
||||
|
||||
### Story 8: Documentation et Guide d'Utilisation des Tags Fantômes
|
||||
|
||||
**ID:** GHOST-TAGS-8
|
||||
**Title:** Documenter le comportement attendu des tags fantômes pour éviter les futures régressions
|
||||
**Priority:** Could Have
|
||||
**Estimation:** 2h
|
||||
**Type:** Documentation
|
||||
|
||||
**En tant que:** développeur
|
||||
**Je veux:** une documentation claire sur le fonctionnement des tags fantômes
|
||||
**Afin que:** les futurs développements respectent ce comportement
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une documentation du système de tags fantômes
|
||||
2. **When** un développeur lit la documentation
|
||||
3. **Then** il comprend:
|
||||
- Comment les tags fantômes sont générés par l'IA
|
||||
- Comment l'utilisateur interagit avec eux
|
||||
- Ce qui se passe quand un tag est ajouté (optimistic update)
|
||||
- Comment les erreurs sont gérées
|
||||
- Pourquoi la note ne doit pas se fermer
|
||||
4. **And** la documentation inclut des diagrammes de séquence
|
||||
5. **And** elle est située dans `docs/ghost-tags-behavior.md`
|
||||
6. **And** elle est référencée dans le README principal
|
||||
|
||||
**Contenu de la Documentation:**
|
||||
|
||||
```markdown
|
||||
# Ghost Tags Behavior
|
||||
|
||||
## Overview
|
||||
Les tags fantômes sont des suggestions de tags générées par l'IA...
|
||||
|
||||
## User Flow
|
||||
1. User types content → AI analyzes
|
||||
2. Ghost tags appear with sparkle icon
|
||||
3. User can:
|
||||
- Click tag body to ADD
|
||||
- Click X to DISMISS
|
||||
4. When added:
|
||||
- Optimistic update (immediate)
|
||||
- API call in background
|
||||
- Visual feedback (checkmark)
|
||||
- NOTE STAYS OPEN
|
||||
|
||||
## Technical Implementation
|
||||
- Event propagation is prevented
|
||||
- Optimistic updates used
|
||||
- Toast only for errors
|
||||
|
||||
## Critical Rules
|
||||
- NEVER close note on tag interaction
|
||||
- ALWAYS use optimistic updates
|
||||
- ALWAYS prevent event propagation
|
||||
```
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `docs/ghost-tags-behavior.md` - Nouveau fichier de documentation
|
||||
- `README.md` - Ajouter une référence
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Revue de la documentation par l'équipe
|
||||
- Vérifier que tout est clair
|
||||
|
||||
**Risques:**
|
||||
- Documentation peut devenir obsolète
|
||||
- Nécessite d'être maintenue à jour
|
||||
|
||||
---
|
||||
|
||||
## Dépendances Entre Stories
|
||||
|
||||
```
|
||||
GHOST-TAGS-1 (Prévenir fermeture) ← CRITIQUE
|
||||
↓
|
||||
GHOST-TAGS-2 (Optimistic update) ← CRITIQUE
|
||||
↓
|
||||
GHOST-TAGS-3 (Feedback visuel) ← AMÉLIORATION UX
|
||||
↓
|
||||
GHOST-TAGS-4 (Retirer toast) ← POLISH
|
||||
↓
|
||||
GHOST-TAGS-5 (Protection fermeture) ← SÉCURITÉ
|
||||
↓
|
||||
GHOST-TAGS-7 (Tests E2E) ← VALIDATION
|
||||
↓
|
||||
GHOST-TAGS-6 (Mode silencieux) ← OPTIONNEL
|
||||
↓
|
||||
GHOST-TAGS-8 (Documentation) ← CONNAISSANCE
|
||||
```
|
||||
|
||||
**Ordre Recommandé:**
|
||||
|
||||
**Sprint 1** (Correction du bug critique - 1-2 jours):
|
||||
1. GHOST-TAGS-1: Prévenir la fermeture (URGENT)
|
||||
2. GHOST-TAGS-2: Optimistic update (URGENT)
|
||||
3. GHOST-TAGS-5: Protection fermeture accidentelle (IMPORTANT)
|
||||
|
||||
**Sprint 2** (Améliorations UX - 1 jour):
|
||||
4. GHOST-TAGS-3: Feedback visuel
|
||||
5. GHOST-TAGS-4: Retirer toast
|
||||
|
||||
**Sprint 3** (Qualité & Documentation - 1 jour):
|
||||
6. GHOST-TAGS-7: Tests E2E
|
||||
7. GHOST-TAGS-8: Documentation
|
||||
|
||||
**Optionnel** (Plus tard):
|
||||
8. GHOST-TAGS-6: Mode silencieux
|
||||
|
||||
---
|
||||
|
||||
## Métriques de Succès
|
||||
|
||||
### Avant/Après
|
||||
|
||||
| Métrique | Avant (Bug) | Après (Corrigé) | Comment Mesurer |
|
||||
|----------|-------------|----------------|-----------------|
|
||||
| Fermeture intempestive | 100% (bug systématique) | 0% | Tests E2E Story 7 |
|
||||
| Toasts intrusifs | Oui (gênant) | Non (ou optionnel) | Tests E2E |
|
||||
| Tags ajoutés avec succès | Variable | 100% (optimistic) | Tests E2E |
|
||||
| Satisfaction utilisateur | 1/5 (très frustrant) | 4/5+ | Feedback post-fix |
|
||||
|
||||
### Objectifs
|
||||
|
||||
- ✅ **0 fermeture accidentelle** lors de l'ajout d'un tag fantôme
|
||||
- ⚡ **Ajout instantané** du tag (< 100ms ressenti)
|
||||
- 🎯 **Pas d'interruption** du workflow d'édition
|
||||
- 🔒 **Fiabilité 100%** sur l'ajout de tag
|
||||
|
||||
---
|
||||
|
||||
## Solutions Techniques Proposées
|
||||
|
||||
### Solution 1: Renforcer la Prévention de Propagation
|
||||
|
||||
Dans `ghost-tags.tsx`, ajouter:
|
||||
|
||||
```typescript
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation(); // ← AJOUTER
|
||||
onSelectTag(suggestion.tag);
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
// ← AJOUTER: prévenir dès le mouseDown
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className={...}
|
||||
>
|
||||
```
|
||||
|
||||
### Solution 2: Isoler le Conteneur Parent
|
||||
|
||||
Dans `note-input.tsx` et `note-editor.tsx`, vérifier que le Card n'a pas de onClick:
|
||||
|
||||
```typescript
|
||||
<Card
|
||||
onClick={(e) => {
|
||||
// ← S'ASSURER qu'il n'y a PAS de onClick ici
|
||||
// ou qu'il prévient bien la propagation
|
||||
}}
|
||||
>
|
||||
```
|
||||
|
||||
### Solution 3: Optimistic Update Pattern
|
||||
|
||||
```typescript
|
||||
const handleSelectGhostTag = async (tag: string) => {
|
||||
// 1. Optimistic update IMMÉDIAT
|
||||
setSelectedLabels(prev => [...prev, tag])
|
||||
|
||||
// 2. Appel API en arrière-plan
|
||||
try {
|
||||
const globalExists = globalLabels.some(l => l.toLowerCase() === tag.toLowerCase())
|
||||
if (!globalExists) {
|
||||
await addLabel(tag)
|
||||
}
|
||||
// PAS de toast ici pour éviter l'interruption
|
||||
} catch (error) {
|
||||
// Rollback en cas d'erreur seulement
|
||||
setSelectedLabels(prev => prev.filter(l => l !== tag))
|
||||
addToast(`Failed to add tag: ${error.message}`, 'error')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tests de Validation
|
||||
|
||||
### Scénario de Test Principal
|
||||
|
||||
```typescript
|
||||
// Test manuel à exécuter après correction:
|
||||
|
||||
1. Ouvrir l'application Memento
|
||||
2. Cliquer sur la zone "Take a note..."
|
||||
3. Taper: "How to implement authentication in Next.js"
|
||||
4. Attendre 2-3 secondes (apparition des tags fantômes)
|
||||
5. Cliquer sur un tag fantôme (ex: "nextjs", "auth")
|
||||
6. ✅ VÉRIFIER: La note reste OUVERTE
|
||||
7. ✅ VÉRIFIER: Le tag apparaît dans les tags sélectionnés
|
||||
8. ✅ VÉRIFIER: Pas de toast intrusif (ou discret)
|
||||
9. ✅ VÉRIFIER: On peut continuer à éditer
|
||||
10. Cliquer sur "Add" pour créer la note
|
||||
11. ✅ VÉRIFIER: La note est créée avec le tag
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fichers Critiques pour l'Implémentation
|
||||
|
||||
Les 5 fichiers les plus importants à modifier:
|
||||
|
||||
1. **`keep-notes/components/ghost-tags.tsx`** - Composant des tags fantômes (gestion événements)
|
||||
2. **`keep-notes/components/note-input.tsx`** - Note input (handleSelectGhostTag)
|
||||
3. **`keep-notes/components/note-editor.tsx`** - Note editor (handleSelectGhostTag)
|
||||
4. **`keep-notes/context/LabelContext.tsx`** - Gestion asynchrone des labels
|
||||
5. **`keep-notes/tests/ghost-tags-workflow.spec.ts`** - Tests E2E à créer
|
||||
|
||||
---
|
||||
|
||||
## Risques et Mitigations
|
||||
|
||||
### Risques Techniques
|
||||
|
||||
| Risque | Probabilité | Impact | Mitigation |
|
||||
|--------|-------------|--------|------------|
|
||||
| La correction casse d'autres fonctionnalités de clic | Moyenne | Moyen | Tests de régression complets |
|
||||
| L'optimistic update crée des incohérences | Faible | Moyen | Gestion d'erreur avec rollback |
|
||||
| Le bug revient avec une future mise à jour | Moyenne | Élevé | Tests E2E automatisés + documentation |
|
||||
|
||||
### Risques UX
|
||||
|
||||
| Risque | Probabilité | Impact | Mitigation |
|
||||
|--------|-------------|--------|------------|
|
||||
| Retirer le toast rend l'action invisible | Moyenne | Faible | Ajouter indicateur visuel sur le tag |
|
||||
- Utilisateurs pourraient ne pas savoir si le tag a été ajouté
|
||||
- Nécessite un autre indicateur visuel de succès
|
||||
| Trop d'indicateurs visuels créent du bruit | Faible | Faible | Design minimaliste et subtil |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps Immédiats
|
||||
|
||||
1. **Corriger le bug critique (Stories 1 & 2)** - Priorité MAXIMALE
|
||||
2. **Tester manuellement** la correction sur plusieurs scénarios
|
||||
3. **Créer les tests E2E** (Story 7) pour éviter la régression
|
||||
4. **Déployer en production** dès que validé
|
||||
5. **Documenter** le comportement (Story 8)
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2026-01-09
|
||||
**Severity:** High (Bug critique UX)
|
||||
**Target Fix:** Sprint 1 (1-2 jours)
|
||||
|
||||
---
|
||||
|
||||
## Annexes
|
||||
|
||||
### A. Capture d'écran du Bug
|
||||
|
||||
(Note: Ajouter une capture d'écran montrant le problème)
|
||||
|
||||
### B. Logs Console
|
||||
|
||||
(Note: Ajouter les logs console pertinents si disponibles)
|
||||
|
||||
### C. Environnement de Test
|
||||
|
||||
- Navigateur: Chrome / Firefox / Safari
|
||||
- OS: Windows / Mac / Linux
|
||||
- Version Memento: 0.2.0
|
||||
463
_bmad-output/planning-artifacts/epic-search-improvement.md
Normal file
463
_bmad-output/planning-artifacts/epic-search-improvement.md
Normal file
@@ -0,0 +1,463 @@
|
||||
# Epic: Amélioration de la Recherche Sémantique - Version 2.0
|
||||
|
||||
**Epic ID:** EPIC-SEARCH-2.0
|
||||
**Status:** Draft
|
||||
**Priority:** High
|
||||
**Created:** 2026-01-09
|
||||
**Owner:** Development Team
|
||||
|
||||
---
|
||||
|
||||
## Description du Problème
|
||||
|
||||
L'actuel système de recherche hybride (mots-clés + sémantique) produit des résultats non pertinents et imprévisibles. Les utilisateurs se plaignent que la recherche "fait n'importe quoi" - les résultats ne correspondent pas à leurs attentes, même quand les notes contiennent les termes recherchés.
|
||||
|
||||
### Analyse des Causes Racines
|
||||
|
||||
Après analyse du code dans `keep-notes/app/actions/notes.ts` (lignes 108-212), les problèmes identifiés sont:
|
||||
|
||||
1. **Seuil de similarité cosine trop bas (0.40)**: Permet des correspondances sémantiques de très faible qualité, créant du bruit dans les résultats
|
||||
2. **RRF (Reciprocal Rank Fusion) mal configuré**: Le constant k=60 n'est pas optimal pour le petit nombre de notes typique dans Keep
|
||||
3. **Pondération fixe recherche mot-clé/sémantique**: Les poids sont hardcodés (3 pour titre, 1 pour contenu, 2 pour labels) sans adaptation au type de requête
|
||||
4. **Absence de validation des embeddings**: Pas de vérification que les embeddings sont correctement générés ou de dimensionnalité cohérente
|
||||
5. **Manque de prétraitement des requêtes**: Pas de stemming, lemmatization, ou expansion de requête
|
||||
6. **Aucune métrique de qualité**: Impossible de mesurer l'amélioration ou la dégradation des performances
|
||||
|
||||
### Impact Utilisateur
|
||||
|
||||
- **Frustration**: Perte de temps à filtrer manuellement les résultats non pertinents
|
||||
- **Perte de confiance**: Les utilisateurs désactivent ou évitent la recherche intelligente
|
||||
- **Expérience dégradée**: Contredit la promesse d'une recherche "intuitive" du PRD
|
||||
|
||||
---
|
||||
|
||||
## Objectifs Mesurables
|
||||
|
||||
1. **Améliorer la précision de recherche de 40%** (mesurée par tests automatisés)
|
||||
2. **Réduire les faux positifs sémantiques de 60%** (seuil plus strict)
|
||||
3. **Temps de réponse < 300ms** pour 1000 notes (objectif PRD existant)
|
||||
4. **Satisfaction utilisateur > 4/5** (feedback post-déploiement)
|
||||
5. **Taux de "serendipity" (résultats sémantiques sans mots-clés) entre 20-40%** (objectif PRD)
|
||||
|
||||
---
|
||||
|
||||
## User Stories
|
||||
|
||||
### Story 1: Validation et Qualité des Embeddings
|
||||
|
||||
**ID:** SEARCH-2.0-1
|
||||
**Title:** Valider la qualité des embeddings générés
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 3h
|
||||
|
||||
**En tant que:** développeur
|
||||
**Je veux:** valider que les embeddings sont correctement générés et stockés
|
||||
**Afin que:** la recherche sémantique fonctionne sur des données de qualité
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une note avec du contenu texte
|
||||
2. **When** l'embedding est généré via `provider.getEmbeddings()`
|
||||
3. **Then** le vecteur doit:
|
||||
- Avoir une dimensionnalité > 0
|
||||
- Contenir des nombres valides (pas de NaN, Infinity)
|
||||
- Avoir une norme L2 normale (entre 0.7 et 1.2)
|
||||
4. **Given** des embeddings existants en base de données
|
||||
5. **When** ils sont chargés via `parseNote()`
|
||||
6. **Then** ils doivent être correctement désérialisés et validés
|
||||
7. **And** une action admin `/api/debug/embeddings/validate` doit lister les notes problématiques
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Ajouter validation dans `parseNote()`
|
||||
- `keep-notes/lib/utils.ts` - Ajouter `validateEmbedding()` et `normalizeEmbedding()`
|
||||
- `keep-notes/app/api/admin/embeddings/validate/route.ts` - Nouveau endpoint debug
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test unitaire `validateEmbedding()` avec vecteurs valides/invalides
|
||||
- Test d'intégration création note → validation embedding
|
||||
- Test endpoint API debug
|
||||
|
||||
**Risques:**
|
||||
- Certains embeddings existants invalides nécessiteront un re-indexing
|
||||
- Performance impact de la validation à chaque chargement
|
||||
|
||||
---
|
||||
|
||||
### Story 2: Optimisation du Seuil de Similarité Sémantique
|
||||
|
||||
**ID:** SEARCH-2.0-2
|
||||
**Title:** Ajuster le seuil de similarité cosine pour éliminer le bruit
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 4h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** ne voir que des résultats sémantiquement pertinents
|
||||
**Afin que:** la recherche me fasse confiance et me fasse gagner du temps
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une requête de recherche sémantique
|
||||
2. **When** les notes sont classées par similarité cosine
|
||||
3. **Then** seules les notes avec similarité >= 0.65 sont considérées (au lieu de 0.40)
|
||||
4. **And** le seuil doit être configurable dans `SystemConfig` (`SEARCH_SEMANTIC_THRESHOLD`)
|
||||
5. **Given** une recherche avec résultats sémantiques faibles
|
||||
6. **When** le seuil est appliqué
|
||||
7. **Then** les faux positifs sont réduits d'au moins 50%
|
||||
8. **And** un test automatisé mesure la réduction des faux positifs
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Ligne 190, remplacer 0.40 par `config.SEARCH_SEMANTIC_THRESHOLD || 0.65`
|
||||
- `keep-notes/lib/config.ts` - Lire la config depuis DB
|
||||
- `keep-notes/tests/search-quality.spec.ts` - Ajouter tests de seuil
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test Playwright: recherche "coding" ne retourne PAS des notes sur "cuisine" (faux positifs)
|
||||
- Test unitaire: vérifier que les scores < seuil sont filtrés
|
||||
- Test de non-régression: s'assurer que les vrais positifs ne sont pas perdus
|
||||
|
||||
**Risques:**
|
||||
- Seuil trop élevé peut éliminer des résultats pertinents (faux négatifs)
|
||||
- Nécessite A/B testing pour trouver le seuil optimal
|
||||
- Différents modèles d'embedding peuvent nécessiter des seuils différents
|
||||
|
||||
---
|
||||
|
||||
### Story 3: Reconfiguration de l'Algorithme RRF
|
||||
|
||||
**ID:** SEARCH-2.0-3
|
||||
**Title:** Optimiser le paramètre k du Reciprocal Rank Fusion
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 3h
|
||||
|
||||
**En tant que:** système
|
||||
**Je veux:** un RRF avec un paramètre k adapté au nombre de notes typique
|
||||
**Afin que:** le ranking hybride reflète mieux la pertinence réelle
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** un RRF avec constant k
|
||||
2. **When** le nombre moyen de notes par utilisateur est < 500
|
||||
3. **Then** k doit être 20 (au lieu de 60) pour mieux pénaliser les bas rangs
|
||||
4. **And** k doit être configurable: `k = max(20, nombre_notes / 10)`
|
||||
5. **Given** deux listes de ranking (mot-clé + sémantique)
|
||||
6. **When** RRF est appliqué
|
||||
7. **Then** les résultats bien classés dans les deux listes sont fortement favorisés
|
||||
8. **And** la formule RRF est documentée dans le code
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Lignes 182-198, ajuster k et ajouter logique adaptive
|
||||
- `keep-notes/lib/utils.ts` - Ajouter `calculateRRFK(totalNotes: number): number`
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test unitaire: vérifier la formule RRF avec différents k
|
||||
- Test d'intégration: comparer rankings avec k=20 vs k=60
|
||||
- Test avec dataset de benchmark (notes + requêtes + résultats attendus)
|
||||
|
||||
**Risques:**
|
||||
- Changer k peut impacter significativement l'ordre des résultats
|
||||
- Nécessite validation utilisateur sur de vraies données
|
||||
- Peut nécessiter des ajustements itératifs
|
||||
|
||||
---
|
||||
|
||||
### Story 4: Pondération Adaptative des Scores de Recherche
|
||||
|
||||
**ID:** SEARCH-2.0-4
|
||||
**Title:** Adapter les poids mot-clé/sémantique selon le type de requête
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 6h
|
||||
|
||||
**En tant que:** utilisateur
|
||||
**Je veux:** que la recherche privilégie les mots-clés pour les termes exacts
|
||||
**Et qu'elle privilégie le sémantique pour les concepts abstraits
|
||||
**Afin que:** les résultats soient toujours pertinents
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une requête avec des guillemets (ex: `"Error 404"`)
|
||||
2. **When** la recherche est exécutée
|
||||
3. **Then** le poids mot-clé est multiplié par 2 (recherche exacte prioritaire)
|
||||
4. **Given** une requête conceptuelle (ex: "comment améliorer...")
|
||||
5. **When** la recherche est exécutée
|
||||
6. **Then** le poids sémantique est multiplié par 1.5 (concept prioritaire)
|
||||
7. **Given** une requête mixte
|
||||
8. **When** aucun pattern n'est détecté
|
||||
9. **Then** les poids par défaut sont utilisés
|
||||
10. **And** la logique de détection est documentée
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Ajouter `detectQueryType()` et ajuster les poids
|
||||
- `keep-notes/lib/types.ts` - Ajouter `QueryType: 'exact' | 'conceptual' | 'mixed'`
|
||||
- `keep-notes/lib/utils.ts` - Ajouter `detectQueryType(query: string): QueryType`
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test unitaire `detectQueryType()` avec différents patterns
|
||||
- Test d'intégration: vérifier que `"Error 404"` privilégie les mots-clés
|
||||
- Test d'intégration: vérifier que "comment cuisiner" privilégie le sémantique
|
||||
- Test Playwright: scénarios de recherche avec guillemets
|
||||
|
||||
**Risques:**
|
||||
- La détection automatique du type de requête peut être imprécise
|
||||
- Nécessite des règles bien pensées pour éviter les effets de bord
|
||||
- Peut nécessiter du machine learning pour être vraiment efficace
|
||||
|
||||
---
|
||||
|
||||
### Story 5: Expansion et Normalisation des Requêtes
|
||||
|
||||
**ID:** SEARCH-2.0-5
|
||||
**Title:** Améliorer les requêtes par expansion et normalisation
|
||||
**Priority:** Could Have
|
||||
**Estimation:** 5h
|
||||
|
||||
**En tant que:** utilisateur francophone/anglophone
|
||||
**Je veux:** que ma recherche trouve les résultats même avec des variations de mots
|
||||
**Afin que:** je n'aie pas à deviner les termes exacts utilisés
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une requête avec des mots au pluriel (ex: "recettes pizzas")
|
||||
2. **When** la recherche est exécutée
|
||||
3. **Then** les singuliers sont aussi recherchés ("recette pizza")
|
||||
4. **Given** une requête avec des accents (ex: "éléphant")
|
||||
5. **When** la recherche est exécutée
|
||||
6. **Then** les variantes sans accents sont aussi recherchées ("elephant")
|
||||
7. **Given** une requête courte (< 3 mots)
|
||||
8. **When** l'expansion est activée
|
||||
9. **Then** des synonymes courants sont ajoutés (ex: "bug" → "erreur", "problème")
|
||||
10. **And** l'expansion est limitée à 3 termes par mot original
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Ajouter `expandQuery()` avant le calcul des scores
|
||||
- `keep-notes/lib/utils.ts` - Implémenter `expandQuery()` et `normalizeText()`
|
||||
- `keep-notes/lib/data/synonyms.json` - Créer une liste de synonymes (FR/EN)
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test unitaire `expandQuery()` avec différents cas
|
||||
- Test d'intégration: recherche "pizzas" trouve notes avec "pizza"
|
||||
- Test d'intégration: recherche "bug" trouve notes avec "erreur"
|
||||
- Test performance: vérifier que l'expansion ne dégrade pas les performances
|
||||
|
||||
**Risques:**
|
||||
- L'expansion de requête peut augmenter significativement les faux positifs
|
||||
- La gestion des synonymes est complexe et contextuelle
|
||||
- Nécessite une base de synonymes bien maintenue
|
||||
- Peut ne pas être pertinent pour toutes les langues
|
||||
|
||||
---
|
||||
|
||||
### Story 6: Interface de Debug et Monitoring de Recherche
|
||||
|
||||
**ID:** SEARCH-2.0-6
|
||||
**Title:** Créer une interface de debug pour analyser la qualité de recherche
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 8h
|
||||
|
||||
**En tant que:** développeur/testeur
|
||||
**Je veux:** visualiser les détails du calcul de score pour chaque résultat
|
||||
**Afin que:** je puisse comprendre et optimiser la recherche
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** une recherche exécutée
|
||||
2. **When** le mode debug est activé (`?debug=true`)
|
||||
3. **Then** chaque résultat affiche:
|
||||
- Score mot-clé brut
|
||||
- Score sémantique brut (similarité cosine)
|
||||
- Score RRF final
|
||||
- Rang dans chaque liste (mot-clé / sémantique)
|
||||
4. **Given** la page `/debug-search`
|
||||
5. **When** j'accède à la page
|
||||
6. **Then** je vois une interface pour:
|
||||
- Tester des requêtes avec tous les paramètres
|
||||
- Comparer différents seuils de similarité
|
||||
- Voir les embeddings des notes
|
||||
7. **And** les métriques sont exportables en JSON
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/app/actions/notes.ts` - Retourner les scores debug si demandé
|
||||
- `keep-notes/app/debug-search/page.tsx` - Créer la page (existante dans git status)
|
||||
- `keep-notes/components/search-debug-results.tsx` - Nouveau composant
|
||||
- `keep-notes/app/api/debug/search/route.ts` - Nouveau endpoint API
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test E2E: accès à la page debug-search
|
||||
- Test API: endpoint retourne bien les scores détaillés
|
||||
- Test visuel: vérifier l'affichage des scores dans l'UI
|
||||
- Test de performance: vérifier que le mode debug n'impacte pas la recherche normale
|
||||
|
||||
**Risques:**
|
||||
- Complexité supplémentaire dans la UI
|
||||
- Nécessite de bien sécuriser l'accès (admin uniquement)
|
||||
- Informations sensibles (embeddings) visibles
|
||||
|
||||
---
|
||||
|
||||
### Story 7: Re-génération et Validation des Embeddings Existants
|
||||
|
||||
**ID:** SEARCH-2.0-7
|
||||
**Title:** Script de re-indexation des embeddings invalides ou manquants
|
||||
**Priority:** Must Have
|
||||
**Estimation:** 4h
|
||||
|
||||
**En tant que:** administrateur système
|
||||
**Je veux:** un script pour régénérer les embeddings des notes existantes
|
||||
**Afin que:** toutes les notes bénéficient de la recherche sémantique
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** des notes avec embeddings manquants ou invalides
|
||||
2. **When** je lance le script `npm run reindex-embeddings`
|
||||
3. **Then** les embeddings sont régénérés pour toutes les notes
|
||||
4. **And** la progression est affichée (X/Y notes traitées)
|
||||
5. **Given** des notes avec des embeddings valides
|
||||
6. **When** le script est lancé avec `--force`
|
||||
7. **Then** tous les embeddings sont régénérés (même les valides)
|
||||
8. **And** le script peut être relancé sans erreurs (idempotent)
|
||||
9. **And** un rapport final résume les succès/erreurs
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/scripts/reindex-embeddings.ts` - Créer le script
|
||||
- `keep-notes/app/actions/admin.ts` - Ajouter `reindexAllEmbeddings()`
|
||||
- `keep-notes/package.json` - Ajouter le script npm
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test du script sur base de données vide
|
||||
- Test du script avec des notes sans embeddings
|
||||
- Test du script avec des embeddings invalides (NaN, dimension 0)
|
||||
- Test du mode `--force`
|
||||
- Test d'idempotence (relancer le script deux fois)
|
||||
|
||||
**Risques:**
|
||||
- Temps d'exécution long si beaucoup de notes (plusieurs minutes/heures)
|
||||
- Peut saturer le provider IA (Ollama/OpenAI) avec trop de requêtes
|
||||
- Nécessite un mécanisme de rate limiting
|
||||
- Peut impacter les performances de l'application si lancé pendant l'utilisation
|
||||
|
||||
---
|
||||
|
||||
### Story 8: Suite de Tests Automatisés de Qualité de Recherche
|
||||
|
||||
**ID:** SEARCH-2.0-8
|
||||
**Title:** Créer des tests automatisés pour mesurer la qualité de recherche
|
||||
**Priority:** Should Have
|
||||
**Estimation:** 6h
|
||||
|
||||
**En tant que:** développeur
|
||||
**Je veux:** une suite de tests automatisés pour valider la qualité de recherche
|
||||
**Afin que:** les améliorations soient mesurables et les régressions détectées
|
||||
|
||||
**Critères d'Acceptation:**
|
||||
1. **Given** un dataset de test (notes + requêtes + résultats attendus)
|
||||
2. **When** les tests sont exécutés
|
||||
3. **Then** les métriques suivantes sont calculées:
|
||||
- Precision: % de résultats pertinents dans le top 10
|
||||
- Recall: % de résultats pertinents trouvés
|
||||
- MRR (Mean Reciprocal Rank): rang moyen du premier résultat pertinent
|
||||
- Faux positifs sémantiques: résultats sans pertinence
|
||||
4. **Given** une modification du code de recherche
|
||||
5. **When** les tests sont relancés
|
||||
6. **Then** une régression de plus de 5% fait échouer les tests
|
||||
7. **And** les tests prennent < 2 minutes à s'exécuter
|
||||
8. **And** un rapport HTML est généré pour analyse
|
||||
|
||||
**Fichiers à Modifier:**
|
||||
- `keep-notes/tests/search-benchmark.spec.ts` - Créer le benchmark
|
||||
- `keep-notes/tests/fixtures/search-dataset.json` - Dataset de test
|
||||
- `keep-notes/tests/utils/search-metrics.ts` - Utilitaires de calcul de métriques
|
||||
- `keep-notes/playwright.config.ts` - Configuration pour générer le rapport HTML
|
||||
|
||||
**Tests Nécessaires:**
|
||||
- Test du test (métatest) avec un dataset trivial
|
||||
- Test avec dataset réel (notes sur tech, cuisine, voyage, etc.)
|
||||
- Test de régression: introduire un bug et vérifier que les tests le détectent
|
||||
- Test de performance: temps d'exécution des tests
|
||||
|
||||
**Risques:**
|
||||
- Création du dataset manuelle et longue
|
||||
- Subjectivité de la "pertinence" (qui décide quoi est pertinent?)
|
||||
- Maintenance du dataset à chaque nouvelle feature
|
||||
- Tests peuvent être "flaky" si les embeddings changent
|
||||
|
||||
---
|
||||
|
||||
## Dépendances Entre Stories
|
||||
|
||||
```
|
||||
SEARCH-2.0-1 (Validation Embeddings)
|
||||
↓
|
||||
SEARCH-2.0-7 (Re-génération) ← dépend de 1 pour détecter les invalides
|
||||
↓
|
||||
SEARCH-2.0-2 (Seuil Similarité) ← dépend de 1 pour des embeddings valides
|
||||
↓
|
||||
SEARCH-2.0-3 (RRF Config)
|
||||
↓
|
||||
SEARCH-2.0-4 (Pondération Adaptative)
|
||||
↓
|
||||
SEARCH-2.0-8 (Tests Automatisés) ← dépend de 2,3,4 pour mesurer les améliorations
|
||||
↓
|
||||
SEARCH-2.0-6 (Debug Interface) ← utile pendant le développement de toutes les autres
|
||||
↓
|
||||
SEARCH-2.0-5 (Expansion Requêtes) ← amélioration optionnelle à la fin
|
||||
```
|
||||
|
||||
**Ordre recommandé:**
|
||||
1. **Sprint 1:** SEARCH-2.0-1, SEARCH-2.0-7 (Base solide avec embeddings valides)
|
||||
2. **Sprint 2:** SEARCH-2.0-2, SEARCH-2.0-3 (Corrections critiques du ranking)
|
||||
3. **Sprint 3:** SEARCH-2.0-4, SEARCH-2.0-6 (Améliorations + tooling)
|
||||
4. **Sprint 4:** SEARCH-2.0-8, SEARCH-2.0-5 (Tests + optimisations optionnelles)
|
||||
|
||||
---
|
||||
|
||||
## Métriques de Succès
|
||||
|
||||
### Avant/Après (Objectifs)
|
||||
|
||||
| Métrique | Avant | Après (Objectif) | Comment Mesurer |
|
||||
|----------|-------|------------------|-----------------|
|
||||
| Précision Top-10 | ~50% (estimé) | 70%+ | Tests automatisés Story 8 |
|
||||
| Faux positifs sémantiques | ~30% | <10% | Tests Playwright |
|
||||
| Temps de réponse (1000 notes) | 200ms | <300ms | Tests performance |
|
||||
| Taux de serendipité | N/A | 20-40% | Tests dataset |
|
||||
| Satisfaction utilisateur | 2/5 (subjectif) | 4/5+ | Sondage post-déploiement |
|
||||
|
||||
---
|
||||
|
||||
## Configuration Système Proposée
|
||||
|
||||
Nouveaux paramètres dans `SystemConfig`:
|
||||
|
||||
```typescript
|
||||
interface SearchConfig {
|
||||
// Seuil de similarité cosine minimum (0-1)
|
||||
SEARCH_SEMANTIC_THRESHOLD: number; // défaut: 0.65
|
||||
|
||||
// Constante k pour RRF (adaptive)
|
||||
SEARCH_RRF_K_BASE: number; // défaut: 20
|
||||
SEARCH_RRF_K_ADAPTIVE: boolean; // défaut: true
|
||||
|
||||
// Pondération mot-clé vs sémantique
|
||||
SEARCH_KEYWORD_BOOST_EXACT: number; // défaut: 2.0 (guillemets)
|
||||
SEARCH_KEYWORD_BOOST_CONCEPTUAL: number; // défaut: 0.7
|
||||
SEARCH_SEMANTIC_BOOST_EXACT: number; // défaut: 0.7
|
||||
SEARCH_SEMANTIC_BOOST_CONCEPTUAL: number; // défaut: 1.5
|
||||
|
||||
// Expansion de requête
|
||||
SEARCH_QUERY_EXPANSION_ENABLED: boolean; // défaut: true
|
||||
SEARCH_QUERY_EXPANSION_MAX_SYNONYMS: number; // défaut: 3
|
||||
|
||||
// Debug
|
||||
SEARCH_DEBUG_MODE: boolean; // défaut: false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fichers Critiques pour l'Implémentation
|
||||
|
||||
Les 5 fichiers les plus importants à modifier:
|
||||
|
||||
1. **keep-notes/app/actions/notes.ts** - Logique de recherche principale (RRF, seuils, ranking)
|
||||
2. **keep-notes/lib/utils.ts** - Fonctions de similarité cosine et nouvelles utilités
|
||||
3. **keep-notes/lib/ai/providers/ollama.ts** - Génération des embeddings avec validation
|
||||
4. **keep-notes/tests/search-quality.spec.ts** - Tests de qualité de recherche
|
||||
5. **keep-notes/lib/config.ts** - Configuration des nouveaux paramètres
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2026-01-09
|
||||
**Agent:** Plan (a551c9b)
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
stepsCompleted: [1]
|
||||
---
|
||||
|
||||
# Implementation Readiness Assessment Report
|
||||
|
||||
**Date:** 2026-01-09
|
||||
**Project:** Keep
|
||||
|
||||
## 1. Document Inventory
|
||||
|
||||
### PRD Documents
|
||||
- prd.md
|
||||
- prd-executive-summary.md
|
||||
- prd-web-app-requirements.md
|
||||
- prd-auth-admin.md
|
||||
|
||||
### Architecture Documents
|
||||
- ⚠️ MISSING
|
||||
|
||||
### Epics & Stories Documents
|
||||
- epics.md
|
||||
|
||||
### UX Design Documents
|
||||
- ⚠️ MISSING
|
||||
|
||||
---
|
||||
75
_bmad-output/planning-artifacts/prd-auth-admin.md
Normal file
75
_bmad-output/planning-artifacts/prd-auth-admin.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# PRD - Authentification Avancée & Administration
|
||||
|
||||
## 1. Contexte & Objectifs
|
||||
L'application Memento dispose actuellement d'une authentification basique. Pour un usage multi-utilisateurs ou privé/familial sécurisé, il est nécessaire d'introduire des rôles (Admin vs Utilisateur standard) et de permettre la gestion des comptes.
|
||||
|
||||
**Objectifs :**
|
||||
- Permettre à un Administrateur de gérer les utilisateurs (création manuelle, suppression).
|
||||
- Permettre à tout utilisateur de modifier ses informations personnelles (nom, mot de passe).
|
||||
- Sécuriser l'application en introduisant des rôles.
|
||||
|
||||
## 2. Spécifications Fonctionnelles
|
||||
|
||||
### 2.1 Gestion des Rôles (Backend)
|
||||
- **Modèle User** : Ajouter un champ `role` avec deux valeurs possibles : `USER` (défaut) et `ADMIN`.
|
||||
- **NextAuth** : Le rôle de l'utilisateur doit être disponible dans la session (via le token JWT) pour être vérifié côté client et serveur.
|
||||
|
||||
### 2.2 Dashboard Admin (`/admin`)
|
||||
**Accès :** Restreint aux utilisateurs ayant le rôle `ADMIN`.
|
||||
**Fonctionnalités :**
|
||||
1. **Liste des utilisateurs** :
|
||||
- Tableau affichant : Nom, Email, Rôle, Date de création.
|
||||
- Actions par ligne : "Supprimer", "Promouvoir Admin / Rétrograder".
|
||||
2. **Création d'utilisateur** :
|
||||
- Un bouton "Nouvel Utilisateur" ouvre une modale ou un formulaire.
|
||||
- Champs : Nom, Email, Mot de passe, Rôle.
|
||||
- Validation : Email unique, mot de passe min 6 caractères.
|
||||
|
||||
### 2.3 Profil Utilisateur (`/settings/profile`)
|
||||
**Accès :** Tout utilisateur connecté.
|
||||
**Fonctionnalités :**
|
||||
1. **Modifier le profil** :
|
||||
- Changer le nom d'affichage.
|
||||
2. **Sécurité** :
|
||||
- Changer le mot de passe (nécessite l'ancien mot de passe pour validation).
|
||||
|
||||
## 3. Spécifications Techniques
|
||||
|
||||
### 3.1 Base de Données (Prisma)
|
||||
Modifier `schema.prisma` :
|
||||
```prisma
|
||||
model User {
|
||||
// ... champs existants
|
||||
role String @default("USER") // ou Enum si SQLite le supporte bien (sinon String géré par app)
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Authentication (NextAuth v5)
|
||||
- Modifier `auth.config.ts` :
|
||||
- Ajouter `role` au type `Session` et `User`.
|
||||
- Dans le callback `jwt`, récupérer le rôle depuis la DB et le persister dans le token.
|
||||
- Dans le callback `session`, passer le rôle du token à la session.
|
||||
|
||||
### 3.3 Server Actions
|
||||
Créer `app/actions/admin.ts` :
|
||||
- `getUsers()`: Retourne la liste (Admin only).
|
||||
- `createUser(data)`: Crée un user avec hash du mot de passe (Admin only).
|
||||
- `deleteUser(id)`: Supprime un user (Admin only).
|
||||
- `updateUserRole(id, role)`: Change le rôle (Admin only).
|
||||
|
||||
Créer `app/actions/profile.ts` :
|
||||
- `updateProfile(data)`: Met à jour nom/email.
|
||||
- `changePassword(oldPwd, newPwd)`: Vérifie l'ancien hash et met à jour.
|
||||
|
||||
### 3.4 Interface Utilisateur (UI)
|
||||
- **Admin** : Utiliser `Table`, `Dialog` et `Form` de Shadcn UI.
|
||||
- **Profil** : Utiliser `Card`, `Input` et `Button` de Shadcn UI.
|
||||
- **Menu** : Ajouter un lien "Admin" dans la Sidebar ou le menu utilisateur, visible uniquement si `role === 'ADMIN'`.
|
||||
|
||||
## 4. Plan d'Implémentation
|
||||
1. **Migration DB** : Ajouter le champ `role` et mettre à jour Prisma.
|
||||
2. **Config Auth** : Mettre à jour NextAuth pour propager le rôle.
|
||||
3. **Backend** : Implémenter les Server Actions (Admin & Profil).
|
||||
4. **Frontend Admin** : Créer la page `/admin` et ses composants.
|
||||
5. **Frontend Profil** : Créer la page `/settings/profile`.
|
||||
6. **Sécurisation** : Ajouter les vérifications de rôle dans le Middleware ou les Layouts.
|
||||
Reference in New Issue
Block a user