HR-ATS-Portal/js/assessments.js

127 lines
7.7 KiB
JavaScript

/* ============================================================
assessments.js — Assessments listing & assign
============================================================ */
window.Views = window.Views || {};
window.Assessments = {};
Views.assessments = function () {
const filters = { q: '', status: '', type: '' };
let table;
const stats = {
total: DB.assessments.length,
completed: DB.assessments.filter(a => a.status === 'Completed').length,
pending: DB.assessments.filter(a => ['Pending', 'In Progress'].includes(a.status)).length,
avg: Math.round(DB.assessments.filter(a => a.score).reduce((s, a) => s + a.score, 0) / (DB.assessments.filter(a => a.score).length || 1))
};
function apply() {
const rows = DB.assessments.filter(a => {
if (filters.status && a.status !== filters.status) return false;
if (filters.type && a.type !== filters.type) return false;
if (filters.q && !(a.candidate + a.jobTitle + a.type).toLowerCase().includes(filters.q.toLowerCase())) return false;
return true;
});
table.update(rows);
}
table = UI.dataTable({
pageSize: 8,
rows: DB.assessments,
columns: [
{ key: 'candidate', label: 'Candidate', sortable: true, render: a => `<div class="user-cell">${UI.avatar(a.candidate, a.initials, a.color)}<div><div class="cell-primary">${a.candidate}</div><div class="cell-sub">${a.jobTitle}</div></div></div>` },
{ key: 'type', label: 'Assessment', sortable: true, render: a => `<div class="cell-primary text-sm">${a.type}</div><div class="cell-sub">${a.duration}</div>` },
{ key: 'assigned', label: 'Assigned', sortable: true, sortValue: a => a.assigned.getTime(), render: a => `<span class="text-muted">${DB.fmtShort(a.assigned)}</span>` },
{ key: 'due', label: 'Due', sortable: true, sortValue: a => a.due.getTime(), render: a => `<span class="text-muted">${DB.fmtShort(a.due)}</span>` },
{ key: 'score', label: 'Score', sortable: true, align: 'center', render: a => a.score !== null ? UI.scoreChip(a.score) : '<span class="text-muted">—</span>' },
{ key: 'status', label: 'Status', sortable: true, render: a => UI.badge(a.status) },
{ key: '_a', label: 'Actions', align: 'right', render: a => `
<div class="row-actions">
<button class="act-btn" data-tip="View" onclick="Assessments.view('${a.id}')">${UI.icon('eye')}</button>
<button class="act-btn" data-tip="Remind" onclick="UI.toast('Reminder sent to ${a.candidate}','info')">${UI.icon('mail')}</button>
</div>` }
]
});
const statusOpts = ['<option value="">All Status</option>'].concat(['Completed', 'In Progress', 'Pending', 'Expired'].map(s => `<option>${s}</option>`)).join('');
const typeOpts = ['<option value="">All Types</option>'].concat([...new Set(DB.assessments.map(a => a.type))].map(t => `<option>${t}</option>`)).join('');
const statCard = (label, val, icn, cls) => `<div class="kpi"><div class="kpi-top"><span class="kpi-label">${label}</span><span class="kpi-icn ${cls}">${UI.icon(icn)}</span></div><div class="kpi-value">${val}</div></div>`;
const html = `
<div class="page">
<div class="page-head">
<div><h1 class="page-title">Assessments</h1><p class="page-sub">Coding tests, take-homes, and evaluations</p></div>
<div class="page-head-actions"><button class="btn btn-primary" onclick="Assessments.assign()">${UI.icon('plus')} Assign Assessment</button></div>
</div>
<div class="grid g-kpi mb-18">
${statCard('Total Assigned', stats.total, 'file', 'i-indigo')}
${statCard('Completed', stats.completed, 'check-circle', 'i-green')}
${statCard('In Progress / Pending', stats.pending, 'clock', 'i-amber')}
${statCard('Average Score', stats.avg + '%', 'target', 'i-teal')}
</div>
<div class="card">
<div class="card-body" style="padding-bottom:0">
<div class="toolbar">
<div class="toolbar-search">${UI.icon('search')}<input id="asSearch" placeholder="Search candidate or assessment…"/></div>
<select class="select" id="asStatus">${statusOpts}</select>
<select class="select" id="asType">${typeOpts}</select>
</div>
</div>
${table.html}
</div>
</div>`;
return {
html,
onMount() {
table.mount();
const s = document.getElementById('asSearch');
s.oninput = () => { filters.q = s.value; apply(); };
document.getElementById('asStatus').onchange = e => { filters.status = e.target.value; apply(); };
document.getElementById('asType').onchange = e => { filters.type = e.target.value; apply(); };
}
};
};
Assessments.view = function (id) {
const a = DB.assessments.find(x => x.id === id);
const body = `
<div class="flex items-center gap-12 mb-18">${UI.avatar(a.candidate, a.initials, a.color, 'avatar-lg')}
<div><div class="ph-name" style="font-size:17px">${a.candidate}</div><div class="ph-role">${a.type} · ${a.jobTitle}</div></div>
<div style="margin-left:auto">${UI.badge(a.status)}</div></div>
<div class="info-grid mb-18">
<div class="info-item"><div class="il">Type</div><div class="iv">${a.type}</div></div>
<div class="info-item"><div class="il">Duration</div><div class="iv">${a.duration}</div></div>
<div class="info-item"><div class="il">Assigned</div><div class="iv">${DB.fmtDate(a.assigned)}</div></div>
<div class="info-item"><div class="il">Due</div><div class="iv">${DB.fmtDate(a.due)}</div></div>
</div>
${a.score !== null ? `
<div class="divider"></div>
<div style="text-align:center;padding:10px 0">
<div style="font-size:44px;font-weight:800;letter-spacing:-1px;color:${a.score >= 70 ? 'var(--success)' : 'var(--warning)'}">${a.score}%</div>
<div class="text-muted">Overall Score</div>
</div>
<div class="mb-18">${UI.pbar(a.score)}</div>
<div class="form-section-title" style="margin-top:0">Section Breakdown</div>
${['Problem Solving', 'Code Quality', 'Communication', 'Time Management'].map(sec => {
const sc = DB.int(60, 98);
return `<div class="flex items-center gap-12" style="margin-bottom:10px"><span style="width:130px;font-size:13px">${sec}</span><div style="flex:1">${UI.pbar(sc)}</div><b style="width:40px;text-align:right">${sc}%</b></div>`;
}).join('')}` : `<div class="empty-state">${UI.icon('clock')}<h3>Assessment not completed</h3><p>Results will appear once the candidate submits.</p></div>`}`;
const footer = `<button class="btn btn-secondary" onclick="UI.closeModal()">Close</button>
<button class="btn btn-primary" onclick="UI.closeModal();Candidates.openProfile('${a.candidateId}')">View Candidate</button>`;
UI.modal({ title: 'Assessment Result', subtitle: a.id, body, footer });
};
Assessments.assign = function () {
const opt = arr => arr.map(o => `<option>${o}</option>`).join('');
const body = `<form><div class="form-grid">
<div class="form-field col-span-2"><label>Candidate</label><select>${opt(DB.candidates.slice(0, 40).map(c => c.name))}</select></div>
<div class="form-field"><label>Assessment Type</label><select>${opt([...new Set(DB.assessments.map(a => a.type))])}</select></div>
<div class="form-field"><label>Time Limit</label><select><option>45 min</option><option>60 min</option><option>90 min</option><option>3 days</option></select></div>
<div class="form-field col-span-2"><label>Due Date</label><input type="date"/></div>
</div></form>`;
const footer = `<button class="btn btn-secondary" onclick="UI.closeModal()">Cancel</button>
<button class="btn btn-primary" onclick="UI.closeModal();UI.toast('Assessment assigned & invite sent','success')">${UI.icon('send')} Assign</button>`;
UI.modal({ title: 'Assign Assessment', subtitle: 'Send an evaluation to a candidate', body, footer });
};