feat(plan4): composants ProductFiltersBar + ProductTable
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
import type { ProductFilters } from '@/types/products'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
filters: ProductFilters
|
||||||
|
total: number
|
||||||
|
onChange: (f: Partial<ProductFilters>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ProductFiltersBar({ filters, total, onChange }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap gap-3 items-center mb-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Rechercher ref ou titre..."
|
||||||
|
value={filters.search}
|
||||||
|
onChange={(e) => onChange({ search: e.target.value })}
|
||||||
|
className="flex-1 min-w-48 bg-slate-700 border border-slate-600 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{(['all', 'active', 'draft'] as const).map((s) => (
|
||||||
|
<button
|
||||||
|
key={s}
|
||||||
|
onClick={() => onChange({ status: s })}
|
||||||
|
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||||
|
filters.status === s
|
||||||
|
? 'bg-indigo-600 text-white'
|
||||||
|
: 'bg-slate-700 text-gray-300 hover:bg-slate-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{s === 'all' ? 'Tous' : s === 'active' ? 'Actifs' : 'Inactifs'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<span className="text-sm text-gray-400 ml-auto">{total} produit{total > 1 ? 's' : ''}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
'use client'
|
||||||
|
import type { CatalogProduct } from '@/types/products'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
products: CatalogProduct[]
|
||||||
|
loading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE = 50
|
||||||
|
|
||||||
|
export function ProductTable({ products, loading }: Props) {
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center py-20 text-gray-400">
|
||||||
|
Chargement des produits…
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (products.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="text-center py-20 text-gray-400 text-sm">
|
||||||
|
Aucun produit trouvé.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayed = products.slice(0, PAGE_SIZE)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr className="text-left text-gray-400 border-b border-slate-700">
|
||||||
|
<th className="pb-3 pr-4 font-medium">Référence</th>
|
||||||
|
<th className="pb-3 pr-4 font-medium">Titre</th>
|
||||||
|
<th className="pb-3 pr-4 font-medium">Prix</th>
|
||||||
|
<th className="pb-3 pr-4 font-medium">Stock</th>
|
||||||
|
<th className="pb-3 pr-4 font-medium">Statut</th>
|
||||||
|
<th className="pb-3 font-medium">Lien</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{displayed.map((p) => (
|
||||||
|
<tr key={p.shopifyId} className="border-b border-slate-700/50 hover:bg-slate-700/30 transition-colors">
|
||||||
|
<td className="py-3 pr-4 font-mono text-indigo-300">{p.ref}</td>
|
||||||
|
<td className="py-3 pr-4 text-white">{p.title}</td>
|
||||||
|
<td className="py-3 pr-4 text-gray-300">{p.price} €</td>
|
||||||
|
<td className="py-3 pr-4 text-gray-300">{p.stock}</td>
|
||||||
|
<td className="py-3 pr-4">
|
||||||
|
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${
|
||||||
|
p.status === 'active'
|
||||||
|
? 'bg-green-900/50 text-green-300'
|
||||||
|
: 'bg-gray-700 text-gray-400'
|
||||||
|
}`}>
|
||||||
|
{p.status === 'active' ? 'Actif' : 'Inactif'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="py-3">
|
||||||
|
<a
|
||||||
|
href={p.shopifyUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-indigo-400 hover:text-indigo-300 text-xs underline"
|
||||||
|
>
|
||||||
|
Shopify ↗
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{products.length > PAGE_SIZE && (
|
||||||
|
<p className="text-center text-gray-400 text-xs mt-4">
|
||||||
|
Affichage des {PAGE_SIZE} premiers résultats sur {products.length}.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user