Succession Planning Dashboard

Succession Planning Dashboard

Key Succession Metrics

Succession Pipeline Overview

Succession Insights

No insights generated yet. Please configure your data.

Succession Planning Dashboard Report

Key Succession Metrics

Succession Pipeline Overview

Succession Insights

No roles defined to generate insights. Please add roles in the Data Configuration tab.

'; } else { const criticalRoles = successionData.filter(role => role.critical); const rolesWithoutReadySuccessors = criticalRoles.filter(role => !role.successors.some(s => s.readiness === 5)); const rolesWithMultipleReadySuccessors = criticalRoles.filter(role => role.successors.filter(s => s.readiness === 5).length >= 2); const allDevelopmentNeeds = {}; successionData.forEach(role => { role.successors.forEach(s => { if (s.developmentNeeds && s.developmentNeeds.trim() !== '') { s.developmentNeeds.split(',').forEach(need => { const trimmedNeed = need.trim(); if (trimmedNeed) { allDevelopmentNeeds[trimmedNeed] = (allDevelopmentNeeds[trimmedNeed] || 0) + 1; } }); } }); }); const sortedDevelopmentNeeds = Object.entries(allDevelopmentNeeds) .sort(([, countA], [, countB]) => countB - countA) .map(([need]) => need); insightsHtml += `

Overall Succession Health: Currently managing ${criticalRoles.length} critical roles.

`; if (rolesWithoutReadySuccessors.length > 0) { insightsHtml += `

Urgent Attention Needed: The following critical roles currently lack a "Ready Now" successor: ${rolesWithoutReadySuccessors.map(r => r.name).join(', ')}. Prioritize identifying and developing candidates for these positions.

`; } else if (criticalRoles.length > 0) { insightsHtml += `

Strong Coverage: All critical roles currently have at least one "Ready Now" successor identified. Good job!

`; } if (rolesWithMultipleReadySuccessors.length > 0) { insightsHtml += `

Robust Pipeline: Roles with multiple "Ready Now" successors, indicating a strong talent pipeline: ${rolesWithMultipleReadySuccessors.map(r => r.name).join(', ')}.

`; } if (sortedDevelopmentNeeds.length > 0) { insightsHtml += `

Common Development Themes: Recurring development needs across successors include: ${sortedDevelopmentNeeds.slice(0, 3).join(', ')}${sortedDevelopmentNeeds.length > 3 ? '...' : ''}. Consider group training or generalized programs for these areas.

`; } else { insightsHtml += `

No common development themes identified. Ensure development needs are specified for successors.

`; } } successionInsights.innerHTML = insightsHtml; } // --- Event Handlers (Globally accessible for onclick attributes) --- /** * Switches between dashboard and data configuration tabs. * @param {string} tabName - 'dashboard' or 'dataConfig'. */ window.switchTab = function(tabName) { // Guard clauses for elements if (!dashboardTabBtn || !dataConfigTabBtn || !dashboardTabContent || !dataConfigTabContent || !prevTabBtn || !nextTabBtn) { console.error("One or more tab elements not found."); return; } currentTab = tabName; // Update button styles dashboardTabBtn.classList.remove('active'); dataConfigTabBtn.classList.remove('active'); dashboardTabContent.classList.add('hidden'); dataConfigTabContent.classList.add('hidden'); if (tabName === 'dashboard') { dashboardTabBtn.classList.add('active'); dashboardTabContent.classList.remove('hidden'); prevTabBtn.disabled = true; nextTabBtn.disabled = false; renderKpiMetrics(); // Re-render dashboard KPI content renderPipelineOverview(); // Re-render pipeline overview generateSuccessionInsights(); // Re-generate insights } else if (tabName === 'dataConfig') { dataConfigTabBtn.classList.add('active'); dataConfigTabContent.classList.remove('hidden'); prevTabBtn.disabled = false; nextTabBtn.disabled = true; renderAllRoleInputSections(); // Re-render input rows when tab is active } }; /** * Navigates between tabs using 'Next' and 'Previous' buttons. * @param {string} direction - 'prev' or 'next'. */ window.navigateTabs = function(direction) { if (direction === 'next') { if (currentTab === 'dashboard') { switchTab('dataConfig'); } } else if (direction === 'prev') { if (currentTab === 'dataConfig') { switchTab('dashboard'); } } }; /** * Adds a new empty role section to the data configuration. */ if (addRoleBtn) { // Null check for addRoleBtn addRoleBtn.onclick = function() { const newRoleId = generateUniqueRoleId(); const newRole = { id: newRoleId, name: '', critical: false, successors: [] }; successionData.push(newRole); renderRoleInputSection(newRole); showMessage('New role added.', 'info'); }; } /** * Updates a specific property of a role in the successionData array. * This function is called directly from input fields' oninput/onchange. * @param {string} roleId - The ID of the role to update. * @param {string} property - The property to update ('name', 'critical'). * @param {*} value - The new value for the property. */ window.updateRoleValue = function(roleId, property, value) { const roleIndex = successionData.findIndex(r => r.id === roleId); if (roleIndex !== -1) { successionData[roleIndex][property] = value; } }; /** * Removes a role from the successionData array and its corresponding section. * @param {string} roleId - The ID of the role to remove. */ window.removeRole = function(roleId) { successionData = successionData.filter(r => r.id !== roleId); const roleSection = document.getElementById(`role-section-${roleId}`); if (roleSection) { roleSection.remove(); showMessage('Role removed successfully.', 'success'); } else { console.warn(`Role section with ID ${roleId} not found for removal.`); } }; /** * Adds a new empty successor card to a specific role. * @param {string} roleId - The ID of the role to add a successor to. */ window.addSuccessor = function(roleId) { const role = successionData.find(r => r.id === roleId); if (role) { const newSuccessorId = `${roleId}_${generateUniqueSuccessorId()}`; // Ensure unique ID per role const newSuccessor = { id: newSuccessorId, name: '', readiness: null, developmentNeeds: '' }; role.successors.push(newSuccessor); renderSuccessorInputCard(roleId, newSuccessor); showMessage('New successor added.', 'info'); } }; /** * Updates a specific property of a successor within a role. * @param {string} roleId - The ID of the parent role. * @param {string} successorId - The ID of the successor to update. * @param {string} property - The property to update ('name', 'readiness', 'developmentNeeds'). * @param {*} value - The new value for the property. */ window.updateSuccessorValue = function(roleId, successorId, property, value) { const role = successionData.find(r => r.id === roleId); if (role) { const successorIndex = role.successors.findIndex(s => s.id === successorId); if (successorIndex !== -1) { role.successors[successorIndex][property] = value; } } }; /** * Removes a successor from a specific role. * @param {string} roleId - The ID of the parent role. * @param {string} successorId - The ID of the successor to remove. */ window.removeSuccessor = function(roleId, successorId) { const role = successionData.find(r => r.id === roleId); if (role) { role.successors = role.successors.filter(s => s.id !== successorId); const successorCard = document.getElementById(`successor-card-${successorId}`); if (successorCard) { successorCard.remove(); showMessage('Successor removed successfully.', 'success'); } else { console.warn(`Successor card with ID ${successorId} not found for removal.`); } } }; /** * Saves the data (which is already updated via oninput) and refreshes the dashboard. */ if (saveDataBtn) { // Null check for saveDataBtn saveDataBtn.onclick = function() { // Clean up data: remove empty roles or successors if needed successionData = successionData.filter(role => role.name.trim() !== '' || role.successors.length > 0 ).map(role => { role.successors = role.successors.filter(s => s.name.trim() !== ''); return role; }); renderKpiMetrics(); // Re-render dashboard KPIs renderPipelineOverview(); // Re-render pipeline overview generateSuccessionInsights(); // Re-generate insights showMessage('Data saved and dashboard updated!', 'success'); }; } /** * Handles the PDF download functionality. */ if (downloadPdfBtn) { // Null check for downloadPdfBtn downloadPdfBtn.onclick = async function() { if (!pdfContent || !pdfKpiMetrics || !pdfPipelineOverview || !pdfSuccessionInsights) { console.error("PDF content elements not found."); showMessage('Error: PDF content elements missing.', 'error'); return; } // Populate the hidden PDF content div with current dashboard data // KPI Metrics for PDF pdfKpiMetrics.innerHTML = `

Key Succession Metrics

`; const totalCriticalRoles = successionData.filter(role => role.critical).length; let rolesWithReadySuccessors = 0; let totalReadinessScore = 0; let totalSuccessors = 0; let readyNowSuccessors = 0; successionData.forEach(role => { if (role.critical) { const readySuccessorsInRole = role.successors.filter(s => s.readiness === 5).length; if (readySuccessorsInRole > 0) { rolesWithReadySuccessors++; } } role.successors.forEach(s => { totalReadinessScore += s.readiness || 0; totalSuccessors++; if (s.readiness === 5) { readyNowSuccessors++; } }); }); const avgReadinessScore = totalSuccessors > 0 ? (totalReadinessScore / totalSuccessors).toFixed(1) : 'N/A'; pdfKpiMetrics.innerHTML += `

Total Critical Roles: ${totalCriticalRoles}

Roles with Ready Now Successors: ${rolesWithReadySuccessors}

Average Successor Readiness: ${avgReadinessScore}

Ready Now Successors Identified: ${readyNowSuccessors}

`; // Pipeline Overview for PDF pdfPipelineOverview.innerHTML = `

Succession Pipeline Overview

`; if (successionData.length === 0) { pdfPipelineOverview.innerHTML += '

No roles defined for the pipeline overview.

'; } else { successionData.forEach(role => { pdfPipelineOverview.innerHTML += `

${role.name} ${role.critical ? '(Critical Role)' : ''}

${role.successors.length === 0 ? '

No successors identified for this role.

' : ''} `; role.successors.forEach(successor => { pdfPipelineOverview.innerHTML += ` `; }); pdfPipelineOverview.innerHTML += `
Successor Name Readiness Development Needs
${successor.name || 'N/A'} ${getReadinessDescription(successor.readiness)} ${successor.developmentNeeds || 'None'}
`; }); } // Succession Insights for PDF pdfSuccessionInsights.innerHTML = `

Succession Insights

`; let insightsForPdf = ''; if (successionData.length === 0) { insightsForPdf = '

No insights generated. Please configure your data.

'; } else { const criticalRoles = successionData.filter(role => role.critical); const rolesWithoutReadySuccessors = criticalRoles.filter(role => !role.successors.some(s => s.readiness === 5)); const rolesWithMultipleReadySuccessors = criticalRoles.filter(role => role.successors.filter(s => s.readiness === 5).length >= 2); const allDevelopmentNeeds = {}; successionData.forEach(role => { role.successors.forEach(s => { if (s.developmentNeeds && s.developmentNeeds.trim() !== '') { s.developmentNeeds.split(',').forEach(need => { const trimmedNeed = need.trim(); if (trimmedNeed) { allDevelopmentNeeds[trimmedNeed] = (allDevelopmentNeeds[trimmedNeed] || 0) + 1; } }); } }); }); const sortedDevelopmentNeeds = Object.entries(allDevelopmentNeeds) .sort(([, countA], [, countB]) => countB - countA) .map(([need]) => need); insightsForPdf += `

Overall Succession Health: Currently managing ${criticalRoles.length} critical roles.

`; if (rolesWithoutReadySuccessors.length > 0) { insightsForPdf += `

Urgent Attention Needed: The following critical roles currently lack a "Ready Now" successor: ${rolesWithoutReadySuccessors.map(r => r.name).join(', ')}. Prioritize identifying and developing candidates for these positions.

`; } else if (criticalRoles.length > 0) { insightsForPdf += `

Strong Coverage: All critical roles currently have at least one "Ready Now" successor identified. Good job!

`; } if (rolesWithMultipleReadySuccessors.length > 0) { insightsForPdf += `

Robust Pipeline: Roles with multiple "Ready Now" successors, indicating a strong talent pipeline: ${rolesWithMultipleReadySuccessors.map(r => r.name).join(', ')}.

`; } if (sortedDevelopmentNeeds.length > 0) { insightsForPdf += `

Common Development Themes: Recurring development needs across successors include: ${sortedDevelopmentNeeds.slice(0, 3).join(', ')}${sortedDevelopmentNeeds.length > 3 ? '...' : ''}. Consider group training or generalized programs for these areas.

`; } else { insightsForPdf += `

No common development themes identified. Ensure development needs are specified for successors.

`; } insightsForPdf += `
`; } pdfSuccessionInsights.innerHTML += insightsForPdf; // Show the hidden content temporarily for html2canvas to render it pdfContent.style.display = 'block'; showMessage('Generating PDF...', 'info'); try { const canvas = await html2canvas(pdfContent, { scale: 2, // Increase scale for better quality useCORS: true, // If you have external images, enable CORS logging: false // Disable console logging from html2canvas }); const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF('p', 'mm', 'a4'); // 'p' for portrait, 'mm' for millimeters, 'a4' size const imgWidth = 210; // A4 width in mm const pageHeight = 297; // A4 height in mm const imgHeight = canvas.height * imgWidth / canvas.width; let heightLeft = imgHeight; let position = 0; pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; while (heightLeft >= 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; } pdf.save('Succession_Planning_Dashboard.pdf'); showMessage('PDF downloaded successfully!', 'success'); } catch (error) { console.error("Error generating PDF:", error); showMessage('Error generating PDF. Please try again.', 'error'); } finally { // Hide the content again after PDF generation pdfContent.style.display = 'none'; } }; } // --- Initial Load --- switchTab('dashboard'); // Start on the dashboard tab renderAllRoleInputSections(); // Render initial data in config tab renderKpiMetrics(); // Render initial data in dashboard tab renderPipelineOverview(); // Render initial pipeline overview generateSuccessionInsights(); // Generate initial insights });
Scroll to Top