feat: stop button for product creation stream
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { useState, useCallback, useEffect, useRef } from 'react'
|
||||
import { Header } from '@/components/layout/Header'
|
||||
import { PendingRowsTable } from '@/components/creation/PendingRowsTable'
|
||||
import { MetafieldResolver } from '@/components/creation/MetafieldResolver'
|
||||
@@ -24,6 +24,8 @@ export default function CreationPage() {
|
||||
const [resolutions, setResolutions] = useState<MetafieldResolution[]>([])
|
||||
const [events, setEvents] = useState<CreationStreamEvent[]>([])
|
||||
const [done, setDone] = useState(false)
|
||||
const [stopped, setStopped] = useState(false)
|
||||
const abortRef = useRef<AbortController | null>(null)
|
||||
|
||||
// Collection
|
||||
const [collections, setCollections] = useState<ShopifyCollection[]>([])
|
||||
@@ -83,14 +85,25 @@ export default function CreationPage() {
|
||||
|
||||
const handleResolved = useCallback((r: MetafieldResolution[]) => { setResolutions(r) }, [])
|
||||
|
||||
const stop = useCallback(() => {
|
||||
abortRef.current?.abort()
|
||||
setStopped(true)
|
||||
setDone(true)
|
||||
}, [])
|
||||
|
||||
const execute = async () => {
|
||||
const controller = new AbortController()
|
||||
abortRef.current = controller
|
||||
setStep('execution')
|
||||
setEvents([])
|
||||
setDone(false)
|
||||
setStopped(false)
|
||||
try {
|
||||
const res = await fetch('/api/creation/execute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ resolutions, tab: selectedTab, collectionOverride: collectionOverride || undefined }),
|
||||
signal: controller.signal,
|
||||
})
|
||||
if (!res.body) return
|
||||
const reader = res.body.getReader()
|
||||
@@ -111,6 +124,11 @@ export default function CreationPage() {
|
||||
} catch { /* ligne incomplète */ }
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if ((e as Error).name !== 'AbortError') {
|
||||
setEvents(prev => [...prev, { type: 'error', title: '', message: (e as Error).message }])
|
||||
}
|
||||
}
|
||||
setDone(true)
|
||||
}
|
||||
|
||||
@@ -140,10 +158,13 @@ export default function CreationPage() {
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
abortRef.current?.abort()
|
||||
abortRef.current = null
|
||||
setStep('famille')
|
||||
setSelectedTab(null)
|
||||
setEvents([])
|
||||
setDone(false)
|
||||
setStopped(false)
|
||||
setByTab({})
|
||||
setTotal(0)
|
||||
setDetectedCollectionId('')
|
||||
@@ -305,13 +326,24 @@ export default function CreationPage() {
|
||||
{step === 'execution' && (
|
||||
<div className="space-y-4">
|
||||
<CreationProgress events={events} done={done} />
|
||||
<div className="flex gap-3">
|
||||
{!done && (
|
||||
<button onClick={stop}
|
||||
className="px-4 py-2 bg-red-700 hover:bg-red-600 text-white rounded-lg text-sm font-medium transition-colors">
|
||||
⏹ Arrêter
|
||||
</button>
|
||||
)}
|
||||
{done && (
|
||||
<>
|
||||
{stopped && <p className="text-amber-400 text-sm self-center">⚠ Création interrompue — les produits déjà créés sont conservés dans Shopify.</p>}
|
||||
<button onClick={reset}
|
||||
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm font-medium transition-colors">
|
||||
Nouvelle session
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user