Team Productivity Dashboard

Team Productivity Dashboard

Setup & Team Context

Input Key Metrics

Task Tracking

Effort Tracking (Optional)

Blockers/Impediments

Team Well-being

Qualitative Review

Team Productivity Dashboard

Your dashboard will appear here once generated.

Quantitative Metrics

Tasks: ${tasksCompleted} / ${tasksPlanned} completed (${tpdDashboardSnapshot.tasksOverdue} overdue)
${taskCompletionRate}%
${ (effortPlanned > 0 || effortCompleted > 0) ? `
Effort (${effortUnit}): ${effortCompleted} / ${effortPlanned} completed
${effortCompletionRate}%
` : ''}
Blockers: ${blockersResolved} / ${blockersIdentified} resolved
${blockerResolutionRate}%
Team Morale: ${tpdDashboardSnapshot.teamMorale} / 5

Qualitative Insights

Major Achievements:

${tpdDashboardSnapshot.achievements}

Key Challenges:

${tpdDashboardSnapshot.challenges}

Lessons Learned:

${tpdDashboardSnapshot.lessonsLearned}

Focus for Next Period:

${tpdDashboardSnapshot.nextPeriodFocus}

`; document.getElementById('tpd-pdf-download').style.display = 'inline-block'; } function tpdDownloadPDF() { if (Object.keys(tpdDashboardSnapshot).length === 0) { alert("Please generate the dashboard first."); return; } const { teamName, reportingPeriod, periodGoals, tasksPlanned, tasksCompleted, tasksOverdue, effortUnit, effortPlanned, effortCompleted, blockersIdentified, blockersResolved, teamMorale, achievements, challenges, lessonsLearned, nextPeriodFocus, calculatedRates } = tpdDashboardSnapshot; const doc = new jsPDF('p', 'pt', 'a4'); const FONT_FAMILY = "helvetica"; const ACCENT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--accent-color').trim(); const PRIMARY_TEXT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--primary-text').trim(); const SECTION_TITLE_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--section-title-color').trim(); const BUTTON_TEXT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--button-text-color').trim(); let yPos = 40; const pageWidth = doc.internal.pageSize.getWidth(); const margin = 40; const usableWidth = pageWidth - (2 * margin); // PDF Header doc.setFillColor(ACCENT_COLOR_HEX); doc.rect(0, 0, pageWidth, yPos + 10, 'F'); doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(18); doc.setTextColor(BUTTON_TEXT_COLOR_HEX); doc.text("Team Productivity Dashboard", pageWidth / 2, yPos, { align: "center" }); yPos += 25; // Context doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); doc.text(`Team/Project: ${teamName}`, margin, yPos); yPos += 12; doc.text(`Reporting Period: ${reportingPeriod}`, margin, yPos); yPos += 12; const genDate = new Date(2025, 4, 17).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }); doc.text(`Generated on: ${genDate}`, margin, yPos); yPos += 20; doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Key Goals for this Period:", margin, yPos); yPos += 15; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); const goalsLines = doc.splitTextToSize(periodGoals, usableWidth); doc.text(goalsLines, margin, yPos); yPos += (goalsLines.length * 12) + 10; // Quantitative Metrics if (yPos > doc.internal.pageSize.getHeight() - 150) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(14); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Quantitative Metrics", margin, yPos); yPos += 20; const metricsData = [ ["Metric", "Planned", "Completed", "Details/Rate"], ["Tasks", tasksPlanned, tasksCompleted, `${tasksOverdue} Overdue, ${calculatedRates.taskCompletionRate}% Complete`], (effortPlanned > 0 || effortCompleted > 0) ? ["Effort ("+effortUnit+")", effortPlanned, effortCompleted, `${calculatedRates.effortCompletionRate}% Complete`] : null, ["Blockers", blockersIdentified, blockersResolved, `${calculatedRates.blockerResolutionRate}% Resolved`], ["Team Morale", "-", "-", `${teamMorale} / 5 Score`] ].filter(row => row !== null); // Filter out null row for effort if not used doc.autoTable({ startY: yPos, head: [metricsData[0]], body: metricsData.slice(1), theme: 'grid', margin: {left: margin, right: margin}, headStyles: { fillColor: ACCENT_COLOR_HEX, textColor: BUTTON_TEXT_COLOR_HEX, fontStyle: 'bold', fontSize:9 }, styles: { font: FONT_FAMILY, fontSize: 8, cellPadding: 3, overflow:'linebreak' }, columnStyles: { 0:{fontStyle:'bold'} } }); yPos = doc.lastAutoTable.finalY + 20; // Qualitative Insights const addQualitativeSection = (title, content) => { if (yPos > doc.internal.pageSize.getHeight() - 60) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text(title, margin, yPos); yPos += 15; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); const lines = doc.splitTextToSize(content, usableWidth); doc.text(lines, margin, yPos); yPos += (lines.length * 12) + 10; }; if (yPos > doc.internal.pageSize.getHeight() - 150) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(14); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Qualitative Insights", margin, yPos); yPos += 20; addQualitativeSection("Major Team Achievements:", achievements); addQualitativeSection("Key Challenges Encountered:", challenges); addQualitativeSection("Key Lessons Learned:", lessonsLearned); addQualitativeSection("Main Focus for Next Period:", nextPeriodFocus); // Footer const pageCount = doc.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); const pageHeight = doc.internal.pageSize.getHeight(); doc.setLineWidth(0.5); doc.setDrawColor(ACCENT_COLOR_HEX); doc.line(margin, pageHeight - 30, pageWidth - margin, pageHeight - 30); doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(8); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); doc.text(`Team Productivity Dashboard - Page ${i} of ${pageCount}`, pageWidth / 2, pageHeight - 20, { align: "center" }); } doc.save(`Team_Dashboard_${teamName.replace(/[^a-z0-9]/gi, '_')}_${reportingPeriod.replace(/[^a-z0-9]/gi, '_')}.pdf`); }
Scroll to Top