import React, { useState, useEffect, useCallback } from 'react'; import { AuthProvider, AuthGuard, useAuth } from './AdminAuth'; import AdminLayout from './AdminLayout'; interface Product { id: number; name: string; slug: string; brand: string; image_url: string | null; cutout_image_url: string | null; is_sneaker: boolean; lowest_price: number; offer_count: number; created_at: string; } function ProductTableContent() { const { token } = useAuth(); const [products, setProducts] = useState([]); const [brands, setBrands] = useState<{ brand: string; count: number }[]>([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [selected, setSelected] = useState>(new Set()); // Filters const [search, setSearch] = useState(''); const [brandFilter, setBrandFilter] = useState(''); const [sneakerFilter, setSneakerFilter] = useState(''); const [cutoutFilter, setCutoutFilter] = useState(''); const [sortBy, setSortBy] = useState('created_at'); const [sortDir, setSortDir] = useState('desc'); const [page, setPage] = useState(1); const limit = 50; const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }; const loadProducts = useCallback(() => { setLoading(true); const params = new URLSearchParams({ page: String(page), limit: String(limit), sort: sortBy, dir: sortDir, }); if (search) params.set('search', search); if (brandFilter) params.set('brand', brandFilter); if (sneakerFilter) params.set('is_sneaker', sneakerFilter); if (cutoutFilter) params.set('has_cutout', cutoutFilter); fetch(`/api/admin/products?${params}`, { headers: { Authorization: `Bearer ${token}` } }) .then(r => r.json()) .then(d => { setProducts(d.products || []); setTotal(d.total || 0); if (d.brands) setBrands(d.brands); }) .catch(() => {}) .finally(() => setLoading(false)); }, [token, page, search, brandFilter, sneakerFilter, cutoutFilter, sortBy, sortDir]); useEffect(() => { loadProducts(); }, [loadProducts]); // Debounce search const [searchInput, setSearchInput] = useState(''); useEffect(() => { const t = setTimeout(() => { setSearch(searchInput); setPage(1); }, 300); return () => clearTimeout(t); }, [searchInput]); const toggleSelect = (id: number) => { setSelected(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); }; const toggleAll = () => { if (selected.size === products.length) setSelected(new Set()); else setSelected(new Set(products.map(p => p.id))); }; const bulkUpdate = async (field: string, value: any) => { await fetch('/api/admin/products', { method: 'PUT', headers, body: JSON.stringify({ ids: [...selected], [field]: value }), }); setSelected(new Set()); loadProducts(); }; const toggleSneaker = async (product: Product) => { await fetch('/api/admin/products', { method: 'PUT', headers, body: JSON.stringify({ id: product.id, is_sneaker: !product.is_sneaker }), }); setProducts(prev => prev.map(p => p.id === product.id ? { ...p, is_sneaker: !p.is_sneaker } : p)); }; const handleSort = (col: string) => { if (sortBy === col) setSortDir(d => d === 'asc' ? 'desc' : 'asc'); else { setSortBy(col); setSortDir('desc'); } setPage(1); }; const totalPages = Math.ceil(total / limit); const sortIcon = (col: string) => sortBy === col ? (sortDir === 'asc' ? ' ↑' : ' ↓') : ''; return (
setSearchInput(e.target.value)} className="bg-[#141414] border border-[#333] rounded-lg px-4 py-2 text-sm text-[#F5F5F5] focus:outline-none focus:border-[#00FF6A] flex-1 min-w-[200px] max-w-md" />
{selected.size > 0 && (
{selected.size} geselecteerd
)} {loading ? (
Laden...
) : products.length === 0 ? (
📦
Geen producten gevonden
) : (
{products.map(p => ( ))}
0} className="accent-[#00FF6A]" /> Product handleSort('name')}> Merk{sortIcon('name')} handleSort('price')}> Laagste prijs{sortIcon('price')} handleSort('offers')}> Aanbiedingen{sortIcon('offers')} Sneaker Cutout
toggleSelect(p.id)} className="accent-[#00FF6A]" />
{p.image_url ? ( ) : (
?
)} {p.name}
{p.brand || '-'} {Number(p.lowest_price) > 0 ? `€${Number(p.lowest_price).toFixed(2)}` : '-'} {p.offer_count} {p.cutout_image_url ? ( ) : ( )}
)}
{total.toLocaleString()} producten
Pagina {page} / {totalPages || 1}
); } export default function ProductTablePage() { return ; }