function SecondsSalesView({ salesUrl, token, id }) { const [entry, setEntry] = React.useState(null); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(''); React.useEffect(() => { let mounted = true; async function load() { try { setLoading(true); 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]); // Purchase modal state (hooks before returns) 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); // 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; }; }, []); 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'); // server returns updated entry (with purchases) 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); } } // Helpers to prepare WhatsApp message and printable bill 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 { // Open WhatsApp web with prefilled message to customer number 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)} |
Please wait while we fetch the information...
{error}
The requested mobile entry could not be found.
Complete information and purchase history
Track all sales for this mobile device
This mobile device is still available for sale. Use the "Record Purchase" button above to add a sale.
Enter customer details and sale information