import React, { useState, useEffect, useMemo, useRef } from 'react'; import { ShoppingCart, Package, Users, Settings, LogOut, Plus, Trash2, Edit, CheckCircle, Clock, XCircle, ChevronRight, Box, ListOrdered, Tag, Search, TrendingUp, AlertCircle, Calendar, Sliders, Info, Link as LinkIcon, Menu, X } from 'lucide-react'; // --- البيانات الافتراضية (Seed Data) --- const INITIAL_CATEGORIES = [ { id: 'c1', name: 'معلبات' }, { id: 'c2', name: 'بقوليات' }, { id: 'c3', name: 'ألبان وأجبان' }, ]; const INITIAL_UNITS = [ { id: 'u1', name: 'كرتونة', pieces: 12 }, { id: 'u2', name: 'بالتة', pieces: 120 }, { id: 'u3', name: 'شيكارة', pieces: 1 }, { id: 'u4', name: 'عبوة', pieces: 6 }, ]; const INITIAL_CUSTOMERS = [ { id: 'cust1', clientId: '1001', name: 'شركة النور للمواد الغذائية', phone: '01012345678', password: '1234' }, { id: 'cust2', clientId: '1002', name: 'سوبر ماركت الهدى', phone: '01112345678', password: '1234' } ]; // توليد منتجات إضافية لتجربة خاصية (التحميل التدريجي - Pagination) const generateMockProducts = () => { let products = [ { id: 'p1', name: 'صلصة طماطم فاين فودز 400جم', image: 'https://placehold.co/150x150/e74c3c/white?text=Tomatoes', categoryId: 'c1', unitId: 'u1', piecePrice: 15, stockPieces: 1200, hasOffer: true, offerMinQty: 10, offerType: 'percentage', offerValue: 10, maxDiscountValue: 150, hasExpiry: true, expiryDate: '2026-12-31', minPurchaseQty: 1, maxPurchaseQty: 50 }, { id: 'p2', name: 'أرز بسمتي السوهاجي', image: 'https://placehold.co/150x150/f1c40f/white?text=Rice', categoryId: 'c2', unitId: 'u3', piecePrice: 400, stockPieces: 50, hasOffer: false, offerMinQty: 0, offerType: 'fixed', offerValue: 0, maxDiscountValue: 0, hasExpiry: false, expiryDate: '', minPurchaseQty: 5, maxPurchaseQty: 0 }, { id: 'p3', name: 'حليب كامل الدسم 1 لتر', image: 'https://placehold.co/150x150/3498db/white?text=Milk', categoryId: 'c3', unitId: 'u4', piecePrice: 35, stockPieces: 300, hasOffer: true, offerMinQty: 5, offerType: 'fixed', offerValue: 15, maxDiscountValue: 0, hasExpiry: true, expiryDate: '2025-08-15', minPurchaseQty: 1, maxPurchaseQty: 0 } ]; // إضافة 30 منتج افتراضي لتجربة التقليب for(let i=4; i<=30; i++) { products.push({ id: `p${i}`, name: `منتج غذائي تجريبي رقم ${i}`, image: `https://placehold.co/150x150/cccccc/333333?text=Product+${i}`, categoryId: i%2===0 ? 'c1' : 'c2', unitId: 'u1', piecePrice: 10 + (i*2), stockPieces: 500 + (i*10), hasOffer: false, offerMinQty: 0, offerType: 'fixed', offerValue: 0, maxDiscountValue: 0, hasExpiry: false, expiryDate: '', minPurchaseQty: 1, maxPurchaseQty: 0 }); } return products; }; const INITIAL_PRODUCTS = generateMockProducts(); const INITIAL_SALES_RULES = [ { id: 'r1', targetProductId: 'p1', conditions: [{ requiredProductId: 'p2', type: 'absolute', value: 2 }] } ]; export default function App() { const [adminCreds, setAdminCreds] = useState({ username: 'admin', password: 'admin' }); const [user, setUser] = useState(null); const [categories, setCategories] = useState(INITIAL_CATEGORIES); const [units, setUnits] = useState(INITIAL_UNITS); const [customers, setCustomers] = useState(INITIAL_CUSTOMERS); const [products, setProducts] = useState(INITIAL_PRODUCTS); const [salesRules, setSalesRules] = useState(INITIAL_SALES_RULES); const [orders, setOrders] = useState([]); const [cart, setCart] = useState([]); const [orderConstraints, setOrderConstraints] = useState({ minOrder: 1000, maxOrder: 0 }); const getProductDetails = (product) => { const unit = units.find(u => u.id === product.unitId) || { name: 'قطعة', pieces: 1 }; const category = categories.find(c => c.id === product.categoryId) || { name: 'غير محدد' }; const unitPrice = product.piecePrice * unit.pieces; const availableUnits = Math.floor(product.stockPieces / unit.pieces); return { ...product, unit, category, unitPrice, availableUnits }; }; const calculateItemPrice = (productInfo, quantity) => { let total = productInfo.unitPrice * quantity; let discount = 0; if (productInfo.hasOffer && quantity >= productInfo.offerMinQty) { if (productInfo.offerType === 'fixed') { discount = productInfo.offerValue; } else if (productInfo.offerType === 'percentage') { discount = total * (productInfo.offerValue / 100); if (productInfo.maxDiscountValue && productInfo.maxDiscountValue > 0) { if (discount > productInfo.maxDiscountValue) { discount = productInfo.maxDiscountValue; } } } } return { subtotal: total, discount, finalTotal: total - discount }; }; if (!user) return ; if (user.role === 'admin') { return setUser(null)} getProductDetails={getProductDetails} />; } if (user.role === 'client') { return setUser(null)} getProductDetails={getProductDetails} calculateItemPrice={calculateItemPrice} />; } return null; } // ========================================== // 1. LOGIN VIEW // ========================================== function LoginView({ onLogin, customers, adminCreds }) { const [type, setType] = useState('client'); const [clientId, setClientId] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const handleLogin = (e) => { e.preventDefault(); setError(''); if (type === 'admin') { if (clientId === adminCreds.username && password === adminCreds.password) { onLogin({ role: 'admin', name: 'المدير' }); } else { setError('بيانات الإدارة غير صحيحة'); } } else { const customer = customers.find(c => c.clientId === clientId && c.password === password); if (customer) { onLogin({ role: 'client', ...customer }); } else { setError('رقم العميل أو كلمة المرور غير صحيحة'); } } }; return (

بوابة المبيعات بالجملة

تسجيل الدخول للنظام

{error &&
{error}
}
setClientId(e.target.value)} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none" />
setPassword(e.target.value)} className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none" />
); } // ========================================== // 2. ADMIN DASHBOARD // ========================================== function AdminDashboard({ state, setters, onLogout, getProductDetails }) { const [activeTab, setActiveTab] = useState('products'); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const tabs = [ { id: 'products', name: 'المنتجات', icon: Package }, { id: 'orders', name: 'الطلبات', icon: ListOrdered }, { id: 'customers', name: 'العملاء', icon: Users }, { id: 'categories', name: 'الفئات', icon: Tag }, { id: 'units', name: 'الوحدات', icon: Box }, { id: 'salesRules', name: 'قواعد البيع', icon: LinkIcon }, { id: 'constraints', name: 'قيود الطلبات', icon: Sliders }, { id: 'adminSettings', name: 'بيانات الدخول', icon: Settings }, ]; return (
{/* Mobile Header */}

لوحة الإدارة

{/* Overlay for mobile sidebar */} {isSidebarOpen && (
setIsSidebarOpen(false)}>
)} {/* Sidebar */}

لوحة الإدارة

{/* Main Content */}
{activeTab === 'products' && } {activeTab === 'orders' && } {activeTab === 'customers' && } {activeTab === 'categories' && } {activeTab === 'units' && } {activeTab === 'salesRules' && } {activeTab === 'constraints' && } {activeTab === 'adminSettings' && }
); } // --- Admin Sub-views --- function AdminProducts({ state, setters, getProductDetails }) { const [isEditing, setIsEditing] = useState(false); const [formData, setFormData] = useState(null); // Search & Pagination States const [searchTerm, setSearchTerm] = useState(''); const [itemsPerPage, setItemsPerPage] = useState(10); const [currentPage, setCurrentPage] = useState(1); const handleAdd = () => { setFormData({ id: Date.now().toString(), name: '', image: '', categoryId: state.categories[0]?.id || '', unitId: state.units[0]?.id || '', piecePrice: 0, stockPieces: 0, hasOffer: false, offerMinQty: 0, offerType: 'fixed', offerValue: 0, maxDiscountValue: 0, hasExpiry: false, expiryDate: '', minPurchaseQty: 1, maxPurchaseQty: 0 }); setIsEditing(true); }; const handleSave = (e) => { e.preventDefault(); const existing = state.products.find(p => p.id === formData.id); if (existing) { setters.setProducts(state.products.map(p => p.id === formData.id ? formData : p)); } else { setters.setProducts([...state.products, formData]); } setIsEditing(false); }; const handleDelete = (id) => { if(confirm('هل أنت متأكد من حذف هذا المنتج؟')) { setters.setProducts(state.products.filter(p => p.id !== id)); } }; if (isEditing) { const selectedUnit = state.units.find(u => u.id === formData.unitId) || { pieces: 1 }; const unitPriceCalc = formData.piecePrice * selectedUnit.pieces; return (

{formData.name ? 'تعديل منتج' : 'إضافة منتج جديد'}

setFormData({...formData, name: e.target.value})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />
setFormData({...formData, image: e.target.value})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" placeholder="https://..." />
setFormData({...formData, piecePrice: parseFloat(e.target.value) || 0})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />
setFormData({...formData, stockPieces: parseInt(e.target.value) || 0})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />

قيود الشراء للعميل (بالوحدة)

setFormData({...formData, minPurchaseQty: parseInt(e.target.value) || 1})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />
setFormData({...formData, maxPurchaseQty: parseInt(e.target.value) || 0})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />
{formData.hasExpiry && (
setFormData({...formData, expiryDate: e.target.value})} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" />
)}
{formData.hasOffer && (
setFormData({...formData, offerMinQty: parseInt(e.target.value) || 1})} className="w-full px-4 py-2 border rounded-lg" placeholder="مثال: 10" />
setFormData({...formData, offerValue: parseFloat(e.target.value) || 0})} className="w-full px-4 py-2 border rounded-lg" />
{formData.offerType === 'percentage' && (
setFormData({...formData, maxDiscountValue: parseFloat(e.target.value) || 0})} className="w-full px-4 py-2 border rounded-lg" />
)}
)}
); } // فلترة وتقسيم المنتجات للصفحات const filteredProducts = state.products.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase())); const totalPages = Math.ceil(filteredProducts.length / itemsPerPage); const currentProducts = filteredProducts.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage); // إعادة التعيين للصفحة الأولى عند البحث useEffect(() => { setCurrentPage(1); }, [searchTerm, itemsPerPage]); return (

إدارة المنتجات

setSearchTerm(e.target.value)} className="w-full pl-4 pr-10 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none" />
{currentProducts.map(p => { const info = getProductDetails(p); return ( ) })} {currentProducts.length === 0 && }
المنتج الفئة والوحدة السعر والمخزون العروض والقيود إجراءات
{p.name} {p.name}
{info.category.name}
{info.unit.name} ({info.unit.pieces} قطعة)
{info.unitPrice} ج.م / {info.unit.name}
المتاح: {info.availableUnits} {info.unit.name}
{p.hasOffer ? (
خصم {p.offerType === 'fixed' ? `${p.offerValue} ج` : `${p.offerValue}%`} {p.offerType === 'percentage' && p.maxDiscountValue > 0 ? ` (أقصى ${p.maxDiscountValue}ج)` : ''}
) : null} {p.hasExpiry && (
انتهاء: {p.expiryDate}
)}
أدنى: {p.minPurchaseQty || 1} | أقصى: {p.maxPurchaseQty > 0 ? p.maxPurchaseQty : 'مفتوح'}
لا توجد منتجات مطابقة
{/* الترقيم (Pagination) */} {totalPages > 1 && (
صفحة {currentPage} من {totalPages}
)}
); } function AdminOrders({ state, setters, getProductDetails }) { const [selectedOrder, setSelectedOrder] = useState(null); const updateOrderStatus = (orderId, newStatus) => { const order = state.orders.find(o => o.id === orderId); if (newStatus === 'cancelled' && order.status !== 'cancelled') { let updatedProducts = [...state.products]; order.items.forEach(item => { const productIndex = updatedProducts.findIndex(p => p.id === item.productId); if(productIndex > -1) { const product = updatedProducts[productIndex]; const unit = state.units.find(u => u.id === product.unitId); updatedProducts[productIndex].stockPieces += (item.quantity * unit.pieces); } }); setters.setProducts(updatedProducts); } setters.setOrders(state.orders.map(o => o.id === orderId ? { ...o, status: newStatus } : o)); if (selectedOrder && selectedOrder.id === orderId) { setSelectedOrder({ ...selectedOrder, status: newStatus }); } }; const getStatusBadge = (status) => { switch(status) { case 'pending': return قيد المراجعة; case 'processing': return جاري التجهيز; case 'shipped': return تم الشحن; case 'delivered': return مكتمل; case 'cancelled': return ملغي; default: return null; } }; if (selectedOrder) { const customer = state.customers.find(c => c.id === selectedOrder.clientId) || { name: 'غير معروف' }; return (

طلب #{selectedOrder.id}

التاريخ: {new Date(selectedOrder.date).toLocaleString('ar-EG')} العميل: {customer.name} (رقم: {customer.clientId}) الهاتف: {customer.phone}
{getStatusBadge(selectedOrder.status)}

تفاصيل المنتجات:

{selectedOrder.items.map((item, idx) => { const product = state.products.find(p => p.id === item.productId); if(!product) return
منتج محذوف
; const info = getProductDetails(product); return (
{product.name}
{item.quantity} {info.unit.name} x {item.price} ج.م
{item.finalTotal} ج.م
{item.discount > 0 &&
خصم: {item.discount} ج.م
}
) })}
إجمالي الطلب {selectedOrder.total} ج.م
); } return (

إدارة الطلبات

{state.orders.slice().reverse().map(order => { const customer = state.customers.find(c => c.id === order.clientId) || { name: 'غير معروف' }; return ( ) })} {state.orders.length === 0 && }
رقم الطلب التاريخ العميل الإجمالي الحالة إجراءات
#{order.id} {new Date(order.date).toLocaleDateString('ar-EG')} {customer.name} {order.total} ج.م {getStatusBadge(order.status)}
لا توجد طلبات حالياً
); } function AdminCustomers({ state, setters }) { const [formData, setFormData] = useState({ id: '', clientId: '', name: '', phone: '', password: '' }); const handleSave = (e) => { e.preventDefault(); if(formData.id) { setters.setCustomers(state.customers.map(c => c.id === formData.id ? formData : c)); } else { setters.setCustomers([...state.customers, { ...formData, id: Date.now().toString() }]); } setFormData({ id: '', clientId: '', name: '', phone: '', password: '' }); }; const handleDelete = (id) => { if(confirm('متأكد من حذف العميل؟')) setters.setCustomers(state.customers.filter(c => c.id !== id)); } return (

إدارة العملاء

{formData.id ? 'تعديل عميل' : 'إضافة عميل'}

setFormData({...formData, clientId: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" /> setFormData({...formData, name: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" /> setFormData({...formData, phone: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" /> setFormData({...formData, password: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" />
{formData.id && }
{state.customers.map(c => ( ))}
رقم العميل الاسم الهاتف الإجراءات
{c.clientId} {c.name} {c.phone}
) } function AdminCategories({ state, setters }) { const [formData, setFormData] = useState({ id: '', name: '' }); const handleSave = (e) => { e.preventDefault(); if(formData.id) { setters.setCategories(state.categories.map(c => c.id === formData.id ? formData : c)); } else { setters.setCategories([...state.categories, { id: Date.now().toString(), name: formData.name }]); } setFormData({ id: '', name: '' }); }; return (

إدارة الفئات

{formData.id ? 'تعديل الفئة' : 'إضافة فئة جديدة'}

setFormData({...formData, name: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" />
{formData.id && }
{state.categories.map(c => ( ))}
اسم الفئةالإجراءات
{c.name}
); } function AdminUnits({ state, setters }) { const [formData, setFormData] = useState({ id: '', name: '', pieces: 1 }); const handleSave = (e) => { e.preventDefault(); if(formData.id) { setters.setUnits(state.units.map(u => u.id === formData.id ? formData : u)); } else { setters.setUnits([...state.units, { id: Date.now().toString(), name: formData.name, pieces: formData.pieces }]); } setFormData({ id: '', name: '', pieces: 1 }); }; return (

إدارة وحدات البيع

{formData.id ? 'تعديل الوحدة' : 'إضافة وحدة جديدة'}

setFormData({...formData, name: e.target.value})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" />
setFormData({...formData, pieces: parseInt(e.target.value) || 1})} className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 outline-none" />
{formData.id && }
{state.units.map(u => ( ))}
اسم الوحدةعدد القطع بداخلهاالإجراءات
{u.name} {u.pieces} قطعة
); } // --- مكون القائمة المنسدلة الذكية (Searchable Select) --- function SearchableSelect({ options, value, onChange, placeholder, className }) { const [isOpen, setIsOpen] = React.useState(false); const [search, setSearch] = React.useState(''); const wrapperRef = React.useRef(null); React.useEffect(() => { function handleClickOutside(event) { if (wrapperRef.current && !wrapperRef.current.contains(event.target)) { setIsOpen(false); } } document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [wrapperRef]); const selectedOption = options.find(o => o.id === value); const filteredOptions = options.filter(o => o.name.toLowerCase().includes(search.toLowerCase())); return (
setIsOpen(!isOpen)} className={`flex items-center justify-between cursor-pointer bg-white px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 ${className || ''}`} > {selectedOption ? selectedOption.name : placeholder}
{isOpen && (
setSearch(e.target.value)} className="w-full px-3 py-1.5 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-50" />
{filteredOptions.length > 0 ? filteredOptions.map(opt => (
{ onChange(opt.id); setIsOpen(false); setSearch(''); }} className={`px-3 py-2 cursor-pointer rounded-md text-sm transition-colors ${value === opt.id ? 'bg-blue-100 text-blue-800 font-bold' : 'hover:bg-blue-50 text-gray-700'}`} > {opt.name}
)) : (
لا توجد نتائج مطابقة
)}
)}
); } function AdminSalesRules({ state, setters }) { const [isEditing, setIsEditing] = useState(false); const [formData, setFormData] = useState({ id: '', targetProductId: '', conditions: [] }); const handleAdd = () => { setFormData({ id: Date.now().toString(), targetProductId: state.products[0]?.id || '', conditions: [{ requiredProductId: state.products[0]?.id || '', type: 'absolute', value: 1 }] }); setIsEditing(true); }; const handleSave = (e) => { e.preventDefault(); if (formData.conditions.length === 0) { alert('يجب إضافة شرط واحد على الأقل.'); return; } if (formData.conditions.some(c => c.requiredProductId === formData.targetProductId)) { alert('لا يمكن ربط المنتج بنفسه كشرط!'); return; } if(formData.id && state.salesRules.find(r => r.id === formData.id)) { setters.setSalesRules(state.salesRules.map(r => r.id === formData.id ? formData : r)); } else { setters.setSalesRules([...state.salesRules, formData]); } setIsEditing(false); }; const addCondition = () => { setFormData({ ...formData, conditions: [...formData.conditions, { requiredProductId: state.products[0]?.id || '', type: 'absolute', value: 1 }] }); }; const updateCondition = (index, field, value) => { const newConditions = [...formData.conditions]; newConditions[index][field] = value; setFormData({ ...formData, conditions: newConditions }); }; const removeCondition = (index) => { setFormData({ ...formData, conditions: formData.conditions.filter((_, i) => i !== index) }); }; if (isEditing) { return (

{formData.id && state.salesRules.find(r => r.id === formData.id) ? 'تعديل قاعدة بيع' : 'إضافة قاعدة بيع جديدة'}

setFormData({...formData, targetProductId: val})} placeholder="ابحث واختر المنتج المشروط..." />

المنتجات المطلوبة لتفعيل الشراء

{formData.conditions.map((cond, idx) => (
updateCondition(idx, 'requiredProductId', val)} placeholder="ابحث واختر المنتج..." />
updateCondition(idx, 'value', parseFloat(e.target.value) || 1)} className="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 text-sm font-bold" />
))}
); } return (

قواعد البيع المتقدمة

{state.salesRules.map(rule => { const targetProduct = state.products.find(p => p.id === rule.targetProductId); if (!targetProduct) return null; return ( ) })} {state.salesRules.length === 0 && }
المنتج المشروط (المراد شراؤه) المنتجات الإلزامية لتفعيل الشراء الإجراءات
{targetProduct.name} {rule.conditions.map((cond, i) => { const reqProduct = state.products.find(p => p.id === cond.requiredProductId); if (!reqProduct) return
منتج محذوف
; return (
يجب شراء: {cond.type === 'ratio' ? `(${cond.value} إضافية لكل قطعة)` : `${cond.value} (كحد أدنى ثابت)`} من "{reqProduct.name}"
); })}
لا توجد قواعد بيع مسجلة حالياً
); } function AdminConstraints({ state, setters }) { const [minOrder, setMinOrder] = useState(state.orderConstraints.minOrder); const [maxOrder, setMaxOrder] = useState(state.orderConstraints.maxOrder); const [msg, setMsg] = useState(''); const handleSave = (e) => { e.preventDefault(); setters.setOrderConstraints({ minOrder: parseFloat(minOrder) || 0, maxOrder: parseFloat(maxOrder) || 0 }); setMsg('تم حفظ قيود الطلبات بنجاح!'); setTimeout(() => setMsg(''), 3000); }; return (

قيود سلة المشتريات

حدد الحد الأدنى والأقصى لقيمة الطلب المسموح للعميل بتأكيده. (أدخل 0 لإلغاء القيد)

{msg &&
{msg}
}
setMinOrder(e.target.value)} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none" />
setMaxOrder(e.target.value)} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none" />
); } function AdminSettings({ state, setters }) { const [username, setUsername] = useState(state.adminCreds.username); const [password, setPassword] = useState(state.adminCreds.password); const [msg, setMsg] = useState(''); const handleSave = (e) => { e.preventDefault(); setters.setAdminCreds({ username, password }); setMsg('تم تحديث بيانات الدخول بنجاح!'); setTimeout(() => setMsg(''), 3000); }; return (

بيانات دخول الإدارة

{msg &&
{msg}
}
setUsername(e.target.value)} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none" />
setPassword(e.target.value)} className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none" />
); } // ========================================== // 3. CLIENT DASHBOARD // ========================================== function ClientDashboard({ user, state, setters, onLogout, getProductDetails, calculateItemPrice }) { const [activeView, setActiveView] = useState('catalog'); const [toast, setToast] = useState(null); const showToast = (message, type = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 3500); }; const cartItemsCount = state.cart.reduce((sum, item) => sum + item.quantity, 0); return (
{/* Toast Notification */} {toast && (
{toast.type === 'error' ? : } {toast.message}
)} {/* Floating Cart Icon */} {activeView !== 'cart' && ( )} {/* Header */}
setActiveView('catalog')}>

جملتي

{user.name}
رقم: {user.clientId}
{/* Main Content */}
{activeView === 'catalog' && } {activeView === 'cart' && } {activeView === 'orders' && }
); } // --- Client Sub-views --- function ClientCatalog({ state, setters, getProductDetails, showToast }) { const [selectedCategory, setSelectedCategory] = useState('all'); const [search, setSearch] = useState(''); const [visibleCount, setVisibleCount] = useState(12); const addToCart = (product, info, quantity) => { const existingIndex = state.cart.findIndex(i => i.productId === product.id); const currentQtyInCart = existingIndex > -1 ? state.cart[existingIndex].quantity : 0; const newTotalQty = currentQtyInCart + quantity; if (newTotalQty > info.availableUnits) { const remaining = info.availableUnits - currentQtyInCart; if (remaining <= 0) { showToast(`عفواً، لقد أضفت كامل الكمية المتاحة في المخزون (${info.availableUnits} ${info.unit.name}) مسبقاً!`, 'error'); } else { showToast(`عفواً، المتاح للإضافة هو ${remaining} ${info.unit.name} فقط!`, 'error'); } return; } if (product.maxPurchaseQty && product.maxPurchaseQty > 0 && newTotalQty > product.maxPurchaseQty) { showToast(`عفواً، الحد الأقصى المسموح بشرائه من هذا المنتج هو ${product.maxPurchaseQty} ${info.unit.name}.`, 'error'); return; } if (newTotalQty < (product.minPurchaseQty || 1)) { showToast(`عفواً، الحد الأدنى لطلب هذا المنتج هو ${product.minPurchaseQty || 1} ${info.unit.name}.`, 'error'); return; } let newCart = [...state.cart]; if (existingIndex > -1) { newCart[existingIndex].quantity = newTotalQty; } else { newCart.push({ productId: product.id, quantity: newTotalQty }); } setters.setCart(newCart); showToast(`تمت إضافة ${quantity} من "${product.name}" إلى السلة بنجاح!`, 'success'); }; // فلترة المنتجات بناء على البحث والفئة const filteredProducts = state.products.filter(p => { const matchCat = selectedCategory === 'all' || p.categoryId === selectedCategory; const matchSearch = p.name.toLowerCase().includes(search.toLowerCase()); return matchCat && matchSearch; }); // إعادة ضبط المنتجات المرئية عند تغير معايير البحث useEffect(() => { setVisibleCount(12); }, [search, selectedCategory]); // منطق التحميل التدريجي (Infinite Scroll) useEffect(() => { const handleScroll = () => { if (window.innerHeight + document.documentElement.scrollTop >= document.documentElement.offsetHeight - 300) { setVisibleCount(prev => prev < filteredProducts.length ? prev + 12 : prev); } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [filteredProducts.length]); const displayedProducts = filteredProducts.slice(0, visibleCount); return (

تصفح المنتجات

setSearch(e.target.value)} className="w-full pl-4 pr-10 py-2.5 border rounded-xl focus:ring-2 focus:ring-blue-500 focus:outline-none shadow-sm" />
{displayedProducts.map(p => { const info = getProductDetails(p); const cartItem = state.cart.find(i => i.productId === p.id); const currentCartQty = cartItem ? cartItem.quantity : 0; const isOutOfStock = info.availableUnits <= 0; return (
{/* مؤشر المنتج داخل السلة المميز */} {currentCartQty > 0 && (
بالسلة: {currentCartQty}
)}
{p.name} {p.hasOffer && (
عرض خاص
)} {isOutOfStock && (
نفذت الكمية
)}
{info.category.name}
{p.hasExpiry && (
انتهاء {p.expiryDate}
)}

{p.name}

{info.unitPrice} ج.م
القطعة: {p.piecePrice} ج.م
الوحدة: {info.unit.name} ({info.unit.pieces})
متاح: {info.availableUnits}
{((p.minPurchaseQty > 1) || (p.maxPurchaseQty > 0)) && (
{p.minPurchaseQty > 1 && `حد أدنى ${p.minPurchaseQty} `} {p.maxPurchaseQty > 0 && `${p.minPurchaseQty > 1 ? ' | ' : ''}حد أقصى ${p.maxPurchaseQty}`}
)} {p.hasOffer && (
خصم {p.offerType === 'fixed' ? `${p.offerValue} ج.م` : `${p.offerValue}%`} عند شراء {p.offerMinQty} أو أكثر.
)}
addToCart(p, info, qty)} />
) })} {filteredProducts.length === 0 && (

لا توجد منتجات مطابقة للبحث

)}
{visibleCount < filteredProducts.length && (
جاري تحميل المزيد من المنتجات...
)}
); } function ProductAddToCart({ isOutOfStock, onAdd, minQty }) { const [qty, setQty] = useState(minQty); useEffect(() => { setQty(minQty); }, [minQty]); const handleAdd = () => { onAdd(qty); setQty(minQty); }; if (isOutOfStock) { return ; } return (
setQty(parseInt(e.target.value) || 0)} className="w-16 md:w-20 px-2 md:px-3 py-2 border rounded-xl text-center font-medium focus:ring-2 focus:ring-blue-500 outline-none text-lg" />
); } function ClientCart({ user, state, setters, getProductDetails, calculateItemPrice, setActiveView, showToast }) { const [checkoutError, setCheckoutError] = useState(''); const updateQty = (productId, newQty) => { setCheckoutError(''); const product = state.products.find(p => p.id === productId); const info = getProductDetails(product); if(newQty <= 0) { setters.setCart(state.cart.filter(i => i.productId !== productId)); return; } if (newQty > info.availableUnits) { showToast(`عفواً، أقصى كمية متاحة في المخزون هي ${info.availableUnits} ${info.unit.name}.`, 'error'); return; } if (product.maxPurchaseQty > 0 && newQty > product.maxPurchaseQty) { showToast(`عفواً، الحد الأقصى المسموح بشرائه من هذا المنتج هو ${product.maxPurchaseQty} ${info.unit.name}.`, 'error'); return; } if (newQty < (product.minPurchaseQty || 1)) { showToast(`عفواً، الحد الأدنى لطلب هذا المنتج هو ${product.minPurchaseQty || 1} ${info.unit.name}.`, 'error'); return; } setters.setCart(state.cart.map(i => i.productId === productId ? { ...i, quantity: newQty } : i)); }; const removeItem = (productId) => { setCheckoutError(''); setters.setCart(state.cart.filter(i => i.productId !== productId)); }; const cartDetails = state.cart.map(item => { const product = state.products.find(p => p.id === item.productId); const info = getProductDetails(product); const pricing = calculateItemPrice(info, item.quantity); return { item, product, info, pricing }; }); const cartTotal = cartDetails.reduce((sum, row) => sum + row.pricing.finalTotal, 0); const totalDiscount = cartDetails.reduce((sum, row) => sum + row.pricing.discount, 0); const validateSalesRules = () => { let errors = []; cartDetails.forEach(row => { const rule = state.salesRules.find(r => r.targetProductId === row.product.id); if (rule) { rule.conditions.forEach(cond => { const reqProduct = state.products.find(p => p.id === cond.requiredProductId); const reqInfo = reqProduct ? getProductDetails(reqProduct) : null; const reqCartItem = state.cart.find(i => i.productId === cond.requiredProductId); const reqQty = reqCartItem ? reqCartItem.quantity : 0; if (reqProduct) { if (cond.type === 'absolute') { if (reqQty < cond.value) { errors.push(`لشراء "${row.product.name}" يجب أن تضيف على الأقل ${cond.value} ${reqInfo.unit.name} من "${reqProduct.name}".`); } } else if (cond.type === 'ratio') { const needed = row.item.quantity * cond.value; if (reqQty < needed) { errors.push(`لكل 1 ${row.info.unit.name} من "${row.product.name}" يجب إضافة ${cond.value} ${reqInfo.unit.name} من "${reqProduct.name}" (المطلوب: ${needed} | المتاح: ${reqQty}).`); } } } }); } }); return errors; }; const handleCheckout = () => { setCheckoutError(''); const constraints = state.orderConstraints; if (constraints.minOrder > 0 && cartTotal < constraints.minOrder) { setCheckoutError(`الحد الأدنى للطلب هو ${constraints.minOrder} ج.م. يرجى إضافة المزيد من المنتجات.`); window.scrollTo(0, 0); return; } if (constraints.maxOrder > 0 && cartTotal > constraints.maxOrder) { setCheckoutError(`الحد الأقصى للطلب هو ${constraints.maxOrder} ج.م. يرجى تعديل سلة المشتريات.`); window.scrollTo(0, 0); return; } const ruleErrors = validateSalesRules(); if (ruleErrors.length > 0) { setCheckoutError(ruleErrors.join('|')); window.scrollTo(0, 0); return; } let isValid = true; let outOfStockItem = ''; for(let row of cartDetails) { if(row.item.quantity > row.info.availableUnits) { isValid = false; outOfStockItem = row.product.name; break; } } if(!isValid) { setCheckoutError(`عذراً، الكمية المطلوبة من "${outOfStockItem}" لم تعد متوفرة في المخزون بالكامل.`); return; } const newOrder = { id: Date.now().toString(), clientId: user.id, date: new Date().toISOString(), status: 'pending', total: cartTotal, items: cartDetails.map(row => ({ productId: row.product.id, quantity: row.item.quantity, price: row.info.unitPrice, discount: row.pricing.discount, finalTotal: row.pricing.finalTotal })) }; let updatedProducts = [...state.products]; cartDetails.forEach(row => { const productIndex = updatedProducts.findIndex(p => p.id === row.product.id); if(productIndex > -1) { updatedProducts[productIndex].stockPieces -= (row.item.quantity * row.info.unit.pieces); } }); setters.setProducts(updatedProducts); setters.setOrders([...state.orders, newOrder]); setters.setCart([]); showToast('تم إرسال طلب الشراء الخاص بك بنجاح!', 'success'); setActiveView('orders'); window.scrollTo(0,0); }; if (state.cart.length === 0) { return (

سلة المشتريات فارغة

قم بتصفح المنتجات وإضافتها للسلة لإنشاء طلب شراء.

); } return (

مراجعة سلة المشتريات

{cartDetails.map((row, idx) => (

{row.product.name}

{row.info.unitPrice} ج.م / {row.info.unit.name}
{row.item.quantity}
{row.pricing.discount > 0 && (
{row.pricing.subtotal} ج.م
)}
{row.pricing.finalTotal} ج.م
{row.pricing.discount > 0 && (
خصم {row.pricing.discount} ج.م
)}
))}

ملخص الطلب

الإجمالي الفرعي {cartTotal + totalDiscount} ج.م
{totalDiscount > 0 && (
إجمالي الخصومات - {totalDiscount} ج.م
)}
الإجمالي النهائي {cartTotal} ج.م
{checkoutError && (
تنبيه: راجع القواعد لتأكيد الطلب
    {checkoutError.split('|').map((err, i) => (
  • {err.trim()}
  • ))}
)}

بمجرد تأكيد الطلب، سيتم حجز الكميات الخاصة بك من المخزون فوراً ولن يتمكن أحد من شرائها.

); } function ClientOrders({ user, state, getProductDetails }) { const [selectedOrder, setSelectedOrder] = useState(null); const myOrders = state.orders.filter(o => o.clientId === user.id).sort((a,b) => b.id - a.id); const getStatusInfo = (status) => { switch(status) { case 'pending': return { label: 'قيد المراجعة', color: 'text-yellow-600 bg-yellow-50 border-yellow-200', icon: Clock }; case 'processing': return { label: 'جاري التجهيز', color: 'text-blue-600 bg-blue-50 border-blue-200', icon: Package }; case 'shipped': return { label: 'في الطريق', color: 'text-purple-600 bg-purple-50 border-purple-200', icon: ChevronRight }; case 'delivered': return { label: 'مكتمل', color: 'text-green-600 bg-green-50 border-green-200', icon: CheckCircle }; case 'cancelled': return { label: 'ملغي', color: 'text-red-600 bg-red-50 border-red-200', icon: XCircle }; default: return { label: 'غير معروف', color: 'text-gray-600 bg-gray-50 border-gray-200', icon: Clock }; } }; if(myOrders.length === 0) { return (

لا توجد طلبات سابقة

لم تقم بإنشاء أي طلبات شراء حتى الآن.

) } if (selectedOrder) { const statusInfo = getStatusInfo(selectedOrder.status); const StatusIcon = statusInfo.icon; return (

طلب #{selectedOrder.id}

تم الإنشاء: {new Date(selectedOrder.date).toLocaleString('ar-EG')}
{statusInfo.label}

تفاصيل المنتجات:

{selectedOrder.items.map((item, idx) => { const product = state.products.find(p => p.id === item.productId); if(!product) return
منتج غير متوفر
; const info = getProductDetails(product); return (
{product.name}
{item.quantity} {info.unit.name} x {item.price} ج.م
{item.finalTotal} ج.م
{item.discount > 0 &&
خصم: {item.discount} ج.م
}
) })}
الإجمالي النهائي {selectedOrder.total} ج.م
); } return (

متابعة طلبات الشراء

{myOrders.map(order => { const statusInfo = getStatusInfo(order.status); const StatusIcon = statusInfo.icon; return ( ) })}
رقم الطلب التاريخ الإجمالي الحالة إجراءات
#{order.id} {new Date(order.date).toLocaleDateString('ar-EG')} {order.total} ج.م {statusInfo.label}
); }