feat: composant SyncProgress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 22:07:04 +02:00
parent 9c3ec2a953
commit 94993c3cbc
+65
View File
@@ -0,0 +1,65 @@
'use client'
interface SyncProgressProps {
log: string[]
progress: { current: number; total: number } | null
summary: { created: number; updated: number; deactivated: number; errors: number; status: string } | null
}
export function SyncProgress({ log, progress, summary }: SyncProgressProps) {
const percent = progress && progress.total > 0
? Math.round((progress.current / progress.total) * 100)
: 0
return (
<div className="flex flex-col gap-4">
{/* Barre de progression */}
{progress && (
<div>
<div className="flex justify-between text-xs text-slate-400 mb-1">
<span>{progress.current} / {progress.total} produits</span>
<span>{percent}%</span>
</div>
<div className="h-2 bg-slate-800 rounded-full overflow-hidden">
<div
className="h-2 bg-indigo-500 rounded-full transition-all duration-300"
style={{ width: `${percent}%` }}
/>
</div>
</div>
)}
{/* Log */}
<div className="bg-slate-950 rounded-xl border border-slate-800 p-3 h-48 overflow-y-auto font-mono text-xs">
{log.length === 0 && (
<p className="text-slate-600">Démarrage de la synchronisation</p>
)}
{log.map((line, i) => (
<p
key={i}
className={line.startsWith('✗') ? 'text-red-400' : 'text-green-400'}
>
{line}
</p>
))}
</div>
{/* Résultat final */}
{summary && (
<div className={`rounded-xl p-4 text-sm font-semibold border ${
summary.status === 'success'
? 'bg-green-950/30 border-green-800 text-green-300'
: summary.status === 'partial'
? 'bg-yellow-950/30 border-yellow-800 text-yellow-300'
: 'bg-red-950/30 border-red-800 text-red-300'
}`}>
{summary.status === 'success' && '✅ '}
{summary.status === 'partial' && '⚠️ '}
{summary.status === 'error' && '❌ '}
Sync terminée {summary.created} créés, {summary.updated} mis à jour,{' '}
{summary.deactivated} désactivés, {summary.errors} erreur{summary.errors > 1 ? 's' : ''}
</div>
)}
</div>
)
}