Install NetPulseAdd to home screen
ISP Management Portal

Dashboard

Customers
Pending
Today IN
Month IN
Recent Payments
Loading…
Highest Due
Loading…
CodeNameMobileAddressBalanceStatus
Loading…
0 selected
DateTypeCodeCustomerAmountDetailBy
Loading…
Plan NameDescriptionBase Price (₹)GST %Selling Price (₹)Status
Loading…
0 selected
UsernameRoleStatusCreatedActions
Loading…
Permissions: Admin — full access. Agent — collect payments & renewals only, cannot delete, export, or manage users/plans.
Import BIX42 Backup

Upload bixapl_backup_db.bin — records are merged, not duplicated.

Database Backup

Auto-backed up daily at 2 AM. Download a manual copy anytime.

Export CSV
👤 Account Info
Username
Role
🔒 Change Password
✅ Password changed successfully!
🏢 Company Information

Appears on printed invoices and receipts.

✅ Saved!
📧 Email & Auto-Backup

Auto-backup emails the database to you daily at 2 AM.

587 = TLS, 465 = SSL
⚠️ Must exactly match your SMTP account — mismatches cause "553 relay" errors
For Gmail: enable 2FA → generate App Password
Auto-backup runs daily at 2:00 AM
`; } // ── Modal helpers ───────────────────────────────────────────────── function om(id){G(id).classList.add('on');} function cm(id){G(id).classList.remove('on');} // ── Toast ───────────────────────────────────────────────────────── let _tt; function toast(msg,isErr){ const el=G('toast');el.textContent=msg;el.className=isErr?'er on':'ok on'; clearTimeout(_tt);_tt=setTimeout(()=>el.classList.remove('on'),3200); } // ── Utils ───────────────────────────────────────────────────────── function G(id){return document.getElementById(id);} function A(n){return(parseFloat(n)||0).toLocaleString('en-IN',{maximumFractionDigits:2});} function N(n){return(parseInt(n)||0).toLocaleString('en-IN');} function AL(n){n=parseFloat(n)||0;if(n>=100000)return(n/100000).toFixed(2)+'L';if(n>=1000)return(n/1000).toFixed(1)+'K';return A(n);} function E(s){return String(s||'').replace(/&/g,'&').replace(//g,'>');} function sE(el,msg){if(!el)return;el.textContent=msg;el.style.display=msg?'block':'none';} function PG(id,page,pages,cb){ const el=G(id);if(!el)return;if(pages<=1){el.innerHTML='';return;} el.innerHTML=`Page ${page} / ${pages}`; } function dbc(fn,ms){let t;return(...a)=>{clearTimeout(t);t=setTimeout(()=>fn(...a),ms);};} window.addEventListener('resize',()=>{const mc=G('ml-cards');if(mc)mc.style.display=window.innerWidth<=768?'flex':'none';}); const IDLE_MS = 60 * 60 * 1000; let _idleTimer = null; function resetIdle(){ if(!TOK) return; clearTimeout(_idleTimer); _idleTimer = setTimeout(()=>{ if(TOK){ logout(); toast('Logged out due to inactivity',true); } }, IDLE_MS); } ['click','keydown','touchstart','scroll'].forEach(ev => document.addEventListener(ev, resetIdle, {passive:true}) ); let selC = new Set(), selPl = new Set(); function toggleAllC(cb){ selC.clear(); if(cb.checked) document.querySelectorAll('.c-cb').forEach(x=>{ selC.add(parseInt(x.value)); x.checked=true; }); else document.querySelectorAll('.c-cb').forEach(x=>{ x.checked=false; }); updateBulkC(); } function onCCb(cb){ const id=parseInt(cb.value); if(cb.checked) selC.add(id); else selC.delete(id); G('c-chk-all').checked = selC.size>0 && selC.size===document.querySelectorAll('.c-cb').length; updateBulkC(); } function updateBulkC(){ const bar=G('c-bulk-bar'); if(!bar)return; bar.classList.toggle('show', selC.size>0 && ROLE==='admin'); const el=G('c-bulk-cnt'); if(el) el.textContent=selC.size+' selected'; } function clearSelC(){ selC.clear(); document.querySelectorAll('.c-cb').forEach(x=>x.checked=false); const all=G('c-chk-all');if(all)all.checked=false; updateBulkC(); } async function bulkDelC(){ if(!selC.size) return; if(!confirm(`Delete ${selC.size} customer(s) and ALL their payments/renewals?\nThis CANNOT be undone.`)) return; const r=await api('POST','/api/customers/bulk-delete',{ids:[...selC]}); if(r?.ok){ toast(`✅ Deleted ${r.deleted} customer(s)`); selC.clear(); loadC(); } else toast(r?.error||'Bulk delete failed',true); } async function delCustomer(id,name){ if(!confirm(`Delete "${name}" and ALL their data (payments, renewals)?\nThis CANNOT be undone.`)) return; const r=await api('DELETE','/api/customers/'+id); if(r?.ok){ toast('Customer deleted'); if(CUR?._id===id)closeDP(); loadC(); } else toast(r?.error||'Delete failed',true); } function toggleAllPl(cb){ selPl.clear(); if(cb.checked) document.querySelectorAll('.pl-cb').forEach(x=>{ selPl.add(parseInt(x.value)); x.checked=true; }); else document.querySelectorAll('.pl-cb').forEach(x=>{ x.checked=false; }); updateBulkPl(); } function onPlCb(cb){ const id=parseInt(cb.value); if(cb.checked) selPl.add(id); else selPl.delete(id); G('pl-chk-all').checked = selPl.size>0 && selPl.size===document.querySelectorAll('.pl-cb').length; updateBulkPl(); } function updateBulkPl(){ const bar=G('pl-bulk-bar'); if(!bar)return; bar.classList.toggle('show', selPl.size>0 && ROLE==='admin'); const el=G('pl-bulk-cnt'); if(el) el.textContent=selPl.size+' selected'; } function clearSelPl(){ selPl.clear(); document.querySelectorAll('.pl-cb').forEach(x=>x.checked=false); const all=G('pl-chk-all');if(all)all.checked=false; updateBulkPl(); } async function bulkDelPl(){ if(!selPl.size) return; if(!confirm(`Delete ${selPl.size} plan(s)?\nPlans used in renewals cannot be deleted.`)) return; const r=await api('POST','/api/plans/bulk-delete',{ids:[...selPl]}); if(r?.ok){ toast(`✅ Deleted ${r.deleted} plan(s)`); selPl.clear(); loadPlans(); } else toast(r?.error||'Bulk delete failed',true); } async function delPlan(id,name){ if(!confirm(`Delete plan "${name}"?\nPlans used in renewals cannot be deleted.`)) return; const r=await api('DELETE','/api/plans/'+id); if(r?.ok){ toast('Plan deleted'); selPl.delete(id); loadPlans(); } else toast(r?.error||'Delete failed',true); } window.addEventListener('DOMContentLoaded',async()=>{ const splash=G('splash'); const tok=localStorage.getItem('np_tok'); if(!tok){ splash.classList.add('out');setTimeout(()=>splash.remove(),400); G('LP').style.display='flex';return; } TOK=tok; const r=await api('GET','/api/stats'); if(r&&!r.error){ try{const p=JSON.parse(atob(tok.split('.')[1]));ROLE=p.role;setUserUI(p.username,p.role);}catch(_){} startApp(); } else { localStorage.removeItem('np_tok');TOK=''; G('LP').style.display='flex'; } splash.classList.add('out');setTimeout(()=>splash.remove(),400); });