Candidate Pipeline Dashboard Pipeline Board Metrics Add Candidate Total Candidates 0 Hired 0 Candidates by Stage × Add New Candidate Full Name Applying for Role Application Date Current Stage Save Candidate PreviousNext Download Dashboard as PDF Applied: ${candidate.date} Move Edit Delete `; cardsContainer.appendChild(card); }); kanbanBoard.appendChild(column); }); } function renderMetrics() { document.getElementById('total-candidates').textContent = candidates.length; const hiredCount = candidates.filter(c => c.stage === 'hired').length; document.getElementById('total-hired').textContent = hiredCount; const breakdownContainer = document.getElementById('stage-breakdown'); breakdownContainer.innerHTML = ''; stages.forEach(stage => { const count = candidates.filter(c => c.stage === stage.id).length; const item = document.createElement('div'); item.className = 'stage-breakdown-item'; item.innerHTML = `${stage.title}${count}`; breakdownContainer.appendChild(item); }); } // --- Modal Logic --- function openModal(mode = 'add', candidateId = null) { candidateForm.reset(); const stageSelect = document.getElementById('candidate-stage'); stageSelect.innerHTML = stages.map(s => `${s.title}`).join(''); if (mode === 'edit') { modalTitle.textContent = 'Edit Candidate'; const candidate = candidates.find(c => c.id === candidateId); document.getElementById('candidate-id').value = candidate.id; document.getElementById('candidate-name').value = candidate.name; document.getElementById('candidate-role').value = candidate.role; document.getElementById('application-date').value = candidate.date; stageSelect.value = candidate.stage; } else { modalTitle.textContent = 'Add New Candidate'; document.getElementById('candidate-id').value = ''; document.getElementById('application-date').valueAsDate = new Date(); } modal.style.display = 'block'; } function closeModal() { modal.style.display = 'none'; } addCandidateBtn.addEventListener('click', () => openModal('add')); closeModalBtn.addEventListener('click', closeModal); window.addEventListener('click', e => { if (e.target == modal) closeModal(); }); // --- CRUD operations --- candidateForm.addEventListener('submit', function(e) { e.preventDefault(); const id = document.getElementById('candidate-id').value; const candidateData = { name: document.getElementById('candidate-name').value, role: document.getElementById('candidate-role').value, date: document.getElementById('application-date').value, stage: document.getElementById('candidate-stage').value }; if (id) { // Edit mode const index = candidates.findIndex(c => c.id == id); candidates[index] = { ...candidates[index], ...candidateData }; } else { // Add mode candidateData.id = Date.now(); // Simple unique ID candidates.push(candidateData); } renderAll(); closeModal(); }); kanbanBoard.addEventListener('click', function(e) { const card = e.target.closest('.candidate-card'); if (!card) return; const candidateId = parseInt(card.dataset.id); if (e.target.classList.contains('edit-btn')) { openModal('edit', candidateId); } if (e.target.classList.contains('delete-btn')) { if (confirm('Are you sure you want to delete this candidate?')) { candidates = candidates.filter(c => c.id !== candidateId); renderAll(); } } if (e.target.classList.contains('move-btn')) { const currentStage = candidates.find(c => c.id === candidateId).stage; const otherStages = stages.filter(s => s.id !== currentStage).map(s => `${s.title} (${s.id})`).join(', '); const newStageId = prompt(`Enter new stage ID to move this candidate to.\nOptions: ${otherStages}`); if (newStageId && stages.some(s => s.id === newStageId)) { const index = candidates.findIndex(c => c.id === candidateId); candidates[index].stage = newStageId; renderAll(); } else if (newStageId) { alert('Invalid stage ID entered.'); } } }); // --- Tab Navigation --- const tabs = document.querySelectorAll('.dashboard-tabs .tab-link'); const tabContents = document.querySelectorAll('.tab-content'); const prevBtn = document.getElementById('prev-tab-btn'); const nextBtn = document.getElementById('next-tab-btn'); let currentTabIndex = 0; function switchTab(index) { tabs.forEach(t => t.classList.remove('active')); tabContents.forEach(c => c.classList.remove('active')); tabs[index].classList.add('active'); tabContents[index].classList.add('active'); currentTabIndex = index; updateNavButtons(); } function updateNavButtons() { /* Logic for disabling/enabling prev/next */ } tabs.forEach((tab, index) => tab.addEventListener('click', () => switchTab(index))); prevBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); }); nextBtn.addEventListener('click', () => { if (currentTabIndex < tabs.length - 1) switchTab(currentTabIndex + 1); }); // --- PDF Download --- document.getElementById('download-pdf-btn').addEventListener('click', () => { const { jsPDF } = window.jspdf; const contentToPrint = document.getElementById('dashboard-content'); const actionControls = document.getElementById('action-area-controls'); const mainControls = document.querySelector('.main-controls'); actionControls.style.display = 'none'; if (mainControls) mainControls.style.display = 'none'; document.querySelectorAll('.candidate-card-actions').forEach(el => el.style.display = 'none'); html2canvas(contentToPrint, { scale: 2 }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'l', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const imgProps = pdf.getImageProperties(imgData); const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; pdf.addImage(imgData, 'PNG', 0, 10, pdfWidth, pdfHeight); pdf.save('Candidate_Pipeline_Dashboard.pdf'); actionControls.style.display = 'flex'; if (mainControls) mainControls.style.display = 'block'; document.querySelectorAll('.candidate-card-actions').forEach(el => el.style.display = 'flex'); }); }); // --- Initial Render --- renderAll(); });