import React, { useState, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { RootState } from '../store'; import { addProduct, updateProduct, deleteProduct } from '../store/slices/productSlice'; import { Upload, Plus, Search, Edit, Trash2, Package } from 'lucide-react'; import toast from 'react-hot-toast'; import api from '../utils/api'; interface ProductFormData { title: string; description: string; price: number; costOfGoods: number; printingCost: number; sku: string; category: string; tags: string; inventory: { quantity: number; lowStockAlert: number; }; } const Products = () => { const { products } = useSelector((state: RootState) => state.products); const dispatch = useDispatch(); const [showAddForm, setShowAddForm] = useState(false); const [editingProduct, setEditingProduct] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [selectedCategory, setSelectedCategory] = useState('all'); const fileInputRef = useRef(null); const [formData, setFormData] = useState({ title: '', description: '', price: 0, costOfGoods: 0, printingCost: 0, sku: '', category: '', tags: '', inventory: { quantity: 0, lowStockAlert: 5 } }); const categories = ['Jewelry', 'Accessories', 'Home & Living', 'Clothing', 'Art', 'Craft Supplies', 'Other']; const handleCSVImport = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; if (!file.name.endsWith('.csv')) { toast.error('Please select a valid CSV file'); return; } const reader = new FileReader(); reader.onload = async (e) => { try { const text = e.target?.result as string; const lines = text.split('\n'); const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, '')); toast.loading('Importing products from CSV...'); const importedProducts = []; for (let i = 1; i < lines.length; i++) { if (lines[i].trim() === '') continue; const values = lines[i].split(',').map(v => v.trim().replace(/"/g, '')); const productData: any = { printingCost: 0 }; headers.forEach((header, index) => { const value = values[index] || ''; switch (header.toLowerCase()) { case 'title': case 'product title': case 'listing title': productData.title = value; break; case 'description': productData.description = value; break; case 'price': case 'listing price': productData.price = parseFloat(value.replace(/[$,]/g, '')) || 0; break; case 'cost': case 'cost of goods': case 'cogs': productData.costOfGoods = parseFloat(value.replace(/[$,]/g, '')) || 0; break; case 'printing cost': case 'print cost': case 'printing': productData.printingCost = parseFloat(value.replace(/[$,]/g, '')) || 0; break; case 'sku': productData.sku = value || `SKU-${Date.now()}-${i}`; break; case 'category': productData.category = value || 'Other'; break; case 'tags': productData.tags = value.split(';').map((t: string) => t.trim()); break; case 'quantity': case 'stock': case 'inventory': productData.quantity = parseInt(value) || 0; break; } }); if (productData.title) { importedProducts.push({ ...productData, inventory: { quantity: productData.quantity || 0, lowStockAlert: 5 }, tags: productData.tags || [], isActive: true }); } } let imported = 0; for (const p of importedProducts) { try { const res = await api.post('/products', p); dispatch(addProduct(res.data)); imported++; } catch {} } toast.success(`Successfully imported ${imported} products`); } catch (error) { console.error('Import error:', error); toast.error('Failed to import CSV file'); } }; reader.readAsText(file); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (editingProduct) { handleUpdateProduct(); } else { handleAddProduct(); } }; const handleAddProduct = async () => { try { const payload = { ...formData, tags: formData.tags.split(',').map(t => t.trim()).filter(t => t), isActive: true }; const res = await api.post('/products', payload); dispatch(addProduct(res.data)); toast.success('Product added successfully'); setShowAddForm(false); setFormData({ title: '', description: '', price: 0, costOfGoods: 0, printingCost: 0, sku: '', category: '', tags: '', inventory: { quantity: 0, lowStockAlert: 5 } }); } catch (error) { toast.error('Failed to add product'); } }; const handleEditProduct = (productId: string) => { const product = products.find(p => p._id === productId); if (product) { setFormData({ title: product.title, description: product.description || '', price: product.price, costOfGoods: product.costOfGoods, printingCost: product.printingCost || 0, sku: product.sku || '', category: product.category || '', tags: product.tags?.join(', ') || '', inventory: { quantity: product.inventory?.quantity || 0, lowStockAlert: product.inventory?.lowStockAlert || 5 } }); setEditingProduct(productId); setShowAddForm(true); } }; const handleUpdateProduct = async () => { if (!editingProduct) return; try { const payload = { ...formData, tags: formData.tags.split(',').map(t => t.trim()).filter(t => t), isActive: true }; const res = await api.put(`/products/${editingProduct}`, payload); dispatch(updateProduct(res.data)); toast.success('Product updated successfully'); setShowAddForm(false); setEditingProduct(null); setFormData({ title: '', description: '', price: 0, costOfGoods: 0, printingCost: 0, sku: '', category: '', tags: '', inventory: { quantity: 0, lowStockAlert: 5 } }); } catch (error) { toast.error('Failed to update product'); } }; const handleDeleteProduct = (productId: string) => { setShowDeleteConfirm(productId); }; const confirmDeleteProduct = async () => { if (showDeleteConfirm) { try { await api.delete(`/products/${showDeleteConfirm}`); dispatch(deleteProduct(showDeleteConfirm)); toast.success('Product deleted successfully'); } catch { toast.error('Failed to delete product'); } setShowDeleteConfirm(null); } }; const cancelDelete = () => { setShowDeleteConfirm(null); }; const cancelEdit = () => { setShowAddForm(false); setEditingProduct(null); setFormData({ title: '', description: '', price: 0, costOfGoods: 0, printingCost: 0, sku: '', category: '', tags: '', inventory: { quantity: 0, lowStockAlert: 5 } }); }; const filteredProducts = products.filter(product => { const matchesSearch = product.title.toLowerCase().includes(searchTerm.toLowerCase()) || product.sku.toLowerCase().includes(searchTerm.toLowerCase()); const matchesCategory = selectedCategory === 'all' || product.category === selectedCategory; return matchesSearch && matchesCategory; }); return (

Products

Manage your product inventory and listings

{/* Filters */}
setSearchTerm(e.target.value)} />
{/* Add Product Form Modal */} {showAddForm && (

{editingProduct ? 'Edit Product' : 'Add New Product'}

setFormData({...formData, title: e.target.value})} />
setFormData({...formData, sku: e.target.value})} />