Files
office_translator/_bmad-output/implementation-artifacts/4-10-dashboard-glossaries-editor-pro.md
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

19 KiB
Raw Blame History

Story 4.10: Dashboard - Glossaries Editor (Pro)

Status: done

Story

En tant qu'utilisateur Pro, Je veux créer et éditer des glossaires dans mon dashboard, de sorte que je puisse personnaliser les traductions LLM avec ma terminologie métier.

Acceptance Criteria

  1. Pro Access Only: La page /dashboard/glossaries n'est accessible qu'aux utilisateurs avec tier === "pro"
  2. Free User Prompt: Les utilisateurs Free voient un message d'upgrade avec CTA vers upgrade au lieu de l'éditeur
  3. List Glossaries: Afficher la liste des glossaires existants avec: name, terms_count, created_at
  4. Create Glossary: Au clic sur "Create New Glossary", ouvrir un dialog/form pour entrer le nom et les termes initiaux
  5. Edit Glossary: Cliquer sur un glossaire pour l'éditer (modifier le nom, ajouter/supprimer/modifier des termes)
  6. Delete Glossary: Supprimer un glossary avec confirmation dialog → DELETE /api/v1/glossaries/{id}
  7. Term Editor: Interface pour gérer les paires source→target avec boutons Add/Remove
  8. CSV Import/Export: Bouton pour exporter le glossaire en CSV et importer depuis un CSV
  9. Max Terms: Si 500 termes atteint par glossaire, désactiver "Add Term" avec message
  10. Error Handling: Afficher toast d'erreur si API retourne erreur (403, 404, etc.)
  11. Loading States: Skeleton/loading pendant les appels API
  12. Colocation: Tous les fichiers dans frontend/src/app/dashboard/glossaries/

Tasks / Subtasks

  • Task 1: Créer la structure de dossiers (AC: #12)

    • 1.1 Créer frontend/src/app/dashboard/glossaries/page.tsx (fichier spécial: minuscules!)
    • 1.2 Créer frontend/src/app/dashboard/glossaries/types.ts
    • 1.3 Créer frontend/src/app/dashboard/glossaries/useGlossaries.ts
  • Task 2: Définir les types TypeScript (AC: #3)

    • 2.1 GlossaryTerm interface: id, source, target, created_at
    • 2.2 Glossary interface: id, name, terms (GlossaryTerm[]), created_at, updated_at
    • 2.3 GlossaryListItem interface: id, name, terms_count, created_at
    • 2.4 GlossaryListResponse interface: data (GlossaryListItem[]), meta (total, page, per_page, total_pages)
    • 2.5 GlossaryDetailResponse interface: data (Glossary), meta ({})
  • Task 3: Créer useGlossaries.ts hook (AC: #3, #4, #6, #10, #11)

    • 3.1 useQuery pour GET /api/v1/glossaries avec TanStack Query
    • 3.2 createGlossary(name, terms[]): mutation POST /api/v1/glossaries
    • 3.3 updateGlossary(id, {name?, terms?}): mutation PATCH /api/v1/glossaries/{id}
    • 3.4 deleteGlossary(id): mutation DELETE /api/v1/glossaries/{id}
    • 3.5 Gestion erreurs: 403 → Pro requis, 404 → Non trouvé
    • 3.6 Loading states: isCreating, isUpdating, isDeleting
    • 3.7 Cache invalidation après create/update/delete
  • Task 4: Créer la page principale (AC: #1, #2, #3, #4, #5, #6, #11)

    • 4.1 Récupérer user.tier via useUser() hook existant
    • 4.2 Si tier !== "pro": afficher <ProUpgradePrompt /> (réutiliser depuis api-keys/)
    • 4.3 Si tier === "pro": afficher <GlossariesManager />
    • 4.4 GlossariesManager: Grille/Liste de cartes avec glossaires
    • 4.5 Chaque carte: nom, nombre de termes, date création, boutons Edit/Delete
    • 4.6 "Create New Glossary" button → ouvre CreateGlossaryDialog
    • 4.7 Click sur carte → ouvre EditGlossaryDialog ou navigue vers édition
  • Task 5: Créer les composants UI (AC: #4, #5, #7, #9)

    • 5.1 GlossaryCard.tsx: Carte individuelle avec nom, terms_count, actions
    • 5.2 CreateGlossaryDialog.tsx: Dialog pour créer un nouveau glossaire
    • 5.3 EditGlossaryDialog.tsx: Dialog pour éditer nom + termes
    • 5.4 TermEditor.tsx: Éditeur de paires source→target avec add/remove
    • 5.5 DeleteGlossaryDialog.tsx: Confirmation dialog pour suppression
  • Task 6: Implémenter CSV Import/Export (AC: #8)

    • 6.1 exportGlossaryToCsv(glossary): Génère fichier CSV et déclenche download
    • 6.2 parseCsvToTerms(file): Parse fichier CSV uploadé en GlossaryTerm[]
    • 6.3 Bouton "Export CSV" dans EditGlossaryDialog
    • 6.4 Bouton "Import CSV" avec file input dans EditGlossaryDialog
    • 6.5 Format CSV: source,target (header optionnel)
  • Task 7: Intégration et tests (AC: Tous)

    • 7.1 npm run build → 0 erreurs TypeScript
    • 7.2 Tester création glossaire → apparaît dans la liste
    • 7.3 Tester édition termes → modifications sauvegardées
    • 7.4 Tester suppression → glossaire retiré de la liste
    • 7.5 Tester Free user → upgrade prompt affiché
    • 7.6 Tester CSV export → fichier téléchargé correct
    • 7.7 Tester CSV import → termes importés correct

Dev Notes

🏗️ Stack Technique

Technologie Version
Next.js 16.0.6 (App Router)
React 19.2.0
TanStack Query v5.90.21
Tailwind CSS configuré
shadcn/ui Card, Dialog, Button, Input, Badge, Toast, Separator
Lucide React BookText, Plus, Trash2, ArrowRight, Download, Upload, Save

📁 Structure Cible (Colocation Pattern)

frontend/src/app/dashboard/glossaries/
├── page.tsx                    # Page principale
├── types.ts                    # TypeScript interfaces
├── useGlossaries.ts            # TanStack Query hook
├── GlossaryCard.tsx            # Carte individuelle
├── CreateGlossaryDialog.tsx    # Dialog création
├── EditGlossaryDialog.tsx      # Dialog édition complète
├── TermEditor.tsx              # Éditeur de paires source→target
├── DeleteGlossaryDialog.tsx    # Confirmation suppression
├── csvUtils.ts                 # Fonctions CSV import/export
└── ProUpgradePrompt.tsx        # Copié/adapté depuis api-keys/

⚠️ Règle absolue (architecture.md):

🚨 FICHIERS SPÉCIAUX: page.tsx → TOUJOURS minuscules
🚨 COLOCATION: Components/hooks/types dans le dossier de leur page

🔗 API Endpoints Backend (Déjà implémentés)

Endpoint Méthode Request Response
/api/v1/glossaries GET ?page=1&per_page=50 200: {data: [GlossaryListItem...], meta: {total, page, per_page, total_pages}}
/api/v1/glossaries POST {name, terms: [{source, target}]} 201: {data: Glossary, meta: {}}
/api/v1/glossaries/{id} GET 200: {data: Glossary, meta: {}}
/api/v1/glossaries/{id} PATCH {name?, terms?} 200: {data: Glossary, meta: {}}
/api/v1/glossaries/{id} DELETE 204: No Content

Codes erreur:

  • 401: Token invalide → rediriger vers login
  • 403: PRO_FEATURE_REQUIRED → afficher upgrade prompt
  • 404: GLOSSARY_NOT_FOUND → glossaire non trouvé
  • 400: TERMS_LIMIT_EXCEEDED → max 500 termes
  • 400: INVALID_GLOSSARY_ID → UUID invalide

📊 API Response Examples

GET /api/v1/glossaries (200):

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Glossaire Technique FR-EN",
      "terms_count": 25,
      "created_at": "2026-02-19T10:30:00Z"
    }
  ],
  "meta": {
    "total": 3,
    "page": 1,
    "per_page": 50,
    "total_pages": 1
  }
}

GET /api/v1/glossaries/{id} (200):

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Glossaire Technique FR-EN",
    "terms": [
      {
        "id": "term-uuid-1",
        "source": "serveur",
        "target": "server",
        "created_at": "2026-02-19T10:30:00Z"
      },
      {
        "id": "term-uuid-2",
        "source": "base de données",
        "target": "database",
        "created_at": "2026-02-19T10:31:00Z"
      }
    ],
    "created_at": "2026-02-19T10:30:00Z",
    "updated_at": "2026-02-20T14:00:00Z"
  },
  "meta": {}
}

POST /api/v1/glossaries (201):

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "name": "Mon Nouveau Glossaire",
    "terms": [
      {"id": "term-uuid-3", "source": "bonjour", "target": "hello", "created_at": "2026-02-23T14:30:00Z"}
    ],
    "created_at": "2026-02-23T14:30:00Z",
    "updated_at": "2026-02-23T14:30:00Z"
  },
  "meta": {}
}

🎨 Patterns UI à Réutiliser

Depuis glossary-context-card.tsx (office-translator-landing-page):

  • Layout grille pour TermEditor: grid-cols-[1fr_32px_1fr_36px]
  • ArrowRight icon entre source et target
  • Input avec font-mono text-xs
  • Button Trash2 avec opacity-0 group-hover:opacity-100
  • Add Term button avec variant="outline" border-dashed

Depuis api-keys/page.tsx (frontend):

  • Structure page: titre + description en haut
  • Card avec header icône + titre + description
  • ProUpgradePrompt component
  • Loading skeleton avec animate-spin
  • useToast pour les notifications

Depuis DashboardSidebar.tsx:

  • Badge Pro avec style: border border-accent/20 bg-accent/10 text-accent

🔄 State Flow

┌─────────────────────────────────────────────────────────┐
│                    page.tsx                              │
│  ┌─────────────────────────────────────────────────────┐│
│  │ useUser() → tier check                               ││
│  │   ├─ tier !== "pro" → <ProUpgradePrompt />          ││
│  │   └─ tier === "pro" → <GlossariesManager />         ││
│  └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                 GlossariesManager                        │
│  ┌─────────────────────────────────────────────────────┐│
│  │ useGlossaries() hook                                 ││
│  │   ├─ glossaries: GlossaryListItem[]                  ││
│  │   ├─ isLoading, isCreating, isUpdating, isDeleting  ││
│  │   ├─ createGlossary(name, terms)                    ││
│  │   ├─ updateGlossary(id, data)                       ││
│  │   └─ deleteGlossary(id)                             ││
│  └─────────────────────────────────────────────────────┘│
│                                                          │
│  ┌─────────────────────────────────────────────────────┐│
│  │ GlossaryGrid                                         ││
│  │   └─ GlossaryCard × N                                ││
│  │       ├─ name, terms_count, created_at               ││
│  │       └─ Actions: Edit, Delete                       ││
│  └─────────────────────────────────────────────────────┘│
│                                                          │
│  [Create New Glossary] → opens CreateGlossaryDialog     │
│  [Edit] → opens EditGlossaryDialog                      │
│  [Delete] → opens DeleteGlossaryDialog                  │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                 EditGlossaryDialog                       │
│  ┌─────────────────────────────────────────────────────┐│
│  │ Name Input                                           ││
│  └─────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────┐│
│  │ TermEditor                                           ││
│  │   ├─ Row: Source Input → Target Input × N           ││
│  │   └─ [Add Term] button                               ││
│  └─────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────┐│
│  │ CSV Actions                                          ││
│  │   ├─ [Export CSV] button                             ││
│  │   └─ [Import CSV] file input                         ││
│  └─────────────────────────────────────────────────────┘│
│  [Save Changes] → PATCH /api/v1/glossaries/{id}         │
└─────────────────────────────────────────────────────────┘

⚠️ Points d'Attention Critiques

  1. Max 500 termes par glossaire: Le backend renvoie 400 TERMS_LIMIT_EXCEEDED. Désactiver le bouton "Add Term" côté client quand on atteint 500.

  2. PATCH vs PUT: Utiliser PATCH pour les updates (partiels). Le backend accepte {name} seul ou {terms} seul ou les deux.

  3. Cache invalidation: Après create/update/delete, invalider le cache TanStack Query:

    queryClient.invalidateQueries({ queryKey: ['glossaries'] });
    
  4. CSV Format:

    • Export: Télécharger avec text/csv Content-Type et filename {glossary_name}.csv
    • Import: Accepter header optionnel source,target ou données directes
  5. Optimistic Updates: Optionnel - pour UX fluide, on peut mettre à jour la liste immédiatement après création/update.

  6. UUID validation côté client: Le backend valide les UUID mais on peut pré-valider pour UX.

📋 CSV Import/Export Format

Export (glossary_name.csv):

source,target
serveur,server
base de données,database
client,client

Import (accepte avec ou sans header):

serveur,server
base de données,database

📋 Checklist de Validation Avant Dev

  • Backend API /api/v1/glossaries est fonctionnel (testé via curl/Postman)
  • useUser() hook retourne bien tier field
  • shadcn/ui components installés: Card, Dialog, Button, Input, Badge, Toast, Separator
  • apiClient.ts gère correctement les erreurs 403/404
  • Navigation sidebar affiche déjà "Glossaries" pour users Pro

References

  • [Source: _bmad-output/planning-artifacts/epics.md#Story-4.10] — Story requirements
  • [Source: _bmad-output/planning-artifacts/architecture.md#Frontend-Architecture] — TanStack Query + colocation
  • [Source: _bmad-output/planning-artifacts/architecture.md#API-Response-Formats] — Format réponse API
  • [Source: routes/glossary_routes.py] — Backend API implementation (GET, POST, PATCH, DELETE)
  • [Source: schemas/glossary_schemas.py] — Pydantic schemas for validation
  • [Source: office-translator-landing-page/components/glossary-context-card.tsx] — UI patterns TermEditor à réutiliser
  • [Source: frontend/src/app/dashboard/api-keys/page.tsx] — Pattern page dashboard Pro
  • [Source: frontend/src/app/dashboard/api-keys/useApiKeys.ts] — Pattern TanStack Query hook
  • [Source: frontend/src/app/dashboard/useUser.ts] — Hook existant pour tier check
  • [Source: frontend/src/lib/apiClient.ts] — API client existant
  • [Source: frontend/src/app/dashboard/constants.ts] — Navigation items (Glossaries déjà présent pour Pro)
  • [Source: _bmad-output/implementation-artifacts/4-9-dashboard-api-keys-management-pro.md] — Story précédente, patterns à réutiliser

Dev Agent Record

Agent Model Used

zai-anthropic/glm-5

Debug Log References

Completion Notes List

  • 2026-02-23: Completed full implementation of Glossaries Editor dashboard page
    • Created all TypeScript types matching backend API schemas
    • Implemented TanStack Query hooks for CRUD operations with cache invalidation
    • Built complete UI with Pro upgrade prompt for free users
    • Implemented TermEditor with max 500 terms limit enforcement
    • Added CSV import/export functionality with proper parsing
    • Fixed TypeScript errors in api-keys/useApiKeys.ts and translate/page.tsx
    • Build passes with 0 errors
  • 2026-02-23: Code review fixes applied
    • [FIX] Added proper API error code parsing (TERMS_LIMIT_EXCEEDED, PRO_FEATURE_REQUIRED, etc.)
    • [FIX] Added CSV import validation for max 500 terms limit with user feedback
    • [FIX] Added pagination support (page, perPage parameters)
    • [FIX] Added automatic 401 redirect to login page
    • [FIX] Improved React key stability in TermEditor using content-based keys
    • [FIX] Added error handling for CSV file read failures

File List

Created files:

  • frontend/src/app/dashboard/glossaries/page.tsx
  • frontend/src/app/dashboard/glossaries/types.ts
  • frontend/src/app/dashboard/glossaries/useGlossaries.ts
  • frontend/src/app/dashboard/glossaries/GlossaryCard.tsx
  • frontend/src/app/dashboard/glossaries/CreateGlossaryDialog.tsx
  • frontend/src/app/dashboard/glossaries/EditGlossaryDialog.tsx
  • frontend/src/app/dashboard/glossaries/TermEditor.tsx
  • frontend/src/app/dashboard/glossaries/DeleteGlossaryDialog.tsx
  • frontend/src/app/dashboard/glossaries/csvUtils.ts
  • frontend/src/app/dashboard/glossaries/ProUpgradePrompt.tsx

Modified files:

  • frontend/src/app/dashboard/api-keys/useApiKeys.ts (fixed TypeScript error)
  • frontend/src/app/dashboard/translate/page.tsx (fixed TypeScript error)

Change Log

  • 2026-02-23: Story created - ready for development
  • 2026-02-23: Implementation complete - all ACs satisfied, ready for review
  • 2026-02-23: Code review completed - 5 issues fixed (2 HIGH, 3 MEDIUM), story marked done