/* ============================================================
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')}
Experience
${i.experience} years
Assigned Recruiter
${i.recruiter}
Received
${DB.fmtDate(i.received)}
Match
${UI.badge(recLabel, rec(recLabel))}
${UI.icon('paperclip')} ${i.attachment}
${UI.icon('eye')} Preview
${Inbox._resumeText(i)}
${UI.icon('user-plus')} Import Candidate
${UI.icon('sparkles')} Parse Resume
${UI.icon('users')} Assign Recruiter
${UI.icon('layers')} Move to Pipeline
${UI.icon('edit')} Add Note
${UI.icon('x')} Reject
`;
}
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
${UI.icon('refresh')} Sync
${UI.icon('upload')} Upload CVs
`;
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: `Close ${UI.icon('user-plus')} Import Candidate `,
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 => `${r.name} `).join('');
UI.modal({
title: 'Assign Recruiter', subtitle: i.name,
body: `Recruiter ${opts}
Current workload is factored automatically. This recruiter has ${DB.getRecruiterByName(i.recruiter) ? DB.getRecruiterByName(i.recruiter).openReqs : 5} open reqs.
`,
footer: `Cancel Assign `
});
};
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: `Note
`,
footer: `Cancel ${UI.icon('check')} Save Note `
});
};
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)}
${DB.fmtShort(e.when)}
`).join('');
return `
Outlook · Microsoft Graph API
Last sync: 2 min ago · ${DB.emails.filter(e => e.unread).length} unread
${UI.icon('refresh')} Sync Mailbox
`;
};
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)}
${UI.icon('eye')} Preview
${e.imported ? `${UI.icon('check')} Already Imported ` :
`${UI.icon('user-plus')} Import Candidate `}
${UI.icon('mail')} Reply
${UI.icon('trash')} Archive
`;
}
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');
};