HR-ATS-Portal/js/tasks.js

132 lines
7.5 KiB
JavaScript

/* ============================================================
tasks.js — Recruitment Tasks (with saved searches & favorites)
============================================================ */
window.Views = window.Views || {};
window.Tasks = {};
Views.tasks = function () {
const state = { filter: 'All' };
function render() {
const el = document.getElementById('taskList');
if (!el) return;
let list = DB.tasks;
if (state.filter === 'Open') list = list.filter(t => !t.done);
else if (state.filter === 'Completed') list = list.filter(t => t.done);
else if (state.filter === 'Overdue') list = list.filter(t => !t.done && t.due < new Date('2026-07-09'));
else if (['High', 'Medium', 'Low'].includes(state.filter)) list = list.filter(t => t.priority === state.filter);
if (!list.length) { el.innerHTML = `<div class="empty-state">${UI.icon('check-square')}<h3>All caught up</h3><p>No tasks in this view.</p></div>`; return; }
const prCls = { High: 'b-red', Medium: 'b-amber', Low: 'b-gray' };
el.innerHTML = list.map(t => {
const overdue = !t.done && t.due < new Date('2026-07-09');
return `<div class="list-row" style="align-items:center">
<span class="checkbox ${t.done ? 'on' : ''}" data-id="${t.id}">${UI.icon('check')}</span>
<div class="lr-main" style="cursor:pointer" onclick="Tasks.open('${t.id}')">
<div class="lr-title" style="${t.done ? 'text-decoration:line-through;color:var(--text-3)' : ''}">${t.title}</div>
<div class="lr-sub">${UI.icon('users')} ${t.assignee} · ${t.type}</div>
</div>
<div class="lr-right">
${UI.badge(t.priority, prCls[t.priority])}
<div class="lr-sub" style="margin-top:4px;${overdue ? 'color:var(--danger);font-weight:600' : ''}">${overdue ? 'Overdue · ' : 'Due '}${DB.fmtShort(t.due)}</div>
</div>
</div>`;
}).join('');
el.querySelectorAll('.checkbox').forEach(chk => chk.onclick = e => {
e.stopPropagation();
const t = DB.tasks.find(x => x.id === chk.dataset.id);
t.done = !t.done; render(); App.updateBadges();
UI.toast(t.done ? 'Task completed' : 'Task reopened', t.done ? 'success' : 'info');
});
}
Tasks._render = render;
const openCount = DB.tasks.filter(t => !t.done).length;
const overdueCount = DB.tasks.filter(t => !t.done && t.due < new Date('2026-07-09')).length;
const filters = ['All', 'Open', 'Completed', 'Overdue', 'High', 'Medium', 'Low'];
// saved searches sidebar
const savedHtml = DB.savedSearches.map(s => `
<div class="list-row" style="cursor:pointer" onclick="Router.go('candidates')">
<span class="kpi-icn i-indigo" style="width:36px;height:36px;border-radius:9px">${UI.icon('bookmark')}</span>
<div class="lr-main"><div class="lr-title">${s.name}</div><div class="lr-sub">${s.filters}</div></div>
<span class="badge b-gray badge-plain">${s.count}</span>
</div>`).join('');
const html = `
<div class="page">
<div class="page-head">
<div><h1 class="page-title">Tasks</h1><p class="page-sub">${openCount} open · ${overdueCount} overdue</p></div>
<div class="page-head-actions"><button class="btn btn-primary" onclick="Tasks.add()">${UI.icon('plus')} New Task</button></div>
</div>
<div class="grid g-2-1">
<div class="card">
<div class="card-body" style="padding-bottom:0">
<div class="seg" id="taskSeg">${filters.map((f, i) => `<button class="${i === 0 ? 'active' : ''}" data-f="${f}">${f}</button>`).join('')}</div>
</div>
<div class="card-body"><div class="list-tight" id="taskList"></div></div>
</div>
<div class="card" style="align-self:start">
<div class="card-head"><div><h3>Saved Searches</h3><span class="ch-sub">Quick candidate filters</span></div>
<button class="act-btn" onclick="UI.toast('New saved search','info')">${UI.icon('plus')}</button></div>
<div class="card-body"><div class="list-tight">${savedHtml}</div></div>
</div>
</div>
</div>`;
return {
html,
onMount() {
render();
document.querySelectorAll('#taskSeg button').forEach(b => b.onclick = () => {
document.querySelectorAll('#taskSeg button').forEach(x => x.classList.remove('active'));
b.classList.add('active'); state.filter = b.dataset.f; render();
});
}
};
};
Tasks.open = function (id) {
const t = DB.tasks.find(x => x.id === id);
const c = DB.getCandidate(t.candidateId);
UI.modal({
title: t.title, subtitle: t.id + ' · ' + t.type,
body: `<div class="info-grid" style="margin-bottom:16px">
<div class="info-item"><div class="il">Assignee</div><div class="iv">${t.assignee}</div></div>
<div class="info-item"><div class="il">Priority</div><div class="iv">${t.priority}</div></div>
<div class="info-item"><div class="il">Due Date</div><div class="iv">${DB.fmtDate(t.due)}</div></div>
<div class="info-item"><div class="il">Status</div><div class="iv">${t.done ? 'Completed' : 'Open'}</div></div>
${c ? `<div class="info-item"><div class="il">Candidate</div><div class="iv">${c.name}</div></div>` : ''}
</div>
<div class="form-field"><label>Notes</label><textarea placeholder="Add task notes…"></textarea></div>`,
footer: `<button class="btn btn-secondary" onclick="UI.closeModal()">Close</button>
${c ? `<button class="btn btn-secondary" onclick="UI.closeModal();Candidates.openProfile('${c.id}')">View Candidate</button>` : ''}
<button class="btn btn-primary" onclick="UI.closeModal();Tasks._complete('${id}')">${UI.icon('check')} Mark Complete</button>`
});
};
Tasks._complete = function (id) { const t = DB.tasks.find(x => x.id === id); t.done = true; Tasks._render(); App.updateBadges(); UI.toast('Task completed', 'success'); };
Tasks.add = function () {
const opt = arr => arr.map(o => `<option>${o}</option>`).join('');
UI.modal({
title: 'New Task', subtitle: 'Create a recruitment task',
body: `<form id="taskForm" novalidate><div class="form-grid">
<div class="form-field col-span-2"><label>Task Title <span class="req">*</span></label><input name="title" placeholder="e.g. Screen candidate"/><span class="field-error">Required</span></div>
<div class="form-field"><label>Priority</label><select name="priority"><option>High</option><option selected>Medium</option><option>Low</option></select></div>
<div class="form-field"><label>Type</label><select name="type">${opt(['Interview', 'Review', 'Offer', 'Admin'])}</select></div>
<div class="form-field"><label>Assignee</label><select name="assignee">${opt(DB.recruiters.map(r => r.name))}</select></div>
<div class="form-field"><label>Due Date</label><input name="due" type="date"/></div>
</div></form>`,
footer: `<button class="btn btn-secondary" onclick="UI.closeModal()">Cancel</button><button class="btn btn-primary" onclick="Tasks._save()">${UI.icon('check')} Create Task</button>`
});
};
Tasks._save = function () {
const form = document.getElementById('taskForm');
UI.clearErrors(form);
const f = Object.fromEntries(new FormData(form));
if (!f.title.trim()) { UI.fieldError(form.querySelector('[name=title]'), 'Required'); return; }
DB.tasks.unshift({ id: 'TSK-' + (50001 + DB.tasks.length), title: f.title, candidateId: null, priority: f.priority, due: f.due ? new Date(f.due) : new Date('2026-07-16'), assignee: f.assignee, done: false, type: f.type });
UI.closeModal(); Tasks._render(); App.updateBadges();
UI.toast('Task created', 'success');
};