function MobileSecondsSalesView({ salesUrl, token, id }) { const [entry, setEntry] = React.useState(null); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(''); // Purchase modal state const [showPurchase, setShowPurchase] = React.useState(false); const [purchaseForm, setPurchaseForm] = React.useState({ customerName: '', phone: '', price: '', bankId: '' }); const [purchaseImages, setPurchaseImages] = React.useState([]); const [purchaseDocs, setPurchaseDocs] = React.useState([]); const [banks, setBanks] = React.useState([]); const [purchaseLoading, setPurchaseLoading] = React.useState(false); React.useEffect(() => { let mounted = true; async function load() { try { setLoading(true); setError(''); const res = await fetch((salesUrl || '') + '/api/seconds-sales', { headers: { Authorization: token ? 'Bearer ' + token : '' } }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load'); const found = (data.rows || []).find((r) => String(r._id) === String(id)); if (mounted) setEntry(found || null); } catch (err) { if (mounted) setError(err.message || 'Failed'); } finally { if (mounted) setLoading(false); } } load(); return () => { mounted = false; }; }, [id, salesUrl, token]); // load banks React.useEffect(() => { let mounted = true; (async () => { try { const res = await fetch((salesUrl || '') + '/api/banks', { headers: { Authorization: token ? 'Bearer ' + token : '' } }); const data = await res.json(); if (res.ok && mounted) setBanks(Array.isArray(data.banks) ? data.banks : []); } catch (err) { console.error('load banks', err); } })(); return () => { mounted = false; }; }, [salesUrl, token]); function purchaseOnChange(e) { const { name, value } = e.target; setPurchaseForm((f) => ({ ...f, [name]: value })); } function filesToBase64(fileList, setter) { const files = Array.from(fileList || []); if (!files.length) return; const promises = files.map( (f) => new Promise((res, rej) => { const reader = new FileReader(); reader.onload = () => res({ name: f.name, base64: reader.result }); reader.onerror = rej; reader.readAsDataURL(f); }) ); Promise.all(promises) .then((arr) => setter((prev) => [...prev, ...arr])) .catch((err) => console.error('file read', err)); } async function submitPurchase(e) { e.preventDefault(); const amount = Number(purchaseForm.price || 0); const bankId = purchaseForm.bankId || ''; if (!amount || amount <= 0) return alert('Enter valid price'); setPurchaseLoading(true); try { const payload = { customerName: purchaseForm.customerName || '', phone: purchaseForm.phone || '', price: amount, bank_id: bankId || undefined, images: purchaseImages || [], documents: purchaseDocs || [] }; const res = await fetch((salesUrl || '') + '/api/seconds-sales/' + encodeURIComponent(entry._id) + '/purchase', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: token ? 'Bearer ' + token : '' }, body: JSON.stringify(payload) }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Purchase API failed'); if (data.entry) setEntry(data.entry); setShowPurchase(false); setPurchaseForm({ customerName: '', phone: '', price: '', bankId: '' }); setPurchaseImages([]); setPurchaseDocs([]); } catch (err) { alert(err.message || 'Purchase failed'); } finally { setPurchaseLoading(false); } } function decodeJwt(tk) { try { const theToken = tk || token || localStorage.getItem('branch_token') || localStorage.getItem('sales_token') || ''; const parts = theToken.split('.'); if (parts.length < 2) return {}; const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/'); const json = decodeURIComponent( atob(base64) .split('') .map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }) .join('') ); return JSON.parse(json); } catch (e) { return {}; } } async function getBranchInfo() { let shopName = ''; let shopContact = ''; try { const url = new URL((salesUrl || '') + '/api/branches'); const res = await fetch(url, { headers: { Authorization: token ? 'Bearer ' + token : '' } }); const data = await res.json(); if (res.ok && Array.isArray(data.branches) && data.branches.length > 0) { const payload = decodeJwt(); const branchId = payload?.branch_id || payload?._id || ''; let found = null; if (branchId) found = data.branches.find((b) => String(b._id) === String(branchId)); if (!found) found = data.branches[0]; shopName = found?.name || ''; shopContact = found?.phoneNumber || found?.phone || ''; } } catch (e) { /* ignore */ } if (!shopName || !shopContact) { const payload = decodeJwt(); shopName = shopName || payload.shopName || payload.name || payload.branchName || ''; shopContact = shopContact || payload.phone || payload.phoneNumber || payload.branchPhone || ''; } return { shopName, shopContact }; } function normalizePhone(raw) { let digits = (raw || '').toString().replace(/[^0-9]/g, ''); if (digits.length === 11 && digits.startsWith('0')) digits = digits.slice(1); if (digits.length === 10) digits = '91' + digits; return digits; } async function handleWhatsAppLastPurchase() { const p = (entry.purchases || [])[(entry.purchases || []).length - 1]; if (!p) return alert('No purchase found'); const { shopName, shopContact } = await getBranchInfo(); const productName = `${entry.mobileName || ''}${entry.model ? ' — ' + entry.model : ''}`.trim(); const date = new Date(p.createdAt || Date.now()).toLocaleString(); const message = `Shop: ${shopName}\nContact: ${shopContact}\n\nProduct: ${productName}\nPrice: ₹ ${Number(p.price || 0).toFixed(2)}\nDate: ${date}\nCustomer: ${p.customerName || '-'} — ${p.phone || '-'}\n\nThanks,\n${shopName}`; const cust = normalizePhone(p.phone || ''); try { const whatsappUrl = window.ENV_CONFIG?.WHATSAPP_WEB_URL || 'https://web.whatsapp.com'; if (cust) window.open(`${whatsappUrl}/send?phone=${cust}&text=${encodeURIComponent(message)}`, '_blank'); else window.open(whatsappUrl + '/', '_blank'); } catch (e) { console.error('open whatsapp', e); } } function handlePrintLastPurchase() { const p = (entry.purchases || [])[(entry.purchases || []).length - 1]; if (!p) return alert('No purchase found'); (async () => { const { shopName, shopContact } = await getBranchInfo(); const productName = `${entry.mobileName || ''}${entry.model ? ' — ' + entry.model : ''}`.trim(); const date = new Date(p.createdAt || Date.now()).toLocaleString(); const total = Number(p.price || 0).toFixed(2); const html = `
| Item | Qty | Unit | Line |
|---|---|---|---|
| ${productName} | 1 | ${Number(p.price || 0).toFixed(2)} | ${Number(p.price || 0).toFixed(2)} |