Work Rhythm Optimizer
Work Rhythm Optimizer

My Typical Daily Profile (Optional Context)

Meal Times:


Log Current Energy Level

Today's Energy Log Entries:

  • No entries for today yet.

Analyze My Energy Rhythm

Full Energy Log (Current Session)

  • No entries logged yet.
'; document.getElementById('wro-suggestions-list').innerHTML = '
  • Not enough data to generate suggestions.
  • '; document.getElementById('wro-analysis-output-section').style.display = 'block'; return; } // By Hour of Day const energyByHour = Array(24).fill(null).map(() => ({ sum: 0, count: 0, avg: 0 })); filteredLogs.forEach(log => { const hour = log.timestamp.getHours(); energyByHour[hour].sum += log.energyLevel; energyByHour[hour].count++; }); energyByHour.forEach(h => { if (h.count > 0) h.avg = h.sum / h.count; }); wro_renderBarChart('wro-hourly-focus-chart', energyByHour, (hour) => `${String(hour).padStart(2, '0')}:00`); // By Day of Week const energyByDay = Array(7).fill(null).map(() => ({ sum: 0, count: 0, avg: 0 })); const DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; filteredLogs.forEach(log => { const day = log.timestamp.getDay(); // 0=Sun, 6=Sat energyByDay[day].sum += log.energyLevel; energyByDay[day].count++; }); energyByDay.forEach(d => { if (d.count > 0) d.avg = d.sum / d.count; }); wro_renderBarChart('wro-daily-focus-chart', energyByDay, (dayIndex) => DAY_NAMES[dayIndex]); // Suggestions wro_generateSuggestions(energyByHour, energyByDay); document.getElementById('wro-analysis-output-section').style.display = 'block'; } function wro_renderBarChart(elementId, dataArray, labelFormatter) { const chartDiv = document.getElementById(elementId); chartDiv.innerHTML = ''; const maxAvg = Math.max(...dataArray.map(d => d.avg), 0) || 1; // Ensure maxAvg is at least 1 to avoid div by zero dataArray.forEach((item, index) => { if (item.count > 0) { const percentage = maxAvg > 0 ? (item.avg / maxAvg * 100) : 0; let barClass = "energy-medium"; if (item.avg >= 4) barClass = "energy-high"; else if (item.avg < 2.5) barClass = "energy-low"; const barItem = document.createElement('div'); barItem.className = 'bar-item'; barItem.innerHTML = ` ${labelFormatter(index)}
    ${item.avg.toFixed(1)}
    `; chartDiv.appendChild(barItem); } }); if (chartDiv.innerHTML === '') chartDiv.innerHTML = "

    Not enough data for this chart.

    "; } function wro_generateSuggestions(hourlyData, dailyData) { const suggestionsUl = document.getElementById('wro-suggestions-list'); suggestionsUl.innerHTML = ''; let suggestions = []; // Hourly suggestions let peakStart = -1; for(let i=0; i < 24; i++) { if (hourlyData[i].avg >= 4) { // High focus if (peakStart === -1) peakStart = i; } else { if (peakStart !== -1) { suggestions.push(`You seem to have peak energy/focus between ${peakStart}:00 and ${i}:00. Schedule demanding tasks here.`); peakStart = -1; } } } if(peakStart !== -1) suggestions.push(`You seem to have peak energy/focus between ${peakStart}:00 and 24:00.`); let lowStart = -1; for(let i=0; i < 24; i++) { if (hourlyData[i].count > 0 && hourlyData[i].avg <= 2) { // Low focus if (lowStart === -1) lowStart = i; } else { if (lowStart !== -1) { suggestions.push(`Energy might be lower between ${lowStart}:00 and ${i}:00. Consider breaks or lighter tasks.`); lowStart = -1; } } } if(lowStart !== -1) suggestions.push(`Energy might be lower between ${lowStart}:00 and 24:00.`); // Daily suggestions const productiveDays = []; const lessProductiveDays = []; const DAY_NAMES_FULL = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; dailyData.forEach((day, index) => { if (day.count > 0) { if (day.avg >= 3.5) productiveDays.push(DAY_NAMES_FULL[index]); else if (day.avg <= 2.5) lessProductiveDays.push(DAY_NAMES_FULL[index]); } }); if(productiveDays.length > 0) suggestions.push(`You tend to have higher energy on: ${productiveDays.join(', ')}.`); if(lessProductiveDays.length > 0) suggestions.push(`Consider lighter schedules or more breaks on: ${lessProductiveDays.join(', ')}.`); if (suggestions.length === 0) { suggestionsUl.innerHTML = '
  • Log more data (e.g., over several days) to get personalized suggestions.
  • '; } else { suggestions.forEach(s => suggestionsUl.innerHTML += `
  • ${s}
  • `); } } // PDF and Clear All function wro_clearAllEnergyLogs() { if (confirm("Are you sure you want to clear ALL logged energy entries? Profile settings will remain.")) { wro_energyLogs = []; wro_logIdCounter = 0; wro_renderTodaysLog(); wro_renderFullLog(); wro_analyzeEnergyLogs(); // Will show empty analysis } } function wro_downloadPDF() { if (!tpc_jsPDF_loaded_WRO || !tpc_autoTable_loaded_WRO) { alert("WRO Error: PDF libraries not loaded."); return; } const fromDateStr = document.getElementById('wro-analyze-from-date').value; const toDateStr = document.getElementById('wro-analyze-to-date').value; const fromDate = fromDateStr ? new Date(fromDateStr + "T00:00:00") : null; const toDate = toDateStr ? new Date(toDateStr + "T23:59:59") : null; const filteredLogs = wro_energyLogs.filter(log => log.timestamp >= fromDate && log.timestamp <= toDate); if (filteredLogs.length === 0) { alert("No data in the selected period to export."); return; } const doc = new TPC_jsPDF_WRO(); const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const darkColor = getComputedStyle(document.documentElement).getPropertyValue('--dark-color').trim(); let finalY = 15; const reportDateStr = new Date().toLocaleDateString(); doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text("Work Rhythm Analysis Report", doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center' }); finalY += 8; doc.setFontSize(10); doc.setTextColor(darkColor); doc.text(`Report Date: ${reportDateStr} | Analysis Period: ${fromDate.toLocaleDateString()} to ${toDate.toLocaleDateString()}`, doc.internal.pageSize.getWidth() / 2, finalY, { align: 'center'}); finalY += 10; // Profile Summary doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("My Daily Profile:", 14, finalY); finalY += 6; doc.setFontSize(9); doc.setTextColor(darkColor); doc.text(`Wake-up: ${wro_profile.wakeUpTime}, Bedtime: ${wro_profile.bedTime}`, 14, finalY); finalY += 5; doc.text(`Breakfast: ${wro_profile.meals.breakfast.start}-${wro_profile.meals.breakfast.end} | Lunch: ${wro_profile.meals.lunch.start}-${wro_profile.meals.lunch.end} | Dinner: ${wro_profile.meals.dinner.start}-${wro_profile.meals.dinner.end}`, 14, finalY); finalY+=7; // Analysis Summary (Tables for Hourly and Daily Avg Focus) // Hourly const energyByHour = Array(24).fill(null).map(() => ({ sum: 0, count: 0, avg: 0 })); filteredLogs.forEach(log => { const hour = log.timestamp.getHours(); energyByHour[hour].sum += log.energyLevel; energyByHour[hour].count++; }); energyByHour.forEach(h => { if (h.count > 0) h.avg = h.sum / h.count; }); const hourlyTableBody = energyByHour.map((data, hour) => data.count > 0 ? [`${String(hour).padStart(2,'0')}:00`, data.avg.toFixed(1), data.count] : null).filter(r => r); if(hourlyTableBody.length > 0){ doc.setFontSize(11); doc.setTextColor(primaryColor); doc.text("Average Energy by Hour of Day:", 14, finalY); finalY+=5; doc.autoTable({ startY: finalY, head: [['Hour', 'Avg. Energy (1-5)', 'Log Count']], body: hourlyTableBody, theme: 'striped', headStyles:{fillColor:primaryColor, textColor:'#fff', fontSize:9}, styles:{fontSize:8}}); finalY = doc.lastAutoTable.finalY + 7; } // Daily const energyByDay = Array(7).fill(null).map(() => ({ sum: 0, count: 0, avg: 0 })); const DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; filteredLogs.forEach(log => { const day = log.timestamp.getDay(); energyByDay[day].sum += log.energyLevel; energyByDay[day].count++; }); energyByDay.forEach(d => { if (d.count > 0) d.avg = d.sum / d.count; }); const dailyTableBody = energyByDay.map((data, day) => data.count > 0 ? [DAY_NAMES[day], data.avg.toFixed(1), data.count] : null).filter(r => r); if(dailyTableBody.length > 0){ if (finalY > 240) { doc.addPage(); finalY = 15; } doc.setFontSize(11); doc.setTextColor(primaryColor); doc.text("Average Energy by Day of Week:", 14, finalY); finalY+=5; doc.autoTable({ startY: finalY, head: [['Day', 'Avg. Energy (1-5)', 'Log Count']], body: dailyTableBody, theme: 'striped', headStyles:{fillColor:primaryColor, textColor:'#fff', fontSize:9}, styles:{fontSize:8}}); finalY = doc.lastAutoTable.finalY + 7; } // Suggestions for PDF const suggestionsList = document.getElementById('wro-suggestions-list').innerText; if (finalY > 250) { doc.addPage(); finalY = 15; } doc.setFontSize(11); doc.setTextColor(primaryColor); doc.text("Personalized Suggestions:", 14, finalY); finalY+=5; doc.setFontSize(9); doc.setTextColor(darkColor); const suggestionLines = doc.splitTextToSize(suggestionsList, doc.internal.pageSize.width - 28); doc.text(suggestionLines, 14, finalY); finalY += suggestionLines.length * 4 + 7; // Detailed Log for PDF if (finalY > 220) { doc.addPage(); finalY = 15; } doc.setFontSize(11); doc.setTextColor(primaryColor); doc.text("Detailed Energy Log (Filtered Period):", 14, finalY); finalY += 6; const logTableBody = filteredLogs.map(entry => [ entry.timestamp.toLocaleString(), entry.energyLevel, entry.activity || '-' ]); doc.autoTable({ startY: finalY, head: [['Timestamp', 'Energy (1-5)', 'Activity Logged']], body: logTableBody, theme: 'grid', headStyles: { fillColor: primaryColor, textColor: '#ffffff', fontSize: 9 }, styles: { fontSize: 8, cellPadding: 1.5, overflow: 'linebreak' } }); doc.save(`Work_Rhythm_Analysis_${new Date().toISOString().slice(0,10)}.pdf`); }
    Scroll to Top