# Synchronisation offline SmartCommon fournit un module de synchronisation complet pour gérer le fonctionnement hors-ligne des applications PWA. ## Architecture Le module sync est composé de : ^ Élément ^ Type ^ Description ^ | ''useSyncClient'' | Hook React | Interface principale pour la synchronisation | | ''SyncEngine'' | Classe | Moteur de synchronisation (push/pull/conflits) | | ''SyncStorage'' | Classe | Couche IndexedDB pour le stockage local (Dexie) | | ''SyncApi'' | Classe | Client HTTP avec auth JWT et retry automatique | | ''ConflictResolver'' | Composant React | Interface de résolution de conflits | ## useSyncClient Voir [[hooks#useSyncClient|Hooks - useSyncClient]] pour la documentation complète du hook. ## Flux de synchronisation ### Push (local vers serveur) ``` 1. L'utilisateur modifie des données localement (create/update/remove) 2. Les changements sont stockés dans IndexedDB (pending_changes) 3. Au prochain sync.push(), les changements sont envoyés au serveur 4. Le serveur confirme ou signale des conflits 5. Les temp_id locaux sont remplacés par les ID serveur ``` ### Pull (serveur vers local) ``` 1. sync.pull() demande les changements depuis lastSyncTime 2. Le serveur renvoie les entités modifiées 3. Les entités locales sont mises à jour 4. En cas de conflit (modification locale + serveur), un conflit est créé ``` ## Gestion des conflits Quand une entité est modifiée localement et sur le serveur, un conflit est créé. ### Résolution programmatique ``` const conflicts = await sync.getConflicts(); for (const conflict of conflicts) { // Garder la version client await sync.resolveConflict(conflict.conflict_id, 'client'); // Garder la version serveur await sync.resolveConflict(conflict.conflict_id, 'server'); // Fusionner manuellement await sync.resolveConflict(conflict.conflict_id, { ...conflict.server_data, label: conflict.client_data.label // garder le label local }); } ``` ### Résolution avec ConflictResolver ``` import { ConflictResolver } from '@cap-rel/smartcommon'; const MyConflictPage = () => { const sync = useSyncClient({ /* ... */ }); const [conflicts, setConflicts] = useState([]); useEffect(() => { sync.getConflicts().then(setConflicts); }, []); if (conflicts.length === 0) return null; return ( { await sync.resolveConflict(conflictId, resolution); setConflicts(prev => prev.filter(c => c.conflict_id !== conflictId)); }} onCancel={() => setConflicts([])} /> ); }; ``` Le composant ''ConflictResolver'' affiche : - Comparaison côte à côte des données client et serveur - Marqueurs sur les champs en conflit - Trois options : garder client, garder serveur, fusionner champ par champ - Navigation entre les conflits multiples ### Props de ConflictResolver ^ Prop ^ Type ^ Description ^ | ''conflicts'' | array | Tableau de conflits (conflict_id, table, object_id, client_data, server_data, field_conflicts) | | ''onResolve'' | function | Appelée quand un conflit est résolu | | ''onCancel'' | function | Appelée pour fermer le résolveur | | ''renderField'' | function | Rendu personnalisé d'un champ (optionnel) | | ''labels'' | object | Libellés de l'interface (optionnel, français par défaut) | ## Schéma IndexedDB Le module sync utilise les stores suivants : ^ Store ^ Description ^ | ''entities'' | Données synchronisées | | ''pending_changes'' | Changements locaux en attente de push | | ''pending_conflicts'' | Conflits non résolus | | ''sync_meta'' | Métadonnées de synchronisation (lastSyncTime, etc.) | | ''local_tombstones'' | Entités supprimées localement | ## Voir aussi * [[hooks|Hooks SmartCommon]] - Tous les hooks * [[pwa|PWA]] - Configuration PWA * [[stockage_de_donnees|Stockage de données]] - Stockage local