|
|
import React, { useState } from 'react'; |
|
|
import { CreditCard as Edit, Trash2, Minus, Plus, ShoppingCart } from 'lucide-react'; |
|
|
import { Product, Language } from '../types'; |
|
|
import { ProductForm } from './ProductForm'; |
|
|
|
|
|
interface ExcelTableProps { |
|
|
products: Product[]; |
|
|
language: Language; |
|
|
onProductUpdate: (product: Product) => void; |
|
|
onProductDelete: (productId: string) => void; |
|
|
onProductAdd: (product: Product) => void; |
|
|
onQuantityChange: (productId: string, change: number) => void; |
|
|
} |
|
|
|
|
|
export const ExcelTable: React.FC<ExcelTableProps> = ({ |
|
|
products, |
|
|
language, |
|
|
onProductUpdate, |
|
|
onProductDelete, |
|
|
onProductAdd, |
|
|
onQuantityChange |
|
|
}) => { |
|
|
const [editingProduct, setEditingProduct] = useState<Product | undefined>(); |
|
|
const [showForm, setShowForm] = useState(false); |
|
|
const [sellQuantities, setSellQuantities] = useState<{ [key: string]: number }>({}); |
|
|
|
|
|
const isArabic = language.code === 'ar'; |
|
|
|
|
|
const labels = { |
|
|
ar: { |
|
|
addProduct: 'إضافة منتج جديد', |
|
|
productName: 'اسم المنتج', |
|
|
quantity: 'الكمية', |
|
|
price: 'السعر', |
|
|
category: 'الفئة', |
|
|
actions: 'الإجراءات', |
|
|
edit: 'تعديل', |
|
|
delete: 'حذف', |
|
|
sell: 'بيع', |
|
|
sellLabel: 'كمية البيع', |
|
|
noProducts: 'لا توجد منتجات', |
|
|
total: 'المجموع', |
|
|
items: 'عنصر', |
|
|
value: 'القيمة الإجمالية' |
|
|
}, |
|
|
en: { |
|
|
addProduct: 'Add New Product', |
|
|
productName: 'Product Name', |
|
|
quantity: 'Quantity', |
|
|
price: 'Price', |
|
|
category: 'Category', |
|
|
actions: 'Actions', |
|
|
edit: 'Edit', |
|
|
delete: 'Delete', |
|
|
sell: 'Sell', |
|
|
sellLabel: 'Sell Qty', |
|
|
noProducts: 'No products found', |
|
|
total: 'Total', |
|
|
items: 'items', |
|
|
value: 'Total Value' |
|
|
} |
|
|
}; |
|
|
|
|
|
const t = labels[language.code]; |
|
|
|
|
|
const totalItems = products.reduce((sum, product) => sum + product.quantity, 0); |
|
|
const totalValue = products.reduce((sum, product) => sum + (product.quantity * product.price), 0); |
|
|
|
|
|
const handleSellProduct = (productId: string) => { |
|
|
const sellQty = sellQuantities[productId] || 1; |
|
|
const product = products.find(p => p.id === productId); |
|
|
if (product && sellQty <= (product.currentStock || product.quantity)) { |
|
|
onQuantityChange(productId, -sellQty); |
|
|
|
|
|
const updatedProduct = { |
|
|
...product, |
|
|
totalSold: (product.totalSold || 0) + sellQty, |
|
|
currentStock: (product.currentStock || product.quantity) - sellQty |
|
|
}; |
|
|
onProductUpdate(updatedProduct); |
|
|
setSellQuantities({ ...sellQuantities, [productId]: 1 }); |
|
|
} |
|
|
}; |
|
|
|
|
|
const formatPrice = (price: number) => { |
|
|
return new Intl.NumberFormat(isArabic ? 'ar-SA' : 'en-US', { |
|
|
style: 'currency', |
|
|
currency: 'SAR' |
|
|
}).format(price); |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<div className="space-y-6"> |
|
|
{/* Stats Cards */} |
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> |
|
|
<div className="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-6 rounded-xl shadow-lg"> |
|
|
<div className="flex items-center justify-between"> |
|
|
<div> |
|
|
<p className="text-blue-100 text-sm">{t.total} {t.items}</p> |
|
|
<p className="text-3xl font-bold">{totalItems.toLocaleString()}</p> |
|
|
</div> |
|
|
<ShoppingCart className="w-10 h-10 text-blue-200" /> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="bg-gradient-to-r from-emerald-500 to-emerald-600 text-white p-6 rounded-xl shadow-lg"> |
|
|
<div className="flex items-center justify-between"> |
|
|
<div> |
|
|
<p className="text-emerald-100 text-sm">{t.value}</p> |
|
|
<p className="text-3xl font-bold">{formatPrice(totalValue)}</p> |
|
|
</div> |
|
|
<div className="w-10 h-10 bg-emerald-400 rounded-full flex items-center justify-center"> |
|
|
<span className="text-2xl">💰</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="bg-gradient-to-r from-amber-500 to-amber-600 text-white p-6 rounded-xl shadow-lg"> |
|
|
<div className="flex items-center justify-between"> |
|
|
<div> |
|
|
<p className="text-amber-100 text-sm">Products</p> |
|
|
<p className="text-3xl font-bold">{products.length}</p> |
|
|
</div> |
|
|
<div className="w-10 h-10 bg-amber-400 rounded-full flex items-center justify-center"> |
|
|
<span className="text-2xl">📦</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* Add Product Button */} |
|
|
<div className="flex justify-between items-center"> |
|
|
<h2 className="text-2xl font-bold text-gray-900"> |
|
|
{isArabic ? 'إدارة المخزون' : 'Inventory Management'} |
|
|
</h2> |
|
|
<button |
|
|
onClick={() => setShowForm(true)} |
|
|
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-medium transition-colors flex items-center gap-2 shadow-lg" |
|
|
> |
|
|
<Plus className="w-5 h-5" /> |
|
|
{t.addProduct} |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
{/* Table */} |
|
|
<div className="bg-white rounded-xl shadow-lg overflow-hidden"> |
|
|
{products.length === 0 ? ( |
|
|
<div className="p-12 text-center"> |
|
|
<div className="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4"> |
|
|
<ShoppingCart className="w-12 h-12 text-gray-400" /> |
|
|
</div> |
|
|
<p className="text-xl text-gray-500 mb-2">{t.noProducts}</p> |
|
|
<p className="text-gray-400 mb-6"> |
|
|
{isArabic ? 'قم بإضافة منتج جديد أو تحميل ملف Excel' : 'Add a new product or upload an Excel file'} |
|
|
</p> |
|
|
</div> |
|
|
) : ( |
|
|
<div className="overflow-x-auto"> |
|
|
<table className="w-full"> |
|
|
<thead className="bg-gray-50 border-b border-gray-200"> |
|
|
<tr> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.supplier} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.productName} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.received} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.sold} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.remaining} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.price} |
|
|
</th> |
|
|
<th className="text-left py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.sell} |
|
|
</th> |
|
|
<th className="text-right py-4 px-6 font-semibold text-gray-700"> |
|
|
{t.actions} |
|
|
</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody className="divide-y divide-gray-200"> |
|
|
{products.map((product) => ( |
|
|
<tr key={product.id} className="hover:bg-gray-50 transition-colors"> |
|
|
<td className="py-4 px-6"> |
|
|
<div className="text-sm"> |
|
|
<div className="font-medium text-gray-900"> |
|
|
{isArabic ? product.supplierAr : product.supplier} |
|
|
</div> |
|
|
<div className="text-gray-500 text-xs"> |
|
|
{product.importDate} |
|
|
</div> |
|
|
</div> |
|
|
</td> |
|
|
<td className="py-4 px-6"> |
|
|
<div> |
|
|
<div className="font-medium text-gray-900"> |
|
|
{isArabic ? product.nameAr : product.nameEn} |
|
|
</div> |
|
|
<div className="text-sm text-gray-500"> |
|
|
{product.secoCode && `Code: ${product.secoCode}`} |
|
|
{product.categoryAr && ( |
|
|
<div className="text-xs text-gray-400"> |
|
|
{isArabic ? product.categoryAr : product.categoryEn} |
|
|
</div> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
</td> |
|
|
<td className="py-4 px-6"> |
|
|
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800"> |
|
|
{product.totalReceived || 0} |
|
|
</span> |
|
|
</td> |
|
|
<td className="py-4 px-6 font-medium"> |
|
|
<span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-100 text-red-800"> |
|
|
{product.totalSold || 0} |
|
|
</span> |
|
|
</td> |
|
|
<td className="py-4 px-6 text-gray-600"> |
|
|
<span className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${ |
|
|
(product.currentStock || product.quantity) > 10 |
|
|
? 'bg-green-100 text-green-800' |
|
|
: (product.currentStock || product.quantity) > 0 |
|
|
? 'bg-yellow-100 text-yellow-800' |
|
|
: 'bg-red-100 text-red-800' |
|
|
}`}> |
|
|
{product.currentStock || product.quantity} |
|
|
</span> |
|
|
</td> |
|
|
<td className="py-4 px-6 font-medium"> |
|
|
{formatPrice(product.price)} |
|
|
</td> |
|
|
<td className="py-4 px-6"> |
|
|
<div className="flex items-center gap-2"> |
|
|
<input |
|
|
type="number" |
|
|
min="1" |
|
|
max={product.currentStock || product.quantity} |
|
|
value={sellQuantities[product.id] || 1} |
|
|
onChange={(e) => setSellQuantities({ |
|
|
...sellQuantities, |
|
|
[product.id]: Math.min(Number(e.target.value), product.currentStock || product.quantity) |
|
|
})} |
|
|
className="w-16 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent" |
|
|
/> |
|
|
<button |
|
|
onClick={() => handleSellProduct(product.id)} |
|
|
disabled={(product.currentStock || product.quantity) === 0} |
|
|
className="p-1 text-emerald-600 hover:bg-emerald-50 rounded transition-colors disabled:text-gray-400 disabled:hover:bg-transparent" |
|
|
title={t.sell} |
|
|
> |
|
|
<Minus className="w-4 h-4" /> |
|
|
</button> |
|
|
</div> |
|
|
</td> |
|
|
<td className="py-4 px-6"> |
|
|
<div className="flex items-center justify-end gap-2"> |
|
|
<button |
|
|
onClick={() => onQuantityChange(product.id, 1)} |
|
|
className="p-2 text-blue-600 hover:bg-blue-50 rounded-lg transition-colors" |
|
|
title="Add 1" |
|
|
> |
|
|
<Plus className="w-4 h-4" /> |
|
|
</button> |
|
|
<button |
|
|
onClick={() => { |
|
|
setEditingProduct(product); |
|
|
setShowForm(true); |
|
|
}} |
|
|
className="p-2 text-amber-600 hover:bg-amber-50 rounded-lg transition-colors" |
|
|
title={t.edit} |
|
|
> |
|
|
<Edit className="w-4 h-4" /> |
|
|
</button> |
|
|
<button |
|
|
onClick={() => onProductDelete(product.id)} |
|
|
className="p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors" |
|
|
title={t.delete} |
|
|
> |
|
|
<Trash2 className="w-4 h-4" /> |
|
|
</button> |
|
|
</div> |
|
|
</td> |
|
|
</tr> |
|
|
))} |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
)} |
|
|
</div> |
|
|
|
|
|
{/* Product Form Modal */} |
|
|
<ProductForm |
|
|
product={editingProduct} |
|
|
isOpen={showForm} |
|
|
onClose={() => { |
|
|
setShowForm(false); |
|
|
setEditingProduct(undefined); |
|
|
}} |
|
|
onSave={(product) => { |
|
|
if (editingProduct) { |
|
|
onProductUpdate(product); |
|
|
} else { |
|
|
onProductAdd(product); |
|
|
} |
|
|
setEditingProduct(undefined); |
|
|
}} |
|
|
language={language} |
|
|
/> |
|
|
</div> |
|
|
); |
|
|
}; |