function CreateSupplier({ salesUrl, token }) { // --- state (declare BEFORE using anywhere) --- const [rows, setRows] = React.useState([]); const [inStockEntries, setInStockEntries] = React.useState([]); const [saving, setSaving] = React.useState(false); const [error, setError] = React.useState(''); const [page, setPage] = React.useState(1); const [pageSize] = React.useState(10); // Feature context const { features, getFeatureLimit, isLimitReached } = window.useSalesFeatures ? window.useSalesFeatures() : { features: {}, getFeatureLimit: () => 999, isLimitReached: () => false }; const supplierLimit = getFeatureLimit('suppliers_limit', 'maxSuppliers'); const currentSupplierCount = rows.length; const isAtLimit = isLimitReached('suppliers_limit', 'maxSuppliers', currentSupplierCount); // Decode JWT helper const decodeJwt = (tk) => { try { const base64 = tk.split('.')[1] || ''; const json = atob(base64.replace(/-/g, '+').replace(/_/g, '/')); return JSON.parse(json); } catch (e) { return null; } }; // Determine effective token: prefer prop token (passed from main), else branch_token from localStorage const storedBranchToken = typeof window !== 'undefined' ? (localStorage.getItem('branch_token') || '') : ''; const effectiveToken = token || storedBranchToken || ''; // Detect if effective token is a branch token const decodedEffective = effectiveToken ? decodeJwt(effectiveToken) : null; const branchUserDecoded = decodedEffective && decodedEffective.branch_id ? decodedEffective : null; const effectiveIsAtLimit = branchUserDecoded ? false : isAtLimit; // Filters const [agencyFilter, setAgencyFilter] = React.useState(''); const [phoneFilter, setPhoneFilter] = React.useState(''); const [panFilter, setPanFilter] = React.useState(''); const [amountSort, setAmountSort] = React.useState(''); // 'high' | 'low' | '' // Form const [form, setForm] = React.useState({ supplierName: '', agencyName: '', phoneNumber: '', address: '', gstNumber: '', panNumber: '' }); const onChange = (e) => { const { name, value } = e.target; setForm((f) => ({ ...f, [name]: value })); }; // --- fetchers --- const fetchSuppliers = async () => { try { setError(''); let res = await fetch(salesUrl + '/api/suppliers', { headers: { Authorization: 'Bearer ' + effectiveToken } }); // If unauthorized and we have a stored branch token, retry with it if (res.status === 401 && storedBranchToken && storedBranchToken !== effectiveToken) { res = await fetch(salesUrl + '/api/suppliers', { headers: { Authorization: 'Bearer ' + storedBranchToken } }); } const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load'); const list = Array.isArray(data.suppliers) ? data.suppliers : []; setRows(list); setPage(1); } catch (err) { // For branch users, don't show raw auth errors in the UI; show nothing if (!branchUserDecoded) setError(err.message); } }; const fetchInStock = async () => { try { let res = await fetch(salesUrl + '/api/in-stock', { headers: { Authorization: 'Bearer ' + effectiveToken } }); if (res.status === 401 && storedBranchToken && storedBranchToken !== effectiveToken) { res = await fetch(salesUrl + '/api/in-stock', { headers: { Authorization: 'Bearer ' + storedBranchToken } }); } const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load in-stock'); setInStockEntries(Array.isArray(data.entries) ? data.entries : []); } catch (err) { console.error(err); } }; React.useEffect(() => { fetchSuppliers(); fetchInStock(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // --- derived maps (must come BEFORE anything that uses them) --- const supplierAmountMap = React.useMemo(() => { const m = {}; for (const e of inStockEntries) { const sid = e.supplier_id?._id || e.supplier_id; if (!sid) continue; m[sid] = (m[sid] || 0) + (Number(e.supplierAmount) || 0); } return m; }, [inStockEntries]); const supplierItemsCountMap = React.useMemo(() => { const m = {}; for (const e of inStockEntries) { const sid = e.supplier_id?._id || e.supplier_id; if (!sid) continue; const cnt = Array.isArray(e.items) ? e.items.length : 0; m[sid] = (m[sid] || 0) + cnt; } return m; }, [inStockEntries]); // --- options (after rows exists) --- const agencyOptions = React.useMemo(() => { const set = new Set(); rows.forEach(r => r.agencyName && set.add(r.agencyName)); return Array.from(set); }, [rows]); const phoneOptions = React.useMemo(() => { const set = new Set(); rows.forEach(r => r.phoneNumber && set.add(r.phoneNumber)); return Array.from(set); }, [rows]); const panOptions = React.useMemo(() => { const set = new Set(); rows.forEach(r => r.panNumber && set.add(r.panNumber)); return Array.from(set); }, [rows]); // --- filtered/sorted data (after supplierAmountMap & rows exist) --- const filteredRows = React.useMemo(() => { let result = rows.filter(r => { const agencyMatch = !agencyFilter || (r.agencyName && r.agencyName.toLowerCase().includes(agencyFilter.toLowerCase())); const phoneMatch = !phoneFilter || (r.phoneNumber && r.phoneNumber.toLowerCase().includes(phoneFilter.toLowerCase())); const panMatch = !panFilter || (r.panNumber && r.panNumber.toLowerCase().includes(panFilter.toLowerCase())); return agencyMatch && phoneMatch && panMatch; }); if (amountSort) { result = result.slice().sort((a, b) => { const aAmt = supplierAmountMap[a._id] || 0; const bAmt = supplierAmountMap[b._id] || 0; return amountSort === 'high' ? bAmt - aAmt : aAmt - bAmt; }); } return result; }, [rows, agencyFilter, phoneFilter, panFilter, amountSort, supplierAmountMap]); // --- pagination (after filteredRows, page, pageSize exist) --- const total = filteredRows.length; const totalPages = Math.max(1, Math.ceil(total / pageSize)); const clampedPage = Math.min(totalPages, Math.max(1, page)); const startIndex = total === 0 ? 0 : (clampedPage - 1) * pageSize + 1; const endIndex = Math.min(clampedPage * pageSize, total); const visible = filteredRows.slice((clampedPage - 1) * pageSize, (clampedPage - 1) * pageSize + pageSize); // --- submit --- const submit = async (e) => { e.preventDefault(); // Check limit before creating (skip for branch users) if (effectiveIsAtLimit) { window.checkSalesFeatureLimit('suppliers_limit', 'maxSuppliers', currentSupplierCount, features, 'Supplier'); return; } setSaving(true); setError(''); try { let res = await fetch(salesUrl + '/api/suppliers', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + effectiveToken }, body: JSON.stringify(form) }); // Retry with stored branch token if API returns unauthorized if (res.status === 401 && storedBranchToken && storedBranchToken !== effectiveToken) { res = await fetch(salesUrl + '/api/suppliers', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + storedBranchToken }, body: JSON.stringify(form) }); } const data = await res.json(); if (!res.ok || !data.success) throw new Error(data.message || 'Save failed'); setForm({ supplierName: '', agencyName: '', phoneNumber: '', address: '', gstNumber: '', panNumber: '' }); await fetchSuppliers(); } catch (err) { if (!branchUserDecoded) setError(err.message); } finally { setSaving(false); } }; return (
Add new supplier for your inventory {!branchUserDecoded && supplierLimit < 999 && ` (${currentSupplierCount}/${supplierLimit} used)`}
Manage your supplier relationships
| No. | Supplier Name | Agency Name | Phone Number | Address | GST Number | PAN Number | Supplier Amount | {!branchUserDecoded &&Branch | }Items Count |
|---|---|---|---|---|---|---|---|---|---|
| {startIndex + i} | {r.supplierName || '-'} | {r.agencyName || '-'} | {r.phoneNumber || '-'} | {r.address || '-'} | {r.gstNumber || '-'} | {r.panNumber || '-'} | {supplierAmountMap[r._id] ? supplierAmountMap[r._id] : 0} | {!branchUserDecoded &&{r.branch_name || '-'} | }{supplierItemsCountMap[r._id] ? supplierItemsCountMap[r._id] : 0} |