feat: add bulk stock sync from Sheets to Shopify

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 10:35:52 +02:00
parent 747f8a47b8
commit a9850c8efd
3 changed files with 195 additions and 4 deletions
+60 -3
View File
@@ -25,6 +25,42 @@ export default function CreationPage() {
const [stopped, setStopped] = useState(false)
const abortRef = useRef<AbortController | null>(null)
// Sync stock
const [stockSyncing, setStockSyncing] = useState(false)
const [stockResult, setStockResult] = useState<{ updated: number; errors: number } | null>(null)
const [stockProgress, setStockProgress] = useState<{ current: number; total: number; title: string } | null>(null)
const syncStock = async () => {
setStockSyncing(true)
setStockResult(null)
setStockProgress(null)
try {
const res = await fetch('/api/creation/sync-stock', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
if (!res.body) return
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) {
if (!line.trim()) continue
try {
const evt = JSON.parse(line)
if (evt.type === 'progress') setStockProgress({ current: evt.current, total: evt.total, title: evt.title })
if (evt.type === 'done') setStockResult({ updated: evt.updated, errors: evt.errors })
} catch { /* */ }
}
}
} finally {
setStockSyncing(false)
setStockProgress(null)
}
}
// Collection
const [collections, setCollections] = useState<ShopifyCollection[]>([])
const [detectedCollectionId, setDetectedCollectionId] = useState<string>('')
@@ -174,9 +210,30 @@ export default function CreationPage() {
{/* Étape 0 — Sélection famille */}
{step === 'famille' && (
<div className="bg-slate-800 rounded-xl p-6">
<h2 className="text-sm font-semibold text-white mb-4">Choisir une famille</h2>
<FamilySelector onSelect={handleSelectTab} />
<div className="space-y-4">
<div className="bg-slate-800 rounded-xl p-6">
<h2 className="text-sm font-semibold text-white mb-4">Choisir une famille</h2>
<FamilySelector onSelect={handleSelectTab} />
</div>
<div className="bg-slate-800 rounded-xl p-6">
<h2 className="text-sm font-semibold text-white mb-1">Synchroniser le stock</h2>
<p className="text-xs text-gray-400 mb-4">Met à jour le stock Shopify pour tous les produits existants (ceux avec un ID Shopify dans le Sheets).</p>
<button onClick={syncStock} disabled={stockSyncing}
className="px-4 py-2 bg-amber-700 hover:bg-amber-600 disabled:opacity-50 text-white rounded-lg text-sm font-medium transition-colors">
{stockSyncing ? (stockProgress ? `Mise à jour… ${stockProgress.current}/${stockProgress.total}` : 'Chargement…') : 'Synchroniser le stock depuis Sheets'}
</button>
{stockSyncing && stockProgress && (
<p className="mt-2 text-xs text-gray-400 truncate"> {stockProgress.title}</p>
)}
{stockResult && (
<p className="mt-3 text-sm">
{stockResult.errors === 0
? <span className="text-green-400"> {stockResult.updated} produit{stockResult.updated > 1 ? 's' : ''} mis à jour</span>
: <span className="text-amber-400"> {stockResult.updated} mis à jour {stockResult.errors} erreur{stockResult.errors > 1 ? 's' : ''}</span>
}
</p>
)}
</div>
</div>
)}