Employee Work Efficiency Scorecard

Employee Work Efficiency Scorecard

Manage Employees

    Manage KPIs (Key Performance Indicators)

    Defined KPIs
    KPI NameUnitTargetHigher is Better?Actions

    Enter Scorecard Data for Employee

    Generate Employee Efficiency Scorecard

    "; return; } ewes_kpiDefinitions.forEach(kpi => { const group = document.createElement('div'); group.className = 'ewes-form-group'; group.innerHTML = ` `; container.appendChild(group); if (existingScorecard && existingScorecard.kpiValues[kpi.id] !== undefined) { document.getElementById(`kpi_actual_${kpi.id}`).value = existingScorecard.kpiValues[kpi.id]; } }); } function ewes_saveScorecardData() { const employeeId = document.getElementById('ewes_selectEmployeeForData').value; const reviewPeriodStart = document.getElementById('ewes_reviewPeriodStart').value; const reviewPeriodEnd = document.getElementById('ewes_reviewPeriodEnd').value; const managerComments = document.getElementById('ewes_managerComments').value.trim(); if (!employeeId || !reviewPeriodStart || !reviewPeriodEnd) { alert("Employee and Review Period must be selected."); return; } if (new Date(reviewPeriodEnd) < new Date(reviewPeriodStart)) { alert("Review End Date cannot be before Start Date."); return; } const kpiValues = {}; let allKpisEntered = true; ewes_kpiDefinitions.forEach(kpi => { const inputEl = document.getElementById(`kpi_actual_${kpi.id}`); if (inputEl && inputEl.value !== '') { kpiValues[kpi.id] = parseFloat(inputEl.value); if(isNaN(kpiValues[kpi.id])) allKpisEntered = false; // Mark if any KPI value is not a number } else { allKpisEntered = false; // Mark if any KPI value is missing } }); if(!allKpisEntered && ewes_kpiDefinitions.length > 0){ // Only warn if KPIs are defined if(!confirm("Not all KPI values have been entered or some are invalid. Save anyway? Missing values will not be scored.")){ return; } } // Check if scorecard data for this employee and period already exists const existingIndex = ewes_scorecardData.findIndex(sc => sc.employeeId == employeeId && sc.reviewPeriodStart === reviewPeriodStart && sc.reviewPeriodEnd === reviewPeriodEnd ); if (existingIndex > -1) { // Update existing ewes_scorecardData[existingIndex].kpiValues = kpiValues; ewes_scorecardData[existingIndex].managerComments = managerComments; } else { // Add new ewes_scorecardData.push({ id: Date.now(), employeeId: parseInt(employeeId), reviewPeriodStart, reviewPeriodEnd, kpiValues, managerComments }); } alert("Scorecard data saved successfully!"); // Optionally clear form or just leave as is for potential further edits } // --- Tab 3: Generate Scorecard --- function ewes_generateScorecard() { const employeeId = document.getElementById('ewes_selectEmployeeForScorecard').value; const reviewStart = document.getElementById('ewes_scorecardPeriodStart').value; const reviewEnd = document.getElementById('ewes_scorecardPeriodEnd').value; const displayContainer = document.getElementById('ewes_scorecardDisplayContainer'); const pdfBtn = document.getElementById('ewes_pdfButton'); if(!displayContainer || !pdfBtn) return; if (!employeeId || !reviewStart || !reviewEnd) { alert("Please select an Employee and Review Period."); displayContainer.style.display = 'none'; pdfBtn.style.display = 'none'; return; } if (new Date(reviewEnd) < new Date(reviewStart)) { alert("Review End Date cannot be before Start Date."); displayContainer.style.display = 'none'; pdfBtn.style.display = 'none'; return; } const scorecard = ewes_scorecardData.find(sc => sc.employeeId == employeeId && sc.reviewPeriodStart === reviewStart && sc.reviewPeriodEnd === reviewEnd ); if (!scorecard) { alert("No scorecard data found for the selected employee and period. Please enter data in Tab 2 first."); displayContainer.style.display = 'none'; pdfBtn.style.display = 'none'; return; } const employee = ewes_employees.find(e => e.id == scorecard.employeeId); document.getElementById('ewes_scEmployeeName').textContent = employee ? employee.name : 'Unknown Employee'; document.getElementById('ewes_scReviewPeriod').textContent = `${scorecard.reviewPeriodStart} to ${scorecard.reviewPeriodEnd}`; const tbody = document.getElementById('ewes_scorecardTable').getElementsByTagName('tbody')[0]; tbody.innerHTML = ''; let kpisMetOrExceeded = 0; let totalKpisForScore = 0; ewes_kpiDefinitions.forEach(kpiDef => { const actualValue = scorecard.kpiValues[kpiDef.id]; if (actualValue === undefined || actualValue === null || isNaN(actualValue)) return; // Skip KPI if no valid data logged totalKpisForScore++; const row = tbody.insertRow(); row.insertCell().textContent = kpiDef.name; row.insertCell().textContent = `${kpiDef.target} ${kpiDef.unit}`; row.insertCell().textContent = `${actualValue} ${kpiDef.unit}`; let performanceText = "N/A"; let varianceText = "-"; let perfClass = ""; const target = kpiDef.target; const variance = actualValue - target; const variancePercent = target !== 0 ? (variance / target * 100) : (actualValue > 0 ? Infinity : 0); if (kpiDef.higherIsBetter === 'yes') { if (actualValue >= target) { performanceText = actualValue > target * 1.05 ? "Exceeded Target" : "Met Target"; // Exceeded if >5% over perfClass = 'ewes-performance-met'; kpisMetOrExceeded++; } else if (actualValue >= target * 0.9) { // Within 10% of target performanceText = "Near Target"; perfClass = 'ewes-performance-near'; } else { performanceText = "Below Target"; perfClass = 'ewes-performance-below'; } varianceText = `${variance > 0 ? '+' : ''}${variance.toFixed(2)} ${kpiDef.unit} (${variancePercent > 0 ? '+' : ''}${variancePercent.toFixed(1)}%)`; } else { // Lower is better if (actualValue <= target) { performanceText = actualValue < target * 0.95 ? "Exceeded Target" : "Met Target"; // Exceeded if >5% under perfClass = 'ewes-performance-met'; kpisMetOrExceeded++; } else if (actualValue <= target * 1.1) { // Within 10% of target (over) performanceText = "Near Target"; perfClass = 'ewes-performance-near'; } else { performanceText = "Above Target"; perfClass = 'ewes-performance-below'; } varianceText = `${variance > 0 ? '+' : ''}${variance.toFixed(2)} ${kpiDef.unit} (${variancePercent > 0 ? '+' : ''}${variancePercent.toFixed(1)}%) (Lower is better)`; } const perfCell = row.insertCell(); perfCell.textContent = performanceText; perfCell.className = perfClass; row.insertCell().textContent = varianceText; }); if (tbody.rows.length === 0) { tbody.innerHTML = `No valid KPI data logged for this scorecard.`; } const overallEfficiencyEl = document.getElementById('ewes_scOverallEfficiency'); if (totalKpisForScore > 0) { const overallScorePercent = (kpisMetOrExceeded / totalKpisForScore * 100); overallEfficiencyEl.textContent = `${overallScorePercent.toFixed(1)}% (${kpisMetOrExceeded}/${totalKpisForScore} KPIs Met/Exceeded)`; if (overallScorePercent >= 80) overallEfficiencyEl.className = 'ewes-performance-met'; else if (overallScorePercent >= 60) overallEfficiencyEl.className = 'ewes-performance-near'; else overallEfficiencyEl.className = 'ewes-performance-below'; } else { overallEfficiencyEl.textContent = "N/A (No KPI data for scoring)"; overallEfficiencyEl.className = ''; } const commentsSection = document.getElementById('ewes_scManagerCommentsSection'); const commentsTextEl = document.getElementById('ewes_scManagerCommentsText'); if(scorecard.managerComments){ commentsTextEl.textContent = scorecard.managerComments; commentsSection.style.display = 'block'; } else { commentsTextEl.textContent = 'No comments provided.'; // commentsSection.style.display = 'none'; // Or show "No comments" } displayContainer.style.display = 'block'; pdfBtn.style.display = 'block'; } // PDF Download function ewes_downloadPDF() { const displayContainer = document.getElementById('ewes_scorecardDisplayContainer'); if (!displayContainer || displayContainer.style.display === 'none') { alert("Please generate a scorecard first."); return; } const doc = new jsPDF(); const primaryColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const textColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim(); const whiteColorPDF = '#FFFFFF'; const empName = document.getElementById('ewes_scEmployeeName').textContent; const reviewPeriod = document.getElementById('ewes_scReviewPeriod').textContent; doc.setFontSize(18); doc.setTextColor(primaryColorPDF); doc.text("Employee Work Efficiency Scorecard", 14, 20); doc.setFontSize(10); doc.setTextColor(textColorPDF); doc.text(`Employee: ${empName}`, 14, 28); doc.text(`Review Period: ${reviewPeriod}`, 14, 33); doc.text(`Generated On: ${EWES_GENERATED_ON_STRING}`, 14, 38); let currentY = 50; doc.autoTable({ html: '#ewes_scorecardTable', startY: currentY, theme: 'grid', headStyles: { fillColor: primaryColorPDF, textColor: whiteColorPDF, fontSize: 9 }, styles: { fontSize: 8, cellPadding: 1.5, overflow: 'linebreak' }, columnStyles: { 0: {cellWidth: 40}, // KPI 1: {cellWidth: 25}, // Target 2: {cellWidth: 25}, // Actual 3: {cellWidth: 30}, // Performance 4: {cellWidth: 'auto'} // Variance }, didParseCell: function (data) { // For coloring performance text if (data.section === 'body' && data.column.index === 3) { // Performance column const text = data.cell.text[0]; if (text.includes("Met") || text.includes("Exceeded")) data.cell.styles.textColor = getComputedStyle(document.documentElement).getPropertyValue('--success-color').trim(); else if (text.includes("Below") || text.includes("Above Target")) data.cell.styles.textColor = getComputedStyle(document.documentElement).getPropertyValue('--danger-color').trim(); else if (text.includes("Near")) data.cell.styles.textColor = getComputedStyle(document.documentElement).getPropertyValue('--warning-color').trim(); } } }); currentY = doc.lastAutoTable.finalY + 10; doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColorPDF); if (currentY > 260) { doc.addPage(); currentY = 20; } doc.text("Overall Efficiency:", 14, currentY); doc.setFont(undefined, 'normal'); doc.setTextColor(textColorPDF); doc.text(document.getElementById('ewes_scOverallEfficiency').textContent, 55, currentY); currentY +=7; const managerComments = document.getElementById('ewes_scManagerCommentsText').textContent; if(managerComments && managerComments !== 'No comments provided.'){ doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColorPDF); if (currentY > 260) { doc.addPage(); currentY = 20; } doc.text("Manager Comments:", 14, currentY); currentY +=7; doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.setTextColor(textColorPDF); const commentLines = doc.splitTextToSize(managerComments, doc.internal.pageSize.getWidth() - 28); // 14 margin L+R doc.text(commentLines, 14, currentY); } doc.save(`Efficiency_Scorecard_${empName.replace(/ /g,'_')}_${reviewPeriod.replace(/ to /g,'-')}.pdf`); }
    Scroll to Top