Smart Work Fatigue Monitor

Smart Work Fatigue Monitor

New Check-in

Work Context

Self-Assessment

Symptoms Checklist

Physical Symptoms:
Mental Symptoms:

Recent Check-in Log

DateWork(h)BrkStressEnergySleepSymptomsNotesAct

Note: Log data is stored in your browser's LocalStorage.

Dashboard Filters

Current Fatigue Indicator (based on latest entry in range)

N/A

Log check-ins and select a date range to view the dashboard.

Key Insights & Recommendations

  • No data to analyze.

No logs found for the selected date range.

'; document.getElementById('swfm-fatigue-indicator-label').textContent = 'N/A'; document.getElementById('swfm-fatigue-needle').style.transform = `rotate(-90deg)`; Object.values(swfmChartInstances).forEach(chart => chart ? chart.destroy() : null); return; } // Update Fatigue Indicator (based on latest log in range) const latestLog = filteredLogs.length > 0 ? filteredLogs[filteredLogs.length - 1] : null; const fatigueScore = swfmCalculateFatigueScore(latestLog); const needleRotation = (fatigueScore / 100) * 180 - 90; // -90 (low) to +90 (high) document.getElementById('swfm-fatigue-needle').style.transform = `rotate(${needleRotation}deg)`; const indicatorLabelEl = document.getElementById('swfm-fatigue-indicator-label'); if (fatigueScore >= 70) { indicatorLabelEl.textContent = "High Fatigue Risk"; indicatorLabelEl.className = 'swfm-indicator-label fatigue-high';} else if (fatigueScore >= 40) { indicatorLabelEl.textContent = "Moderate Fatigue"; indicatorLabelEl.className = 'swfm-indicator-label fatigue-moderate';} else { indicatorLabelEl.textContent = "Low Fatigue / Good State"; indicatorLabelEl.className = 'swfm-indicator-label fatigue-low';} // Prepare chart data const stressData = filteredLogs.map(log => ({x: log.datetime, y: log.stressLevel})); const energyData = filteredLogs.map(log => ({x: log.datetime, y: log.energyLevel})); const sleepData = filteredLogs.map(log => ({x: log.datetime, y: log.sleepQuality})); // Symptom frequency let symptomCounts = {}; filteredLogs.forEach(log => { [...log.physicalSymptoms, ...log.mentalSymptoms].forEach(symptom => { symptomCounts[symptom] = (symptomCounts[symptom] || 0) + 1; }); }); const sortedSymptoms = Object.entries(symptomCounts).sort((a,b) => b[1] - a[1]).slice(0,5); // Top 5 // Dashboard HTML (Chart Canvases) document.getElementById('swfm-dashboard-display').innerHTML = `

Stress Level Over Time

Energy Level Over Time

Sleep Quality Over Time

Top Reported Symptoms

`; // Render Charts swfmRenderLineChart('swfm-stress-chart', stressData, 'Stress Level (1-5)', 'var(--danger-color)'); swfmRenderLineChart('swfm-energy-chart', energyData, 'Energy Level (1-5)', 'var(--success-color)'); swfmRenderLineChart('swfm-sleep-chart', sleepData, 'Sleep Quality (1-5)', 'var(--primary-color)'); swfmRenderBarChart('swfm-symptoms-chart', sortedSymptoms.map(s=>s[0]), sortedSymptoms.map(s=>s[1]), 'Symptom Frequency', 'var(--accent-color)'); // Generate Insights const insightsListEl = document.getElementById('swfm-insights-list'); insightsListEl.innerHTML = ''; let insightsGenerated = 0; if (filteredLogs.length > 0) { const avgStress = filteredLogs.reduce((sum,l)=>sum+l.stressLevel,0)/filteredLogs.length; const avgEnergy = filteredLogs.reduce((sum,l)=>sum+l.energyLevel,0)/filteredLogs.length; const avgSleep = filteredLogs.reduce((sum,l)=>sum+l.sleepQuality,0)/filteredLogs.length; const avgBreaks = filteredLogs.reduce((sum,l)=>sum+l.breaksTaken,0)/filteredLogs.length; if(avgStress >= 4) { insightsListEl.innerHTML += `
  • Average stress level is high (${avgStress.toFixed(1)}/5). Consider stress management techniques or workload review.
  • `; insightsGenerated++;} if(avgEnergy <= 2) { insightsListEl.innerHTML += `
  • Average energy level is low (${avgEnergy.toFixed(1)}/5). Ensure sufficient rest, nutrition, and breaks.
  • `; insightsGenerated++;} if(avgSleep <= 2) { insightsListEl.innerHTML += `
  • Average sleep quality is poor (${avgSleep.toFixed(1)}/5). Prioritize sleep hygiene for better recovery and daytime energy.
  • `; insightsGenerated++;} if(avgBreaks < 2 && filteredLogs.some(l => l.workDuration > 4)) { insightsListEl.innerHTML += `
  • Average breaks taken is low (${avgBreaks.toFixed(1)}). Regular short breaks (5-10 min every hour) can significantly reduce fatigue during long work periods.
  • `; insightsGenerated++;} if(symptomCounts["Eye Strain"] && symptomCounts["Eye Strain"] > filteredLogs.length / 3) { insightsListEl.innerHTML += `
  • 'Eye Strain' is frequently reported. Remember the 20-20-20 rule: every 20 minutes, look at something 20 feet away for 20 seconds. Ensure proper screen ergonomics.
  • `; insightsGenerated++;} if(symptomCounts["Headache"] && symptomCounts["Headache"] > filteredLogs.length / 3) { insightsListEl.innerHTML += `
  • 'Headache' is frequently reported. Ensure proper hydration, screen brightness, and consider if stress or eye strain are contributing factors.
  • `; insightsGenerated++;} } if(insightsGenerated === 0 && filteredLogs.length > 0) insightsListEl.innerHTML += `
  • Review your trend charts for personal patterns. Consistent logging helps identify what impacts your fatigue levels.
  • `; else if (filteredLogs.length === 0) insightsListEl.innerHTML += `
  • No data in the selected range to provide insights.
  • `; } function swfmRenderLineChart(canvasId, dataPoints, label, color) { // dataPoints = [{x: Date, y: value}] const ctx = document.getElementById(canvasId); if (!ctx) return; if (swfmChartInstances[canvasId]) swfmChartInstances[canvasId].destroy(); swfmChartInstances[canvasId] = new Chart(ctx.getContext('2d'), { type: 'line', data: { datasets: [{ label: label, data: dataPoints, borderColor: color, tension: 0.1, fill: false }] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', time: { unit: 'day', tooltipFormat: 'MMM dd, yyyy', displayFormats: {'day': 'MMM dd'} } }, y: { beginAtZero: false, suggestedMin: 1, suggestedMax: (label.includes('Sleep') || label.includes('Energy') || label.includes('Stress')) ? 5 : undefined, title: {display:true, text:label.split('(')[0].trim()}} } } }); } function swfmRenderBarChart(canvasId, labels, data, chartLabel, color) { const ctx = document.getElementById(canvasId); if (!ctx) return; if (swfmChartInstances[canvasId]) swfmChartInstances[canvasId].destroy(); swfmChartInstances[canvasId] = new Chart(ctx.getContext('2d'), { type: 'bar', data: { labels: labels, datasets: [{ label: chartLabel, data: data, backgroundColor: color }] }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { beginAtZero: true, title: {display:true, text:"Frequency"} } } } }); } // PDF Download function swfmDownloadPDF() { const dashboardDisplayElement = document.getElementById('swfm-dashboard-display'); if (!dashboardDisplayElement || dashboardDisplayElement.innerHTML.includes("Log check-ins") || swfmFatigueLogs.length === 0) { alert("Please log some data and analyze trends first."); return; } const analysisStartDate = document.getElementById('swfm-analysis-start-date').value; const analysisEndDate = document.getElementById('swfm-analysis-end-date').value; const fatigueIndicatorLabel = document.getElementById('swfm-fatigue-indicator-label').textContent; const fatigueNeedleTransform = document.getElementById('swfm-fatigue-needle').style.transform; let pdfHTML = `

    Work Fatigue Monitor Report

    `; pdfHTML += `

    Analysis Period: ${analysisStartDate} to ${analysisEndDate}

    `; pdfHTML += `

    Current Fatigue Indicator

    ${fatigueIndicatorLabel}
    `; const chartsToInclude = [ {instance: swfmChartInstances['swfm-stress-chart'], title: "Stress Level Over Time"}, {instance: swfmChartInstances['swfm-energy-chart'], title: "Energy Level Over Time"}, {instance: swfmChartInstances['swfm-sleep-chart'], title: "Sleep Quality Over Time"}, {instance: swfmChartInstances['swfm-symptoms-chart'], title: "Top Reported Symptoms"} ]; chartsToInclude.forEach(chartInfo => { if(chartInfo.instance){ pdfHTML += `

    ${chartInfo.title}

    `; } }); const insightsContent = document.getElementById('swfm-insights-list').outerHTML; if(insightsContent) pdfHTML += `

    Key Insights & Recommendations

    ${insightsContent}
    `; const filteredLogsForPDF = swfmFatigueLogs.filter(log => { const logDateOnly = log.datetime.toISOString().split('T')[0]; return logDateOnly >= analysisStartDate && logDateOnly <= analysisEndDate; }); if (filteredLogsForPDF.length > 0) { pdfHTML += `

    Detailed Log (${analysisStartDate} to ${analysisEndDate})

    `; pdfHTML += ``; filteredLogsForPDF.forEach(log => { const symptomsSummary = [...log.physicalSymptoms, ...log.mentalSymptoms]; pdfHTML += ``; }); pdfHTML += `
    DateWork(h)BrkStressEnergySleepSymptomsNotes
    ${log.datetime.toLocaleString([],{dateStyle:'short',timeStyle:'short'})}${log.workDuration.toFixed(1)}${log.breaksTaken} ${log.stressLevel}${log.energyLevel}${log.sleepQuality} ${symptomsSummary.length > 0 ? symptomsSummary.join(', ').substring(0,30)+'...' : 'None'} ${log.notes.substring(0,30)}${log.notes.length > 30 ? '...' : ''}
    `; } const pdfContainer = document.getElementById('swfm-report-content-for-pdf'); pdfContainer.innerHTML = pdfHTML; // Manually apply gradient to gauge for PDF because CSS gradients in html2pdf are tricky const gaugePdf = pdfContainer.querySelector('.swfm-indicator-gauge'); if(gaugePdf) { const c = getComputedStyle(document.documentElement); gaugePdf.style.backgroundImage = `linear-gradient(to right, ${c.getPropertyValue('--success-color')}, ${c.getPropertyValue('--warning-color')}, ${c.getPropertyValue('--danger-color')})`; } const opt = { margin: [0.5, 0.4, 0.5, 0.4], filename: `Work_Fatigue_Report_${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."); }); }
    Scroll to Top