/* ============================================================ pipeline.js — Kanban board (drag & drop) + Talent Pool ============================================================ */ window.Views = window.Views || {}; window.Pipeline = {}; // Stage colours reference CSS tokens so the board re-tints with the theme. const KANBAN_STAGES = [ { name: 'Applied', color: 'var(--stage-1)' }, { name: 'Screening', color: 'var(--stage-2)' }, { name: 'Assessment', color: 'var(--stage-3)' }, { name: 'Interview', color: 'var(--stage-4)' }, { name: 'Offer', color: 'var(--stage-5)' }, { name: 'Hired', color: 'var(--stage-6)' }, { name: 'Rejected', color: 'var(--stage-7)' } ]; Views.pipeline = function () { const jobFilter = { id: '' }; function columns() { const list = jobFilter.id ? DB.candidates.filter(c => c.jobId === jobFilter.id) : DB.candidates; return KANBAN_STAGES.map(st => { const cards = list.filter(c => c.stage === st.name); return `

${st.name}

${cards.length}
${cards.map(c => Pipeline._card(c)).join('')}
`; }).join(''); } const jobOpts = [''].concat(DB.jobs.filter(j => j.status === 'Open').map(j => ``)).join(''); const html = `

Pipeline

Drag candidates between stages to update their status

${columns()}
`; return { html, onMount() { Pipeline._bindDnd(); document.getElementById('pipeJob').onchange = e => { jobFilter.id = e.target.value; document.getElementById('kanban').innerHTML = columns(); Pipeline._bindDnd(); }; } }; }; Pipeline._card = function (c) { return `
${UI.avatar(c.name, c.initials, c.color)}
${c.name}
${c.currentTitle}
${c.jobTitle}
${c.skills.slice(0, 3).map(s => `${s}`).join('')}
${c.currentCompany}${UI.scoreChip(c.aiScore)}
`; }; Pipeline._bindDnd = function () { let dragged = null; document.querySelectorAll('.k-card').forEach(card => { card.addEventListener('dragstart', e => { dragged = card; card.classList.add('dragging'); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', card.dataset.id); }); card.addEventListener('dragend', () => { card.classList.remove('dragging'); dragged = null; }); // prevent click-through opening profile right after drag card.addEventListener('click', e => { if (card._justDropped) { e.stopPropagation(); card._justDropped = false; } }); }); document.querySelectorAll('.kanban-cards').forEach(zone => { zone.addEventListener('dragover', e => { e.preventDefault(); zone.classList.add('drag-over'); }); zone.addEventListener('dragleave', () => zone.classList.remove('drag-over')); zone.addEventListener('drop', e => { e.preventDefault(); zone.classList.remove('drag-over'); if (!dragged) return; const id = dragged.dataset.id; const cand = DB.getCandidate(id); const newStage = zone.dataset.stage; if (cand.stage === newStage) return; cand.stage = newStage; cand.status = newStage; zone.appendChild(dragged); // update counts document.querySelectorAll('.kanban-col').forEach(col => { col.querySelector('.k-count').textContent = col.querySelectorAll('.k-card').length; }); UI.toast(`${cand.name} moved to ${newStage}`, 'success'); }); }); }; // ---------------- Talent Pool ---------------- Views.talentpool = function () { const filters = { q: '', dept: '' }; // Talent pool = candidates not currently in active loop (silver medalists / passive talent) const pool = DB.candidates.filter(c => ['Rejected', 'Applied', 'Hired'].includes(c.stage)); function render(list) { const grid = document.getElementById('poolGrid'); if (!grid) return; if (!list.length) { grid.innerHTML = `
${UI.icon('search')}

No talent found

`; return; } grid.innerHTML = list.map(c => `
${UI.avatar(c.name, c.initials, c.color, 'avatar-lg')}
${c.name}
${c.currentTitle}
${UI.scoreChip(c.aiScore)}
${c.skills.slice(0, 4).map(s => `${s}`).join('')}
${UI.icon('briefcase')} ${c.experience} yrs ${c.currentCompany} ${UI.badge(c.source, 'b-gray')}
`).join(''); } function apply() { let list = pool.filter(c => { if (filters.dept && c.department !== filters.dept) return false; if (filters.q && !(c.name + c.currentCompany + c.skills.join(' ')).toLowerCase().includes(filters.q.toLowerCase())) return false; return true; }); render(list); } const deptOpts = [''].concat(DB.departments.map(d => ``)).join(''); const html = `

Talent Pool

${pool.length} silver-medalists & passive candidates to re-engage

`; return { html, onMount() { apply(); const s = document.getElementById('poolSearch'); s.oninput = () => { filters.q = s.value; apply(); }; document.getElementById('poolDept').onchange = e => { filters.dept = e.target.value; apply(); }; } }; };