Work Session Planner

Work Session Planner

Organize your tasks into focused work and break intervals.

Session Settings

Add Tasks

Task List

No tasks added yet.

`; } else { tasks.forEach(task => { const taskEl = document.createElement('div'); taskEl.className = 'flex items-center justify-between bg-gray-100 p-3 rounded-md'; taskEl.innerHTML = ` ${task.name}
${task.duration} min
`; taskListContainer.appendChild(taskEl); }); } }; const renderPlan = () => { const { schedule, totalWork, totalBreak, endTime } = generateSchedule(); scheduleContainer.innerHTML = ''; if (schedule.length === 0) { scheduleContainer.innerHTML = `

Add tasks on the previous tab to generate a session plan.

`; } schedule.forEach(item => { const itemEl = document.createElement('div'); itemEl.className = `schedule-item ${item.type}`; itemEl.innerHTML = `
${item.time}
${item.title}
${item.duration} min
`; scheduleContainer.appendChild(itemEl); }); totalWorkTimeEl.textContent = `${totalWork} min`; totalBreakTimeEl.textContent = `${totalBreak} min`; endTimeEl.textContent = endTime; }; const renderAll = () => { renderTaskList(); renderPlan(); }; // --- CORE LOGIC --- // const generateSchedule = () => { const workDuration = parseInt(settings.workDuration.value); const shortBreakDuration = parseInt(settings.shortBreak.value); const longBreakDuration = parseInt(settings.longBreak.value); const sessionsForLongBreak = parseInt(settings.sessionsBeforeLongBreak.value); const startTimeValue = settings.startTime.value; let schedule = []; let remainingTasks = JSON.parse(JSON.stringify(tasks)); let totalWork = 0; let totalBreak = 0; let sessionCounter = 0; let currentTime = new Date(); if (startTimeValue) { const [hours, minutes] = startTimeValue.split(':'); currentTime.setHours(hours, minutes, 0, 0); } const formatTime = (date) => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); while (remainingTasks.length > 0) { sessionCounter++; totalWork += workDuration; let timeForThisWorkBlock = workDuration; let workBlockTasks = []; while (timeForThisWorkBlock > 0 && remainingTasks.length > 0) { let currentTask = remainingTasks[0]; if (currentTask.duration <= timeForThisWorkBlock) { workBlockTasks.push(`${currentTask.name} (${currentTask.duration} min)`); timeForThisWorkBlock -= currentTask.duration; remainingTasks.shift(); } else { workBlockTasks.push(`${currentTask.name} (partial)`); currentTask.duration -= timeForThisWorkBlock; timeForThisWorkBlock = 0; } } const workStartTime = formatTime(currentTime); schedule.push({ type: 'work', time: workStartTime, title: `Work Interval: ${workBlockTasks.join(', ')}`, duration: workDuration }); currentTime.setMinutes(currentTime.getMinutes() + workDuration); if (remainingTasks.length > 0) { let breakDuration; let breakType; if (sessionCounter % sessionsForLongBreak === 0) { breakDuration = longBreakDuration; breakType = 'Long Break'; totalBreak += longBreakDuration; } else { breakDuration = shortBreakDuration; breakType = 'Short Break'; totalBreak += shortBreakDuration; } const breakStartTime = formatTime(currentTime); schedule.push({ type: 'break', time: breakStartTime, title: breakType, duration: breakDuration }); currentTime.setMinutes(currentTime.getMinutes() + breakDuration); } } const endTime = schedule.length > 0 ? formatTime(currentTime) : (startTimeValue ? formatTime(currentTime) : '--:--'); return { schedule, totalWork, totalBreak, endTime }; }; // --- EVENT HANDLERS --- // taskForm.addEventListener('submit', (e) => { e.preventDefault(); const name = taskNameInput.value.trim(); const duration = parseInt(taskDurationInput.value); if (!name || isNaN(duration) || duration <= 0) { alert('Please enter a valid task name and duration.'); return; } tasks.push({ id: Date.now(), name, duration }); taskForm.reset(); renderTaskList(); }); window.deleteTask = (id) => { tasks = tasks.filter(task => task.id !== id); renderTaskList(); }; downloadPdfBtn.addEventListener('click', () => { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const { schedule, totalWork, totalBreak, endTime } = generateSchedule(); pdf.setFontSize(18); pdf.text("Work Session Plan", 40, 40); pdf.setFontSize(12); pdf.text(`Total Work: ${totalWork} min | Total Break: ${totalBreak} min | End Time: ${endTime}`, 40, 60); if (schedule.length > 0) { pdf.autoTable({ startY: 80, head: [['Time', 'Type', 'Details', 'Duration (min)']], body: schedule.map(item => [item.time, item.type.charAt(0).toUpperCase() + item.type.slice(1), item.title, item.duration]), theme: 'grid', headStyles: { fillColor: [41, 128, 185] }, didParseCell: function (data) { if (data.row.raw[1] === 'Break') { data.cell.styles.fillColor = '#f0fdf4'; // Light green data.cell.styles.textColor = '#15803d'; // Darker green } } }); } else { pdf.text("No tasks were added to generate a plan.", 40, 90); } pdf.save('Work_Session_Plan.pdf'); }); // --- TAB NAVIGATION --- // const updateTabs = (targetTab) => { currentTab = tabs.indexOf(targetTab); tabContents.forEach(content => content.classList.add('hidden')); tabButtons.forEach(button => button.classList.remove('active')); const activeContent = document.getElementById(`${targetTab}-tab`); const activeButton = document.querySelector(`.tab-btn[data-tab="${targetTab}"]`); if(activeContent) activeContent.classList.remove('hidden'); if(activeButton) activeButton.classList.add('active'); prevBtn.disabled = currentTab === 0; nextBtn.disabled = currentTab === tabs.length - 1; if (targetTab === 'plan') { renderPlan(); } }; tabButtons.forEach(button => { button.addEventListener('click', () => updateTabs(button.dataset.tab)); }); prevBtn.addEventListener('click', () => { if (currentTab > 0) { updateTabs(tabs[currentTab - 1]); } }); nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) { updateTabs(tabs[currentTab + 1]); } }); // --- INITIALIZATION --- // const loadSampleData = () => { tasks = [ { id: Date.now() + 1, name: 'Draft quarterly report introduction', duration: 45 }, { id: Date.now() + 2, name: 'Prepare slides for marketing team presentation', duration: 60 }, { id: Date.now() + 3, name: 'Review and reply to client emails', duration: 20 }, ]; renderAll(); }; loadSampleData(); updateTabs('setup'); // Set initial tab state });
Scroll to Top