/* ============================================================
interviews.js — Interviews list, upcoming, mini calendar
============================================================ */
window.Views = window.Views || {};
window.Interviews = {};
Views.interviews = function () {
const filters = { q: '', status: '', type: '' };
let table;
const upcoming = DB.interviews.filter(iv => iv.status === 'Scheduled').slice(0, 4);
const stats = {
scheduled: DB.interviews.filter(i => i.status === 'Scheduled').length,
completed: DB.interviews.filter(i => i.status === 'Completed').length,
today: 5,
cancelled: DB.interviews.filter(i => ['Cancelled', 'No Show'].includes(i.status)).length
};
function apply() {
const rows = DB.interviews.filter(iv => {
if (filters.status && iv.status !== filters.status) return false;
if (filters.type && iv.type !== filters.type) return false;
if (filters.q && !(iv.candidate + iv.jobTitle + iv.interviewers.join(' ')).toLowerCase().includes(filters.q.toLowerCase())) return false;
return true;
});
table.update(rows);
}
table = UI.dataTable({
pageSize: 8,
rows: DB.interviews,
columns: [
{ key: 'candidate', label: 'Candidate', sortable: true, render: iv => `
${UI.avatar(iv.candidate, iv.candInitials, iv.color)}
${iv.candidate}
${iv.jobTitle}
` },
{ key: 'type', label: 'Round', sortable: true, render: iv => UI.badge(iv.type, 'b-indigo') },
{ key: 'when', label: 'Date & Time', sortable: true, sortValue: iv => iv.when.getTime(), render: iv => `${DB.fmtShort(iv.when)}
${iv.when.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })} · ${iv.duration}m
` },
{ key: 'meeting', label: 'Type', render: iv => `${UI.icon(iv.meeting === 'Video Call' ? 'video' : iv.meeting === 'Phone' ? 'phone' : 'map')} ${iv.meeting}` },
{ key: 'interviewers', label: 'Interviewers', render: iv => UI.avatarStack(iv.interviewers) },
{ key: 'status', label: 'Status', sortable: true, render: iv => UI.badge(iv.status) },
{ key: 'feedback', label: 'Feedback', render: iv => iv.feedback ? UI.badge(iv.feedback) : '—' },
{ key: '_a', label: 'Actions', align: 'right', render: iv => `
` }
]
});
const statusOpts = [''].concat(['Scheduled', 'Completed', 'Cancelled', 'No Show'].map(s => ``)).join('');
const typeOpts = [''].concat(DB.interviewTypes.map(t => ``)).join('');
const statCard = (label, val, icn, cls) => `${label}${UI.icon(icn)}
${val}
`;
const upcomingHtml = upcoming.map(iv => `
${UI.avatar(iv.candidate, iv.candInitials, iv.color)}
${iv.candidate}
${iv.type} · ${iv.meeting}
${DB.fmtShort(iv.when)}
${iv.when.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}
`).join('');
const html = `
Interviews
Manage and track all interview activity
${statCard('Scheduled', stats.scheduled, 'calendar', 'i-blue')}
${statCard('Completed', stats.completed, 'check-circle', 'i-green')}
${statCard('Today', stats.today, 'clock', 'i-purple')}
${statCard('Cancelled / No-show', stats.cancelled, 'x-circle', 'i-red')}
Up Next
Scheduled sessions
`;
return {
html,
onMount() {
table.mount();
const s = document.getElementById('ivSearch');
s.oninput = () => { filters.q = s.value; apply(); };
document.getElementById('ivStatus').onchange = e => { filters.status = e.target.value; apply(); };
document.getElementById('ivType').onchange = e => { filters.type = e.target.value; apply(); };
}
};
};
Interviews.feedback = function (id) {
const iv = DB.interviews.find(i => i.id === id);
// pick evaluation template by department
const job = DB.jobs.find(j => j.title === iv.jobTitle);
const dept = job ? job.department : 'All';
const tmpl = DB.evalTemplates.find(t => t.dept === dept) || DB.evalTemplates.find(t => t.dept === 'All');
const tmplOpts = DB.evalTemplates.map(t => ``).join('');
const ratingRow = (crit) => `
${crit}
${[1, 2, 3, 4, 5].map(n => `${UI.icon('star')}`).join('')}
`;
const body = `
${UI.avatar(iv.candidate, iv.candInitials, iv.color, 'avatar-lg')}
${iv.candidate}
${iv.type} · ${iv.jobTitle}
${UI.badge(iv.status)}
Dynamic Form
Upload Sheet
Both
${tmpl.criteria.map(ratingRow).join('')}
${UI.icon('upload')}
Upload evaluation sheet
PDF, DOC, or DOCX · scanned scorecards supported
${['PDF', 'DOC', 'DOCX'].map(t => `${t}`).join('')}
Capture structured ratings and attach a signed sheet — both are stored on the scorecard.
${tmpl.criteria.slice(0, 3).map(ratingRow).join('')}
${UI.icon('file')}
Interviewer_Scorecard.pdf
Attached · 214 KB
${UI.badge('Uploaded', 'b-green')}
`;
const footer = `
`;
UI.modal({ title: 'Interview Evaluation', subtitle: iv.id + ' · ' + iv.type, body, footer, size: 'modal-lg' });
// wire tabs
const panes = document.querySelectorAll('#evalPanes .tab-pane');
document.querySelectorAll('#evalTabs .tab').forEach(tab => tab.onclick = () => {
document.querySelectorAll('#evalTabs .tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
panes.forEach(p => p.classList.remove('active'));
panes[+tab.dataset.etab].classList.add('active');
});
// wire star ratings
Interviews._bindStars();
// template switch rebuilds criteria
const tmplSel = document.getElementById('evalTmpl');
if (tmplSel) tmplSel.onchange = () => {
const t = DB.evalTemplates.find(x => x.name === tmplSel.value);
document.getElementById('critList').innerHTML = t.criteria.map(ratingRow).join('');
Interviews._bindStars();
};
// recommendation seg
document.querySelectorAll('[data-rec]').forEach(b => b.onclick = () => {
b.parentElement.querySelectorAll('button').forEach(x => x.classList.remove('active'));
b.classList.add('active');
});
};
Interviews._bindStars = function () {
document.querySelectorAll('.rating-stars').forEach(group => {
group.querySelectorAll('.rs').forEach(star => star.onclick = () => {
const val = +star.dataset.val;
group.querySelectorAll('.rs').forEach(s => s.classList.toggle('on', +s.dataset.val <= val));
});
});
};
Interviews.schedule = function () {
const opt = arr => arr.map(o => ``).join('');
const body = ``;
const footer = `
`;
UI.modal({ title: 'Schedule Interview', subtitle: 'Set up a new interview session', body, footer });
};