feat: sélecteur de famille (onglet Sheets) en sync et création
- GET /api/sheets/tabs : liste les onglets Google Sheets disponibles - readSheetProducts(tab?) et fetchPendingProducts(set, tab?) acceptent un filtre - /api/sync/preview?tab=xxx et /api/creation/preview?tab=xxx filtrent sur un onglet - Composant FamilySelector : grille de boutons chargeant les onglets dynamiquement - Page Sync : étape 0 de sélection famille avant l'analyse - Page Création : étape famille avant analyse, badge de famille sélectionnée Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,14 @@ import { Header } from '@/components/layout/Header'
|
||||
import { PendingRowsTable } from '@/components/creation/PendingRowsTable'
|
||||
import { MetafieldResolver } from '@/components/creation/MetafieldResolver'
|
||||
import { CreationProgress } from '@/components/creation/CreationProgress'
|
||||
import { FamilySelector } from '@/components/common/FamilySelector'
|
||||
import type { PendingProduct, MetafieldResolution, CreationStreamEvent } from '@/types/creation'
|
||||
|
||||
type Step = 'analyse' | 'metafields' | 'confirmation' | 'execution'
|
||||
type Step = 'famille' | 'analyse' | 'metafields' | 'confirmation' | 'execution'
|
||||
|
||||
export default function CreationPage() {
|
||||
const [step, setStep] = useState<Step>('analyse')
|
||||
const [step, setStep] = useState<Step>('famille')
|
||||
const [selectedTab, setSelectedTab] = useState<string | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [byTab, setByTab] = useState<Record<string, PendingProduct[]>>({})
|
||||
@@ -19,11 +21,19 @@ export default function CreationPage() {
|
||||
const [events, setEvents] = useState<CreationStreamEvent[]>([])
|
||||
const [done, setDone] = useState(false)
|
||||
|
||||
const handleSelectTab = (tab: string) => {
|
||||
setSelectedTab(tab)
|
||||
setStep('analyse')
|
||||
}
|
||||
|
||||
const analyse = async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const res = await fetch('/api/creation/preview')
|
||||
const url = selectedTab
|
||||
? `/api/creation/preview?tab=${encodeURIComponent(selectedTab)}`
|
||||
: '/api/creation/preview'
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
if (data.error) throw new Error(data.error)
|
||||
setByTab(data.byTab)
|
||||
@@ -78,6 +88,15 @@ export default function CreationPage() {
|
||||
]
|
||||
const currentIdx = STEPS.findIndex(s => s.id === step)
|
||||
|
||||
const reset = () => {
|
||||
setStep('famille')
|
||||
setSelectedTab(null)
|
||||
setEvents([])
|
||||
setDone(false)
|
||||
setByTab({})
|
||||
setTotal(0)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title="Création produits" />
|
||||
@@ -93,13 +112,26 @@ export default function CreationPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-800 rounded-xl p-6">
|
||||
{/* É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>
|
||||
)}
|
||||
|
||||
<div className="bg-slate-800 rounded-xl p-6" style={{ display: step === 'famille' ? 'none' : undefined }}>
|
||||
{error && <div className="mb-4 p-3 bg-red-900/30 border border-red-700 rounded-lg text-red-300 text-sm">{error}</div>}
|
||||
|
||||
{step === 'analyse' && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2 bg-indigo-950/50 border border-indigo-800/50 rounded-lg px-3 py-2 w-fit">
|
||||
<span className="text-indigo-400 text-sm">📂 Famille :</span>
|
||||
<span className="text-white text-sm font-semibold">{selectedTab}</span>
|
||||
<button onClick={() => { setStep('famille'); setSelectedTab(null) }} className="ml-2 text-xs text-slate-400 hover:text-slate-200">changer</button>
|
||||
</div>
|
||||
<p className="text-gray-300 text-sm">
|
||||
Lit tous les onglets du Google Sheets et détecte les lignes où <code className="text-indigo-300">Publié=1</code> et <code className="text-indigo-300">ID vide</code>.
|
||||
Détecte les lignes de <strong className="text-white">{selectedTab}</strong> où <code className="text-indigo-300">Publié=1</code> et <code className="text-indigo-300">ID vide</code>.
|
||||
</p>
|
||||
<button onClick={analyse} disabled={loading}
|
||||
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 text-white rounded-lg text-sm font-medium transition-colors">
|
||||
@@ -152,7 +184,7 @@ export default function CreationPage() {
|
||||
<div className="space-y-4">
|
||||
<CreationProgress events={events} done={done} />
|
||||
{done && (
|
||||
<button onClick={() => { setStep('analyse'); setEvents([]); setDone(false); setByTab({}); setTotal(0) }}
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user