Smart Task Schedule Customizer
Add New Task
Current Task List
No tasks added yet.
Scheduling Availability
Please input tasks, define availability, and click "Generate Customized Schedule" on the first tab.
${task.name}
Priority: ${task.priority} | Est. Time: ${task.estTime} min ${task.dueDate ? ` | Due: ${new Date(task.dueDate + "T00:00:00").toLocaleDateString()}` : ''}
`;
taskListDiv.appendChild(taskItem);
});
}
function stscGenerateAvailabilityInputs() {
const container = document.getElementById('stsc-daily-availability-setup');
const numDaysEl = document.getElementById('stsc-schedule-days');
const startDateEl = document.getElementById('stsc-schedule-start-date');
if (!container || !numDaysEl || !startDateEl) return;
const numDays = parseInt(numDaysEl.value) || 1;
const startDateVal = startDateEl.value;
let startDate = startDateVal ? new Date(startDateVal + "T00:00:00") : new Date();
container.innerHTML = ''; // Clear previous
for (let i = 0; i < numDays; i++) {
const dayDate = new Date(startDate);
dayDate.setDate(startDate.getDate() + i);
const dayDiv = document.createElement('div');
dayDiv.classList.add('stsc-day-availability-input');
dayDiv.innerHTML = `
`;
container.appendChild(dayDiv);
}
}
function stscGetPriorityValue(priority) {
if (priority === 'High') return 1;
if (priority === 'Medium') return 2;
if (priority === 'Low') return 3;
return 4; // Default for unknown
}
function stscGenerateSchedule() {
if (stscTasks.length === 0) {
alert("Please add some tasks first.");
return;
}
const scheduleStartDateVal = document.getElementById('stsc-schedule-start-date').value;
if(!scheduleStartDateVal){
alert("Please set a schedule start date.");
return;
}
let tasksToSchedule = JSON.parse(JSON.stringify(stscTasks)); // Deep copy
tasksToSchedule.forEach(task => task.scheduled = false);
tasksToSchedule.sort((a, b) => {
// Priority: High (1) > Medium (2) > Low (3)
const priorityDiff = stscGetPriorityValue(a.priority) - stscGetPriorityValue(b.priority);
if (priorityDiff !== 0) return priorityDiff;
// Due Date: Earlier first (null/empty due dates are considered last)
if (a.originalDueDateObj && b.originalDueDateObj) {
const dueDateDiff = a.originalDueDateObj - b.originalDueDateObj;
if (dueDateDiff !== 0) return dueDateDiff;
} else if (a.originalDueDateObj) return -1; // a has due date, b doesn't
else if (b.originalDueDateObj) return 1; // b has due date, a doesn't
// Estimated Time: Shorter first
return a.estTime - b.estTime;
});
const numScheduleDays = parseInt(document.getElementById('stsc-schedule-days').value);
const scheduleStartDate = new Date(scheduleStartDateVal + "T00:00:00");
let dailySlots = [];
for (let i = 0; i < numScheduleDays; i++) {
const dayDate = new Date(scheduleStartDate);
dayDate.setDate(scheduleStartDate.getDate() + i);
const availabilityInput = document.getElementById(`stsc-avail-day-${i}`);
dailySlots.push({
date: dayDate,
availableMins: availabilityInput ? parseInt(availabilityInput.value) : 0,
tasks: [],
totalScheduledMins: 0
});
}
tasksToSchedule.forEach(task => {
if (task.scheduled) return; // Skip if already somehow marked (should not happen here)
for (let slot of dailySlots) {
// Check due date: Task must be scheduled on or before its due date if it has one.
// And slot date must be >= task creation/start context if applicable (not an input yet, assume all tasks can start anytime)
if (task.originalDueDateObj && slot.date > task.originalDueDateObj) {
// If we passed the due date for this task for this slot, it might become unscheduled or scheduled late.
// For now, if we can't fit it before or on due date, it might become unscheduled.
continue;
}
if (task.estTime <= slot.availableMins) {
slot.tasks.push(task);
slot.availableMins -= task.estTime;
slot.totalScheduledMins += task.estTime;
task.scheduled = true;
task.scheduledOnDate = slot.date; // Mark which day it was scheduled
break; // Move to next task
}
}
});
let scheduleHTML = 'Priority: ${task.priority} | Est. Time: ${task.estTime} min ${task.dueDate ? ` | Due: ${new Date(task.dueDate + "T00:00:00").toLocaleDateString()}` : ''}
Generated Schedule
'; dailySlots.forEach(slot => { const dayStatus = slot.totalScheduledMins > (document.getElementById(`stsc-avail-day-${dailySlots.indexOf(slot)}`) ? parseInt(document.getElementById(`stsc-avail-day-${dailySlots.indexOf(slot)}`).value) : 0) ? 'stsc-overloaded' : 'stsc-ok'; const originalAvailable = document.getElementById(`stsc-avail-day-${dailySlots.indexOf(slot)}`) ? parseInt(document.getElementById(`stsc-avail-day-${dailySlots.indexOf(slot)}`).value) : 0; scheduleHTML += `${slot.date.toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
Available: ${originalAvailable} min |
Scheduled: ${slot.totalScheduledMins} min |
Status: ${slot.totalScheduledMins > originalAvailable ? 'Overloaded' : (slot.totalScheduledMins > 0 ? 'Scheduled' : 'Empty')}
`;
if (slot.tasks.length > 0) {
slot.tasks.forEach(task => {
scheduleHTML += `
${task.name} (Est: ${task.estTime} min)
${task.dueDate ? ` - Due: ${new Date(task.dueDate+"T00:00:00").toLocaleDateString()}` : ''}
`;
});
} else {
scheduleHTML += "No tasks scheduled for this day.
"; } scheduleHTML += `Unscheduled Tasks (${unscheduledTasks.length})
`; unscheduledTasks.forEach(task => { scheduleHTML += `
${task.name} (Priority: ${task.priority}, Est: ${task.estTime} min
${task.dueDate ? `, Due: ${new Date(task.dueDate+"T00:00:00").toLocaleDateString()}` : ''})
- Reason: Not enough time or past due before fitting.
`;
});
}
const scheduleOutputEl = document.getElementById('stsc-schedule-output');
if(scheduleOutputEl) scheduleOutputEl.innerHTML = scheduleHTML;
stscOpenTab(null, 'stsc-schedule');
stscUpdateNavButtons();
}
function stscDownloadPDF() {
const scheduleOutputElement = document.getElementById('stsc-schedule-output');
if (!scheduleOutputElement || scheduleOutputElement.innerHTML.includes("Please input tasks")) {
alert("Please generate a schedule first.");
return;
}
let pdfHTML = `Customized Task Schedule
`; // Add Initial Task List to PDF pdfHTML += `Initial Task List (${stscTasks.length})
`; if (stscTasks.length > 0) { stscTasks.forEach(task => { pdfHTML += `
${task.name}
Priority: ${task.priority} | Est. Time: ${task.estTime} min ${task.dueDate ? ` | Due: ${new Date(task.dueDate+"T00:00:00").toLocaleDateString()}` : ''}
`;
});
} else {
pdfHTML += "Priority: ${task.priority} | Est. Time: ${task.estTime} min ${task.dueDate ? ` | Due: ${new Date(task.dueDate+"T00:00:00").toLocaleDateString()}` : ''}
No tasks were initially listed.
"; } // Add schedule content (already generated and in scheduleOutputElement) pdfHTML += scheduleOutputElement.innerHTML; // This already has "Generated Schedule" h3 and then day breakdowns & unscheduled const pdfContainer = document.getElementById('stsc-report-content-for-pdf'); if (!pdfContainer) return; pdfContainer.innerHTML = pdfHTML; const opt = { margin: [0.5, 0.3, 0.5, 0.3], filename: 'Customized_Task_Schedule.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, useCORS: true, logging: false, scrollX: 0, scrollY: 0, windowWidth: pdfContainer.scrollWidth }, 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."); }); }