Internal Promotion Dashboard
Promotion Rate
0%
Avg. Time to Promotion
0 yrs
Managerial Promotions
0
Avg. Performance Score
0/5
Promotion Rate by Department
Promotions by Gender
Performance vs. Tenure at Promotion
Promotion Log (YTD)
| Employee | Old Role | New Role | Department | Date |
|---|
Manage Promotion Records
All Promotion Records
| Employee | Department | New Role | Action |
|---|
Error: A required library is missing.
'; return; } // --- DATA MANAGEMENT --- const TOTAL_EMPLOYEES = 250; let promotions = JSON.parse(localStorage.getItem('ipd_promotions')) || []; const getSampleData = () => [ { id: 1, name: 'Jessica Miller', gender: 'Female', department: 'Engineering', oldRole: 'Software Engineer II', newRole: 'Senior Software Engineer', isManager: false, tenure: 3.5, performance: 4.8, date: '2025-02-15' }, { id: 2, name: 'David Wilson', gender: 'Male', department: 'Sales', oldRole: 'Sales Associate', newRole: 'Account Executive', isManager: false, tenure: 2.1, performance: 4.5, date: '2025-03-01' }, { id: 3, name: 'Sarah Davis', gender: 'Female', department: 'Marketing', oldRole: 'Marketing Specialist', newRole: 'Marketing Manager', isManager: true, tenure: 4.0, performance: 4.6, date: '2025-04-20' }, { id: 4, name: 'John Smith', gender: 'Male', department: 'Engineering', oldRole: 'Senior Software Engineer', newRole: 'Engineering Manager', isManager: true, tenure: 5.2, performance: 4.9, date: '2025-05-10' }, { id: 5, name: 'Michael Brown', gender: 'Male', department: 'Operations', oldRole: 'Operations Analyst', newRole: 'Senior Operations Analyst', isManager: false, tenure: 1.8, performance: 4.2, date: '2025-06-05' }, ]; if (promotions.length === 0) promotions = getSampleData(); const saveState = () => localStorage.setItem('ipd_promotions', JSON.stringify(promotions)); // --- CHART INSTANCES & UTILITIES --- let deptChart, genderChart, perfTenureChart; const tabButtons = document.querySelectorAll('.ipd-tab-button'); const tabContents = document.querySelectorAll('.ipd-tab-content'); const nextBtn = document.getElementById('ipd-next-btn'); const prevBtn = document.getElementById('ipd-prev-btn'); // --- RENDER FUNCTIONS --- const renderAll = () => { try { renderKPIs(); renderDeptChart(); renderGenderChart(); renderPerfTenureChart(); renderLogTable(); renderManageTable(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const renderKPIs = () => { const promotionRate = (promotions.length / TOTAL_EMPLOYEES) * 100; const avgTimeToPromo = promotions.length > 0 ? promotions.reduce((sum,p) => sum + p.tenure, 0) / promotions.length : 0; const managerPromos = promotions.filter(p => p.isManager).length; const avgPerfScore = promotions.length > 0 ? promotions.reduce((sum,p) => sum + p.performance, 0) / promotions.length : 0; document.getElementById('ipd-promo-rate-kpi').textContent = `${promotionRate.toFixed(1)}%`; document.getElementById('ipd-avg-time-kpi').textContent = `${avgTimeToPromo.toFixed(1)} yrs`; document.getElementById('ipd-manager-promo-kpi').textContent = managerPromos; document.getElementById('ipd-perf-score-kpi').textContent = `${avgPerfScore.toFixed(1)}/5`; }; const renderDeptChart = () => { // Assuming a static headcount per department for rate calculation const deptHeadcounts = { 'Engineering': 80, 'Sales': 60, 'Marketing': 40, 'Operations': 50, 'HR': 20 }; const promosByDept = promotions.reduce((acc, p) => { acc[p.department] = (acc[p.department] || 0) + 1; return acc; }, {}); const rates = Object.keys(deptHeadcounts).map(dept => { const rate = ((promosByDept[dept] || 0) / deptHeadcounts[dept]) * 100; return rate.toFixed(1); }); const options = { chart: { type: 'bar', height: 350, toolbar: { show: false } }, series: [{ name: 'Promotion Rate', data: rates }], xaxis: { categories: Object.keys(deptHeadcounts) }, yaxis: { labels: { formatter: val => `${val}%` } }, plotOptions: { bar: { horizontal: false, distributed: true } }, legend: { show: false } }; if(deptChart) deptChart.destroy(); document.querySelector("#ipd-dept-chart").innerHTML = ''; deptChart = new ApexCharts(document.querySelector("#ipd-dept-chart"), options); deptChart.render(); }; const renderGenderChart = () => { const promosByGender = promotions.reduce((acc, p) => { acc[p.gender] = (acc[p.gender] || 0) + 1; return acc; }, {}); const options = { chart: { type: 'donut', height: 350 }, series: Object.values(promosByGender), labels: Object.keys(promosByGender), legend: { position: 'bottom' } }; if(genderChart) genderChart.destroy(); document.querySelector("#ipd-gender-chart").innerHTML = ''; genderChart = new ApexCharts(document.querySelector("#ipd-gender-chart"), options); genderChart.render(); }; const renderPerfTenureChart = () => { const seriesData = promotions.map(p => ({ x: p.tenure, y: p.performance })); const options = { chart: { type: 'scatter', height: 350, zoom: { enabled: false }, toolbar: { show: false } }, series: [{ name: 'Promotion', data: seriesData }], xaxis: { type: 'numeric', title: { text: 'Tenure (Years)' }, tickAmount: 5 }, yaxis: { tickAmount: 5, min: 3.5, max: 5, title: { text: 'Performance Score' } }, tooltip: { y: { formatter: (val) => val.toFixed(1) }, x: { formatter: (val) => `${val.toFixed(1)} years` } } }; if(perfTenureChart) perfTenureChart.destroy(); document.querySelector("#ipd-perf-tenure-chart").innerHTML = ''; perfTenureChart = new ApexCharts(document.querySelector("#ipd-perf-tenure-chart"), options); perfTenureChart.render(); }; const renderLogTable = () => { const tbody = document.getElementById('ipd-log-tbody'); tbody.innerHTML = ''; promotions.sort((a,b) => new Date(b.date) - new Date(a.date)).forEach(p => { tbody.innerHTML += `