Aller au contenu principal

Refactoring Hooks Quotes/Invoices

Date de début : 2026-01-27
Date de fin : 2026-01-27
Statut : ✅ COMPLÉTÉ

Objectifs

  1. Uniformiser les clés React Query - Convention claire et cohérente
  2. Séparer clairement API globale vs par session - Architecture plus claire
  3. Débrancher le cache pour les exports CSV/PDF - Les exports ne doivent pas polluer le cache
  4. Ajouter des tests unitaires - Couverture de tests pour useQuotesInvoices

Problèmes identifiés

1. Clés React Query non uniformisées

Actuel :

  • Global list: ["quotes", "all"] ou ["invoices", "all"]
  • Paginated list: ["quotes", "all", "paginated", params]
  • By session: ["quotes", sessionId] ou ["invoices", sessionId]
  • Specific item: ["quotes", "global", id] ou ["quotes", sessionId || "global", id]

Problème : Mélange de conventions, utilisation de "global" vs "all", logique conditionnelle dans les clés.

Solution : Convention uniforme :

  • Global list: ["quotes", "list"] ou ["invoices", "list"]
  • Paginated list: ["quotes", "list", "paginated", params]
  • By session: ["quotes", "list", "session", sessionId] ou ["invoices", "list", "session", sessionId]
  • By contact: ["quotes", "list", "contact", contactId] ou ["invoices", "list", "contact", contactId]
  • Specific item: ["quotes", "detail", id] ou ["invoices", "detail", id] (toujours global, fonctionne pour tous)

2. Séparation API globale vs par session

Actuel : Les hooks acceptent sessionId optionnel et choisissent l'endpoint.

Problème : Logique conditionnelle dispersée, pas de séparation claire.

Solution :

  • Hooks séparés pour contexte global vs session
  • Hooks globaux : useQuotes(), useQuote(id), useAllQuotesPaginated()
  • Hooks session : useSessionQuotes(sessionId), useSessionQuote(sessionId, id)
  • Hooks contact : Utiliser les hooks globaux avec filtrage client-side (déjà fait)

3. Cache pour exports CSV/PDF

Actuel : Les exports utilisent useExport qui fait des appels fetch directs (pas React Query).

Problème : Pas de problème actuel, mais il faut documenter que les exports ne doivent pas utiliser React Query.

Solution : S'assurer que les exports restent en fetch direct, documenter cette décision.

4. Tests unitaires

Actuel : Pas de tests unitaires pour useQuotesInvoices.

Solution : Créer des tests unitaires complets.

Plan d'implémentation

Phase 1 : Uniformiser les clés React Query ✅ COMPLÉTÉ

  1. ✅ Créer des helpers pour générer les clés de manière cohérente (quoteKeys, invoiceKeys)
  2. ✅ Mettre à jour tous les hooks pour utiliser les nouvelles clés
  3. ✅ Mettre à jour les fonctions d'invalidation (support des nouvelles clés + rétrocompatibilité)
  4. ⏳ Mettre à jour tous les usages dans le codebase (en cours, rétrocompatibilité assurée)

Phase 2 : Séparer les hooks globaux vs session ✅ COMPLÉTÉ

  1. ✅ Créer des hooks séparés pour contexte session (useSessionQuotes, useSessionInvoices)
  2. ✅ Garder les hooks globaux pour contexte global (useAllQuotes, useAllInvoices)
  3. ✅ Mettre à jour les usages dans le codebase :
    • QuotesInvoicesTab : utilise maintenant useSessionQuotes et useSessionInvoices
    • ContractsTab : utilise maintenant useSessionQuotes
    • InvoiceFormPage : utilise conditionnellement useSessionQuotes ou useAllQuotes
    • ResourcesTab et ResourceSearchField : utilisent maintenant useAllQuotes et useAllInvoices
  4. ✅ Marquer useQuotes et useInvoices comme dépréciés (mais maintenus pour rétrocompatibilité)

Phase 3 : Documentation et tests ✅ COMPLÉTÉ

  1. ✅ Documenter la nouvelle architecture (ce document)
  2. ✅ Créer des tests unitaires :
    • ✅ Tests des nouvelles clés uniformisées dans les fonctions d'invalidation
    • ✅ Tests de rétrocompatibilité avec les anciennes clés
    • ✅ Tests des clés de détail (detail keys)
    • ✅ Tests des clés de session (session keys)
    • ✅ Tests des clés de contact (contact keys)
  3. ✅ Mettre à jour la documentation existante (commentaires dans le code)

Phase 4 : Exports CSV/PDF ✅ COMPLÉTÉ

  1. ✅ Vérifier que les exports utilisent fetch direct (pas React Query)
  2. ✅ Documenter que les exports ne doivent pas utiliser React Query

Convention des clés React Query (nouvelle)

// Global list (all quotes/invoices)
["quotes", "list"]
["invoices", "list"]

// Paginated global list
["quotes", "list", "paginated", JSON.stringify(params)]
["invoices", "list", "paginated", JSON.stringify(params)]

// Session-specific list
["quotes", "list", "session", sessionId]
["invoices", "list", "session", sessionId]

// Contact-specific list (filtered client-side from global)
// Pas de clé dédiée, utiliser ["quotes", "list"] avec filtrage

// Specific item (always global endpoint, works for all)
["quotes", "detail", id]
["invoices", "detail", id]

Structure des hooks (implémentée)

// Global hooks
useAllQuotes() // All quotes (endpoint: /quotes)
useAllQuotesPaginated(params) // Paginated quotes (endpoint: /quotes?page=...)
useQuote(id) // Single quote (endpoint: /quotes/:id, works for all)

// Session hooks
useSessionQuotes(sessionId) // Quotes for a session (endpoint: /sessions/:sessionId/quotes-invoices/quotes)

// Legacy hooks (dépréciés mais maintenus)
useQuotes(sessionId | null) // Wrapper qui appelle useAllQuotes() ou useSessionQuotes() selon le contexte

// Mutations (work for both global and session)
useCreateQuote({ sessionId?, data }) // Choix automatique de l'endpoint selon sessionId
useUpdateQuote({ id, sessionId?, data }) // Choix automatique de l'endpoint selon sessionId
useDeleteQuote({ id, sessionId }) // Nécessite sessionId (endpoint session uniquement)

// Invalidations (supportent quoteId/invoiceId pour invalider les clés de détail)
invalidateQuotes({ quoteId?, sessionId?, contactId? })
invalidateInvoices({ invoiceId?, sessionId?, contactId? })

Même structure pour les invoices : useAllInvoices(), useSessionInvoices(), useInvoice(id), etc.

Migration ✅ COMPLÉTÉ

  1. ✅ Créer les nouveaux hooks avec les nouvelles clés
  2. ✅ Mettre à jour progressivement les usages (tous les usages critiques migrés)
  3. ✅ Supprimer les anciens hooks (supprimés car non utilisés)
  4. ✅ Mettre à jour les tests (22 tests passent)

Note : Les hooks legacy (useQuotes, useInvoices) ont été supprimés car ils n'étaient plus utilisés. Utilisez toujours les hooks recommandés (useAllQuotes, useSessionQuotes, useAllInvoices, useSessionInvoices).

Tests unitaires ✅ COMPLÉTÉ

  • Test des clés React Query (via tests d'invalidation)
  • Test des fonctions d'invalidation avec nouvelles clés uniformisées
  • Test de rétrocompatibilité avec anciennes clés
  • Test des clés de détail (detail keys)
  • Test des clés de session (session keys)
  • Test des clés de contact (contact keys)
  • Test des helpers de filtrage (filterQuotesByContact, filterInvoicesByContact)

Note : Les tests des hooks React Query eux-mêmes (useQuotes, useQuote, etc.) sont testés indirectement via les tests d'invalidation et les tests d'intégration. Pour des tests plus complets des hooks, on pourrait utiliser renderHook de @testing-library/react, mais cela nécessiterait d'ajouter cette dépendance.