'use client' import { useState, useEffect } from 'react' import type { MetafieldDefinition, MetafieldResolution } from '@/types/creation' import { normalizeMetafieldKey } from '@/lib/shopifyMetafields' interface Props { specificColumns: string[] onResolved: (resolutions: MetafieldResolution[]) => void } export function MetafieldResolver({ specificColumns, onResolved }: Props) { const [definitions, setDefinitions] = useState([]) const [matched, setMatched] = useState>({}) const [unmatched, setUnmatched] = useState([]) const [resolutions, setResolutions] = useState>({}) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { if (specificColumns.length === 0) { setLoading(false); onResolved([]); return } const params = specificColumns.map(c => encodeURIComponent(c)).join(',') fetch(`/api/creation/metafields?columns=${params}`) .then(r => r.json()) .then(data => { if (data.error) throw new Error(data.error) setDefinitions(data.definitions) setMatched(data.matched) setUnmatched(data.unmatched) const init: Record = { ...data.matched } // Défaut : 'skip' — l'utilisateur choisit explicitement ce qu'il veut créer data.unmatched.forEach((col: string) => { init[col] = { columnHeader: col, action: 'skip', namespace: 'custom', key: normalizeMetafieldKey(col) } }) setResolutions(init) }) .catch(e => setError(e.message)) .finally(() => setLoading(false)) // eslint-disable-next-line react-hooks/exhaustive-deps }, [specificColumns.join(',')]) useEffect(() => { if (!loading) onResolved(Object.values(resolutions)) // eslint-disable-next-line react-hooks/exhaustive-deps }, [resolutions, loading]) const updateResolution = (col: string, partial: Partial) => { setResolutions(prev => ({ ...prev, [col]: { ...prev[col], ...partial } })) } if (loading) return

Analyse des métachamps Shopify…

if (error) return

{error}

if (specificColumns.length === 0) return

Aucune colonne spécifique détectée.

return (
{Object.entries(matched).length > 0 && (

✓ {Object.entries(matched).length} colonne{Object.entries(matched).length > 1 ? 's' : ''} avec correspondance automatique

{Object.entries(matched).map(([col, res]) => (
{col} → {res.namespace}.{res.key}
))}
)} {unmatched.length > 0 && (

Ces colonnes n'ont pas de métachamp correspondant dans Shopify. Choisissez pour chacune : Créer nouveau (nouveau métachamp Shopify), Associer (utiliser un existant), ou Ignorer (ne pas synchroniser).

{unmatched.map(col => { const res = resolutions[col] if (!res) return null return (

{col}

{res.action === 'create' && ✓ Sera créé dans Shopify} {res.action === 'map' && ✓ Associé à un existant} {res.action === 'skip' && — Ignoré}
{(['create', 'map', 'skip'] as const).map(action => ( ))}
{res.action === 'create' && (
updateResolution(col, { key: e.target.value })} placeholder="ex: puissance" className="w-full bg-slate-600 border border-slate-500 rounded px-2 py-1 text-xs text-white focus:outline-none focus:ring-1 focus:ring-indigo-500" />
updateResolution(col, { namespace: e.target.value })} placeholder="custom" className="w-full bg-slate-600 border border-slate-500 rounded px-2 py-1 text-xs text-white focus:outline-none focus:ring-1 focus:ring-indigo-500" />
)} {res.action === 'map' && (
)}
) })}
{/* Résumé des actions */}
{Object.values(resolutions).filter(r => r.action === 'create').length} à créer {Object.values(resolutions).filter(r => r.action === 'map').length} à associer {Object.values(resolutions).filter(r => r.action === 'skip').length} ignorés
)}
) }