feat: recherche textuelle dans la sélection de métachamp existant

Remplace le <select> par un input filtrant en temps réel sur le nom,
la clé et le namespace — utile quand il y a beaucoup de métachamps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 12:58:48 +02:00
parent 42d0e2f855
commit 731d0dbde6
+52 -10
View File
@@ -1,5 +1,5 @@
'use client'
import { useState, useEffect } from 'react'
import { useState, useEffect, useRef } from 'react'
import type { MetafieldDefinition, MetafieldResolution } from '@/types/creation'
import { normalizeMetafieldKey } from '@/lib/shopifyMetafields'
@@ -15,6 +15,9 @@ export function MetafieldResolver({ specificColumns, onResolved }: Props) {
const [resolutions, setResolutions] = useState<Record<string, MetafieldResolution>>({})
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [searches, setSearches] = useState<Record<string, string>>({})
const [openDropdown, setOpenDropdown] = useState<string | null>(null)
const dropdownRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (specificColumns.length === 0) { setLoading(false); onResolved([]); return }
@@ -47,6 +50,16 @@ export function MetafieldResolver({ specificColumns, onResolved }: Props) {
setResolutions(prev => ({ ...prev, [col]: { ...prev[col], ...partial } }))
}
const filteredDefs = (col: string) => {
const q = (searches[col] ?? '').toLowerCase()
if (!q) return definitions
return definitions.filter(d =>
d.name.toLowerCase().includes(q) ||
d.key.toLowerCase().includes(q) ||
d.namespace.toLowerCase().includes(q)
)
}
if (loading) return <p className="text-gray-400 text-sm">Analyse des métachamps Shopify</p>
if (error) return <p className="text-red-400 text-sm">{error}</p>
if (specificColumns.length === 0) return <p className="text-gray-400 text-sm">Aucune colonne spécifique détectée.</p>
@@ -133,16 +146,45 @@ export function MetafieldResolver({ specificColumns, onResolved }: Props) {
</div>
)}
{res.action === 'map' && (
<div>
<div className="relative" ref={openDropdown === col ? dropdownRef : undefined}>
<label className="block text-xs text-gray-400 mb-1">Métachamp existant</label>
<select
value={res.existingDefinitionId ?? ''}
onChange={e => { const def = definitions.find(d => d.id === e.target.value); if (def) updateResolution(col, { existingDefinitionId: def.id, key: def.key, namespace: def.namespace }) }}
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-blue-500"
>
<option value="">-- Choisir un métachamp existant --</option>
{definitions.map(d => <option key={d.id} value={d.id}>{d.name} ({d.namespace}.{d.key})</option>)}
</select>
{res.existingDefinitionId ? (
<div className="flex items-center gap-2">
<span className="flex-1 text-xs text-blue-300 bg-slate-600 border border-slate-500 rounded px-2 py-1 truncate">
{definitions.find(d => d.id === res.existingDefinitionId)?.name ?? res.key} ({res.namespace}.{res.key})
</span>
<button type="button" onClick={() => { updateResolution(col, { existingDefinitionId: undefined, key: normalizeMetafieldKey(col), namespace: 'custom' }); setSearches(p => ({ ...p, [col]: '' })); setOpenDropdown(col) }} className="text-xs text-gray-400 hover:text-white px-1"></button>
</div>
) : (
<div>
<input
type="text"
autoFocus
value={searches[col] ?? ''}
onChange={e => { setSearches(p => ({ ...p, [col]: e.target.value })); setOpenDropdown(col) }}
onFocus={() => setOpenDropdown(col)}
placeholder="Rechercher un métachamp…"
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-blue-500"
/>
{openDropdown === col && (
<div className="absolute z-10 mt-1 w-full bg-slate-700 border border-slate-600 rounded shadow-lg max-h-48 overflow-y-auto">
{filteredDefs(col).length === 0 ? (
<p className="text-xs text-gray-400 px-3 py-2">Aucun résultat</p>
) : filteredDefs(col).map(d => (
<button
key={d.id}
type="button"
onMouseDown={() => { updateResolution(col, { existingDefinitionId: d.id, key: d.key, namespace: d.namespace }); setOpenDropdown(null) }}
className="w-full text-left px-3 py-1.5 text-xs text-white hover:bg-slate-600 flex justify-between gap-2"
>
<span>{d.name}</span>
<span className="text-gray-400 shrink-0">{d.namespace}.{d.key}</span>
</button>
))}
</div>
)}
</div>
)}
</div>
)}
</div>