// Sidebar and HeaderBar are now loaded from src/components via index.html function App() { const [email, setEmail] = React.useState(''); const [password, setPassword] = React.useState(''); const [token, setToken] = React.useState(localStorage.getItem('sales_token') || ''); const [hasAccess, setHasAccess] = React.useState(null); // null=unknown, true/false const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(''); const [view, setView] = React.useState((location.hash || '#bank').slice(1)); const [planId, setPlanId] = React.useState(''); const [branchLimit, setBranchLimit] = React.useState(0); const [branchUser, setBranchUser] = React.useState(null); // Device detection for responsive layout const { isMobile } = useDeviceDetection(); const SALES_URL = window.ENV_CONFIG?.SALES_API_URL || 'http://127.0.0.1:9000'; const decodeJwt = (tk) => { try { const base64 = tk.split('.')[1]; const json = atob(base64.replace(/-/g, '+').replace(/_/g, '/')); return JSON.parse(json); } catch { return null; } }; const login = async (e) => { e.preventDefault(); setError(''); setLoading(true); try { const res = await fetch(SALES_URL + '/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || data.message || 'Login failed'); if (!data.token) throw new Error('No token returned'); // Ensure branch token is removed so only one token type exists in this browser try { localStorage.removeItem('branch_token'); } catch (e) {} localStorage.setItem('sales_token', data.token); setToken(data.token); setHasAccess(true); setBranchUser(null); try { window.dispatchEvent(new Event('sales-login')); } catch (__) {} const payload = data.payload || decodeJwt(data.token); if (payload?.mongoPlanId) setPlanId(payload.mongoPlanId); if (Number.isFinite(payload?.branchLimit)) setBranchLimit(payload.branchLimit); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const checkAccess = async (tk = token) => { setError(''); setLoading(true); try { const res = await fetch(SALES_URL + '/auth/verify', { headers: { Authorization: 'Bearer ' + tk } }); const data = await res.json(); if (!res.ok || !data.valid) throw new Error(data.message || 'Token invalid'); setHasAccess(true); if (data.token) { localStorage.setItem('sales_token', data.token); setToken(data.token); } const decoded = data.decoded || data.payload || decodeJwt(data.token || tk); if (decoded?.mongoPlanId) setPlanId(decoded.mongoPlanId); if (Number.isFinite(decoded?.branchLimit)) setBranchLimit(decoded.branchLimit); } catch (err) { setError(err.message); setHasAccess(false); } finally { setLoading(false); } }; React.useEffect(() => { if (localStorage.getItem('token') && !localStorage.getItem('sales_token')) { localStorage.removeItem('token'); } if (token && hasAccess === null) checkAccess(token); const onHash = () => setView((location.hash || '#bank').slice(1)); window.addEventListener('hashchange', onHash); // Handle branch-login events dispatched from BranchLogin so same-tab updates work const onBranchLoginEvent = () => { const bt = localStorage.getItem('branch_token'); if (bt) { const d = decodeJwt(bt); setBranchUser(d || null); // Ensure sales token isn't used setToken(''); setHasAccess(false); try { location.hash = '#branch'; } catch (_) {} } }; window.addEventListener('branch-login', onBranchLoginEvent); const onSalesLoginEvent = () => { // sales login happened elsewhere in-app/tab: clear branchUser setBranchUser(null); }; window.addEventListener('sales-login', onSalesLoginEvent); // update branchUser state when branch_token changes elsewhere const syncBranchUser = () => { const bt = localStorage.getItem('branch_token'); if (bt) { const d = decodeJwt(bt); setBranchUser(d || null); } else setBranchUser(null); }; syncBranchUser(); window.addEventListener('storage', syncBranchUser); return () => { window.removeEventListener('hashchange', onHash); window.removeEventListener('storage', syncBranchUser); window.removeEventListener('branch-login', onBranchLoginEvent); window.removeEventListener('sales-login', onSalesLoginEvent); }; }, []); if (loading && hasAccess === null) return
Loading…
; // If we're a branch user, render the app immediately (use branch_token as effective token) const effectiveToken = branchUser ? (localStorage.getItem('branch_token') || '') : token; // If we're not a branch user and we don't have a valid sales token, show auth screens if (!branchUser && ((!token) || hasAccess === false)) { // Allow visiting public branch-login route even without sales token if ((location.hash || '#bank').slice(1) === 'branch-login') { if (isMobile) { return ( {}} active="branch-login" onSelect={() => {}} planId="" branchLimit={0} branchUser={null} > ); } return
; } // No sales token and not a branch user -> show sales login if (isMobile) { return ( checkAccess()} loading={loading} error={error} token={token} /> ); } return (
🚀

Welcome Back

Sign in to your SalesPro account

setEmail(e.target.value)} type="email" className="form-input" placeholder="Enter your email" required />
setPassword(e.target.value)} type="password" className="form-input" placeholder="Enter your password" required />
{token && ( )}
{error && (
{error}
)}

Need a branch account? Sign in as Branch

); } const logout = () => { localStorage.removeItem('sales_token'); localStorage.removeItem('branch_token'); setToken(''); setBranchUser(null); setHasAccess(false); location.hash = '#bank'; }; const branchLogout = () => { // Only remove branch token, keep sales token for admin localStorage.removeItem('branch_token'); setBranchUser(null); // Restore admin access by checking existing sales token const salesToken = localStorage.getItem('sales_token'); if (salesToken) { setToken(salesToken); checkAccess(salesToken); // Trigger feature reload for admin account try { window.dispatchEvent(new Event('sales-login')); } catch (e) { console.log('Event dispatch failed:', e); } } location.hash = '#bank'; }; // Helper function to get page title const getTitle = () => { if (branchUser) return 'Branch Dashboard'; switch (view) { case 'bank': return 'Payment Methods'; case 'bank-history': return 'Payment History'; case 'supplier': return 'Supplier Management'; case 'branch': return 'Branch Management'; case 'whatsapp-contact': return 'WhatsApp Contacts'; case 'whatsapp-stock': return 'WhatsApp Inventory'; case 'product-sales': return 'Point of Sale'; case 'sales-track': return 'Sales Analytics'; case 'branch-expense': return 'Expenses'; case 'seconds-sales': return 'Quick Sales'; case 'offer': return 'Promotions'; default: return 'Master Inventory'; } }; // Helper function to get page subtitle const getSubtitle = () => { if (branchUser) return 'Manage your branch operations and sales'; switch (view) { case 'bank': return 'Set up payment methods for your business'; case 'bank-history': return 'View all payment transactions and balances'; case 'supplier': return 'Manage your suppliers and vendors'; case 'branch': return 'Create and manage branch locations'; case 'whatsapp-contact': return 'Manage WhatsApp customer contacts'; case 'whatsapp-stock': return 'Track WhatsApp-specific inventory'; case 'product-sales': return 'Process customer sales and transactions'; case 'sales-track': return 'Monitor sales performance and trends'; case 'branch-expense': return 'Record branch expenses and costs'; case 'seconds-sales': return 'Quick sale processing for busy periods'; case 'offer': return 'Create and manage promotional offers'; default: return 'Track and manage your complete inventory'; } }; // Main content component const MainContent = () => { // Show mobile dashboard only on default view when on mobile if (isMobile && view === 'bank') { return ( { setView(actionId); try { location.hash = '#' + actionId; } catch {} }} /> ); } return ( <> {branchUser ? ( // branch-specific welcome screen as the first nav item view === 'branch-welcome' || view === '' || view === 'branch' ? (

Welcome

Welcome, {branchUser.name || 'Branch User'}!

) : null ) : null} {view === 'bank' ? ( ) : view === 'bank-history' ? ( window.BankHistory ? React.createElement(window.BankHistory, { salesUrl: SALES_URL, token: effectiveToken }) : React.createElement('div', { style: { padding: '20px' } }, 'Loading Payment History...') ) : (!branchUser && view === 'gst-calculator') ? ( (window.GstCalculatorView ? React.createElement(window.GstCalculatorView) : (
🧮
Loading…
)) ) : (view === 'supplier') ? ( ) : (!branchUser && view === 'branch') ? ( (planId === 'sales-gold' || planId === 'sales-premium') ? ( ) : (
🔒
Upgrade Required
Branch management is available on Sales Gold and Premium plans.
) ) : view === 'branch-login' ? ( ) : view === 'branch-supply' ? ( ) : (!branchUser && view === 'whatsapp-stock') ? ( (window.WhatsappStock ? React.createElement(window.WhatsappStock, { salesUrl: SALES_URL, token: effectiveToken }) : (
📦💬
Loading…
)) ) : (!branchUser && view === 'whatsapp-contact') ? ( (window.WhatsappContact ? React.createElement(window.WhatsappContact, { salesUrl: SALES_URL, token: effectiveToken }) : (
💬
Loading…
)) ) : (!branchUser && view === 'seconds-sales') ? ( (window.SecondsSales ? React.createElement(window.SecondsSales, { salesUrl: SALES_URL, token: effectiveToken }) : (
📊
Loading…
)) ) : (branchUser && view === 'seconds-sales') ? ( (window.SecondsSales ? React.createElement(window.SecondsSales, { salesUrl: SALES_URL, token: effectiveToken }) : (
📊
Loading…
)) ) : (branchUser && (view || '').startsWith('seconds-sales-view-')) ? ( // extract id after prefix (() => { const id = (view || '').replace('seconds-sales-view-', ''); return (window.SecondsSalesView ? React.createElement(window.SecondsSalesView, { salesUrl: SALES_URL, token: effectiveToken, id }) : (
📊
Loading…
)); })() ) : view === 'branch-supply-history' ? ( (window.BranchSupplyHistory ? React.createElement(window.BranchSupplyHistory, { salesUrl: SALES_URL, token: effectiveToken }) : (
📦
Loading…
Branch Supply History component not loaded yet.
)) ) : view === 'stock-history' ? ( (window.StockHistory ? React.createElement(window.StockHistory, { salesUrl: SALES_URL, token: effectiveToken, branchUser }) : (
📚
Loading…
Stock History component not loaded yet.
)) ) : view === 'branch-expense' ? ( (window.BranchNewExpense ? React.createElement(window.BranchNewExpense, { salesUrl: SALES_URL, token: effectiveToken, branchUser }) : (
💸
Loading…
)) ) : view === 'sales-track' ? ( (window.SalesTrack ? React.createElement(window.SalesTrack, { salesUrl: SALES_URL, token: effectiveToken }) : (
📊
Loading…
)) ) : view === 'product-sales' ? ( (window.ProductSales ? React.createElement(window.ProductSales, { salesUrl: SALES_URL, token: effectiveToken }) : (
🛍️
Loading…
)) ) : ( view === 'instock' && branchUser ? ( ) : ( ) )} ); }; // Render mobile or desktop layout based on device detection if (isMobile) { return ( ); } return (
); } // Modern CreateBranch component with enhanced UI function CreateBranch({ salesUrl, token, planId, branchLimit = 0 }) { const [form, setForm] = React.useState({ name: '', address: '', gstNo: '', phoneNumber: '', email: '', password: '', confirmPassword: '' }); const [rows, setRows] = React.useState([]); const [saving, setSaving] = React.useState(false); const [error, setError] = React.useState(''); const [page, setPage] = React.useState(1); const [pageSize] = React.useState(10); const onChange = (e) => { const { name, value } = e.target; setForm((f) => ({ ...f, [name]: value })); }; const loadBranches = async () => { try { setError(''); const res = await fetch(salesUrl + '/api/branches', { headers: { Authorization: 'Bearer ' + token } }); const data = await res.json(); if (!res.ok) throw new Error(data.message || 'Failed to load branches'); setRows(Array.isArray(data.branches) ? data.branches : []); setPage(1); } catch (e) { setError(e.message); } }; React.useEffect(() => { loadBranches(); }, []); const submit = async (e) => { e.preventDefault(); setError(''); if (form.password !== form.confirmPassword) return setError('Passwords do not match'); if (rows.length >= branchLimit) return setError('Branch limit reached for your plan'); setSaving(true); try { const res = await fetch(salesUrl + '/api/branches', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token }, body: JSON.stringify({ name: form.name, address: form.address, gstNo: form.gstNo, // Added GST No field phoneNumber: form.phoneNumber, email: form.email, password: form.password, }), }); const data = await res.json(); if (!res.ok || !data.success) throw new Error(data.message || 'Create failed'); setForm({ name: '', address: '', gstNo: '', phoneNumber: '', email: '', password: '', confirmPassword: '' }); await loadBranches(); } catch (e) { setError(e.message); } finally { setSaving(false); } }; // pagination const total = rows.length; const startIndex = total === 0 ? 0 : (page - 1) * pageSize + 1; const endIndex = Math.min(page * pageSize, total); const totalPages = Math.max(1, Math.ceil(total / pageSize)); const visible = rows.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize); return (
{/* Statistics Cards */}
🏪
Branch Limit
{Number.isFinite(branchLimit) ? branchLimit : 0}
Plan: {planId || 'Basic'}
🌟
Active Branches
{rows.length}
{rows.length > 0 ? 'Operational' : 'Getting Started'}
📊
Available Slots
{Math.max(0, branchLimit - rows.length)}
{branchLimit - rows.length > 0 ? 'Ready to expand' : 'Limit reached'}
{/* Create Branch Form */}

Create New Branch

Add a new branch location to expand your business

{branchLimit > 0 ? `Using ${rows.length} of ${branchLimit} branches` : 'Upgrade to enable branches'}
{error && (
{error}
)}
{/* Branches Table */}

Branch Locations

Manage all your business locations

{visible.length === 0 ? (
No Branches Yet
Create your first branch location to get started
) : ( <>
{visible.map((r, i) => ( ))}
No. Branch Name Address Contact Email GST No Status
{startIndex + i} {r.name || '-'} {r.address || '-'} {r.phoneNumber || '-'} {r.email || '-'} {r.gstNo || '-'} Active
{total === 0 ? 'No branches found' : `Showing ${startIndex} to ${endIndex} of ${total} branches`}
Page {page} of {totalPages}
)}
); } const rootEl = document.getElementById('root'); const root = ReactDOM.createRoot(rootEl); // Wrap App with SalesFeatureProvider const AppWithFeatures = () => { return React.createElement(window.SalesFeatureProvider, {}, React.createElement(App)); }; root.render(React.createElement(AppWithFeatures));