'use client'; import { useState, useEffect, useMemo } from 'react'; import { useParams, notFound } from 'next/navigation'; import { productService, printerModelService } from '@/src/services/api'; import { Product, ProductCategory, ProductCondition, PrinterModel } from '@/src/types/product'; import '@/src/styles/select.css'; const CATEGORY_MAP: Record = { stampaci: { category: 'printer', label: 'Stampac', plural: 'Stampaci' }, ploce: { category: 'build_plate', label: 'Ploca', plural: 'Ploce' }, mlaznice: { category: 'nozzle', label: 'Mlaznica', plural: 'Mlaznice' }, delovi: { category: 'spare_part', label: 'Deo', plural: 'Delovi' }, oprema: { category: 'accessory', label: 'Oprema', plural: 'Oprema' }, }; const CONDITION_LABELS: Record = { new: 'Novo', used_like_new: 'Korisceno - kao novo', used_good: 'Korisceno - dobro', used_fair: 'Korisceno - pristojno', }; // Categories that support printer compatibility const PRINTER_COMPAT_CATEGORIES: ProductCategory[] = ['build_plate', 'nozzle', 'spare_part']; export default function CategoryPage() { const params = useParams(); const slug = params.category as string; const categoryConfig = CATEGORY_MAP[slug]; // If slug is not recognized, show not found if (!categoryConfig) { notFound(); } const { category, label, plural } = categoryConfig; const [products, setProducts] = useState([]); const [printerModels, setPrinterModels] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [editingProduct, setEditingProduct] = useState(null); const [showAddForm, setShowAddForm] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [sortField, setSortField] = useState('name'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc'); const [selectedProducts, setSelectedProducts] = useState>(new Set()); const fetchProducts = async () => { try { setLoading(true); const [data, models] = await Promise.all([ productService.getAll({ category }), PRINTER_COMPAT_CATEGORIES.includes(category) ? printerModelService.getAll().catch(() => []) : Promise.resolve([]), ]); setProducts(data); setPrinterModels(models); } catch (err) { setError('Greska pri ucitavanju proizvoda'); console.error('Fetch error:', err); } finally { setLoading(false); } }; useEffect(() => { fetchProducts(); }, [category]); const handleSort = (field: string) => { if (sortField === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortOrder('asc'); } }; const filteredAndSorted = useMemo(() => { let filtered = products; if (searchTerm) { const search = searchTerm.toLowerCase(); filtered = products.filter(p => p.name.toLowerCase().includes(search) || p.description?.toLowerCase().includes(search) ); } return [...filtered].sort((a, b) => { let aVal: any = a[sortField as keyof Product] || ''; let bVal: any = b[sortField as keyof Product] || ''; if (sortField === 'price' || sortField === 'stock') { aVal = Number(aVal) || 0; bVal = Number(bVal) || 0; return sortOrder === 'asc' ? aVal - bVal : bVal - aVal; } aVal = String(aVal).toLowerCase(); bVal = String(bVal).toLowerCase(); if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1; if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1; return 0; }); }, [products, searchTerm, sortField, sortOrder]); const handleSave = async (product: Partial & { printer_model_ids?: string[] }) => { try { const dataToSave = { ...product, category, }; if (product.id) { await productService.update(product.id, dataToSave); } else { await productService.create(dataToSave); } setEditingProduct(null); setShowAddForm(false); fetchProducts(); } catch (err: any) { const msg = err.response?.data?.error || err.message || 'Greska pri cuvanju proizvoda'; setError(msg); console.error('Save error:', err); } }; const handleDelete = async (id: string) => { if (!confirm('Da li ste sigurni da zelite obrisati ovaj proizvod?')) return; try { await productService.delete(id); fetchProducts(); } catch (err) { setError('Greska pri brisanju proizvoda'); console.error('Delete error:', err); } }; const handleBulkDelete = async () => { if (selectedProducts.size === 0) return; if (!confirm(`Obrisati ${selectedProducts.size} proizvoda?`)) return; try { await Promise.all(Array.from(selectedProducts).map(id => productService.delete(id))); setSelectedProducts(new Set()); fetchProducts(); } catch (err) { setError('Greska pri brisanju proizvoda'); console.error('Bulk delete error:', err); } }; const toggleSelection = (id: string) => { const next = new Set(selectedProducts); if (next.has(id)) next.delete(id); else next.add(id); setSelectedProducts(next); }; const toggleSelectAll = () => { if (selectedProducts.size === filteredAndSorted.length) { setSelectedProducts(new Set()); } else { setSelectedProducts(new Set(filteredAndSorted.map(p => p.id))); } }; if (loading) { return (
Ucitavanje...
); } return (
{/* Page Header */}

{plural}

{products.length} proizvoda ukupno

{!showAddForm && !editingProduct && ( )} {selectedProducts.size > 0 && ( )}
{error && (
{error}
)} {/* Search */}
setSearchTerm(e.target.value)} className="w-full px-4 py-2 pl-10 text-white/60 bg-white/[0.04] border border-white/[0.08] rounded-2xl focus:outline-none focus:border-blue-500" />
{/* Add/Edit Form */} {(showAddForm || editingProduct) && ( { setEditingProduct(null); setShowAddForm(false); }} /> )} {/* Products Table */}
{filteredAndSorted.map(product => ( ))} {filteredAndSorted.length === 0 && ( )}
0 && selectedProducts.size === filteredAndSorted.length} onChange={toggleSelectAll} className="w-4 h-4 text-blue-600 bg-white/[0.06] border-white/[0.08] rounded" /> handleSort('name')} className="px-4 py-3 text-left text-xs font-medium text-white/60 uppercase cursor-pointer hover:bg-white/[0.08]"> Naziv {sortField === 'name' && (sortOrder === 'asc' ? '\u2191' : '\u2193')} handleSort('condition')} className="px-4 py-3 text-left text-xs font-medium text-white/60 uppercase cursor-pointer hover:bg-white/[0.08]"> Stanje {sortField === 'condition' && (sortOrder === 'asc' ? '\u2191' : '\u2193')} handleSort('price')} className="px-4 py-3 text-left text-xs font-medium text-white/60 uppercase cursor-pointer hover:bg-white/[0.08]"> Cena {sortField === 'price' && (sortOrder === 'asc' ? '\u2191' : '\u2193')} handleSort('stock')} className="px-4 py-3 text-left text-xs font-medium text-white/60 uppercase cursor-pointer hover:bg-white/[0.08]"> Kolicina {sortField === 'stock' && (sortOrder === 'asc' ? '\u2191' : '\u2193')} Popust Akcije
toggleSelection(product.id)} className="w-4 h-4 text-blue-600 bg-white/[0.06] border-white/[0.08] rounded" />
{product.image_url && ( {product.name} )}
{product.name}
{product.description && (
{product.description}
)}
{CONDITION_LABELS[product.condition] || product.condition} {product.price.toLocaleString('sr-RS')} RSD {product.stock > 2 ? ( {product.stock} ) : product.stock > 0 ? ( {product.stock} ) : ( 0 )} {product.sale_active && product.sale_percentage ? ( -{product.sale_percentage}% ) : ( - )}
Nema proizvoda u ovoj kategoriji
); } // Product Form Component function ProductForm({ product, printerModels, showPrinterCompat, onSave, onCancel, }: { product?: Product; printerModels: PrinterModel[]; showPrinterCompat: boolean; onSave: (data: Partial & { printer_model_ids?: string[] }) => void; onCancel: () => void; }) { const [formData, setFormData] = useState({ name: product?.name || '', description: product?.description || '', price: product?.price || 0, condition: (product?.condition || 'new') as ProductCondition, stock: product?.stock || 0, image_url: product?.image_url || '', attributes: JSON.stringify(product?.attributes || {}, null, 2), }); const [selectedPrinterIds, setSelectedPrinterIds] = useState([]); // Load compatible printers on mount useEffect(() => { if (product?.compatible_printers) { // Map names to IDs const ids = printerModels .filter(m => product.compatible_printers?.includes(m.name)) .map(m => m.id); setSelectedPrinterIds(ids); } }, [product, printerModels]); const handleChange = (e: React.ChangeEvent) => { const { name, value, type } = e.target; setFormData(prev => ({ ...prev, [name]: type === 'number' ? (parseFloat(value) || 0) : value, })); }; const togglePrinter = (id: string) => { setSelectedPrinterIds(prev => prev.includes(id) ? prev.filter(pid => pid !== id) : [...prev, id] ); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!formData.name.trim()) { alert('Naziv je obavezan'); return; } let attrs = {}; try { attrs = formData.attributes.trim() ? JSON.parse(formData.attributes) : {}; } catch { alert('Atributi moraju biti validan JSON'); return; } onSave({ id: product?.id, name: formData.name, description: formData.description || undefined, price: formData.price, condition: formData.condition, stock: formData.stock, image_url: formData.image_url || undefined, attributes: attrs, printer_model_ids: showPrinterCompat ? selectedPrinterIds : undefined, }); }; return (

{product ? 'Izmeni proizvod' : 'Dodaj proizvod'}