/* ============================================================ inbox.js — Central Recruitment Inbox + Outlook Email tab ============================================================ */ window.Views = window.Views || {}; window.Inbox = {}; Views.inbox = function () { const state = { tab: 'All Applications', selected: null, emailSelected: null, q: '' }; const tabs = ['All Applications', 'Unread', 'Imported', 'Processed', 'Rejected', 'Duplicates', 'Email']; function filtered() { let list = DB.inbox; if (state.tab === 'Unread') list = list.filter(i => i.processing === 'Unread'); else if (state.tab === 'Imported') list = list.filter(i => i.processing === 'Imported'); else if (state.tab === 'Processed') list = list.filter(i => i.processing === 'Processed'); else if (state.tab === 'Rejected') list = list.filter(i => i.processing === 'Rejected'); else if (state.tab === 'Duplicates') list = list.filter(i => i.duplicate); if (state.q) list = list.filter(i => (i.name + i.position + i.source).toLowerCase().includes(state.q.toLowerCase())); return list; } function counts() { return { 'All Applications': DB.inbox.length, 'Unread': DB.inbox.filter(i => i.processing === 'Unread').length, 'Imported': DB.inbox.filter(i => i.processing === 'Imported').length, 'Processed': DB.inbox.filter(i => i.processing === 'Processed').length, 'Rejected': DB.inbox.filter(i => i.processing === 'Rejected').length, 'Duplicates': DB.inbox.filter(i => i.duplicate).length, 'Email': DB.emails.filter(e => e.unread).length }; } function sourceChip(item) { const m = item.sourceMeta; // The dot carries the partner's brand colour; the label uses theme text. // Rendering 11px labels in the partner colour failed AA in both themes. // `--chip` carries the source colour; CSS mixes the tint. String-concat // alpha ("#0a66c214") breaks for the tokenised sources (var(--c1)14). return `${item.source}`; } function renderList() { const el = document.getElementById('inboxList'); if (!el) return; const list = filtered(); if (!list.length) { el.innerHTML = `
${UI.icon('inbox')}

Nothing here

No applications in this view.

`; return; } el.innerHTML = list.map(i => `
${UI.avatar(i.name, i.initials, i.color)}
${i.name} ${i.duplicate ? 'DUP' : ''}
${i.position}
${sourceChip(i)} ${UI.badge(i.processing)}
${DB.relTime(Math.round((new Date('2026-07-09T20:00') - i.received) / 60000))}
${UI.scoreChip(i.atsScore)}
`).join(''); el.querySelectorAll('.inbox-item').forEach(row => row.onclick = () => { state.selected = row.dataset.id; const it = DB.inbox.find(x => x.id === state.selected); if (it) it.unread = false; renderList(); renderDetail(); App.updateBadges(); }); } function renderDetail() { const el = document.getElementById('inboxDetail'); if (!el) return; const i = DB.inbox.find(x => x.id === state.selected); if (!i) { el.innerHTML = `
${UI.icon('inbox')}

Select an application

Choose an item from the list to view details and take action.

`; return; } const rec = DB.atsRecommendationClass; const recLabel = i.atsScore >= 82 ? 'Strong Match' : i.atsScore >= 65 ? 'Potential Match' : 'Weak Match'; el.innerHTML = `
${UI.avatar(i.name, i.initials, i.color, 'avatar-lg')}
${i.name}
${i.position}
${sourceChip(i)} ${UI.badge(i.processing)} ${UI.badge(i.resumeStatus, i.resumeStatus === 'Parsed' ? 'b-green' : i.resumeStatus === 'Failed' ? 'b-red' : 'b-amber')}
${i.atsScore}
ATS Score
Email
${i.email}
Phone
${i.phone}
Experience
${i.experience} years
Assigned Recruiter
${i.recruiter}
Received
${DB.fmtDate(i.received)}
Match
${UI.badge(recLabel, rec(recLabel))}
${UI.icon('paperclip')} ${i.attachment}
${Inbox._resumeText(i)}
`; } function renderBody() { const body = document.getElementById('inboxBody'); if (state.tab === 'Email') { body.innerHTML = Inbox._emailView(); Inbox._bindEmail(state); return; } body.innerHTML = `
`; renderList(); renderDetail(); const s = document.getElementById('inboxSearch'); s.oninput = () => { state.q = s.value; renderList(); }; } Inbox._render = { list: renderList, detail: renderDetail, body: renderBody }; Inbox._state = state; const c = counts(); const tabHtml = tabs.map(t => `
${t} ${c[t]}
`).join(''); const html = `

Recruitment Inbox

Every candidate, every source — one unified queue

Microsoft Graph API · Connected
${tabHtml}
`; return { html, onMount() { renderBody(); document.querySelectorAll('#inboxTabs .tab').forEach(tab => tab.onclick = () => { state.tab = tab.dataset.tab; state.selected = null; document.querySelectorAll('#inboxTabs .tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); renderBody(); }); } }; }; Inbox._resumeText = function (i) { return `${i.name.toUpperCase()}\n${i.email} · ${i.phone}\n${'—'.repeat(30)}\nPROFESSIONAL SUMMARY\n${i.experience} years of experience. Applied for ${i.position} via ${i.source}.\n\nEXPERIENCE\n• ${DB.pick(DB.companies)} — Senior role (2021–Present)\n• ${DB.pick(DB.companies)} — Associate (2018–2021)\n\nEDUCATION\n• Bachelor's Degree, Computer Science\n\nSKILLS\n• ${DB.pick(DB.skillsPool)}, ${DB.pick(DB.skillsPool)}, ${DB.pick(DB.skillsPool)}, ${DB.pick(DB.skillsPool)}`; }; Inbox.viewResume = function (id) { const i = DB.inbox.find(x => x.id === id); UI.modal({ title: i.attachment, subtitle: 'Resume preview · ' + i.name, body: `
${Inbox._resumeText(i)}
`, footer: ``, size: 'modal-lg' }); }; Inbox.parse = function (id) { const i = DB.inbox.find(x => x.id === id); i.resumeStatus = 'Parsing'; Inbox._render.detail(); UI.toast('Parsing resume with AI…', 'info'); setTimeout(() => { i.resumeStatus = 'Parsed'; i.atsScore = DB.int(60, 96); Inbox._render.list(); Inbox._render.detail(); UI.toast('Resume parsed — profile fields extracted', 'success'); }, 1100); }; Inbox.import = function (id) { const i = DB.inbox.find(x => x.id === id); const job = DB.getJob(i.jobId) || DB.jobs[0]; // create candidate const newC = { id: 'CAN-' + (5001 + DB.candidates.length), name: i.name, initials: i.initials, color: i.color, email: i.email, phone: i.phone, jobId: job.id, jobTitle: job.title, department: job.department, experience: i.experience, currentCompany: DB.pick(DB.companies), currentTitle: job.title, location: DB.pick(DB.locations), stage: 'Applied', status: 'Applied', aiScore: i.atsScore, source: i.source, recruiter: i.recruiter, recruiterId: '', applied: new Date('2026-07-09'), education: "Bachelor's Degree", skills: job.skills.slice(0, 4), rating: '4.0', salary: DB.int(90, 180) * 1000, matchedSkills: job.skills.slice(0, 3), missingSkills: job.skills.slice(3), recommendation: i.atsScore >= 82 ? 'Strong Match' : 'Potential Match', subScores: { skills: i.atsScore, experience: i.atsScore, education: 80, keywords: i.atsScore, location: 100, salary: 90 }, noticePeriod: '1 month', availability: '2 weeks', certifications: [], favorite: false, interviewStatus: 'Not Scheduled' }; DB.candidates.unshift(newC); i.processing = 'Imported'; i.unread = false; Inbox._render.list(); Inbox._render.detail(); App.updateBadges(); UI.toast(`${i.name} imported → Applied stage of ${job.title}`, 'success'); }; Inbox.moveToPipeline = function (id) { const i = DB.inbox.find(x => x.id === id); if (i.processing !== 'Imported' && i.processing !== 'Processed') Inbox.import(id); i.processing = 'Processed'; Inbox._render.list(); Inbox._render.detail(); UI.toast(`${i.name} moved to pipeline`, 'success'); setTimeout(() => Router.go('pipeline'), 700); }; Inbox.assign = function (id) { const i = DB.inbox.find(x => x.id === id); const opts = DB.recruiters.map(r => ``).join(''); UI.modal({ title: 'Assign Recruiter', subtitle: i.name, body: `

Current workload is factored automatically. This recruiter has ${DB.getRecruiterByName(i.recruiter) ? DB.getRecruiterByName(i.recruiter).openReqs : 5} open reqs.

`, footer: `` }); }; Inbox._doAssign = function (id) { const i = DB.inbox.find(x => x.id === id); i.recruiter = document.getElementById('assignRec').value; UI.closeModal(); Inbox._render.detail(); UI.toast('Recruiter assigned to ' + i.name, 'success'); }; Inbox.note = function (id) { const i = DB.inbox.find(x => x.id === id); UI.modal({ title: 'Add Note', subtitle: i.name, body: `
`, footer: `` }); }; Inbox.reject = function (id) { const i = DB.inbox.find(x => x.id === id); i.processing = 'Rejected'; i.unread = false; Inbox._render.list(); Inbox._render.detail(); App.updateBadges(); UI.toast(`${i.name} rejected`, 'warning'); }; // ---------------- Email (Outlook) tab ---------------- Inbox._emailView = function () { const st = Inbox._state; const listHtml = DB.emails.map(e => `
${UI.avatar(e.from, e.initials, e.color)}
${e.from}
${e.subject}
Outlook${e.imported ? UI.badge('Imported', 'b-green') : ''}
${DB.fmtShort(e.when)}
`).join(''); return `
Outlook · Microsoft Graph API Last sync: 2 min ago · ${DB.emails.filter(e => e.unread).length} unread
${listHtml}
`; }; Inbox._bindEmail = function (state) { const detail = document.getElementById('emailDetail'); function renderDetail() { const e = DB.emails.find(x => x.id === state.emailSelected); if (!e) { detail.innerHTML = `
${UI.icon('mail')}

Select an email

Preview email body and resume attachments here.

`; return; } detail.innerHTML = `

${e.subject}

${e.imported ? UI.badge('Imported', 'b-green') : UI.badge('New', 'b-blue')}
${UI.avatar(e.from, e.initials, e.color)}
${e.from}
${e.fromEmail} · ${DB.fmtDate(e.when)}
${e.body}
${UI.icon('file')}
${e.attachment}
${e.attachmentSize} · PDF
${UI.scoreChip(e.atsScore)}
${e.imported ? `` : ``}
`; } document.querySelectorAll('#emailList .inbox-item').forEach(row => row.onclick = () => { state.emailSelected = row.dataset.email; const e = DB.emails.find(x => x.id === state.emailSelected); if (e) e.unread = false; document.querySelectorAll('#emailList .inbox-item').forEach(r => r.classList.remove('active', 'unread')); row.classList.add('active'); renderDetail(); App.updateBadges(); }); renderDetail(); }; Inbox._importEmail = function (id) { const e = DB.emails.find(x => x.id === id); const job = DB.getJob(e.jobId) || DB.jobs[0]; DB.candidates.unshift({ id: 'CAN-' + (5001 + DB.candidates.length), name: e.from, initials: e.initials, color: e.color, email: e.fromEmail, phone: '+1 (555) 000-0000', jobId: job.id, jobTitle: job.title, department: job.department, experience: DB.int(2, 10), currentCompany: DB.pick(DB.companies), currentTitle: job.title, location: DB.pick(DB.locations), stage: 'Applied', status: 'Applied', aiScore: e.atsScore, source: 'Microsoft Outlook', recruiter: DB.pick(DB.recruiters).name, recruiterId: '', applied: new Date('2026-07-09'), education: "Bachelor's Degree", skills: job.skills.slice(0, 4), rating: '4.0', salary: 130000, matchedSkills: job.skills.slice(0, 3), missingSkills: job.skills.slice(3), recommendation: 'Potential Match', subScores: { skills: e.atsScore, experience: 80, education: 80, keywords: e.atsScore, location: 100, salary: 90 }, noticePeriod: '1 month', availability: '2 weeks', certifications: [], favorite: false, interviewStatus: 'Not Scheduled' }); e.imported = true; e.unread = false; Inbox._bindEmail(Inbox._state); App.updateBadges(); UI.toast(`${e.from} imported from Outlook → ${job.title}`, 'success'); };