No work outputs logged. Please log outputs in Tab 1 to see the dashboard.
';
return;
}
const startDateStr = document.getElementById('woa-analysis-start-date').value;
const endDateStr = document.getElementById('woa-analysis-end-date').value;
const filterCategory = document.getElementById('woa-filter-category-dashboard').value;
if(!startDateStr || !endDateStr) { return; }
const startDate = new Date(startDateStr + "T00:00:00");
const endDate = new Date(endDateStr + "T23:59:59");
let filteredOutputs = woaLoggedOutputs.filter(out => out.dateObj >= startDate && out.dateObj <= endDate);
if (filterCategory !== "All") {
filteredOutputs = filteredOutputs.filter(out => out.category === filterCategory);
}
if (filteredOutputs.length === 0 && woaCurrentTabIndex === 1) { // Check if on dashboard tab
document.getElementById('woa-dashboard-display').innerHTML = '
No work outputs found for the selected filters/date range.
';
if(woaChartCategoryInstance) woaChartCategoryInstance.destroy();
if(woaChartImpactInstance) woaChartImpactInstance.destroy();
if(woaChartTrendInstance) woaChartTrendInstance.destroy();
return;
}
let timeByCategory = {}; woaCategories.forEach(cat => timeByCategory[cat] = 0);
let countByCategory = {}; woaCategories.forEach(cat => countByCategory[cat] = 0);
let timeByImpact = { High: 0, Medium: 0, Low: 0 };
let countByImpact = { High: 0, Medium: 0, Low: 0 };
let totalLoggedTime = 0;
let totalOutputsCount = 0;
filteredOutputs.forEach(out => {
timeByCategory[out.category] = (timeByCategory[out.category] || 0) + (out.timeSpent * out.quantity);
countByCategory[out.category] = (countByCategory[out.category] || 0) + out.quantity;
timeByImpact[out.impact] = (timeByImpact[out.impact] || 0) + (out.timeSpent * out.quantity);
countByImpact[out.impact] = (countByImpact[out.impact] || 0) + out.quantity;
totalLoggedTime += (out.timeSpent * out.quantity);
totalOutputsCount += out.quantity;
});
// Prepare data for Trend Chart (Output count over time)
let trendData = {}; // { "YYYY-MM-DD": count }
filteredOutputs.forEach(out => {
const dateKey = out.dateObj.toISOString().split('T')[0];
trendData[dateKey] = (trendData[dateKey] || 0) + out.quantity;
});
const sortedTrendData = Object.keys(trendData).sort((a,b) => new Date(a) - new Date(b)).map(dateKey => ({x: new Date(dateKey+"T00:00:00"), y: trendData[dateKey]}));
let dashboardHTML = `
Key Metrics (${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()})
- Total Outputs Logged: ${totalOutputsCount}
- Total Time on Outputs: ${totalLoggedTime.toFixed(1)} hrs
- Avg. Time per Output: ${(totalOutputsCount > 0 ? totalLoggedTime / totalOutputsCount : 0).toFixed(1)} hrs
- Time on High-Impact: ${(timeByImpact["High"] / 60 * 100 / (totalLoggedTime||1)).toFixed(0)}% (${(timeByImpact["High"]).toFixed(1)} hrs)
Output Count by Category
Time Spent by Category
Output Count by Impact
Time Spent by Impact
Output Trend (Count over Time)
`;
document.getElementById('woa-dashboard-display').innerHTML = dashboardHTML;
// Render Charts
woaRenderBarOrPieChart('woa-category-count-chart', countByCategory, 'Output Count by Category', 'bar');
woaRenderBarOrPieChart('woa-category-time-chart', timeByCategory, 'Time (hrs) by Category', 'pie');
woaRenderBarOrPieChart('woa-impact-count-chart', countByImpact, 'Output Count by Impact', 'pie');
woaRenderBarOrPieChart('woa-impact-time-chart', timeByImpact, 'Time (hrs) by Impact', 'bar');
woaRenderLineChart('woa-output-trend-chart', sortedTrendData, 'Output Count Over Time');
// Generate Insights
const insightsListEl = document.getElementById('woa-insights-list');
insightsListEl.innerHTML = '';
let insightsGenerated = 0;
if(totalLoggedTime > 0){
if ((timeByImpact["Low"] / totalLoggedTime) > 0.3) { insightsListEl.innerHTML += `
Over 30% of your time is spent on activities you perceive as Low Impact. Consider strategies to reduce or delegate these.`; insightsGenerated++;}
if ((timeByCategory["Focused Work"] / totalLoggedTime) < 0.4 && timeByCategory["Focused Work"] !== undefined) { insightsListEl.innerHTML += `
Less than 40% of time is on 'Focused Work'. If deep work is important, look for ways to increase dedicated focus blocks.`; insightsGenerated++;}
if ((timeByCategory["Meetings"] / totalLoggedTime) > 0.35 && timeByCategory["Meetings"] !== undefined) { insightsListEl.innerHTML += `
Meetings take up over 35% of your logged time. Evaluate their necessity and efficiency.`; insightsGenerated++;}
// Find category with most time spent
let maxTime = 0; let maxCat = '';
for(const cat in timeByCategory){ if(timeByCategory[cat] > maxTime) {maxTime = timeByCategory[cat]; maxCat = cat;} }
if(maxCat && totalLoggedTime > 0) insightsListEl.innerHTML += `
Your most time-consuming category is '${maxCat}', accounting for ${(maxTime/totalLoggedTime*100).toFixed(0)}% of logged hours.`;
}
if(insightsGenerated === 0 && totalLoggedTime > 0) insightsListEl.innerHTML += `
Your output distribution seems balanced based on the current rules. Keep tracking to identify personal trends!`;
else if (totalLoggedTime === 0) insightsListEl.innerHTML += `
No output logged in this period to provide insights.`;
}
function woaRenderBarOrPieChart(canvasId, dataObject, chartLabel, type = 'pie') {
const ctx = document.getElementById(canvasId);
if (!ctx) return;
const labels = Object.keys(dataObject).filter(key => dataObject[key] > 0); // Only show categories with data
const data = labels.map(key => dataObject[key]);
const chartColors = ['#7E57C2', '#26C6DA', '#FFCA28', '#66BB6A', '#EF5350', '#AB47BC', '#FFA726', '#42A5F5', '#26A69A', '#EC407A'];
const backgroundColors = labels.map((_, i) => chartColors[i % chartColors.length]);
let existingChartInstance;
if (canvasId === 'woa-category-count-chart') existingChartInstance = window.woaChartCatCountInst;
else if (canvasId === 'woa-category-time-chart') existingChartInstance = window.woaChartCatTimeInst;
else if (canvasId === 'woa-impact-count-chart') existingChartInstance = window.woaChartImpCountInst;
else if (canvasId === 'woa-impact-time-chart') existingChartInstance = window.woaChartImpTimeInst;
if (existingChartInstance) existingChartInstance.destroy();
const chartConfig = {
type: type,
data: { labels: labels, datasets: [{ label: chartLabel, data: data, backgroundColor: backgroundColors, borderColor: '#fff', borderWidth: 1 }] },
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { position: type==='pie'?'right':'top', labels:{boxWidth:15, font:{size:10}} },
tooltip: {
callbacks: {
label: function(context) {
let label = context.label || '';
if (label) label += ': ';
if (context.parsed !== null) {
label += context.parsed.toFixed(1) + (chartLabel.toLowerCase().includes("time") ? ' hrs' : '');
if (type === 'pie') {
const total = context.dataset.data.reduce((a,b) => a+b, 0);
const percentage = total > 0 ? (context.parsed / total * 100).toFixed(1) : 0;
label += ` (${percentage}%)`;
}
}
return label;
}
}
}
}
}
};
if(type === 'bar') {
chartConfig.options.scales = { y: { beginAtZero: true, title: {display: true, text: chartLabel.includes("Time") ? "Hours" : "Count"} } };
}
const newChartInstance = new Chart(ctx.getContext('2d'), chartConfig);
if (canvasId === 'woa-category-count-chart') window.woaChartCatCountInst = newChartInstance;
else if (canvasId === 'woa-category-time-chart') window.woaChartCatTimeInst = newChartInstance;
else if (canvasId === 'woa-impact-count-chart') window.woaChartImpCountInst = newChartInstance;
else if (canvasId === 'woa-impact-time-chart') window.woaChartImpTimeInst = newChartInstance;
}
function woaRenderLineChart(canvasId, dataPoints, chartLabel) { // dataPoints = [{x: Date, y: value}]
const ctx = document.getElementById(canvasId);
if (!ctx) return;
if (window.woaChartTrendInstance) window.woaChartTrendInstance.destroy();
window.woaChartTrendInstance = new Chart(ctx.getContext('2d'), {
type: 'line',
data: {
datasets: [{
label: chartLabel,
data: dataPoints,
borderColor: 'var(--primary-color)',
backgroundColor: 'rgba(126, 87, 194, 0.1)',
tension: 0.1,
fill: true
}]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: {
x: { type: 'time', time: { unit: 'day', tooltipFormat: 'MMM dd, yyyy', displayFormats: {'day': 'MMM dd'} } },
y: { beginAtZero: true, title: { display: true, text: "Output Count" } }
},
plugins: { legend: { display: false } }
}
});
}
// PDF Download
function woaDownloadPDF() {
const dashboardDisplayElement = document.getElementById('woa-dashboard-display');
if (!dashboardDisplayElement || dashboardDisplayElement.innerHTML.includes("Log work outputs")) {
alert("Please generate the dashboard first.");
return;
}
const analysisStartDate = document.getElementById('woa-analysis-start-date').value;
const analysisEndDate = document.getElementById('woa-analysis-end-date').value;
let pdfHTML = `
Work Output Analysis Report
`;
pdfHTML += `
Analysis Period: ${analysisStartDate} to ${analysisEndDate}
`;
const metricsCardContent = dashboardDisplayElement.querySelector('.woa-metrics-card');
if(metricsCardContent) pdfHTML += `
Key Metrics
${metricsCardContent.innerHTML.replace(/
.*<\/h4>/, '')}`; // Remove h4 for PDF
const chartsToInclude = [
{instance: window.woaChartCatCountInst, title: "Output Count by Category"},
{instance: window.woaChartCatTimeInst, title: "Time Spent by Category"},
{instance: window.woaChartImpCountInst, title: "Output Count by Impact"},
{instance: window.woaChartImpTimeInst, title: "Time Spent by Impact"},
{instance: window.woaChartTrendInst, title: "Output Trend (Count over Time)"}
];
chartsToInclude.forEach(chartInfo => {
if(chartInfo.instance){
pdfHTML += `
${chartInfo.title}
`;
}
});
const insightsCardContent = dashboardDisplayElement.querySelector('.woa-insights-card');
if(insightsCardContent) pdfHTML += `${insightsCardContent.innerHTML.replace(/.*<\/h4>/, 'Analysis Insights
')}`;
const filteredOutputsForPDF = woaLoggedOutputs.filter(out => {
const outDateOnly = out.dateObj.toISOString().split('T')[0];
return outDateOnly >= analysisStartDate && outDateOnly <= analysisEndDate;
});
if (filteredOutputsForPDF.length > 0) {
pdfHTML += `
Detailed Output Log (${analysisStartDate} to ${analysisEndDate})
`;
pdfHTML += `| Date | Output | Category | Qty | Time (hrs) | Impact | Complexity |
`;
filteredOutputsForPDF.forEach(out => {
pdfHTML += `
| ${out.dateObj.toLocaleDateString()} | ${out.name} | ${out.category} |
${out.quantity} | ${out.timeSpent.toFixed(1)} |
${out.impact} | ${out.complexity || 'N/A'} |
`;
});
pdfHTML += `
`;
}
const pdfContainer = document.getElementById('woa-report-content-for-pdf');
pdfContainer.innerHTML = pdfHTML;
pdfContainer.querySelectorAll('.woa-impact-tag').forEach(tag => {
if (tag.classList.contains('impact-High')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--impact-high-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--impact-high-text');}
else if (tag.classList.contains('impact-Medium')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--impact-medium-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--impact-medium-text');}
else if (tag.classList.contains('impact-Low')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--impact-low-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--impact-low-text');}
});
const opt = {
margin: [0.5, 0.3, 0.5, 0.3], filename: `Work_Output_Analysis_${analysisStartDate}_to_${analysisEndDate}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, logging: false, scrollX:0, scrollY: -window.scrollY, windowWidth: pdfContainer.scrollWidth + 100 },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' },
pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
};
html2pdf().from(pdfContainer).set(opt).save().then(() => {
pdfContainer.innerHTML = '';
}).catch(err => {
console.error("Error generating PDF:", err);
pdfContainer.innerHTML = '';
alert("An error occurred while generating the PDF. Check console for details.");
});
}