Time Blocking Tool

Time Blocking Tool

${name}: ${formatMinutes(duration)}

` ).join(''); // Chart const chartCanvas = document.getElementById('category-chart'); if (categoryChart) categoryChart.destroy(); categoryChart = new Chart(chartCanvas, { type: 'pie', data: { labels: Object.keys(timeByCategory), datasets: [{ data: Object.values(timeByCategory), backgroundColor: categories.map(c => c.color), hoverOffset: 4 }] }, options: { responsive: true, maintainAspectRatio: false } }); }; // --- MODAL HANDLING --- const openModal = (startTime, blockId = null) => { modal.style.display = 'block'; modalStartTime.value = startTime.toISOString(); if (blockId) { const dateKey = new Date(startTime).toISOString().slice(0, 10); const block = blocks[dateKey].find(b => b.id === blockId); modalTitle.textContent = 'Edit Task'; modalTaskName.value = block.name; modalTaskCategory.value = block.category; modalDuration.value = block.duration; modalDeleteBtn.classList.remove('hidden'); modalDeleteBtn.dataset.blockId = blockId; } else { modalTitle.textContent = 'Add Task'; modalTaskName.value = ''; modalTaskCategory.selectedIndex = 0; modalDuration.value = '30'; modalDeleteBtn.classList.add('hidden'); delete modalDeleteBtn.dataset.blockId; } }; const closeModal = () => { modal.style.display = 'none'; }; const saveBlock = () => { const startTime = new Date(modalStartTime.value); const dateKey = startTime.toISOString().slice(0, 10); const name = modalTaskName.value.trim(); const category = modalTaskCategory.value; const duration = parseInt(modalDuration.value, 10); if (!name) { alert("Task name cannot be empty."); return; } if (!blocks[dateKey]) blocks[dateKey] = []; const existingBlockId = modalDeleteBtn.dataset.blockId; if (existingBlockId) { // Editing const blockIndex = blocks[dateKey].findIndex(b => b.id == existingBlockId); if (blockIndex > -1) { blocks[dateKey][blockIndex] = { ...blocks[dateKey][blockIndex], name, category, duration }; } } else { // Adding new const newBlock = { id: Date.now(), name, category, startTime: startTime.toISOString(), duration }; blocks[dateKey].push(newBlock); } saveData(); renderSchedule(new Date(dateKey + 'T00:00:00')); closeModal(); }; const deleteBlock = () => { const blockId = modalDeleteBtn.dataset.blockId; const startTime = new Date(modalStartTime.value); const dateKey = startTime.toISOString().slice(0, 10); if (confirm("Are you sure you want to delete this block?")) { blocks[dateKey] = blocks[dateKey].filter(b => b.id != blockId); saveData(); renderSchedule(new Date(dateKey + 'T00:00:00')); closeModal(); } }; // --- CATEGORY MANAGEMENT --- const addCategory = () => { const name = newCategoryNameInput.value.trim(); const color = newCategoryColorInput.value; if (name && !categories.some(c => c.name === name)) { categories.push({ name, color }); saveData(); renderCategories(); newCategoryNameInput.value = ''; } }; const deleteCategory = (name) => { if (confirm(`Delete category "${name}"? This will not remove existing blocks with this category.`)) { categories = categories.filter(c => c.name !== name); saveData(); renderCategories(); } }; // --- TAB MANAGEMENT --- window.changeTab = (tabId) => { currentTab = tabId; ['tab1', 'tab2', 'tab3'].forEach(id => { document.getElementById(id).classList.toggle('hidden', id !== tabId); document.getElementById(`${id}-button`).classList.toggle('active', id === tabId); }); if (tabId === 'tab2') renderDashboard(); updateButtonVisibility(); }; const updateButtonVisibility = () => { const prevBtn = document.getElementById('prev-button'); const nextBtn = document.getElementById('next-button'); const downloadBtn = document.getElementById('download-pdf-button'); prevBtn.classList.toggle('hidden', currentTab === 'tab1'); nextBtn.classList.toggle('hidden', currentTab === 'tab3'); downloadBtn.classList.toggle('hidden', currentTab !== 'tab1'); }; window.handleNext = () => { const tabs = ['tab1', 'tab2', 'tab3']; const currentIndex = tabs.indexOf(currentTab); if (currentIndex < 2) changeTab(tabs[currentIndex + 1]); }; window.handlePrev = () => { const tabs = ['tab1', 'tab2', 'tab3']; const currentIndex = tabs.indexOf(currentTab); if (currentIndex > 0) changeTab(tabs[currentIndex - 1]); }; // --- PDF DOWNLOAD --- window.downloadPDF = async () => { const { jsPDF } = window.jspdf; const scheduleEl = document.getElementById('schedule-container'); const date = new Date(scheduleDateInput.value + 'T00:00:00').toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); try { const canvas = await html2canvas(scheduleEl, { scale: 2 }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'portrait', unit: 'px', format: 'a4' }); pdf.setFontSize(18); pdf.text(`Daily Schedule - ${date}`, 40, 40); const pdfWidth = pdf.internal.pageSize.getWidth(); const imgWidth = canvas.width; const imgHeight = canvas.height; const ratio = imgWidth / imgHeight; const newWidth = pdfWidth - 80; const newHeight = newWidth / ratio; pdf.addImage(imgData, 'PNG', 40, 60, newWidth, newHeight); pdf.save(`schedule-${scheduleDateInput.value}.pdf`); } catch (error) { console.error("PDF generation failed:", error); alert("Could not generate PDF."); } }; // --- EVENT LISTENERS --- scheduleDateInput.addEventListener('change', (e) => renderSchedule(new Date(e.target.value + 'T00:00:00'))); scheduleContainer.addEventListener('click', (e) => { const slot = e.target.closest('.block-slot'); const block = e.target.closest('.task-block'); if (block) { const date = new Date(scheduleDateInput.value + 'T00:00:00'); openModal(date, parseInt(block.dataset.blockId, 10)); } else if (slot) { const hour = parseInt(slot.dataset.hour, 10); const date = new Date(scheduleDateInput.value + 'T00:00:00'); date.setHours(hour); openModal(date); } }); modalSaveBtn.addEventListener('click', saveBlock); modalCancelBtn.addEventListener('click', closeModal); modalDeleteBtn.addEventListener('click', deleteBlock); addCategoryBtn.addEventListener('click', addCategory); categoryList.addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON' && e.target.dataset.name) { deleteCategory(e.target.dataset.name); } }); // --- INITIALIZATION --- loadData(); scheduleDateInput.valueAsDate = new Date(); renderSchedule(new Date()); renderCategories(); updateButtonVisibility(); });
Scroll to Top