"; 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`);
}