`).join('');
populateEmployeeSelect();
}
function populateEmployeeSelect() {
taskEmployeeSelect.innerHTML = employees.map(emp => `
`).join('');
}
function renderTaskList() {
const table = `
| Task |
Assigned To |
Status |
Time (hrs) |
Actions |
${tasks.map(task => {
const employee = employees.find(e => e.id === task.employeeId);
return `
| ${task.name} |
${employee ? employee.name : 'N/A'} |
${task.status} |
${task.time} |
|
`
}).join('')}
`;
taskListContainer.innerHTML = table;
}
function renderCharts() {
if(charts.scoreChart) charts.scoreChart.destroy();
if(charts.statusChart) charts.statusChart.destroy();
// Score Chart
const employeeScores = employees.map(emp => {
const empTasks = tasks.filter(t => t.employeeId === emp.id && t.status === 'Completed');
const avgScore = empTasks.length > 0 ? empTasks.reduce((sum, t) => sum + t.score, 0) / empTasks.length : 0;
return avgScore;
});
const scoreCtx = document.getElementById('scoreChart').getContext('2d');
charts.scoreChart = new Chart(scoreCtx, {
type: 'bar',
data: {
labels: employees.map(e => e.name),
datasets: [{
label: 'Average Score',
data: employeeScores,
backgroundColor: 'rgba(59, 130, 246, 0.5)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}]
},
options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, max: 100 } } }
});
// Status Chart
const statusCounts = tasks.reduce((acc, task) => {
acc[task.status] = (acc[task.status] || 0) + 1;
return acc;
}, {});
const statusCtx = document.getElementById('statusChart').getContext('2d');
charts.statusChart = new Chart(statusCtx, {
type: 'pie',
data: {
labels: Object.keys(statusCounts),
datasets: [{
data: Object.values(statusCounts),
backgroundColor: ['#10B981', '#F59E0B', '#EF4444'],
}]
},
options: { responsive: true, maintainAspectRatio: false }
});
}
// --- LOGIC FUNCTIONS ---
function calculateProductivityScore(time, status) {
if (status !== 'Completed') return 0;
// Simple score: less time is better. Capped at 100.
let score = Math.max(0, 100 - (time * 5));
if (status === 'Overdue') score *= 0.8; // Penalty for being overdue
return Math.round(score);
}
function clearTaskForm() {
taskForm.reset();
taskIdInput.value = '';
saveTaskBtn.textContent = 'Log Task';
}
// --- EVENT HANDLERS ---
employeeForm.addEventListener('submit', (e) => {
e.preventDefault();
const id = employeeIdInput.value ? parseInt(employeeIdInput.value) : null;
const name = employeeNameInput.value;
if (id) {
const index = employees.findIndex(emp => emp.id === id);
employees[index].name = name;
} else {
const newId = employees.length > 0 ? Math.max(...employees.map(e => e.id)) + 1 : 1;
employees.push({ id: newId, name });
}
employeeForm.reset();
employeeIdInput.value = '';
saveEmployeeBtn.textContent = 'Add Employee';
renderAll();
});
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const id = taskIdInput.value ? parseInt(taskIdInput.value) : null;
const taskData = {
name: taskNameInput.value,
employeeId: parseInt(taskEmployeeSelect.value),
status: taskStatusSelect.value,
time: parseFloat(taskTimeInput.value),
};
taskData.score = calculateProductivityScore(taskData.time, taskData.status);
if (id) {
const index = tasks.findIndex(t => t.id === id);
tasks[index] = { ...tasks[index], ...taskData };
} else {
const newId = tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1;
tasks.push({ id: newId, ...taskData });
}
clearTaskForm();
renderAll();
});
window.editEmployee = (id) => {
const emp = employees.find(e => e.id === id);
employeeIdInput.value = emp.id;
employeeNameInput.value = emp.name;
saveEmployeeBtn.textContent = 'Update';
};
window.deleteEmployee = (id) => {
if (confirm('Are you sure? This will also unassign their tasks.')) {
employees = employees.filter(e => e.id !== id);
tasks.forEach(t => { if(t.employeeId === id) t.employeeId = null; });
renderAll();
}
};
window.editTask = (id) => {
const task = tasks.find(t => t.id === id);
taskIdInput.value = task.id;
taskNameInput.value = task.name;
taskEmployeeSelect.value = task.employeeId;
taskStatusSelect.value = task.status;
taskTimeInput.value = task.time;
saveTaskBtn.textContent = 'Update Task';
changeTab(null, 'tracker');
};
window.deleteTask = (id) => {
if (confirm('Delete this task?')) {
tasks = tasks.filter(t => t.id !== id);
renderAll();
}
};
function downloadPDF() {
const { jsPDF } = window.jspdf;
const pdfContentEl = document.getElementById('pdf-content');
let reportHTML = `
Productivity Report
`;
employees.forEach(emp => {
const empTasks = tasks.filter(t => t.employeeId === emp.id);
const avgScore = empTasks.length > 0 ? Math.round(empTasks.reduce((sum, t) => sum + t.score, 0) / empTasks.length) : 'N/A';
reportHTML += `
${emp.name} - Avg Score: ${avgScore}
| Task |
Status |
Time (hrs) |
Score |
${empTasks.map(t => `
| ${t.name} |
${t.status} |
${t.time} |
${t.score} |
`).join('')}
`;
});
pdfContentEl.innerHTML = `
${reportHTML}
`;
html2canvas(pdfContentEl, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgProps = pdf.getImageProperties(imgData);
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
pdf.addImage(imgData, 'PNG', 20, 20, pdfWidth - 40, pdfHeight - 40);
pdf.save('Productivity_Report.pdf');
});
}
function renderAll() {
renderEmployeeList();
renderTaskList();
renderCharts();
}
// --- TAB NAVIGATION ---
window.changeTab = function(evt, tabName) {
document.querySelectorAll(".tab-content").forEach(tc => tc.classList.remove('active'));
document.querySelectorAll(".tab-button").forEach(tb => tb.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
const button = evt ? evt.currentTarget : document.querySelector(`.tab-button[onclick*="'${tabName}'"]`);
if(button) button.classList.add('active');
currentTab = tabName;
updateNavButtons();
if (tabName === 'dashboard') renderCharts();
}
function updateNavButtons() {
const currentIndex = tabs.indexOf(currentTab);
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex === tabs.length - 1;
prevBtn.classList.toggle('opacity-50', prevBtn.disabled);
nextBtn.classList.toggle('opacity-50', nextBtn.disabled);
}
prevBtn.addEventListener('click', () => {
const currentIndex = tabs.indexOf(currentTab);
if (currentIndex > 0) changeTab(null, tabs[currentIndex - 1]);
});
nextBtn.addEventListener('click', () => {
const currentIndex = tabs.indexOf(currentTab);
if (currentIndex < tabs.length - 1) changeTab(null, tabs[currentIndex + 1]);
});
// --- INITIALIZATION ---
clearFormBtn.addEventListener('click', clearTaskForm);
downloadPdfBtn.addEventListener('click', downloadPDF);
renderAll();
updateNavButtons();
});