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 = `Receipt` + `

TAX INVOICE

${shopName || 'Shop'}
${shopContact || ''}
` + `
Date: ${date}
` + `` + `` + `
ItemQtyUnitLine
${productName}1${Number(p.price || 0).toFixed(2)}${Number(p.price || 0).toFixed(2)}
` + `
Thanks for your purchase!
`; const w = window.open('', '_blank'); if (!w) { alert('Popup blocked: allow popups to print directly'); return; } w.document.open(); w.document.write(html); w.document.close(); w.focus(); setTimeout(() => { try { w.print(); } catch (e) { /* ignore */ } }, 300); })(); } const fileLinks = (arr) => (arr || []).map((f, i) => { const filename = f.filename || (f.path ? f.path.split(/[\\/]/).pop() : null); const base = (salesUrl || '').replace(/\/+$/, '') || ''; const url = filename ? `${base}/uploads/seconds-sales/${filename}` : f.path || '#'; return (
{f.originalName || filename || 'file'}
); }); if (loading) { return (
Loading Entry
Please wait…
); } if (error) { return (
⚠️
Error
{error}
); } if (!entry) { return (
📱
Entry Not Found
This mobile entry could not be found.
); } const lastPurchase = (entry.purchases || [])[(entry.purchases || []).length - 1]; return (
{entry.mobileName || 'Mobile Entry'} Details
{entry.model || ''}
Device
IMEI: {entry.imeNo || '—'}
Specification: {entry.specification || '—'}
Condition: {entry.mobileCondition || '—'}
Color: {entry.colour || '—'}
Seller
Name: {entry.sellerName || '—'}
Address: {entry.sellerAddress || '—'}
Reference: {(entry.referenceName || '—') + (entry.referenceNumber ? ' — ' + entry.referenceNumber : '')}
Reason: {entry.reasonForSale || '—'}
Payment & Proof
Proof: {(entry.proofType || '—') + (entry.proofNo ? ' — ' + entry.proofNo : '')}
₹{Number(entry.valueOfProduct || 0).toFixed(2)}
Attachments
Images ({(entry.images || []).length})
{(entry.images || []).length ? fileLinks(entry.images) :
No images
}
Documents ({(entry.documents || []).length})
{(entry.documents || []).length ? fileLinks(entry.documents) :
No documents
}
Signatures ({(entry.signatures || []).length})
{(entry.signatures || []).length ? fileLinks(entry.signatures) :
No signatures
}
Purchase History
{(entry.purchases || []).length === 0 ? (
No purchases yet.
) : (
{(entry.purchases || []).map((p, idx) => (
SOLD #{idx + 1}
{p.customerName || 'Unknown Customer'}
{p.phone || 'No phone'}
₹{Number(p.price || 0).toFixed(2)}
{new Date(p.createdAt || Date.now()).toLocaleString('en-IN', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
{(p.documents || []).length ? (
Documents
{fileLinks(p.documents)}
) : null} {(p.images || []).length ? (
Images
{fileLinks(p.images)}
) : null}
))}
)} {lastPurchase ? (
) : null}
{showPurchase ? (
setShowPurchase(false)} >
e.stopPropagation()} >
Record Purchase
filesToBase64(e.target.files, setPurchaseDocs)} style={{ width: '100%', marginTop: 6 }} /> {purchaseDocs.length ?
{purchaseDocs.length} selected
: null}
filesToBase64(e.target.files, setPurchaseImages)} style={{ width: '100%', marginTop: 6 }} /> {purchaseImages.length ?
{purchaseImages.length} selected
: null}
) : null}
); } window.MobileSecondsSalesView = MobileSecondsSalesView;