feat: add bulk stock sync from Sheets to Shopify
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user