Smart Workload Distribution Assistant

Smart Workload Distribution Assistant

Assign tasks intelligently based on team capacity and skills.

Team Workload Overview

Workload Distribution (Hours)

Skills: ${member.skills.join(', ')}

`; }); }; const renderChart = (workloadData) => { if (workloadChart) workloadChart.destroy(); const ctx = document.getElementById('workloadChart').getContext('2d'); workloadChart = new Chart(ctx, { type: 'bar', data: { labels: workloadData.map(m => m.name), datasets: [{ label: 'Current Workload (Hours)', data: workloadData.map(m => m.currentLoad), backgroundColor: '#3B82F6' }, { label: 'Capacity (Hours)', data: workloadData.map(m => m.capacity), backgroundColor: '#E5E7EB' }] }, options: { scales: { x: { stacked: false }, y: { stacked: false, beginAtZero: true } } } }); }; // --- MODAL & FORM LOGIC --- app.showTaskModal = () => { const select = document.getElementById('taskAssigneeSelect'); select.innerHTML = ''; team.forEach(m => select.innerHTML += ``); document.getElementById('taskModal').classList.remove('hidden'); }; app.showMemberModal = (id = null) => { const modal = document.getElementById('memberModal'); const title = document.getElementById('memberModalTitle'); const idInput = document.getElementById('memberIdInput'); const nameInput = document.getElementById('memberNameInput'); const capacityInput = document.getElementById('memberCapacityInput'); const skillsInput = document.getElementById('memberSkillsInput'); if (id) { const member = team.find(m => m.id === id); title.textContent = 'Edit Team Member'; idInput.value = member.id; nameInput.value = member.name; capacityInput.value = member.capacity; skillsInput.value = member.skills.join(', '); } else { title.textContent = 'Add Team Member'; idInput.value = ''; nameInput.value = ''; capacityInput.value = 40; skillsInput.value = ''; } modal.classList.remove('hidden'); }; app.hideModal = (modalId) => document.getElementById(modalId).classList.add('hidden'); // --- CORE LOGIC --- app.saveTask = () => { const desc = document.getElementById('taskDescInput').value; const hours = parseFloat(document.getElementById('taskHoursInput').value); const skill = document.getElementById('taskSkillInput').value.trim(); let assigneeId = parseInt(document.getElementById('taskAssigneeSelect').value); if (!desc || !hours) { alert('Description and hours are required.'); return; } if (!assigneeId) { // Auto-assign logic const availableMembers = team .map(member => ({ ...member, currentLoad: tasks.filter(t => t.assigneeId === member.id).reduce((sum, t) => sum + t.hours, 0) })) .filter(member => (member.currentLoad + hours) <= member.capacity && (!skill || member.skills.map(s => s.toLowerCase()).includes(skill.toLowerCase()))) .sort((a, b) => (a.currentLoad / a.capacity) - (b.currentLoad / b.capacity)); // Assign to least busy if (availableMembers.length > 0) { assigneeId = availableMembers[0].id; } else { alert('No suitable team member found with available capacity or required skill. Please assign manually or adjust task.'); return; } } tasks.push({ id: Date.now(), desc, hours, skill, assigneeId }); saveData(); renderWorkloadDashboard(); app.hideModal('taskModal'); }; app.saveMember = () => { const id = document.getElementById('memberIdInput').value; const name = document.getElementById('memberNameInput').value; if (!name.trim()) { alert('Name is required.'); return; } const memberData = { name, capacity: parseInt(document.getElementById('memberCapacityInput').value) || 40, skills: document.getElementById('memberSkillsInput').value.split(',').map(s => s.trim()).filter(Boolean) }; if (id) { const member = team.find(m => m.id == id); Object.assign(member, memberData); } else { team.push({ id: Date.now(), ...memberData }); } saveData(); renderTeamConfig(); app.hideModal('memberModal'); }; app.deleteMember = (id) => { if (confirm('Are you sure? This will unassign all their tasks.')) { team = team.filter(m => m.id !== id); tasks.forEach(task => { if (task.assigneeId === id) task.assigneeId = null; }); saveData(); renderTeamConfig(); renderWorkloadDashboard(); } }; // --- UTILS & HELPERS --- app.changeTab = (tabIndex) => { document.querySelectorAll('[id^="tabContent-"]').forEach(el => el.classList.add('hidden')); document.querySelectorAll('[id^="tab-"]').forEach(el => el.classList.replace('tab-active', 'tab-inactive')); document.getElementById(`tabContent-${tabIndex}`).classList.remove('hidden'); document.getElementById(`tab-${tabIndex}`).classList.replace('tab-inactive', 'tab-active'); }; app.downloadReportPDF = () => { const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFontSize(20); doc.text('Team Workload Report', 105, 15, { align: 'center' }); const head = [['Team Member', 'Skills', 'Capacity (h)', 'Current Load (h)', 'Utilization']]; const body = team.map(member => { const currentLoad = tasks.filter(t => t.assigneeId === member.id).reduce((sum, t) => sum + t.hours, 0); const utilization = member.capacity > 0 ? `${((currentLoad / member.capacity) * 100).toFixed(1)}%` : 'N/A'; return [member.name, member.skills.join(', '), member.capacity, currentLoad, utilization]; }); doc.autoTable({ head, body, startY: 25 }); doc.save('workload_report.pdf'); }; // --- INITIALIZATION --- loadData(); renderWorkloadDashboard(); renderTeamConfig(); });
Scroll to Top