Refactoring Hooks Quotes/Invoices
Date de début : 2026-01-27
Date de fin : 2026-01-27
Statut : ✅ COMPLÉTÉ
Objectifs
- Uniformiser les clés React Query - Convention claire et cohérente
- Séparer clairement API globale vs par session - Architecture plus claire
- Débrancher le cache pour les exports CSV/PDF - Les exports ne doivent pas polluer le cache
- 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É
- ✅ Créer des helpers pour générer les clés de manière cohérente (
quoteKeys,invoiceKeys) - ✅ Mettre à jour tous les hooks pour utiliser les nouvelles clés
- ✅ Mettre à jour les fonctions d'invalidation (support des nouvelles clés + rétrocompatibilité)
- ⏳ 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É
- ✅ Créer des hooks séparés pour contexte session (
useSessionQuotes,useSessionInvoices) - ✅ Garder les hooks globaux pour contexte global (
useAllQuotes,useAllInvoices) - ✅ Mettre à jour les usages dans le codebase :
- ✅
QuotesInvoicesTab: utilise maintenantuseSessionQuotesetuseSessionInvoices - ✅
ContractsTab: utilise maintenantuseSessionQuotes - ✅
InvoiceFormPage: utilise conditionnellementuseSessionQuotesouuseAllQuotes - ✅
ResourcesTabetResourceSearchField: utilisent maintenantuseAllQuotesetuseAllInvoices
- ✅
- ✅ Marquer
useQuotesetuseInvoicescomme dépréciés (mais maintenus pour rétrocompatibilité)
Phase 3 : Documentation et tests ✅ COMPLÉTÉ
- ✅ Documenter la nouvelle architecture (ce document)
- ✅ 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)
- ✅ Mettre à jour la documentation existante (commentaires dans le code)
Phase 4 : Exports CSV/PDF ✅ COMPLÉTÉ
- ✅ Vérifier que les exports utilisent fetch direct (pas React Query)
- ✅ 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É
- ✅ Créer les nouveaux hooks avec les nouvelles clés
- ✅ Mettre à jour progressivement les usages (tous les usages critiques migrés)
- ✅ Supprimer les anciens hooks (supprimés car non utilisés)
- ✅ 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.