/** * BarcodeSheet Component * Generates professional barcode labels for inventory products with PDF download * - Filters: Product type, specific products, quantity control * - Downloads as PDF instead of printing */ function BarcodeSheet({ entries, onClose }) { const [barcodeData, setBarcodeData] = React.useState([]); const [filteredData, setFilteredData] = React.useState([]); const [filters, setFilters] = React.useState({ type: 'all', // 'all', 'mobile', 'accessory' productNo: 'all', customQuantity: {} }); const [downloading, setDownloading] = React.useState(false); // Generate barcode data from entries React.useEffect(() => { const data = []; entries.forEach(entry => { const category = entry.category || ''; entry.items?.forEach(item => { // Check if it's a mobile by category OR by having IMEI numbers const hasImei = Array.isArray(item.imes) && item.imes.length > 0 && item.imes.some(imei => imei && imei.trim()); const isMobile = category.toLowerCase().includes('mobile') || category.toLowerCase() === 'phone' || hasImei; if (isMobile && hasImei) { // For mobiles: create separate barcode for each IMEI item.imes.forEach((imei, index) => { if (imei && imei.trim()) { data.push({ id: `${item.productNo}-${imei}`, productNo: item.productNo || 'N/A', productName: item.productName || 'Unnamed Product', brand: item.brand || '', model: item.model || '', imei: imei, barcodeValue: imei.trim(), type: 'Mobile', quantity: 1 }); } }); } else { // For accessories: barcode = product number const quantity = parseInt(item.quantity) || 1; data.push({ id: `${item.productNo}`, productNo: item.productNo || 'N/A', productName: item.productName || 'Unnamed Product', brand: item.brand || '', model: item.model || '', imei: null, barcodeValue: item.productNo || 'N/A', type: 'Accessory', quantity: quantity, maxQuantity: quantity }); } }); }); setBarcodeData(data); }, [entries]); // Apply filters React.useEffect(() => { let filtered = [...barcodeData]; // Filter by type if (filters.type !== 'all') { const targetType = filters.type === 'mobile' ? 'Mobile' : 'Accessory'; filtered = filtered.filter(item => item.type === targetType); } // Filter by product if (filters.productNo !== 'all') { filtered = filtered.filter(item => item.productNo === filters.productNo); } // Expand accessories based on custom quantity const expanded = []; filtered.forEach(item => { if (item.type === 'Accessory') { const qty = filters.customQuantity[item.productNo] || item.quantity; for (let i = 0; i < qty; i++) { expanded.push({ ...item, id: `${item.id}-${i}` }); } } else { expanded.push(item); } }); setFilteredData(expanded); }, [barcodeData, filters]); // Generate barcodes React.useEffect(() => { if (filteredData.length > 0 && window.JsBarcode) { setTimeout(() => { filteredData.forEach((item, index) => { try { const canvas = document.getElementById(`barcode-${index}`); if (canvas) { const cleanValue = item.barcodeValue.replace(/[^a-zA-Z0-9]/g, '').toUpperCase(); if (cleanValue.length > 0) { window.JsBarcode(canvas, cleanValue, { format: 'CODE128', width: 2, height: 60, displayValue: true, fontSize: 12, margin: 5 }); } } } catch (error) { console.error('Barcode generation error:', error); } }); }, 100); } }, [filteredData]); // Get unique products for filter const uniqueProducts = React.useMemo(() => { const products = new Map(); barcodeData.forEach(item => { if (!products.has(item.productNo)) { products.set(item.productNo, { productNo: item.productNo, productName: item.productName, brand: item.brand, model: item.model, type: item.type }); } }); return Array.from(products.values()); }, [barcodeData]); const handleDownloadPDF = async () => { if (filteredData.length === 0) { alert('No labels to download!'); return; } setDownloading(true); try { const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const pageWidth = 210; const pageHeight = 297; const margin = 10; const labelWidth = (pageWidth - margin * 3) / 2; const labelHeight = 50; const gap = 10; let x = margin; let y = margin; let labelCount = 0; for (let i = 0; i < filteredData.length; i++) { const item = filteredData[i]; const canvas = document.getElementById(`barcode-${i}`); if (canvas) { // Add new page if needed if (y + labelHeight > pageHeight - margin) { doc.addPage(); x = margin; y = margin; } // Draw border doc.setDrawColor(200); doc.rect(x, y, labelWidth, labelHeight); // Product name doc.setFontSize(12); doc.setFont(undefined, 'bold'); const productNameLines = doc.splitTextToSize(item.productName, labelWidth - 4); doc.text(productNameLines, x + labelWidth / 2, y + 6, { align: 'center' }); let currentY = y + 6 + (productNameLines.length * 5); // Brand/Model if (item.brand || item.model) { doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.text(`${item.brand} ${item.model}`, x + labelWidth / 2, currentY + 4, { align: 'center' }); currentY += 4; } // Product No doc.setFontSize(9); doc.setTextColor(100); doc.text(`Product No: ${item.productNo}`, x + labelWidth / 2, currentY + 4, { align: 'center' }); currentY += 4; // IMEI if present if (item.imei) { doc.setFontSize(8); doc.setTextColor(200, 150, 0); doc.text(`IMEI: ${item.imei}`, x + labelWidth / 2, currentY + 4, { align: 'center' }); currentY += 4; } // Add barcode image const imgData = canvas.toDataURL('image/png'); doc.addImage(imgData, 'PNG', x + 5, currentY + 2, labelWidth - 10, 15); // Type badge doc.setFontSize(8); doc.setFont(undefined, 'bold'); if (item.type === 'Mobile') { doc.setTextColor(30, 64, 175); } else { doc.setTextColor(21, 128, 61); } doc.text(item.type, x + labelWidth / 2, currentY + 20, { align: 'center' }); doc.setTextColor(0); // Move to next position labelCount++; if (labelCount % 2 === 0) { x = margin; y += labelHeight + gap; } else { x += labelWidth + gap; } } } doc.save(`Barcode_Labels_${new Date().getTime()}.pdf`); } catch (error) { console.error('PDF generation error:', error); alert('Error generating PDF. Please try again.'); } finally { setDownloading(false); } }; const handleQuantityChange = (productNo, value) => { setFilters(prev => ({ ...prev, customQuantity: { ...prev.customQuantity, [productNo]: parseInt(value) || 0 } })); }; return (
{filteredData.length} label{filteredData.length !== 1 ? 's' : ''} ready to download
No Labels to Display
Adjust your filters or add products to your inventory.