// Mobile-optimized InStock view function MobileInStock({ salesUrl, token }) { const [suppliers, setSuppliers] = React.useState([]); const [banks, setBanks] = React.useState([]); const [entries, setEntries] = React.useState([]); const [showAddForm, setShowAddForm] = React.useState(false); const [supplierId, setSupplierId] = React.useState(''); const [bankId, setBankId] = React.useState(''); const [supplierAmount, setSupplierAmount] = React.useState(''); const [gstAmount, setGstAmount] = React.useState(''); const [category, setCategory] = React.useState(''); const [items, setItems] = React.useState([ { productNo: '', productName: '', brand: '', model: '', quantity: 1, costPrice: '', validity: '', imes: [] } ]); const [saving, setSaving] = React.useState(false); const [error, setError] = React.useState(''); const loadSuppliers = async () => { try { const storedBranchToken = typeof window !== 'undefined' ? (localStorage.getItem('branch_token') || '') : ''; const effectiveToken = token || storedBranchToken || ''; const res = await fetch(salesUrl + '/api/suppliers', { headers: { Authorization: 'Bearer ' + effectiveToken } }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load suppliers'); setSuppliers(Array.isArray(data.suppliers) ? data.suppliers : []); } catch (e) { setError(e.message); } }; const loadBanks = async () => { try { const storedBranchToken = typeof window !== 'undefined' ? (localStorage.getItem('branch_token') || '') : ''; const effectiveToken = token || storedBranchToken || ''; const res = await fetch(salesUrl + '/api/banks', { headers: { Authorization: 'Bearer ' + effectiveToken } }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load banks'); setBanks(Array.isArray(data.banks) ? data.banks : []); } catch (e) { setError(e.message); } }; const loadEntries = async () => { try { const storedBranchToken = typeof window !== 'undefined' ? (localStorage.getItem('branch_token') || '') : ''; const effectiveToken = token || storedBranchToken || ''; const res = await fetch(salesUrl + '/api/in-stock', { headers: { Authorization: 'Bearer ' + effectiveToken } }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load'); setEntries(Array.isArray(data.entries) ? data.entries : []); } catch (e) { setError(e.message); } }; React.useEffect(() => { loadSuppliers(); loadBanks(); loadEntries(); }, []); const canSubmit = supplierId && bankId && items.every(it => it.productName); const totalProductAmount = items.reduce((sum, it) => sum + ((Number(it.quantity) || 0) * (Number(it.costPrice) || 0)), 0); const totalBillAmount = (Number(supplierAmount) || 0) + (Number(gstAmount) || 0); const addRow = () => setItems(it => [...it, { productNo: '', productName: '', brand: '', model: '', quantity: 1, costPrice: '', validity: '', imes: [] }]); const updateItem = (idx, field, value) => setItems(list => list.map((it, i) => { if (i !== idx) return it; if (field === 'quantity') { const qty = Number(value) || 0; const prevImes = Array.isArray(it.imes) ? it.imes.slice(0, qty) : []; while (prevImes.length < qty) prevImes.push(''); return { ...it, [field]: value, imes: prevImes }; } return { ...it, [field]: value }; })); const removeRow = (idx) => setItems(list => list.filter((_, i) => i !== idx)); // Helper to generate random alphanumeric string (2-9 chars) like desktop `InStockView` function randomProductNo() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const len = Math.floor(Math.random() * 3) + 2; // 2 to 9 let str = ''; for (let i = 0; i < len; i++) { str += chars.charAt(Math.floor(Math.random() * chars.length)); } return str; } const submit = async () => { setSaving(true); setError(''); try { const storedBranchToken = typeof window !== 'undefined' ? (localStorage.getItem('branch_token') || '') : ''; const effectiveToken = token || storedBranchToken || ''; const res = await fetch(salesUrl + '/api/in-stock', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + effectiveToken }, body: JSON.stringify({ supplier_id: supplierId, bank_id: bankId, supplierAmount: Number(supplierAmount) || 0, gstAmount: Number(gstAmount) || 0, category, items: items.map(it => ({ productNo: it.productNo && String(it.productNo).trim() ? it.productNo : randomProductNo(), productName: it.productName, brand: it.brand, model: it.model, quantity: Number(it.quantity) || 1, costPrice: Number(it.costPrice) || 0, validity: it.validity, imes: Array.isArray(it.imes) ? it.imes.filter(x => x && String(x).trim()) : [] })) }) }); const data = await res.json(); if (!res.ok || !data.success) throw new Error(data.message || 'Save failed'); // Reset form setSupplierId(''); setBankId(''); setSupplierAmount(''); setGstAmount(''); setCategory(''); setItems([{ productNo: '', productName: '', brand: '', model: '', quantity: 1, costPrice: '', validity: '', imes: [] }]); setShowAddForm(false); await loadEntries(); } catch (err) { setError(err.message); } finally { setSaving(false); } }; const formatCurrency = (n) => new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(n || 0); return (
{/* Header */}

📦 Product Inventory

{entries.length} stock entries

{/* Add Form */} {showAddForm && (

➕ Add Stock Entry

{/* Supplier & Bank */}
setSupplierAmount(e.target.value)} placeholder="0.00" step="0.01" />
setGstAmount(e.target.value)} placeholder="0.00" step="0.01" />
{/* Products */}

Products

{items.map((item, idx) => (
updateItem(idx, 'productName', e.target.value)} placeholder="Enter product name" required />
updateItem(idx, 'productNo', e.target.value)} placeholder="SKU/Code" />
updateItem(idx, 'brand', e.target.value)} placeholder="Brand name" />
updateItem(idx, 'model', e.target.value)} placeholder="Model number" />
updateItem(idx, 'quantity', e.target.value)} min="1" />
updateItem(idx, 'costPrice', e.target.value)} placeholder="0.00" step="0.01" />
{items.length > 1 && ( )}
))}
{/* Summary */}
Product Amount: {formatCurrency(totalProductAmount)}
Total Bill: {formatCurrency(totalBillAmount)}
{error && (
⚠️ {error}
)}
)} {/* Stock Entries List */}

📋 Stock Entries

{entries.length === 0 ? (
📦
No Stock Entries
Add your first stock entry above
) : (
{entries.map((entry, idx) => (
{new Date(entry.createdAt).toLocaleDateString()}
{entry.items?.length || 0} product(s)
Total: {formatCurrency((entry.supplierAmount || 0) + (entry.gstAmount || 0))}
))}
)}
); } // Register globally window.MobileInStock = MobileInStock;