import { useEffect,useRef,useState } from 'react' import { fetchSyncStatus,newRequestId,startSync,syncIsActive,type SyncStatus } from '../api/sync' const labels:Record={idle:'Synchronisation inactive',requested:'Synchronisation demandée',waiting_android_publication:'Publication Android requise',running:'Synchronisation en cours',local_import_committed:'Données desktop validées',published:'Génération retour publiée',waiting_acknowledgement:'Accusé Android attendu',peer_consumed:'Pair confirmé',completed:'Synchronisation confirmée',failed:'Échec de synchronisation',interrupted:'Synchronisation interrompue',explicitly_degraded:'Pair incompatible'} export function SyncControl({onCommitted}:{onCommitted:()=>void}){const [status,setStatus]=useState(null);const [error,setError]=useState('');const committed=useRef(-1) const accept=(next:SyncStatus)=>{setStatus(next);setError('');if(['local_import_committed','published','waiting_acknowledgement','peer_consumed','completed'].includes(next.phase)&&(next.progress_revision??0)>committed.current){committed.current=next.progress_revision??0;onCommitted()}} useEffect(()=>{let stopped=false;let timer:number|undefined;const poll=async()=>{try{const next=await fetchSyncStatus();if(stopped)return;accept(next);if(syncIsActive(next.phase))timer=window.setTimeout(poll,750)}catch{if(!stopped)setError('Statut de synchronisation indisponible')}};void poll();return()=>{stopped=true;if(timer!==undefined)window.clearTimeout(timer)}},[onCommitted]) const start=async()=>{setError('');try{accept(await startSync(newRequestId()));const poll=async()=>{try{const next=await fetchSyncStatus();accept(next);if(syncIsActive(next.phase))window.setTimeout(poll,750)}catch{setError('Résultat inconnu : vérifiez le statut avant de réessayer')}};window.setTimeout(poll,250)}catch(reason){setError(reason instanceof Error?reason.message:'Démarrage impossible')}} const active=status?syncIsActive(status.phase):false return
{error||labels[status?.phase??'idle']}{status?.diagnostic&&

{status.diagnostic}

}{status?.sessions_reconciled!==null&&status?.sessions_reconciled!==undefined&&

Séances rapprochées : {status.sessions_reconciled}

}{status?.phase==='waiting_android_publication'&&

Ouvrez l’écran de synchronisation Android pour publier une génération fraîche.

}{(status?.drafts?.length??0)>0&&

Brouillons reçus

    {status!.drafts!.map(d=>
  • {d.draft_id} · {d.state} · {d.session_type} · {d.occurrence_count} occurrence(s)
  • )}
}{status?.finished_at&&

Dernier résultat : {status.finished_at}

}
}