${type}: Avg. Focus ${data.avgFocus.toFixed(1)}/5 (from ${data.count} logs)
`;
swro_userPatterns.taskTypeFocusCorrelation[type] = data.avgFocus;
});
swro_userPatterns.lastAnalyzedRange = {start: startDateStr, end: endDateStr};
document.getElementById('swro_patternsResultsContainer').style.display = 'block';
}
// --- Tab 3: Optimize Routine ---
function swro_addSchedulableTask() {
const name = document.getElementById('swro_scheduleTaskName').value.trim();
const estHours = document.getElementById('swro_scheduleTaskHours').value;
const estMinutesIn = document.getElementById('swro_scheduleTaskMinutes').value;
const estTotalMinutes = swro_parseHHMMToMinutes(estHours, estMinutesIn);
const priority = document.getElementById('swro_scheduleTaskPriority').value;
const taskType = document.getElementById('swro_scheduleTaskType').value;
const deadline = document.getElementById('swro_scheduleTaskDeadline').value || null;
if (!name || estTotalMinutes <= 0) { alert("Task name and valid estimated duration are required."); return; }
swro_tasksToSchedule.push({id: Date.now(), name, estMinutes: estTotalMinutes, priority, taskType, deadline});
document.getElementById('swro_scheduleTaskName').value = '';
document.getElementById('swro_scheduleTaskHours').value = '';
document.getElementById('swro_scheduleTaskMinutes').value = '';
document.getElementById('swro_scheduleTaskDeadline').value = '';
swro_renderSchedulableTasksList();
}
function swro_removeSchedulableTask(taskId) {
swro_tasksToSchedule = swro_tasksToSchedule.filter(t => t.id != taskId);
swro_renderSchedulableTasksList();
}
function swro_renderSchedulableTasksList() {
const listUl = document.getElementById('swro_schedulableTasksList');
listUl.innerHTML = '';
if (swro_tasksToSchedule.length === 0) { listUl.innerHTML = "
No tasks added for planning yet."; return; }
swro_tasksToSchedule.forEach(task => {
const li = document.createElement('li');
li.textContent = `${task.name} (${swro_formatMinutesToHHMM(task.estMinutes)}) - Prio: ${task.priority}, Type: ${task.taskType}`;
const removeBtn = document.createElement('button');
removeBtn.type = 'button'; removeBtn.textContent = 'Remove';
removeBtn.className = 'swro-button swro-delete-button swro-small-button';
removeBtn.onclick = () => swro_removeSchedulableTask(task.id);
li.appendChild(removeBtn);
listUl.appendChild(li);
});
}
function swro_generateOptimizedRoutine() {
const optimizeDate = document.getElementById('swro_optimizeDate').value;
const workStartTimeStr = document.getElementById('swro_optimizeWorkStart').value;
const workEndTimeStr = document.getElementById('swro_optimizeWorkEnd').value;
if (!optimizeDate || !workStartTimeStr || !workEndTimeStr) { alert("Please set date and work start/end times for optimization."); return; }
if (swro_tasksToSchedule.length === 0) { alert("Add tasks to the plan first."); return; }
if (swro_userPatterns.lastAnalyzedRange === null && swro_dailyLogs.length > 5) {
if(!confirm("You haven't analyzed your patterns recently. Optimize using default assumptions or analyze patterns first for better suggestions? (Click OK for default, Cancel to go analyze)")){
swro_openTab(null, 'swro_patternsTab'); // Simulate click without event
return;
}
}
swro_optimizedRoutine = [];
const warnings = [];
let availableWorkMinutes = swro_timeStrToMinutes(workEndTimeStr) - swro_timeStrToMinutes(workStartTimeStr);
let totalScheduledTaskMinutes = swro_tasksToSchedule.reduce((sum, task) => sum + task.estMinutes, 0);
// Simplified heuristic:
// 1. Sort tasks: Deadline, then Priority, then by type if it matches peak.
// 2. Create time slots based on work hours and identified peak/low energy hours.
// 3. Fill slots.
const sortedTasks = [...swro_tasksToSchedule].sort((a, b) => {
if (a.deadline && b.deadline) { if (a.deadline !== b.deadline) return new Date(a.deadline) - new Date(b.deadline); }
else if (a.deadline) return -1; else if (b.deadline) return 1;
if (swro_priorityMap[a.priority] !== swro_priorityMap[b.priority]) return swro_priorityMap[a.priority] - swro_priorityMap[b.priority];
// Prefer Deep Work for Peak times. This heuristic could be more complex.
if (a.taskType === "Deep Work" && b.taskType !== "Deep Work") return -1;
if (b.taskType === "Deep Work" && a.taskType !== "Deep Work") return 1;
return a.name.localeCompare(b.name);
});
let currentTime = swro_timeStrToMinutes(workStartTimeStr);
const workEndMinutes = swro_timeStrToMinutes(workEndTimeStr);
const peakTimes = swro_userPatterns.peakFocusHours.map(pt => ({start: swro_timeStrToMinutes(pt.start), end: swro_timeStrToMinutes(pt.end)}));
sortedTasks.forEach(task => {
let scheduledThisTask = false;
let suggestedStartTime = -1;
let reasoning = "";
// Try to schedule Deep Work / High Prio in peak times first
if ((task.taskType === "Deep Work" || task.priority === "High") && peakTimes.length > 0) {
for(const peakSlot of peakTimes) {
const slotStart = Math.max(currentTime, peakSlot.start);
const slotEnd = peakSlot.end;
if (slotStart < workEndMinutes && slotStart < slotEnd && (slotEnd - slotStart) >= task.estMinutes) {
if (slotStart + task.estMinutes <= workEndMinutes) {
suggestedStartTime = slotStart;
reasoning = "Scheduled in identified peak productivity slot.";
scheduledThisTask = true;
break;
}
}
}
}
// If not scheduled in peak, try general scheduling
if (!scheduledThisTask) {
if (currentTime + task.estMinutes <= workEndMinutes) {
suggestedStartTime = currentTime;
reasoning = task.deadline ? "Prioritized by deadline." : "Scheduled by priority.";
if (task.taskType === "Shallow Work" || task.taskType === "Admin") {
// Check if this falls into a low energy period
const currentHour = Math.floor(suggestedStartTime / 60);
if(swro_userPatterns.lowEnergyHours.some(slot => currentHour >= Math.floor(swro_timeStrToMinutes(slot.start)/60) && currentHour < Math.floor(swro_timeStrToMinutes(slot.end)/60) )){
reasoning += " Good for low energy time.";
}
}
scheduledThisTask = true;
}
}
if (scheduledThisTask) {
swro_optimizedRoutine.push({
taskName: task.name, estMinutes: task.estMinutes, priority: task.priority, taskType: task.taskType, deadline:task.deadline,
suggestedStartTime: swro_minutesToTimeStr(suggestedStartTime),
suggestedEndTime: swro_minutesToTimeStr(suggestedStartTime + task.estMinutes),
reasoning: reasoning
});
currentTime = suggestedStartTime + task.estMinutes; // This is greedy, doesn't look back to fill gaps from peak scheduling.
// A more complex algorithm would manage available blocks.
} else {
warnings.push(`Could not schedule "${task.name}" (${swro_formatMinutesToHHMM(task.estMinutes)}).`);
}
});
// Simple check for total time vs available.
if (currentTime > workEndMinutes && totalScheduledTaskMinutes > availableWorkMinutes) {
warnings.push(`Total planned task time (${swro_formatMinutesToHHMM(totalScheduledTaskMinutes)}) exceeds available work hours (${swro_formatMinutesToHHMM(availableWorkMinutes)}).`);
}
// Render routine
const routineTbody = document.getElementById('swro_optimizedRoutineTable').getElementsByTagName('tbody')[0];
routineTbody.innerHTML = '';
if (swro_optimizedRoutine.length === 0 && warnings.length === 0) {
routineTbody.innerHTML = `
| No routine generated. Check tasks and work hours. |
`;
} else {
swro_optimizedRoutine.forEach(item => {
const row = routineTbody.insertRow();
row.insertCell().textContent = `${item.suggestedStartTime} - ${item.suggestedEndTime}`;
row.insertCell().textContent = item.taskName;
row.insertCell().textContent = swro_formatMinutesToHHMM(item.estMinutes);
const prioCell = row.insertCell(); prioCell.textContent = item.priority; prioCell.className = `swro-priority-${item.priority.toLowerCase()}`;
row.insertCell().textContent = item.taskType;
row.insertCell().textContent = item.reasoning;
});
}
document.getElementById('swro_routineWarningText').textContent = warnings.join(' ');
document.getElementById('swro_optimizedRoutineContainer').style.display = 'block';
document.getElementById('swro_pdfButtonOptimize').style.display = 'block';
}
// PDF for Optimized Routine
function swro_downloadOptimizedRoutinePDF() {
if (document.getElementById('swro_optimizedRoutineContainer').style.display === 'none') {
alert("Please generate an optimized routine first."); return;
}
const doc = new jsPDF({orientation: 'landscape'});
const primaryColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim();
const textColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim();
const whiteColorPDF = '#FFFFFF';
const optimizeDate = document.getElementById('swro_optimizeDate').value;
const workStart = document.getElementById('swro_optimizeWorkStart').value;
const workEnd = document.getElementById('swro_optimizeWorkEnd').value;
doc.setFontSize(18); doc.setTextColor(primaryColorPDF);
doc.text("Optimized Work Routine Plan", 14, 20);
doc.setFontSize(10); doc.setTextColor(textColorPDF);
doc.text(`For Date: ${optimizeDate}`, 14, 28);
doc.text(`Work Hours: ${workStart} - ${workEnd}`, 14, 33);
if(swro_userPatterns.peakFocusHours.length > 0){
doc.text(`Your Identified Peak Times: ${swro_userPatterns.peakFocusHours.map(p=>`${p.start}-${p.end}`).join(', ')}`, 14, 38);
currentY = 43;
} else {
currentY = 38;
}
doc.text(`Generated On: ${SWRO_GENERATED_ON_STRING}`, 14, currentY); currentY += 10;
const warnings = document.getElementById('swro_routineWarningText').textContent;
if (warnings) {
doc.setFontSize(10); doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--danger-color').trim());
const warningLines = doc.splitTextToSize(warnings, doc.internal.pageSize.getWidth() - 28);
doc.text(warningLines, 14, currentY);
currentY += (warningLines.length * 5) + 5;
doc.setTextColor(textColorPDF); // Reset
}
doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColorPDF);
if(currentY > 180) { doc.addPage(); currentY = 20; }
doc.text("Suggested Routine:", 14, currentY); currentY += 7;
doc.autoTable({ html: '#swro_optimizedRoutineTable', startY: currentY, theme: 'grid',
headStyles: {fillColor: primaryColorPDF, textColor: whiteColorPDF, fontSize:9},
styles:{fontSize:8, cellPadding:1.5},
columnStyles: { 0:{cellWidth:25}, 1:{cellWidth:50}, 2:{cellWidth:20}, 3:{cellWidth:20}, 4:{cellWidth:30}, 5:{cellWidth:'auto'} }
});
doc.save(`Optimized_Work_Routine_${optimizeDate}.pdf`);
}