import React, { useState, useEffect } from 'react'; import { Search, ShoppingCart, Plus, Minus, Trash2, CreditCard, Banknote, Globe, Calendar, CheckCircle2, MapPin } from 'lucide-react'; import Toast from '../Owner/Components/Toast'; export default function POS() { const [products, setProducts] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [cart, setCart] = useState([]); const [loading, setLoading] = useState(true); const [paymentMethod, setPaymentMethod] = useState(''); const [paymentMethods, setPaymentMethods] = useState([]); const [processing, setProcessing] = useState(false); const [success, setSuccess] = useState(false); const [adjustedTotal, setAdjustedTotal] = useState(''); const [adjustmentRemarks, setAdjustmentRemarks] = useState(''); const [branches, setBranches] = useState([]); const [selectedBranch, setSelectedBranch] = useState(window.__APP_DATA__?.user?.branch_id || ''); const [toast, setToast] = useState(null); const [showSuccessModal, setShowSuccessModal] = useState(false); const showToast = (message, type = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 3000); }; useEffect(() => { if (window.__APP_DATA__?.role === 'owner') { const fetchBranches = async () => { try { const response = await fetch('/api/branches?status=Active'); const data = await response.json(); setBranches(data || []); if (data?.length > 0 && !selectedBranch) { setSelectedBranch(data[0].id); } } catch (error) { console.error('Error fetching branches:', error); } }; fetchBranches(); } const fetchPaymentMethods = async () => { try { const res = await fetch('/api/masters/payment_method'); if (res.ok) { const data = await res.json(); const activeOnes = data.filter(m => m.status === 'Active'); setPaymentMethods(activeOnes); if (activeOnes.length > 0 && !paymentMethod) { setPaymentMethod(activeOnes[0].name); } } } catch (error) { console.error('Error fetching payment methods:', error); } }; fetchPaymentMethods(); }, []); useEffect(() => { const fetchProducts = async () => { if (!selectedBranch && window.__APP_DATA__?.role !== 'owner') return; setLoading(true); try { const url = `/api/inventory/products?branch_id=${selectedBranch}`; const response = await fetch(url); const data = await response.json(); setProducts(data || []); } catch (error) { console.error('Error fetching products:', error); } finally { setLoading(false); } }; fetchProducts(); }, [selectedBranch]); const subtotal = cart.reduce((sum, item) => sum + (item.selling_price * item.quantity), 0); const vatAmount = subtotal * 0.05; const totalWithVat = subtotal + vatAmount; // Sync adjusted total when base total changes, if not manually edited useEffect(() => { setAdjustedTotal(totalWithVat.toFixed(2)); }, [totalWithVat]); const filteredProducts = products.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || (p.category?.name || '').toLowerCase().includes(searchQuery.toLowerCase()) ); const addToCart = (product) => { setCart(prev => { const existing = prev.find(item => item.id === product.id); if (existing) { if (existing.quantity >= product.current_stock) { showToast(`Only ${product.current_stock} units available in stock.`, 'error'); return prev; } return prev.map(item => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ); } if (product.current_stock <= 0) { showToast("Product is out of stock.", 'error'); return prev; } return [...prev, { ...product, quantity: 1 }]; }); }; const updateQuantity = (item, delta) => { setCart(prev => prev.map(cartItem => { if (cartItem.id === item.id) { const newQty = cartItem.quantity + delta; if (newQty > item.current_stock) { showToast(`Only ${item.current_stock} units available in stock.`, 'error'); return cartItem; } return { ...cartItem, quantity: Math.max(1, newQty) }; } return cartItem; })); }; const removeFromCart = (id) => { setCart(prev => prev.filter(item => item.id !== id)); }; const handleProcessPayment = async () => { if (cart.length === 0) return; if (!paymentMethod) { showToast("Please select a mode of payment before processing.", 'error'); return; } setProcessing(true); try { const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const response = await fetch('/api/inventory/sales', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken }, body: JSON.stringify({ date: new Date().toISOString().split('T')[0], payment_method: paymentMethod, branch_id: selectedBranch, total_amount: parseFloat(adjustedTotal), remarks: parseFloat(adjustedTotal) !== totalWithVat ? adjustmentRemarks : '', items: cart.map(item => ({ product_id: item.id, quantity: item.quantity, unit_price: item.selling_price })) }) }); if (response.ok) { setSuccess(true); setShowSuccessModal(true); setCart([]); setAdjustmentRemarks(''); setTimeout(() => setSuccess(false), 3000); } } catch (error) { console.error('Payment failed:', error); } finally { setProcessing(false); } }; return ( <> {toast && setToast(null)} />}
{/* Product Catalog Column */}

Product Catalog

{window.__APP_DATA__?.role === 'owner' && ( )}
{branches.find(b => b.id.toString() === selectedBranch.toString())?.name || window.__APP_DATA__?.branch?.name || 'Loading...'}
{/* Styled Search Bar */}
setSearchQuery(e.target.value)} />
{/* Products Grid */} {loading ? (
{[1, 2, 3, 4, 5, 6, 7, 8].map(i => (
))}
) : (
{filteredProducts.map(product => (

{product.name}

{product.category?.name}

0 ? 'bg-emerald-50 text-emerald-600' : 'bg-red-50 text-red-600'}`}> {product.current_stock} left
{parseFloat(product.selling_price).toFixed(2)}
))}
)}
{/* Right Sidebar: Current Order */}
{/* Header */}

Current Order

{/* Billing Date Section */}
{/* Cart Content */}
{cart.length === 0 ? (

Cart is empty

) : ( cart.map(item => (

{item.name}

{item.quantity}

{parseFloat(item.selling_price * item.quantity).toFixed(2)}

)) )}
{/* Summary & Footer */}

Sub-Total

{subtotal.toFixed(2)} AED

VAT

5%

{vatAmount.toFixed(2)} AED

Total Amount

{totalWithVat.toFixed(2)} AED

setAdjustedTotal(e.target.value)} className="w-full bg-white border-2 border-red-50 py-1.5 px-3 rounded-lg text-sm font-black text-gray-900 focus:outline-none focus:border-red-200 transition-all shadow-inner" /> AED
{parseFloat(adjustedTotal) !== totalWithVat && (