Online Time Blocking Planner

Time Blocking Planner

Task Pool

minutes

${task.name}

${task.duration} mins

`; dropZone.appendChild(taskBlock); }); }; // --- DATA & LOGIC --- const addTask = (e) => { e.preventDefault(); const name = taskNameInput.value.trim(); const duration = parseInt(taskDurationInput.value); if (!name || !duration) return; state.taskIdCounter++; state.tasks.push({ id: state.taskIdCounter, name, duration, isScheduled: false }); taskNameInput.value = ''; renderTaskList(); }; const deleteTask = (taskId) => { state.tasks = state.tasks.filter(t => t.id !== taskId); renderTaskList(); }; const scheduleTask = (taskId, hour) => { const task = state.tasks.find(t => t.id === taskId); if (!task) return; task.isScheduled = true; if (!state.scheduledTasks[state.currentDate]) { state.scheduledTasks[state.currentDate] = []; } state.scheduledTasks[state.currentDate].push({ id: Date.now(), taskId: task.id, startHour: hour }); renderTaskList(); renderScheduledTasks(); }; const unscheduleTask = (scheduledId) => { const scheduledForDate = state.scheduledTasks[state.currentDate] || []; const scheduledTask = scheduledForDate.find(st => st.id === scheduledId); if (!scheduledTask) return; const task = state.tasks.find(t => t.id === scheduledTask.taskId); if (task) task.isScheduled = false; state.scheduledTasks[state.currentDate] = scheduledForDate.filter(st => st.id !== scheduledId); renderTaskList(); renderScheduledTasks(); }; // --- DRAG & DROP HANDLERS --- const handleDragStart = (e) => { state.draggedElement = e.target; e.dataTransfer.setData('text/plain', e.target.dataset.taskId); e.target.classList.add('dragging'); document.querySelectorAll('.drop-zone').forEach(zone => zone.parentElement.classList.add('drop-active')); }; const handleDragEnd = (e) => { e.target.classList.remove('dragging'); document.querySelectorAll('.time-slot').forEach(slot => slot.classList.remove('drop-active', 'drop-target')); }; const handleDragOver = (e) => { e.preventDefault(); const dropZone = e.target.closest('.time-slot'); if (dropZone) { document.querySelectorAll('.time-slot').forEach(slot => slot.classList.remove('drop-target')); dropZone.classList.add('drop-target'); } }; const handleDrop = (e) => { e.preventDefault(); const dropZone = e.target.closest('.time-slot'); if (dropZone) { const taskId = parseInt(e.dataTransfer.getData('text/plain')); const hour = parseInt(dropZone.dataset.hour); scheduleTask(taskId, hour); } }; // --- PDF EXPORT --- const handlePdfDownload = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-export-area'); const title = `Time Block Plan for ${plannerDateTitle.textContent}`; html2canvas(pdfContent, { scale: 2 }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' }); const margin = 40; const pdfWidth = pdf.internal.pageSize.getWidth() - (margin * 2); const pdfHeight = (canvas.height * pdfWidth) / canvas.width; pdf.setFontSize(18); pdf.text(title, margin, margin); pdf.addImage(imgData, 'PNG', margin, margin + 20, pdfWidth, pdfHeight); pdf.save(`planner-${state.currentDate}.pdf`); }); }; // --- EVENT LISTENERS --- addTaskForm.addEventListener('submit', addTask); datePicker.addEventListener('change', (e) => { state.currentDate = e.target.value; renderPlanner(); }); downloadPdfBtn.addEventListener('click', handlePdfDownload); // Event delegation for dynamic elements taskList.addEventListener('click', (e) => { if (e.target.closest('.delete-task-btn')) { const taskId = parseInt(e.target.closest('.task-item').dataset.taskId); deleteTask(taskId); } }); timeBlockPlanner.addEventListener('click', (e) => { if (e.target.closest('.unschedule-btn')) { const scheduledId = parseInt(e.target.closest('.blocked-task').dataset.scheduledId); unscheduleTask(scheduledId); } }); // Drag and drop event listeners taskList.addEventListener('dragstart', handleDragStart); taskList.addEventListener('dragend', handleDragEnd); timeBlockPlanner.addEventListener('dragover', handleDragOver); timeBlockPlanner.addEventListener('drop', handleDrop); timeBlockPlanner.addEventListener('dragleave', () => { document.querySelectorAll('.time-slot').forEach(slot => slot.classList.remove('drop-target')); }); // --- INITIALIZATION --- const init = () => { datePicker.value = state.currentDate; state.tasks = [ { id: 1, name: 'Team Stand-up Meeting', duration: 30, isScheduled: false }, { id: 2, name: 'Deep Work: Project Alpha', duration: 120, isScheduled: false }, { id: 3, name: 'Respond to client emails', duration: 45, isScheduled: false }, { id: 4, name: 'Lunch Break', duration: 60, isScheduled: false }, ]; state.taskIdCounter = 4; renderTaskList(); renderPlanner(); }; init(); });
Scroll to Top