'use client' import { useState, useCallback } from 'react' import { Header } from '@/components/layout/Header' import { SyncStepper } from '@/components/sync/SyncStepper' import { DiffTable } from '@/components/sync/DiffTable' import { SyncProgress } from '@/components/sync/SyncProgress' import { SyncHistory } from '@/components/sync/SyncHistory' import type { DiffResult, SyncChange, SyncHistoryEntry } from '@/types/sync' type Step = 1 | 2 | 3 | 4 export default function SyncPage() { const [step, setStep] = useState(1) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [diff, setDiff] = useState(null) const [history, setHistory] = useState([]) const [historyLoaded, setHistoryLoaded] = useState(false) // Log streaming state const [log, setLog] = useState([]) const [progress, setProgress] = useState<{ current: number; total: number } | null>(null) const [summary, setSummary] = useState<{ created: number; updated: number; deactivated: number; errors: number; status: string } | null>(null) // Étape 1 → 2 : lancer le preview const handleAnalyze = useCallback(async () => { setLoading(true) setError(null) try { const res = await fetch('/api/sync/preview') const data = await res.json() if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`) setDiff(data) setStep(2) } catch (err) { setError((err as Error).message) } finally { setLoading(false) } }, []) // Étape 3 → 4 : lancer la sync en streaming const handleExecute = useCallback(async () => { if (!diff) return setStep(4) setLog([]) setProgress(null) setSummary(null) const actionableChanges: SyncChange[] = diff.changes.filter((c) => c.type !== 'unchanged') try { const res = await fetch('/api/sync/execute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ changes: actionableChanges }), }) if (!res.ok) { const data = await res.json() throw new Error(data.error || `HTTP ${res.status}`) } const reader = res.body!.getReader() const decoder = new TextDecoder() let buffer = '' while (true) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) const lines = buffer.split('\n') buffer = lines.pop() ?? '' for (const line of lines.filter(Boolean)) { try { const event = JSON.parse(line) if (event.type === 'progress') { setProgress({ current: event.current, total: event.total }) setLog((prev) => [...prev, event.message]) } else if (event.type === 'done') { setSummary(event.summary) setHistoryLoaded(false) // force reload history } } catch { /* ligne incomplète */ } } } } catch (err) { setLog((prev) => [...prev, `❌ Erreur : ${(err as Error).message}`]) } }, [diff]) // Charger l'historique au besoin const loadHistory = useCallback(async () => { if (historyLoaded) return try { const res = await fetch('/api/sync/history') if (res.ok) { setHistory(await res.json()) setHistoryLoaded(true) } } catch { /* silencieux */ } }, [historyLoaded]) const actionableCount = diff ? diff.summary.create + diff.summary.update + diff.summary.deactivate : 0 return ( <>
{/* Stepper */} {/* Étape 1 — Analyser */} {step === 1 && (

Lit Google Sheets et compare avec le catalogue Shopify actuel pour générer l'aperçu des changements.

{error && (
{error}
)}
)} {/* Étape 2 — Aperçu diff */} {step === 2 && diff && (
)} {/* Étape 3 — Confirmation */} {step === 3 && diff && (

{actionableCount} changement{actionableCount > 1 ? 's' : ''} vont être appliqués à Shopify

{diff.summary.create > 0 && `${diff.summary.create} créations · `} {diff.summary.update > 0 && `${diff.summary.update} mises à jour · `} {diff.summary.deactivate > 0 && `${diff.summary.deactivate} désactivations`}

)} {/* Étape 4 — Progression */} {step === 4 && (
{summary && (
)}
)} {/* Historique */}
{historyLoaded && }
) }