Smart Task Schedule Customizer

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.

"; return; } taskListDiv.innerHTML = ''; stscTasks.forEach(task => { const taskItem = document.createElement('div'); taskItem.classList.add('stsc-task-item', `stsc-priority-${task.priority}`); taskItem.innerHTML = `
${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 = '

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 += `
`; }); const unscheduledTasks = tasksToSchedule.filter(task => !task.scheduled); if (unscheduledTasks.length > 0) { 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 += "

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."); }); }
Scroll to Top