import React, { useState, useMemo } from 'react'; import { FilamentV2, isFilamentV2 } from '../types/filament.v2'; import { Filament } from '../types/filament'; import { ColorSwatch } from './ColorSwatch'; import { InventoryBadge } from './InventoryBadge'; import { MaterialBadge } from './MaterialBadge'; import { EnhancedFilters } from './EnhancedFilters'; import '../styles/select.css'; interface FilamentTableV2Props { filaments: (Filament | FilamentV2)[]; loading?: boolean; error?: string; } export const FilamentTableV2: React.FC = ({ filaments, loading, error }) => { const [searchTerm, setSearchTerm] = useState(''); const [sortField, setSortField] = useState('color.name'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc'); const [filters, setFilters] = useState({ brand: '', material: '', storageCondition: '', isRefill: null as boolean | null, color: '' }); // Convert legacy filaments to V2 format for display const normalizedFilaments = useMemo(() => { return filaments.map(f => { if (isFilamentV2(f)) return f; // Convert legacy format const legacy = f as Filament; const material = { base: legacy.tip || 'PLA', modifier: legacy.finish !== 'Basic' ? legacy.finish : undefined }; const storageCondition = legacy.vakum?.toLowerCase().includes('vakuum') ? 'vacuum' : legacy.otvoreno?.toLowerCase().includes('otvorena') ? 'opened' : 'sealed'; const totalQuantity = parseInt(legacy.kolicina) || 1; const availableQuantity = totalQuantity > 0 ? totalQuantity : 0; return { id: legacy.id || `legacy-${Math.random().toString(36).substr(2, 9)}`, brand: legacy.brand, type: legacy.tip as any || 'PLA', material, color: { name: legacy.boja }, weight: { value: 1000, unit: 'g' as const }, diameter: 1.75, inventory: { total: totalQuantity, available: availableQuantity, inUse: 0, locations: { vacuum: storageCondition === 'vacuum' ? totalQuantity : 0, opened: storageCondition === 'opened' ? totalQuantity : 0, printer: 0 } }, pricing: { purchasePrice: legacy.cena ? parseFloat(legacy.cena) : undefined, currency: 'RSD' as const }, condition: { isRefill: legacy.refill === 'Da', storageCondition: storageCondition as any }, tags: [], createdAt: '', updatedAt: '' } as FilamentV2; }); }, [filaments]); // Get unique values for filters const uniqueValues = useMemo(() => ({ brands: [...new Set(normalizedFilaments.map(f => f.brand))].sort(), materials: [...new Set(normalizedFilaments.map(f => f.material.base))].sort(), colors: [...new Set(normalizedFilaments.map(f => f.color.name))].sort() }), [normalizedFilaments]); // Filter and sort filaments const filteredAndSortedFilaments = useMemo(() => { let filtered = normalizedFilaments.filter(filament => { // Only show available filaments if (filament.inventory.available === 0) return false; // Search filter const searchLower = searchTerm.toLowerCase(); const matchesSearch = filament.brand.toLowerCase().includes(searchLower) || filament.material.base.toLowerCase().includes(searchLower) || (filament.material.modifier?.toLowerCase().includes(searchLower)) || filament.color.name.toLowerCase().includes(searchLower) || false; // SKU removed // Other filters const matchesBrand = !filters.brand || filament.brand === filters.brand; const matchesMaterial = !filters.material || filament.material.base === filters.material || `${filament.material.base}-${filament.material.modifier}` === filters.material; const matchesStorage = !filters.storageCondition || filament.condition.storageCondition === filters.storageCondition; const matchesRefill = filters.isRefill === null || filament.condition.isRefill === filters.isRefill; const matchesColor = !filters.color || filament.color.name === filters.color; return matchesSearch && matchesBrand && matchesMaterial && matchesStorage && matchesRefill && matchesColor; }); // Sort filtered.sort((a, b) => { let aVal: any = a; let bVal: any = b; // Handle nested properties const fields = sortField.split('.'); for (const field of fields) { aVal = aVal?.[field]; bVal = bVal?.[field]; } if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1; if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1; return 0; }); return filtered; }, [normalizedFilaments, searchTerm, filters, sortField, sortOrder]); const handleSort = (field: string) => { if (sortField === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortOrder('asc'); } }; // Inventory summary const inventorySummary = useMemo(() => { const summary = { totalSpools: 0, availableSpools: 0, vacuumCount: 0, openedCount: 0, refillCount: 0 }; normalizedFilaments.forEach(f => { summary.totalSpools += f.inventory.total; summary.availableSpools += f.inventory.available; summary.vacuumCount += f.inventory.locations.vacuum; summary.openedCount += f.inventory.locations.opened; if (f.condition.isRefill) { summary.refillCount += f.inventory.total; } }); return summary; }, [normalizedFilaments]); if (loading) { return
Učitavanje...
; } if (error) { return
{error}
; } return (
{/* Inventory Summary */}
Ukupno filamenta
{inventorySummary.totalSpools}
Dostupno
{inventorySummary.availableSpools}
Vakum
{inventorySummary.vacuumCount}
Otvoreno
{inventorySummary.openedCount}
Refill
{inventorySummary.refillCount}
{/* Search Bar */}
setSearchTerm(e.target.value)} className="w-full px-4 py-2 pl-10 pr-4 text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:border-blue-500" />
{/* Enhanced Filters */} {/* Icon Legend */}

Legenda stanja:

Vakuum pakovanje
Otvoreno
{/* Table */}
{filteredAndSortedFilaments.map(filament => ( ))}
handleSort('brand')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Brend handleSort('material.base')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Materijal handleSort('color.name')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Boja Stanje Težina handleSort('pricing.purchasePrice')} className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"> Cena Status
{filament.brand}
{filament.inventory.locations.vacuum > 0 && ( )} {filament.inventory.locations.opened > 0 && ( )} {filament.inventory.locations.printer > 0 && ( )}
{filament.weight.value}{filament.weight.unit} {(() => { // PLA Basic pricing logic if (filament.material.base === 'PLA' && !filament.material.modifier) { if (filament.condition.isRefill && filament.condition.storageCondition !== 'opened') { return '3.499 RSD'; } else if (!filament.condition.isRefill && filament.condition.storageCondition === 'vacuum') { return '3.999 RSD'; } } // Show original price if available return filament.pricing.purchasePrice ? `${filament.pricing.purchasePrice.toLocaleString('sr-RS')} ${filament.pricing.currency}` : '-'; })()}
{filament.condition.isRefill && ( Refill )} {filament.inventory.available === 1 && ( Poslednji )}
Prikazano {filteredAndSortedFilaments.length} dostupnih filamenata
); };