espace-paie-odentas/FIX_UPLOAD_MANUEL_CONTRAT_SIGNE.md
odentas c148c46796 fix: Utiliser le client admin pour bypasser RLS lors de l'upload manuel du contrat signé
Le problème était que l'update du champ contract_pdf_s3_key utilisait le client Supabase normal (avec RLS) au lieu du client admin (service role). Cela empêchait potentiellement la mise à jour du contrat ou l'accès côté client.

Changements:
- Import de createClient depuis @supabase/supabase-js
- Création d'un adminClient avec SUPABASE_SERVICE_ROLE_KEY
- Utilisation de adminClient pour l'update au lieu de sb
- Ajout de logs pour le debug
2025-12-19 17:46:27 +01:00

4.9 KiB

Fix : Rafraîchissement des documents après upload manuel du contrat signé

Problème identifié

Lorsqu'un contrat de travail était uploadé manuellement depuis la page staff (staff/contrats/[id]), il n'apparaissait pas immédiatement sur la page client (contrats/[id]).

Cause racine

Le composant DocumentsCard utilisé par la page client effectuait des appels fetch directs dans des useEffect avec uniquement contractId comme dépendance. Ces appels n'étaient donc déclenchés qu'au montage du composant, et pas lors d'un upload manuel effectué depuis une autre page.

Côté staff, le système fonctionnait correctement car il utilisait React Query avec invalidation de cache.

Solution implémentée

1. Ajout d'un mécanisme de refresh dans DocumentsCard

Fichier : components/contrats/DocumentsCard.tsx

  • Ajout d'une prop refreshTrigger?: number
  • Ajout de refreshTrigger comme dépendance dans tous les useEffect qui récupèrent les données
  • Ajout de setLoadingSignedPdf(true) au début de fetchSignedPdf pour afficher un loader lors du rechargement
interface DocumentsCardProps {
  contractId: string;
  contractNumber?: string;
  contractData?: { ... };
  showPayslips?: boolean;
  refreshTrigger?: number; // ← Nouvelle prop
}

useEffect(() => {
  const fetchSignedPdf = async () => {
    setLoadingSignedPdf(true); // ← Ajouté
    // ... code de récupération
  };
  fetchSignedPdf();
}, [contractId, refreshTrigger]); // ← refreshTrigger ajouté

2. Système d'événements personnalisés

Fichier : components/staff/contracts/ManualSignedContractUpload.tsx

Après un upload réussi, émission d'un événement personnalisé :

// Émettre un événement personnalisé pour rafraîchir les documents côté client
if (typeof window !== 'undefined') {
  window.dispatchEvent(new CustomEvent('refreshContractDocuments'));
}

3. Écoute de l'événement dans la page client

Fichier : app/(app)/contrats/[id]/page.tsx

  • Ajout d'un state documentsRefreshTrigger
  • Ajout d'un listener pour l'événement refreshContractDocuments
  • Passage de refreshTrigger au composant DocumentsCard
const [documentsRefreshTrigger, setDocumentsRefreshTrigger] = useState<number>(0);

useEffect(() => {
  const handleRefreshDocuments = () => {
    console.log('🔄 Rafraîchissement des documents demandé');
    setDocumentsRefreshTrigger(prev => prev + 1);
  };

  window.addEventListener('refreshContractDocuments', handleRefreshDocuments);
  
  return () => {
    window.removeEventListener('refreshContractDocuments', handleRefreshDocuments);
  };
}, []);

// Dans le JSX
<DocumentsCard 
  contractId={id}
  contractNumber={data.numero}
  contractData={{ ... }}
  refreshTrigger={documentsRefreshTrigger}
/>

Flux de données

  1. Upload manuel : L'utilisateur staff upload un contrat signé via ManualSignedContractUpload
  2. Upload S3 : Le fichier est uploadé sur S3 et contract_pdf_s3_key est mis à jour dans la base de données
  3. Invalidation React Query : La query ["signed-contract-pdf", contract.id] est invalidée côté staff
  4. Événement custom : Un événement refreshContractDocuments est émis globalement
  5. Réception côté client : La page client écoute cet événement et incrémente documentsRefreshTrigger
  6. Rechargement : DocumentsCard détecte le changement de refreshTrigger et re-déclenche ses useEffect
  7. Affichage : Le contrat signé apparaît dans la liste des documents

Avantages de cette approche

  • Compatibilité : Fonctionne même si la page client n'est pas en React Query
  • Découplage : Les composants ne sont pas fortement couplés
  • Extensible : D'autres composants peuvent écouter le même événement si nécessaire
  • Performance : Pas de polling inutile, rechargement uniquement quand nécessaire

Tests à effectuer

  • Upload manuel d'un contrat depuis la page staff
  • Vérifier que le contrat apparaît sur la page client (après F5 ou event)
  • Vérifier que l'upload fonctionne toujours côté staff (avec React Query)
  • Vérifier les logs console pour confirmer le déclenchement de l'événement

Commit

fix: Rafraîchir les documents côté client après upload manuel du contrat signé

- Ajout d'une prop refreshTrigger à DocumentsCard pour forcer le rechargement
- Ajout d'un listener d'événement custom 'refreshContractDocuments' dans la page client
- Émission de l'événement après l'upload réussi dans ManualSignedContractUpload
- Fix: Le contrat signé apparaît maintenant sur la page client après upload manuel depuis staff

Notes

  • Cette solution est temporaire en attendant une migration complète vers React Query dans DocumentsCard
  • À terme, il serait préférable de centraliser toutes les requêtes de documents dans React Query
  • L'événement refreshContractDocuments pourrait être typé avec TypeScript pour plus de sécurité