import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useNavigate } from 'react-router-dom' import { Library, Search, Box, CheckCircle2, Clock, AlertTriangle, LayoutGrid, List, Trash2, X, } from 'lucide-react' import { toast } from 'sonner' import { listProducts, deleteProduct } from '../api/products' import type { Product } from '../api/products' const CATEGORY_LABELS: Record = { TRB: 'TRB', Kugellager: 'Kugellager', CRB: 'CRB', Gleitlager: 'Gleitlager', SRB_TORB: 'SRB/TORB', Linear_schiene: 'Linear', Anschlagplatten: 'Anschlag', } function CadStatusChip({ status }: { status: string | null }) { if (!status) return ( no STEP ) if (status === 'completed') return ( ready ) if (status === 'processing') return ( processing ) if (status === 'failed') return ( failed ) return ( {status} ) } function ProductCard({ product, onClick, selected, onSelect }: { product: Product onClick: () => void selected: boolean onSelect: (checked: boolean) => void }) { return (
{/* Checkbox overlay */}
e.stopPropagation()} > onSelect(e.target.checked)} className="w-4 h-4 cursor-pointer" />
{/* Thumbnail */}
{(product.render_image_url || product.thumbnail_url) ? ( {product.name ) : ( )}
{product.pim_id}

{product.name || no name}

{product.category_key && ( {CATEGORY_LABELS[product.category_key] || product.category_key} )} {product.baureihe && ( {product.baureihe} )}
) } export default function ProductLibraryPage() { const navigate = useNavigate() const qc = useQueryClient() const [search, setSearch] = useState('') const [categoryFilter, setCategoryFilter] = useState('') const [hasCadFilter, setHasCadFilter] = useState('') const [materialsFilter, setMaterialsFilter] = useState('') const [view, setView] = useState<'grid' | 'table'>('grid') const [selected, setSelected] = useState>(new Set()) const { data: products, isLoading } = useQuery({ queryKey: ['products', { search, categoryFilter, hasCadFilter, materialsFilter }], queryFn: () => listProducts({ q: search || undefined, category_key: categoryFilter || undefined, has_cad: hasCadFilter === 'yes' ? true : hasCadFilter === 'no' ? false : undefined, materials_filter: materialsFilter || undefined, limit: 200, }), }) // ── Selection helpers ────────────────────────────────────────────────── const toggleOne = (id: string) => { setSelected((prev) => { const n = new Set(prev) n.has(id) ? n.delete(id) : n.add(id) return n }) } const allSelected = !!products?.length && products.every((p) => selected.has(p.id)) const toggleAll = () => { if (!products) return setSelected(allSelected ? new Set() : new Set(products.map((p) => p.id))) } // ── Bulk delete ──────────────────────────────────────────────────────── const deleteMut = useMutation({ mutationFn: async (ids: string[]) => { await Promise.all(ids.map((id) => deleteProduct(id, true))) }, onSuccess: (_, ids) => { toast.success(`${ids.length} product${ids.length > 1 ? 's' : ''} deleted`) setSelected(new Set()) qc.invalidateQueries({ queryKey: ['products'] }) }, onError: (e: any) => toast.error(e.response?.data?.detail || 'Delete failed'), }) const handleDeleteSelected = () => { const ids = [...selected] if (!ids.length) return if (!confirm(`Delete ${ids.length} product${ids.length > 1 ? 's' : ''}? This cannot be undone.`)) return deleteMut.mutate(ids) } return (
{/* Header */}

Product Library

{products ? `${products.length} products` : 'Loading\u2026'}

{/* View toggle */}
{/* Filters */}
setSearch(e.target.value)} className="w-full pl-9 pr-3 py-2 border border-border-default rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-accent" />
{/* Content */} {isLoading ? (
Loading products\u2026
) : !products?.length ? (

No products found

Upload an Excel file to populate the library

) : view === 'grid' ? ( /* ── Grid view ─────────────────────────────────────────────────── */
{products.map((product) => ( navigate(`/products/${product.id}`)} selected={selected.has(product.id)} onSelect={() => toggleOne(product.id)} /> ))}
) : ( /* ── Table view ────────────────────────────────────────────────── */
{products.map((product) => ( navigate(`/products/${product.id}`)} > ))}
PIM-ID Name Category Baureihe CAD Status
e.stopPropagation()}> toggleOne(product.id)} className="w-4 h-4 cursor-pointer" />
{(product.render_image_url || product.thumbnail_url) ? ( ) : ( )}
{product.pim_id} {product.name || no name} {product.category_key && ( {CATEGORY_LABELS[product.category_key] || product.category_key} )} {product.baureihe || '\u2014'}
)} {/* ── Floating action bar ───────────────────────────────────────── */} {selected.size > 0 && (
{selected.size} selected
)}
) }