Workload Balancer

Workload Balancer

Manage Team Members

Current Team

    Add New Task

    Task List (To Be Balanced)

      Total Tasks: ${member.taskCount}, Total Effort: ${member.totalEffort.toFixed(1)} units

      `; memberAssignmentsDiv.appendChild(memberDiv); // Add to chart const barGroup = document.createElement('div'); barGroup.className = 'chart-bar-group'; const bar = document.createElement('div'); bar.className = 'chart-bar'; const effortToDisplay = (strategy === 'byEffort' || member.totalEffort > 0) ? member.totalEffort : member.taskCount; const barHeight = (effortToDisplay / maxEffort) * 100; bar.style.height = `${Math.max(5, barHeight)}%`; // Min height 5% for visibility bar.textContent = effortToDisplay.toFixed(1); bar.title = `Effort: ${member.totalEffort.toFixed(1)}, Tasks: ${member.taskCount}`; const label = document.createElement('div'); label.className = 'chart-label'; label.textContent = member.name; barGroup.appendChild(bar); barGroup.appendChild(label); effortChartDiv.appendChild(barGroup); }); balancedWorkloadResultsDiv.style.display = 'block'; } clearBalancedWorkloadBtn.addEventListener('click', () => { currentBalancedWorkload = null; displayBalancedWorkload(null); }); // --- PDF Export --- downloadWorkloadPdfBtn.addEventListener('click', async () => { if (!currentBalancedWorkload) { alert('No workload balanced to download.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'a4'); // Using points for better control with html2canvas const { strategy, memberWorkloads } = currentBalancedWorkload; const strategyText = balancingStrategySelect.options[balancingStrategySelect.selectedIndex].text; // Title and Info doc.setFontSize(18); doc.setTextColor(parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(1,3),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(3,5),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(5,7),16)); doc.text('Workload Balance Report', 40, 60); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Strategy Used: ${strategyText}`, 40, 80); doc.text(`Generated on: ${new Date().toLocaleString()}`, 40, 95); let yPos = 120; // Add chart image if (html2canvas && effortChartDiv.offsetHeight > 0) { // Check if chart is visible try { const canvas = await html2canvas(effortChartDiv, { scale: 2, backgroundColor: null }); // Increase scale for better quality, null bg for transparency const imgData = canvas.toDataURL('image/png'); const imgProps = doc.getImageProperties(imgData); const pdfWidth = doc.internal.pageSize.getWidth() - 80; // Page width - margins const imgHeight = (imgProps.height * pdfWidth) / imgProps.width; doc.setFontSize(12); doc.setTextColor(parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(1,3),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(3,5),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(5,7),16)); doc.text('Workload Distribution Chart:', 40, yPos); yPos += 15; doc.addImage(imgData, 'PNG', 40, yPos, pdfWidth, imgHeight); yPos += imgHeight + 20; } catch (error) { console.error("Error generating chart image for PDF:", error); doc.setFontSize(10); doc.setTextColor(255,0,0); // Red doc.text("Chart image could not be generated.", 40, yPos); yPos += 15; } } // Add assignments per member memberWorkloads.forEach(member => { if (yPos > doc.internal.pageSize.getHeight() - 100) { // Check for page break before each member section doc.addPage(); yPos = 40; } doc.setFontSize(14); doc.setTextColor(parseInt(getComputedStyle(document.documentElement).getPropertyValue('--accent-color').substring(1,3),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--accent-color').substring(3,5),16), parseInt(getComputedStyle(document.documentElement).getPropertyValue('--accent-color').substring(5,7),16)); doc.text(member.name, 40, yPos); yPos += 10; const summaryText = `Total Tasks: ${member.taskCount}, Total Effort: ${member.totalEffort.toFixed(1)} units`; doc.setFontSize(10); doc.setTextColor(50); doc.text(summaryText, 40, yPos); yPos += 15; if (member.assignedTasks.length > 0) { const taskTableBody = member.assignedTasks.map(task => [ task.description, task.effort.toFixed(1), task.priority ]); doc.autoTable({ startY: yPos, head: [['Task Description', 'Effort Units', 'Priority']], body: taskTableBody, theme: 'striped', headStyles: { fillColor: [108, 117, 125] }, // --secondary-color margin: { left: 40, right: 40 }, didParseCell: function (data) { const priorityColorsPDF = { high: getComputedStyle(document.documentElement).getPropertyValue('--danger-color').trim(), medium: getComputedStyle(document.documentElement).getPropertyValue('--warning-color').trim(), low: getComputedStyle(document.documentElement).getPropertyValue('--info-color').trim(), }; if (data.column.index === 2 && data.cell.section === 'body') { // Priority column const priorityValue = data.cell.raw.toString().toLowerCase(); if (priorityColorsPDF[priorityValue]) { data.cell.styles.textColor = priorityColorsPDF[priorityValue]; data.cell.styles.fontStyle = 'bold'; } } } }); yPos = doc.lastAutoTable.finalY + 15; } else { doc.text('No tasks assigned.', 45, yPos); yPos += 15; } }); doc.save('Workload_Balance_Report.pdf'); }); // --- Initializations --- renderTeamList(); renderTaskList(); resetTaskForm(); })();
      Scroll to Top