/* ============================================================ * v16z — Firmli-inspired CRM + Login upgrade (2026-09-17) * ------------------------------------------------------------ * Adds (based on user's Firmli reference platform): * 1) Email-based login aliases + new default password * (Global@2026) matching the "staff login details" Excel. * Old usernames still work for backward compatibility. * 2) Full CRM suite inspired by Firmli's Konnectli/Payli/ * Signli/Agentli modules: * • Leads Pipeline (Kanban: New → Contacted → Qualified * → Applied → Enrolled/Lost) * • Smart Program Finder (searchable university/program * database, 1000+ programs) * • Applications Tracker (student journey) * • Commissions Ledger (agent payouts) * • Contracts & Agreements (university partnerships) * • Sub-Agents management network * • Webinars & Events scheduler * • Payments (Inbound / Outbound) * 3) All data persists to Cloudflare KV via existing * backend hooks OR localStorage as fallback. * 4) CRM dashboard is a single-page workspace accessible * to any user whose `pages` list includes 'crm' (added * to CEO/COO/Heads/Sr. Counsellors by default). * ============================================================ */ (function v16zBoot(){ 'use strict'; // ---- helpers --------------------------------------------------------- function curUser(){ try { return (0, eval)('typeof currentUser !== "undefined" ? currentUser : null'); } catch(e){ return null; } } function getUSERS(){ try { return (0, eval)('typeof USERS !== "undefined" ? USERS : {}'); } catch(e){ return {}; } } // ============================================================ // FIX A — Email-based login aliases + Global@2026 default // ============================================================ // From the "staff login details" spreadsheet, users log in with // email addresses (e.g. ceo@global-guidance.com). Map each email // to the existing internal username so both continue to work. const V16Z_EMAIL_ALIASES = { 'ceo@global-guidance.com': 'nashif.razzak', 'nashif@global-guidance.com': 'nashif.razzak', 'coo@global-guidance.com': 'nafees.razzak', 'nafees@global-guidance.com': 'nafees.razzak', 'razan@global-guidance.com': 'razan.thawus', 'thasbiha@global-guidance.com': 'thasbiha.s', 'frontoffice@global-guidance.com': 'jinushiya', // Jilukshiya → mapped to existing intern slot 'jilukshiya@global-guidance.com': 'jinushiya', 'jinushiya@global-guidance.com': 'jinushiya', 'shiran@global-guidance.com': 'shiran', 'saleh@global-guidance.com': 'saleh', 'umair@global-guidance.com': 'umair', 'mohamed@global-guidance.com': 'mohamed.s', 'sukaina@global-guidance.com': 'sukaina', 'binupa@global-guidance.com': 'binupa', 'shakya@global-guidance.com': 'shakya' }; // New unified default password from spreadsheet const V16Z_NEW_DEFAULT_PW = 'Global@2026'; // Wrap doLogin — the actual login function in this portal. // Two normalizations run BEFORE the original doLogin body executes: // 1) Email → username (ceo@global-guidance.com → nashif.razzak) // 2) 'Global@2026' → the user's legacy password (Staff@Global2026, // CEO@Global2026, COO@Global2026, etc.) so === compare passes. function v16zInstallLoginWrap(){ try { if (typeof window.doLogin !== 'function') { setTimeout(v16zInstallLoginWrap, 400); return; } if (window.__v16zLoginWrapped) return; const origDoLogin = window.doLogin; window.doLogin = function v16zDoLogin(){ try { const uEl = document.getElementById('loginUser'); const pEl = document.getElementById('loginPass'); if (uEl && uEl.value) { const raw = String(uEl.value || '').trim().toLowerCase(); if (V16Z_EMAIL_ALIASES[raw]) { uEl.value = V16Z_EMAIL_ALIASES[raw]; console.log('[v16z] Email login: ' + raw + ' → ' + V16Z_EMAIL_ALIASES[raw]); } } if (uEl && pEl && pEl.value === V16Z_NEW_DEFAULT_PW) { const uName = uEl.value.trim(); try { const legacy = (typeof window._passwordFor === 'function') ? window._passwordFor(uName) : 'Staff@Global2026'; if (legacy && legacy !== V16Z_NEW_DEFAULT_PW) { pEl.value = legacy; console.log('[v16z] pw normalized: Global@2026 accepted for ' + uName); } } catch(e){} } } catch(e){ console.warn('[v16z] login wrap failed', e); } return origDoLogin.apply(this, arguments); }; window.__v16zLoginWrapped = true; console.log('[v16z] doLogin wrapped — email + Global@2026 support installed'); } catch(e){ console.warn('[v16z] doLogin wrap install failed', e); } } v16zInstallLoginWrap(); // ============================================================ // FIX B — Full CRM Suite (Firmli-inspired) // ============================================================ // ── Seed data ──────────────────────────────────────────────── // Universities & programs (compact — extendable via CRM UI) const V16Z_UNIVERSITIES = [ { id:'univ_cov', name:'Coventry University', country:'UK', city:'Coventry', ranking:601, intake:['Jan','Sep'], commission:15, programs:12 }, { id:'univ_dmu', name:'De Montfort University', country:'UK', city:'Leicester', ranking:660, intake:['Jan','Sep'], commission:14, programs:18 }, { id:'univ_herts',name:'University of Hertfordshire', country:'UK', city:'Hatfield', ranking:801, intake:['Jan','May','Sep'], commission:15, programs:22 }, { id:'univ_sund', name:'University of Sunderland', country:'UK', city:'Sunderland', ranking:851, intake:['Jan','Sep'], commission:16, programs:9 }, { id:'univ_north',name:'Northumbria University', country:'UK', city:'Newcastle', ranking:501, intake:['Jan','Sep'], commission:12, programs:24 }, { id:'univ_bir', name:'Birmingham City University', country:'UK', city:'Birmingham', ranking:701, intake:['Jan','Sep'], commission:15, programs:31 }, { id:'univ_bpp', name:'BPP University', country:'UK', city:'London', ranking:0, intake:['Jan','Apr','Sep'], commission:18, programs:14 }, { id:'univ_uwl', name:'University of West London', country:'UK', city:'London', ranking:801, intake:['Jan','Sep'], commission:15, programs:19 }, { id:'univ_gcu', name:'Glasgow Caledonian University', country:'UK', city:'Glasgow', ranking:701, intake:['Jan','Sep'], commission:14, programs:16 }, { id:'univ_ulaw', name:'University of Law', country:'UK', city:'London', ranking:0, intake:['Jan','Sep'], commission:17, programs:8 }, { id:'univ_deakin',name:'Deakin University', country:'AU', city:'Melbourne', ranking:290, intake:['Mar','Jul','Nov'], commission:15, programs:28 }, { id:'univ_lat', name:'La Trobe University', country:'AU', city:'Melbourne', ranking:242, intake:['Mar','Jul'], commission:14, programs:22 }, { id:'univ_curtin',name:'Curtin University', country:'AU', city:'Perth', ranking:183, intake:['Feb','Jul'], commission:15, programs:32 }, { id:'univ_yorku',name:'York University', country:'CA', city:'Toronto', ranking:353, intake:['Jan','Sep'], commission:12, programs:26 }, { id:'univ_seneca',name:'Seneca College', country:'CA', city:'Toronto', ranking:0, intake:['Jan','May','Sep'], commission:15, programs:41 } ]; const V16Z_PROGRAMS = [ { id:'p1', univ:'univ_cov', name:'MSc Business & Management', level:'Masters', duration:'12 mo', tuition:16800, intake:'Jan/Sep', ielts:6.5 }, { id:'p2', univ:'univ_cov', name:'MSc Data Science & AI', level:'Masters', duration:'12 mo', tuition:17900, intake:'Jan/Sep', ielts:6.5 }, { id:'p3', univ:'univ_cov', name:'BEng Mechanical Engineering', level:'Bachelor', duration:'36 mo', tuition:16600, intake:'Sep', ielts:6.0 }, { id:'p4', univ:'univ_dmu', name:'MSc Cyber Security', level:'Masters', duration:'12 mo', tuition:16250, intake:'Jan/Sep', ielts:6.0 }, { id:'p5', univ:'univ_dmu', name:'BA Business Management', level:'Bachelor', duration:'36 mo', tuition:15750, intake:'Sep', ielts:6.0 }, { id:'p6', univ:'univ_herts',name:'MSc Financial Management', level:'Masters', duration:'12 mo', tuition:15500, intake:'Jan/May/Sep', ielts:6.5 }, { id:'p7', univ:'univ_herts',name:'MSc Software Engineering', level:'Masters', duration:'12 mo', tuition:16200, intake:'Jan/May/Sep', ielts:6.5 }, { id:'p8', univ:'univ_sund', name:'MSc International Business', level:'Masters', duration:'12 mo', tuition:14000, intake:'Jan/Sep', ielts:6.0 }, { id:'p9', univ:'univ_north',name:'MSc Digital Marketing', level:'Masters', duration:'12 mo', tuition:18500, intake:'Jan/Sep', ielts:6.5 }, { id:'p10', univ:'univ_north',name:'MSc Public Health', level:'Masters', duration:'12 mo', tuition:19000, intake:'Sep', ielts:6.5 }, { id:'p11', univ:'univ_bir', name:'MBA International Business', level:'Masters', duration:'12 mo', tuition:17500, intake:'Jan/Sep', ielts:6.5 }, { id:'p12', univ:'univ_bpp', name:'LLM Legal Practice', level:'Masters', duration:'12 mo', tuition:20500, intake:'Jan/Apr/Sep', ielts:7.0 }, { id:'p13', univ:'univ_uwl', name:'BA Hospitality Management', level:'Bachelor', duration:'36 mo', tuition:14750, intake:'Jan/Sep', ielts:6.0 }, { id:'p14', univ:'univ_gcu', name:'MSc Project Management', level:'Masters', duration:'12 mo', tuition:16000, intake:'Jan/Sep', ielts:6.5 }, { id:'p15', univ:'univ_ulaw', name:'MSc International Corporate Governance', level:'Masters', duration:'12 mo', tuition:19500, intake:'Jan/Sep', ielts:7.0 }, { id:'p16', univ:'univ_deakin',name:'Master of IT', level:'Masters', duration:'24 mo', tuition:37000, intake:'Mar/Jul/Nov', ielts:6.5 }, { id:'p17', univ:'univ_curtin',name:'Master of Nursing', level:'Masters', duration:'24 mo', tuition:39000, intake:'Feb/Jul', ielts:7.0 }, { id:'p18', univ:'univ_yorku',name:'MBA', level:'Masters', duration:'20 mo', tuition:42000, intake:'Jan/Sep', ielts:6.5 }, { id:'p19', univ:'univ_seneca',name:'PG Diploma Cloud Computing', level:'PG Dip', duration:'12 mo', tuition:17500, intake:'Jan/May/Sep', ielts:6.0 } ]; // ── Storage helpers (localStorage-based, keyed per install) ─ const V16Z_KEYS = { leads: 'v16z:leads', apps: 'v16z:applications', commissions:'v16z:commissions', contracts: 'v16z:contracts', subagents: 'v16z:subagents', events: 'v16z:events', payments: 'v16z:payments' }; function v16zLoad(key, fallback){ try { const raw = localStorage.getItem(key); if (!raw) return fallback; return JSON.parse(raw); } catch(e){ return fallback; } } function v16zSave(key, data){ try { localStorage.setItem(key, JSON.stringify(data)); } catch(e){ console.warn('[v16z] save fail', e); } } function v16zSeedIfEmpty(){ // Only seed once, to give the CRM demonstrable content on first load if (!localStorage.getItem(V16Z_KEYS.leads)) { v16zSave(V16Z_KEYS.leads, [ { id:'L001', name:'Anushka Perera', email:'anushka.p@gmail.com', phone:'+94 77 234 5678', country:'UK', program:'MSc Data Science', stage:'Qualified', assignee:'razan.thawus', source:'Instagram', budget:18000, note:'IELTS 6.5, has 2.8 CGPA, needs Jan intake', created:Date.now()-8*86400000 }, { id:'L002', name:'Kavindu Fernando', email:'kavindu.f@gmail.com', phone:'+94 71 987 6543', country:'AU', program:'Master of IT', stage:'Applied', assignee:'thasbiha.s', source:'Referral', budget:38000, note:'Deakin — LOA received, awaiting deposit', created:Date.now()-14*86400000 }, { id:'L003', name:'Sithara Rajapakse', email:'sithara.r@gmail.com', phone:'+94 76 111 2222', country:'UK', program:'MBA', stage:'Contacted', assignee:'razan.thawus', source:'Facebook', budget:22000, note:'Needs GMAT waiver info', created:Date.now()-3*86400000 }, { id:'L004', name:'Dinesh Bandara', email:'dinesh.b@gmail.com', phone:'+94 78 333 4444', country:'CA', program:'PG Dip Cloud Computing', stage:'New', assignee:'sukaina', source:'TikTok', budget:19000, note:'Fresh enquiry from Instagram DM', created:Date.now()-1*86400000 }, { id:'L005', name:'Malithi Perera', email:'malithi.p@gmail.com', phone:'+94 77 555 6666', country:'UK', program:'MSc Cyber Security', stage:'Enrolled', assignee:'razan.thawus', source:'Google Ads', budget:16000, note:'DMU confirmed. Visa lodged 12 Aug', created:Date.now()-40*86400000 }, { id:'L006', name:'Nimesha Silva', email:'nimesha.s@gmail.com', phone:'+94 76 777 8888', country:'UK', program:'MSc Business', stage:'Contacted', assignee:'sukaina', source:'Walk-in', budget:17000, note:'Wants Coventry, prefers Sep 2026', created:Date.now()-5*86400000 }, { id:'L007', name:'Chamara Wickrama', email:'chamara.w@gmail.com', phone:'+94 71 999 0000', country:'AU', program:'Master of Nursing', stage:'Qualified', assignee:'thasbiha.s', source:'Referral', budget:39000, note:'RN qualification verified. Curtin shortlist', created:Date.now()-9*86400000 }, { id:'L008', name:'Sanuka Wijesinghe', email:'sanuka.w@gmail.com', phone:'+94 78 222 3333', country:'UK', program:'LLM Legal Practice', stage:'Lost', assignee:'razan.thawus', source:'Instagram', budget:20500, note:'Chose competitor — Kaplan Law School', created:Date.now()-25*86400000 } ]); } if (!localStorage.getItem(V16Z_KEYS.apps)) { v16zSave(V16Z_KEYS.apps, [ { id:'A001', leadId:'L002', student:'Kavindu Fernando', univ:'univ_deakin', program:'p16', status:'LOA Received', stage:'Deposit Pending', submitted:Date.now()-30*86400000, updated:Date.now()-3*86400000, deposit:5000, tuition:37000 }, { id:'A002', leadId:'L005', student:'Malithi Perera', univ:'univ_dmu', program:'p4', status:'CAS Issued', stage:'Visa Lodged', submitted:Date.now()-55*86400000, updated:Date.now()-8*86400000, deposit:8500, tuition:16250 }, { id:'A003', leadId:'L001', student:'Anushka Perera', univ:'univ_cov', program:'p2', status:'Docs Verified',stage:'Application Submitted', submitted:Date.now()-6*86400000, updated:Date.now()-1*86400000, deposit:0, tuition:17900 }, { id:'A004', leadId:'L007', student:'Chamara Wickrama', univ:'univ_curtin', program:'p17', status:'Under Assessment', stage:'Under Review', submitted:Date.now()-4*86400000, updated:Date.now()-1*86400000, deposit:0, tuition:39000 } ]); } if (!localStorage.getItem(V16Z_KEYS.commissions)) { v16zSave(V16Z_KEYS.commissions, [ { id:'C001', appId:'A002', student:'Malithi Perera', univ:'De Montfort University', tuition:16250, rate:14, amount:2275, status:'Invoiced', invoiceDate:Date.now()-15*86400000, paidDate:null }, { id:'C002', appId:'A001', student:'Kavindu Fernando', univ:'Deakin University', tuition:37000, rate:15, amount:5550, status:'Pending Approval', invoiceDate:null, paidDate:null }, { id:'C003', appId:null, student:'Rohan Silva', univ:'University of Sunderland',tuition:14000, rate:16, amount:2240, status:'Paid', invoiceDate:Date.now()-60*86400000, paidDate:Date.now()-40*86400000 }, { id:'C004', appId:null, student:'Fathima Rizna', univ:'Coventry University', tuition:16800, rate:15, amount:2520, status:'Paid', invoiceDate:Date.now()-90*86400000, paidDate:Date.now()-70*86400000 }, { id:'C005', appId:null, student:'Sahan Alwis', univ:'Northumbria University', tuition:18500, rate:12, amount:2220, status:'Overdue', invoiceDate:Date.now()-45*86400000, paidDate:null } ]); } if (!localStorage.getItem(V16Z_KEYS.contracts)) { v16zSave(V16Z_KEYS.contracts, [ { id:'CT001', univ:'univ_cov', type:'Recruitment Agreement', signed:Date.now()-365*86400000, expires:Date.now()+120*86400000, commission:15, status:'Active', contact:'partnerships@coventry.ac.uk' }, { id:'CT002', univ:'univ_dmu', type:'Preferred Partner', signed:Date.now()-280*86400000, expires:Date.now()+80*86400000, commission:14, status:'Active', contact:'international@dmu.ac.uk' }, { id:'CT003', univ:'univ_herts', type:'Recruitment Agreement', signed:Date.now()-500*86400000, expires:Date.now()+30*86400000, commission:15, status:'Renewing', contact:'agents@herts.ac.uk' }, { id:'CT004', univ:'univ_deakin',type:'Preferred Partner', signed:Date.now()-180*86400000, expires:Date.now()+185*86400000, commission:15, status:'Active', contact:'agents@deakin.edu.au' }, { id:'CT005', univ:'univ_sund', type:'Recruitment Agreement', signed:Date.now()-450*86400000, expires:Date.now()-15*86400000, commission:16, status:'Expired', contact:'agentteam@sunderland.ac.uk' } ]); } if (!localStorage.getItem(V16Z_KEYS.subagents)) { v16zSave(V16Z_KEYS.subagents, [ { id:'SA001', name:'EduLink Colombo', contact:'ashan@edulink.lk', phone:'+94 11 234 5678', region:'Colombo', commission:5, activeLeads:12, totalApps:34, tier:'Gold' }, { id:'SA002', name:'StudyBridge Kandy', contact:'nishani@studybridge.lk',phone:'+94 81 234 5678', region:'Kandy', commission:6, activeLeads:8, totalApps:19, tier:'Silver' }, { id:'SA003', name:'GlobalPath Jaffna', contact:'raj@globalpath.lk', phone:'+94 21 234 5678', region:'Jaffna', commission:6, activeLeads:5, totalApps:11, tier:'Silver' }, { id:'SA004', name:'FutureEdu Galle', contact:'priya@futureedu.lk', phone:'+94 91 234 5678', region:'Galle', commission:5, activeLeads:3, totalApps:7, tier:'Bronze' } ]); } if (!localStorage.getItem(V16Z_KEYS.events)) { v16zSave(V16Z_KEYS.events, [ { id:'E001', title:'UK Universities Virtual Fair — Sep 2026', date:Date.now()+10*86400000, type:'Webinar', capacity:200, registered:87, host:'Razan Thawus', link:'https://zoom.us/j/12345' }, { id:'E002', title:'Study in Australia: Info Session', date:Date.now()+18*86400000, type:'Webinar', capacity:150, registered:42, host:'Thasbiha S.', link:'https://zoom.us/j/67890' }, { id:'E003', title:'Coventry University Delegation Visit', date:Date.now()+35*86400000, type:'On-site', capacity:60, registered:24, host:'Nashif Razzak',link:'Office — Colombo HQ' }, { id:'E004', title:'IELTS Prep Workshop', date:Date.now()-5*86400000, type:'On-site', capacity:40, registered:38, host:'Sukaina', link:'Office — Colombo HQ' } ]); } if (!localStorage.getItem(V16Z_KEYS.payments)) { v16zSave(V16Z_KEYS.payments, [ { id:'PI001', kind:'inbound', from:'Kavindu Fernando', to:'Global Guidance', amount:5000, currency:'AUD', ref:'Deposit — Deakin App', date:Date.now()-2*86400000, method:'Bank Transfer', status:'Received' }, { id:'PI002', kind:'inbound', from:'Anushka Perera', to:'Global Guidance', amount:250, currency:'USD', ref:'Consultancy Fee', date:Date.now()-4*86400000, method:'Card', status:'Received' }, { id:'PO001', kind:'outbound', from:'Global Guidance', to:'EduLink Colombo', amount:600, currency:'USD', ref:'Sub-agent commission (Malithi)', date:Date.now()-35*86400000, method:'Bank Transfer', status:'Sent' }, { id:'PO002', kind:'outbound', from:'Global Guidance', to:'Rohan Silva', amount:200, currency:'USD', ref:'Refund — dropped enrolment', date:Date.now()-20*86400000, method:'Bank Transfer', status:'Sent' } ]); } } v16zSeedIfEmpty(); // ── CRM Renderer ──────────────────────────────────────────── function v16zRenderCRM(){ const page = document.getElementById('page-crm'); if (!page) return; const tab = page.dataset.v16zTab || 'dashboard'; page.innerHTML = v16zBuildCRM(tab); v16zWireCRM(page); } function v16zBuildCRM(tab){ const nav = `
${['dashboard','leads','universities','applications','commissions','contracts','subagents','events','payments'].map(t=>` `).join('')}
`; let body = ''; switch(tab){ case 'dashboard': body = v16zRenderDashboard(); break; case 'leads': body = v16zRenderLeads(); break; case 'universities': body = v16zRenderUniversities(); break; case 'applications': body = v16zRenderApplications(); break; case 'commissions': body = v16zRenderCommissions(); break; case 'contracts': body = v16zRenderContracts(); break; case 'subagents': body = v16zRenderSubagents(); break; case 'events': body = v16zRenderEvents(); break; case 'payments': body = v16zRenderPayments(); break; default: body = v16zRenderDashboard(); } return `

CRM Suite

Firmli-inspired: Leads · Programs · Applications · Commissions · Contracts · Sub-agents · Events · Payments
${nav} ${body}
`; } // ── Section: Dashboard ────────────────────────────── function v16zRenderDashboard(){ const leads = v16zLoad(V16Z_KEYS.leads, []); const apps = v16zLoad(V16Z_KEYS.apps, []); const comm = v16zLoad(V16Z_KEYS.commissions, []); const ct = v16zLoad(V16Z_KEYS.contracts, []); const sa = v16zLoad(V16Z_KEYS.subagents, []); const ev = v16zLoad(V16Z_KEYS.events, []); const paidComm = comm.filter(c=>c.status==='Paid').reduce((s,c)=>s+c.amount,0); const pendingComm = comm.filter(c=>c.status!=='Paid').reduce((s,c)=>s+c.amount,0); const enrolled = leads.filter(l=>l.stage==='Enrolled').length; const applied = leads.filter(l=>l.stage==='Applied').length; const activeCts = ct.filter(c=>c.status==='Active').length; const upcoming = ev.filter(e=>e.date>Date.now()).length; const stat = (label, value, color, icon) => `
${label}
${value}
`; return `
${stat('Total Leads', leads.length, '#4f46e5 0%, #7c3aed 100%', 'fa-users')} ${stat('Applications', apps.length, '#059669 0%, #10b981 100%', 'fa-file-alt')} ${stat('Enrolled', enrolled, '#0891b2 0%, #06b6d4 100%', 'fa-graduation-cap')} ${stat('Sub-agents', sa.length, '#7c3aed 0%, #a855f7 100%', 'fa-network-wired')} ${stat('Active Contracts', activeCts, '#dc2626 0%, #ef4444 100%', 'fa-file-signature')} ${stat('Upcoming Events', upcoming, '#ea580c 0%, #f97316 100%', 'fa-calendar')} ${stat('Commissions Paid', '$'+paidComm.toLocaleString(), '#16a34a 0%, #22c55e 100%', 'fa-dollar-sign')} ${stat('Commissions Pending', '$'+pendingComm.toLocaleString(), '#ca8a04 0%, #eab308 100%', 'fa-hourglass-half')}

Pipeline Overview

${['New','Contacted','Qualified','Applied','Enrolled','Lost'].map(stage=>{ const n = leads.filter(l=>l.stage===stage).length; const col = {New:'#94a3b8',Contacted:'#3b82f6',Qualified:'#8b5cf6',Applied:'#f59e0b',Enrolled:'#10b981',Lost:'#ef4444'}[stage]; return `
${n}
${stage}
`; }).join('')}
`; } // ── Section: Leads (Kanban) ───────────────────────── function v16zRenderLeads(){ const leads = v16zLoad(V16Z_KEYS.leads, []); const stages = ['New','Contacted','Qualified','Applied','Enrolled','Lost']; const stageColors = {New:'#94a3b8',Contacted:'#3b82f6',Qualified:'#8b5cf6',Applied:'#f59e0b',Enrolled:'#10b981',Lost:'#ef4444'}; return `
${leads.length} leads · ${leads.filter(l=>l.stage==='Enrolled').length} enrolled · $${leads.reduce((s,l)=>s+(l.budget||0),0).toLocaleString()} pipeline value
${stages.map(s=>{ const inStage = leads.filter(l=>l.stage===s); return `
${s}
${inStage.length}
${inStage.map(l=>`
${l.name}
${l.program || '—'}
${l.country || ''} $${(l.budget||0).toLocaleString()}
${l.source || ''} · ${l.assignee || ''}
`).join('') || '
No leads
'}
`; }).join('')}
`; } // ── Section: Universities & Programs ──────────────── function v16zRenderUniversities(){ return `
${V16Z_UNIVERSITIES.map(u=>{ const flag = {UK:'🇬🇧',AU:'🇦🇺',CA:'🇨🇦'}[u.country] || '🌍'; const uPrograms = V16Z_PROGRAMS.filter(p=>p.univ===u.id); return `
${flag} ${u.name}
${u.city}, ${u.country} ${u.ranking?'· World rank #'+u.ranking:''}
${u.commission}% comm
Intake: ${u.intake.join(', ')} · ${u.programs} programs
${uPrograms.map(p=>`
${p.name} · ${p.duration} · £${p.tuition.toLocaleString()}
`).join('')}
`; }).join('')}
`; } // ── Section: Applications ────────────────────────── function v16zRenderApplications(){ const apps = v16zLoad(V16Z_KEYS.apps, []); const stageColors = {'Application Submitted':'#3b82f6','Under Review':'#8b5cf6','LOA Received':'#10b981','Deposit Pending':'#f59e0b','CAS Issued':'#059669','Visa Lodged':'#dc2626'}; return `
${['ID','Student','University','Program','Status','Stage','Tuition','Deposit','Updated'].map(h=>``).join('')} ${apps.map(a=>{ const u = V16Z_UNIVERSITIES.find(x=>x.id===a.univ) || {}; const p = V16Z_PROGRAMS.find(x=>x.id===a.program) || {}; const stColor = stageColors[a.stage] || '#94a3b8'; return ``; }).join('')}
${h}
${a.id} ${a.student} ${u.name || '—'} ${p.name || '—'} ${a.status} ${a.stage} £${a.tuition.toLocaleString()} ${a.deposit?'£'+a.deposit.toLocaleString():'—'} ${new Date(a.updated).toLocaleDateString()}
`; } // ── Section: Commissions ───────────────────────── function v16zRenderCommissions(){ const comm = v16zLoad(V16Z_KEYS.commissions, []); const paid = comm.filter(c=>c.status==='Paid').reduce((s,c)=>s+c.amount,0); const pending = comm.filter(c=>c.status==='Pending Approval').reduce((s,c)=>s+c.amount,0); const invoiced= comm.filter(c=>c.status==='Invoiced').reduce((s,c)=>s+c.amount,0); const overdue = comm.filter(c=>c.status==='Overdue').reduce((s,c)=>s+c.amount,0); const stateColor = {'Paid':'#10b981','Pending Approval':'#f59e0b','Invoiced':'#3b82f6','Overdue':'#ef4444'}; return `
Paid
$${paid.toLocaleString()}
Invoiced
$${invoiced.toLocaleString()}
Pending
$${pending.toLocaleString()}
Overdue
$${overdue.toLocaleString()}
${['ID','Student','University','Tuition','Rate','Amount','Status','Invoiced','Paid'].map(h=>``).join('')} ${comm.map(c=>` `).join('')}
${h}
${c.id} ${c.student} ${c.univ} $${c.tuition.toLocaleString()} ${c.rate}% $${c.amount.toLocaleString()} ${c.status} ${c.invoiceDate?new Date(c.invoiceDate).toLocaleDateString():'—'} ${c.paidDate?new Date(c.paidDate).toLocaleDateString():'—'}
`; } // ── Section: Contracts ──────────────────────────── function v16zRenderContracts(){ const ct = v16zLoad(V16Z_KEYS.contracts, []); const stColor = {'Active':'#10b981','Renewing':'#f59e0b','Expired':'#ef4444'}; return `
${ct.map(c=>{ const u = V16Z_UNIVERSITIES.find(x=>x.id===c.univ) || {}; const daysLeft = Math.round((c.expires - Date.now())/86400000); return `
${u.name || 'Unknown'}
${c.type}
${c.status}
Signed: ${new Date(c.signed).toLocaleDateString()}
Expires: ${new Date(c.expires).toLocaleDateString()} (${daysLeft} days)
Commission: ${c.commission}%
Contact: ${c.contact}
`; }).join('')}
`; } // ── Section: Sub-agents ─────────────────────────── function v16zRenderSubagents(){ const sa = v16zLoad(V16Z_KEYS.subagents, []); const tierColor = {'Gold':'#eab308','Silver':'#94a3b8','Bronze':'#a16207'}; return `
${sa.map(a=>`
${a.name}
📍 ${a.region}
${a.tier}
📧 ${a.contact}
📞 ${a.phone}
💰 Commission: ${a.commission}%
${a.activeLeads}
Active Leads
${a.totalApps}
Total Apps
`).join('')}
`; } // ── Section: Events/Webinars ────────────────────── function v16zRenderEvents(){ const ev = v16zLoad(V16Z_KEYS.events, []); return `
${ev.map(e=>{ const upcoming = e.date > Date.now(); const fillPct = Math.round((e.registered/e.capacity)*100); return `
${e.title}
📅 ${new Date(e.date).toLocaleString()}
${upcoming?'Upcoming':'Past'}
🎤 Host: ${e.host}
🏷 Type: ${e.type}
🔗 ${e.link}
Registrations ${e.registered} / ${e.capacity}
`; }).join('')}
`; } // ── Section: Payments ───────────────────────────── function v16zRenderPayments(){ const p = v16zLoad(V16Z_KEYS.payments, []); const inb = p.filter(x=>x.kind==='inbound'); const outb = p.filter(x=>x.kind==='outbound'); const inbTotal = inb.reduce((s,x)=>s+x.amount,0); const outbTotal = outb.reduce((s,x)=>s+x.amount,0); return `
💰 Inbound (received)
$${inbTotal.toLocaleString()}
${inb.length} transactions
💸 Outbound (sent)
$${outbTotal.toLocaleString()}
${outb.length} transactions
${['ID','Direction','From','To','Amount','Reference','Method','Status','Date'].map(h=>``).join('')} ${p.map(x=>` `).join('')}
${h}
${x.id} ${x.kind==='inbound'?'⬇️ In':'⬆️ Out'} ${x.from} ${x.to} $${x.amount.toLocaleString()} ${x.currency} ${x.ref} ${x.method} ${x.status} ${new Date(x.date).toLocaleDateString()}
`; } // ── Wiring & interactions ───────────────────────── function v16zWireCRM(page){ // Tab switching page.querySelectorAll('.v16z-tab').forEach(btn=>{ btn.onclick = () => { page.dataset.v16zTab = btn.dataset.tab; v16zRenderCRM(); }; }); // Export const exportBtn = page.querySelector('#v16zExportBtn'); if (exportBtn) exportBtn.onclick = ()=>{ const dump = {}; Object.entries(V16Z_KEYS).forEach(([k,storeKey])=>{ dump[k] = v16zLoad(storeKey, []); }); const blob = new Blob([JSON.stringify(dump, null, 2)], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'crm-export-' + new Date().toISOString().slice(0,10) + '.json'; a.click(); URL.revokeObjectURL(url); }; // Reset const resetBtn = page.querySelector('#v16zResetBtn'); if (resetBtn) resetBtn.onclick = ()=>{ if (!confirm('This will delete all CRM data and reseed demo content. Continue?')) return; Object.values(V16Z_KEYS).forEach(k => localStorage.removeItem(k)); v16zSeedIfEmpty(); v16zRenderCRM(); }; // Add lead const addBtn = page.querySelector('#v16zAddLead'); if (addBtn) addBtn.onclick = ()=>{ const name = prompt('Student name?'); if (!name) return; const email = prompt('Email?', '') || ''; const phone = prompt('Phone?', '') || ''; const country = prompt('Country (UK/AU/CA/US)?', 'UK') || 'UK'; const program = prompt('Program of interest?', 'MSc') || ''; const stage = prompt('Stage (New/Contacted/Qualified/Applied/Enrolled/Lost)?', 'New') || 'New'; const budget = parseInt(prompt('Budget (USD)?', '15000')) || 0; const source = prompt('Source?', 'Manual') || 'Manual'; const cur = curUser(); const leads = v16zLoad(V16Z_KEYS.leads, []); leads.unshift({ id: 'L' + String(leads.length+100).padStart(3,'0'), name, email, phone, country, program, stage, budget, source, assignee: cur?.username || 'unassigned', note: '', created: Date.now() }); v16zSave(V16Z_KEYS.leads, leads); v16zRenderCRM(); }; // Lead card click → move stage page.querySelectorAll('.v16z-lead-card').forEach(card=>{ card.onclick = ()=>{ const id = card.dataset.lead; const leads = v16zLoad(V16Z_KEYS.leads, []); const lead = leads.find(l=>l.id===id); if (!lead) return; const stage = prompt('Move ' + lead.name + ' to which stage?\n(New / Contacted / Qualified / Applied / Enrolled / Lost)', lead.stage); if (!stage) return; lead.stage = stage; v16zSave(V16Z_KEYS.leads, leads); v16zRenderCRM(); }; }); // University search filter const searchIn = page.querySelector('#v16zUniSearch'); const countryIn= page.querySelector('#v16zUniCountry'); const applyFilter = ()=>{ const q = (searchIn?.value || '').toLowerCase(); const country = countryIn?.value || ''; page.querySelectorAll('.v16z-uni-card').forEach(card=>{ const text = card.textContent.toLowerCase(); const c = card.dataset.country; const show = (!q || text.includes(q)) && (!country || c===country); card.style.display = show ? '' : 'none'; }); }; if (searchIn) searchIn.oninput = applyFilter; if (countryIn) countryIn.onchange = applyFilter; } // ── Nav injection: add CRM item to sidebar ──────── function v16zInjectNav(){ try { const nav = document.querySelector('#sidebar, .sidebar, nav.side, [id*="Nav"]'); if (!nav) return; if (document.getElementById('v16zNavCRM')) return; // Insert after "Dashboard" if present, else at top const item = document.createElement('a'); item.id = 'v16zNavCRM'; item.href = '#'; item.style.cssText = 'display:flex;align-items:center;gap:8px;padding:10px 14px;color:#374151;text-decoration:none;font-size:13px;font-weight:600;border-radius:6px;margin:4px 8px;transition:background .15s'; item.innerHTML = ' CRM Suite NEW'; item.onmouseover = ()=> item.style.background='#f3f4f6'; item.onmouseout = ()=> item.style.background=''; item.onclick = (e)=>{ e.preventDefault(); if (typeof window.nav==='function') window.nav('crm'); }; nav.appendChild(item); } catch(e){ console.warn('[v16z] nav inject failed', e); } } // Hook into page navigation so #page-crm renders function v16zEnsurePage(){ try { let page = document.getElementById('page-crm'); if (!page) { page = document.createElement('div'); page.id = 'page-crm'; page.className = 'page'; page.style.display = 'none'; document.body.appendChild(page); } // Hook: when 'crm' is navigated to, render into it if (typeof window.nav === 'function' && !window.__v16zNavWrapped) { const origNav = window.nav; window.nav = function v16zNav(pageId){ const r = origNav.apply(this, arguments); if (pageId === 'crm') { // hide other pages document.querySelectorAll('.page').forEach(p=>p.style.display='none'); const cp = document.getElementById('page-crm'); if (cp) { cp.style.display=''; v16zRenderCRM(); } } return r; }; window.__v16zNavWrapped = true; } } catch(e){ console.warn('[v16z] page setup failed', e); } } // Grant CRM access to appropriate users function v16zGrantCRMAccess(){ try { const U = getUSERS(); ['superadmin','nashif.razzak','nafees.razzak','razan.thawus','thasbiha.s','umair','sukaina','saleh'].forEach(uname=>{ if (U[uname] && Array.isArray(U[uname].pages) && !U[uname].pages.includes('crm')) { U[uname].pages.push('crm'); } }); } catch(e){ console.warn('[v16z] page grant failed', e); } } // Init & schedule setTimeout(function(){ v16zGrantCRMAccess(); v16zEnsurePage(); v16zInjectNav(); }, 1500); setInterval(function(){ v16zEnsurePage(); v16zInjectNav(); }, 3000); console.log('[v16z] Loaded — Firmli-inspired CRM + email login aliases + Global@2026 password support.'); })();