No activities logged. Please log activities in Tab 1 to see the dashboard.
';
return;
}
const startDateStr = document.getElementById('whod-analysis-start-date').value;
const endDateStr = document.getElementById('whod-analysis-end-date').value;
if(!startDateStr || !endDateStr) {
// This can happen if called before inputs are fully set, like on initial load before date defaults are fully applied.
// It might be better to call this explicitly after defaults are set and tab is opened.
// For now, if date inputs are not ready, don't proceed.
return;
}
const startDate = new Date(startDateStr + "T00:00:00");
const endDate = new Date(endDateStr + "T23:59:59");
const filteredActivities = whodLoggedActivities.filter(act => act.dateObj >= startDate && act.dateObj <= endDate);
if (filteredActivities.length === 0) {
document.getElementById('whod-dashboard-display').innerHTML = '
No activities logged in the selected date range.
';
if(whodChartCategoryInstance) whodChartCategoryInstance.destroy();
if(whodChartValueInstance) whodChartValueInstance.destroy();
return;
}
let timeByCategory = {};
let timeByValue = { High: 0, Medium: 0, Low: 0 };
let totalLoggedMinutes = 0;
WHOD_ACTIVITY_CATEGORIES.forEach(cat => timeByCategory[cat] = 0); // Initialize categories
filteredActivities.forEach(act => {
timeByCategory[act.category] = (timeByCategory[act.category] || 0) + act.duration;
timeByValue[act.value] = (timeByValue[act.value] || 0) + act.duration;
totalLoggedMinutes += act.duration;
});
const totalHoursLogged = (totalLoggedMinutes / 60).toFixed(1);
let focusedWorkMinutes = timeByCategory["Focused Work"] || 0;
let meetingMinutes = timeByCategory["Meetings"] || 0;
let highValueMinutes = timeByValue["High"] || 0;
// Build Dashboard HTML
let dashboardHTML = `
Key Metrics (${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()})
- Total Hours Logged: ${totalHoursLogged} hrs
- Focused Work: ${(focusedWorkMinutes / 60).toFixed(1)} hrs (${totalLoggedMinutes > 0 ? (focusedWorkMinutes/totalLoggedMinutes*100).toFixed(0) : 0}%)
- Meetings: ${(meetingMinutes / 60).toFixed(1)} hrs (${totalLoggedMinutes > 0 ? (meetingMinutes/totalLoggedMinutes*100).toFixed(0) : 0}%)
- High Value Activities: ${(highValueMinutes / 60).toFixed(1)} hrs (${totalLoggedMinutes > 0 ? (highValueMinutes/totalLoggedMinutes*100).toFixed(0) : 0}%)
Time Allocation by Category
Time Allocation by Perceived Value
Optimization Insights & Recommendations
`;
document.getElementById('whod-dashboard-display').innerHTML = dashboardHTML;
// Generate Charts
whodRenderPieChart('whod-category-chart', timeByCategory, 'Time by Category');
whodRenderPieChart('whod-value-chart', timeByValue, 'Time by Value');
// Generate Insights
const insightsListEl = document.getElementById('whod-insights-list');
insightsListEl.innerHTML = ''; // Clear previous
let insightsGenerated = 0;
if (totalLoggedMinutes > 0) {
if ((meetingMinutes / totalLoggedMinutes) > 0.4) { insightsListEl.innerHTML += `
Meetings constitute over 40% of your logged time. Review if all are essential or could be optimized (e.g., shorter, fewer attendees, email instead).`; insightsGenerated++;}
if ((timeByCategory["Admin"] / totalLoggedMinutes) > 0.2 && timeByValue["Low"] > (timeByCategory["Admin"] * 0.5) ) { insightsListEl.innerHTML += `
A significant portion of time is on Admin tasks, potentially of low value. Explore batching these tasks or delegation.`; insightsGenerated++;}
if ((focusedWorkMinutes / totalLoggedMinutes) < 0.3) { insightsListEl.innerHTML += `
Focused work is less than 30% of logged time. Try scheduling dedicated 'deep work' blocks and minimizing distractions during them.`; insightsGenerated++;}
if ((timeByCategory["Interruptions"] / totalLoggedMinutes) > 0.1) { insightsListEl.innerHTML += `
Interruptions account for over 10% of time. Identify common sources and create strategies to reduce them (e.g., status messages, focused time slots).`; insightsGenerated++;}
if ((highValueMinutes / totalLoggedMinutes) < 0.4) { insightsListEl.innerHTML += `
Less than 40% of time is spent on high-value activities. Align daily tasks with key goals to maximize impact. Consider if low-value tasks can be reduced.`; insightsGenerated++;}
}
if(insightsGenerated === 0 && totalLoggedMinutes > 0) {
insightsListEl.innerHTML += `
Your time distribution seems relatively balanced based on common heuristics. Continue to monitor and adjust as needed!`;
} else if (totalLoggedMinutes === 0) {
insightsListEl.innerHTML += `
No activities logged in this period to provide insights.`;
}
}
function whodRenderPieChart(canvasId, dataObject, chartLabel) {
const ctx = document.getElementById(canvasId);
if (!ctx) return;
const labels = Object.keys(dataObject).filter(key => dataObject[key] > 0);
const data = labels.map(key => dataObject[key]);
// Predefined color palette for consistency in charts
const chartColors = [
'#42A5F5', '#66BB6A', '#FFCA28', '#EF5350', '#AB47BC', '#7E57C2',
'#26C6DA', '#FF7043', '#8D6E63', '#78909c'
];
const backgroundColors = labels.map((_, i) => chartColors[i % chartColors.length]);
let existingChartInstance = canvasId === 'whod-category-chart' ? whodChartCategoryInstance : whodChartValueInstance;
if (existingChartInstance) {
existingChartInstance.destroy();
}
const chartInstance = new Chart(ctx.getContext('2d'), {
type: 'pie',
data: {
labels: labels,
datasets: [{
label: chartLabel,
data: data,
backgroundColor: backgroundColors,
borderColor: '#fff',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'right', 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 / 60).toFixed(1) + ' hrs'; // Show hours in tooltip
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 (canvasId === 'whod-category-chart') whodChartCategoryInstance = chartInstance;
else whodChartValueInstance = chartInstance;
}
// PDF Download
function whodDownloadPDF() {
const dashboardDisplayElement = document.getElementById('whod-dashboard-display');
if (!dashboardDisplayElement || dashboardDisplayElement.innerHTML.includes("Log activities and select")) {
alert("Please generate the dashboard first by logging activities and selecting a date range.");
return;
}
const analysisStartDate = document.getElementById('whod-analysis-start-date').value;
const analysisEndDate = document.getElementById('whod-analysis-end-date').value;
let pdfHTML = `
Work Hour Optimization Report
`;
pdfHTML += `
Analysis Period: ${analysisStartDate} to ${analysisEndDate}
`;
// Key Metrics (extract from generated dashboard)
const metricsCardContent = dashboardDisplayElement.querySelector('.whod-metrics-card');
if(metricsCardContent) pdfHTML += `
Key Metrics
${metricsCardContent.innerHTML}`;
// Charts (as images)
if (whodChartCategoryInstance) {
pdfHTML += `
Time Allocation by Category
`;
}
if (whodChartValueInstance) {
pdfHTML += `
Time Allocation by Perceived Value
`;
}
// Insights
const insightsCardContent = dashboardDisplayElement.querySelector('.whod-insights-card');
if(insightsCardContent) pdfHTML += `
${insightsCardContent.innerHTML}`;
// Optional: Detailed Activity Log for the period
const filteredActivities = whodLoggedActivities.filter(act => {
const actDateOnly = act.dateObj.toISOString().split('T')[0];
return actDateOnly >= analysisStartDate && actDateOnly <= analysisEndDate;
});
if (filteredActivities.length > 0) {
pdfHTML += `
Detailed Activity Log (${analysisStartDate} to ${analysisEndDate})
`;
pdfHTML += `
| Date | Activity | Category | Start | End | Duration (min) | Value |
`;
filteredActivities.forEach(act => {
pdfHTML += `
| ${act.dateObj.toLocaleDateString()} | ${act.name} | ${act.category} |
${act.startTime} | ${act.endTime} | ${act.duration} |
${act.value} |
`;
});
pdfHTML += `
`;
}
const pdfContainer = document.getElementById('whod-report-content-for-pdf');
pdfContainer.innerHTML = pdfHTML;
// Re-apply styles for value tags in PDF dynamically
pdfContainer.querySelectorAll('.whod-value-tag').forEach(tag => {
if (tag.classList.contains('value-High')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--value-high-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--value-high-text');}
else if (tag.classList.contains('value-Medium')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--value-medium-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--value-medium-text');}
else if (tag.classList.contains('value-Low')) { tag.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--value-low-bg'); tag.style.color = getComputedStyle(document.documentElement).getPropertyValue('--value-low-text');}
});
const opt = {
margin: [0.5, 0.4, 0.5, 0.4], filename: `WorkHour_Optimization_Dashboard_${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 }, // Ensure content fits
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.");
});
}