Time Blocking Planner
Work Day Hours & Granularity
Task Categories & Colors
Effective Time Blocking Guide
- 1. Define Your Core Settings First
- Go to the "Settings & Categories" tab. Set your typical work start/end times and time slot granularity (e.g., 30 minutes). Create task categories that make sense for your workflow (e.g., "Deep Work," "Meetings," "Admin," "Learning," "Personal Errands," "Breaks") and assign them distinct colors.
- 2. Plan Your Day
- Navigate to the "Daily Planner" tab. Select the date you want to plan. Click "Add New Time Block".
- 3. Create Time Blocks
- In the modal:
- Give your task/activity a clear name.
- Set the Start Time and End Time for the block.
- Assign a Category to the block. This will color-code it on your timeline.
- Add any optional notes.
- 4. Be Realistic with Durations
- Estimate task durations as accurately as possible. It's often better to slightly overestimate than underestimate. Remember to include buffer time if needed.
- 5. Schedule Breaks
- Don't forget to block out time for short breaks, lunch, or any other personal needs. Create a "Break" category for this. Consistent breaks improve focus and prevent burnout.
- 6. Group Similar Tasks (Batching)
- If you have many small, similar tasks (like responding to emails or making calls), try to group them into a single, dedicated time block.
- 7. Prioritize Important Tasks
- Schedule your most important or high-concentration tasks ("Deep Work") during times of the day when you know you're most productive and least likely to be interrupted.
- 8. Review and Adapt
- Your first time-blocked schedule might not be perfect. At the end of the day, review how it went. Did you stick to the plan? Were your time estimates accurate? Make adjustments to your categories or typical block durations for future planning.
- 9. Stick to the Plan (Mostly)
- The goal of time blocking is to be more intentional with your time. Try to adhere to your schedule, but also allow for some flexibility when urgent, unexpected items arise. If you deviate, consciously decide how to adjust the rest of your day.
×
Add Time Block
Error: Work End Time must be after Start Time. Check settings.
${block.taskName}
${tbpFormatTimeForDisplay(block.startTime)} - ${tbpFormatTimeForDisplay(block.endTime)}
${block.notes && blockHeightPx > 40 ? `${block.notes.substring(0,50)}${block.notes.length > 50 ? '...' : ''}
` : ''}
`; // Only show notes if block is tall enough
blockEl.onclick = () => tbpOpenBlockModal(block.id);
tbpTimelineGridEl.appendChild(blockEl);
});
}
// --- Daily Planner: Block Modal & CRUD ---
window.tbpOpenBlockModal = function(blockId = null) { // Make globally accessible
if (!tbpCurrentSelectedDate) {
alert("Please select a date first.");
return;
}
if(!tbpBlockForm || !tbpBlockIdInput || !tbpBlockDateInput || !tbpBlockModalTitleEl || !tbpDeleteBlockBtn || !tbpBlockTaskNameInput || !tbpBlockStartTimeInput || !tbpBlockEndTimeInput || !tbpBlockCategorySelectEl || !tbpBlockNotesInput ) {
console.error("Modal elements not found"); return;
}
tbpBlockForm.reset();
tbpBlockIdInput.value = '';
tbpBlockDateInput.value = tbpCurrentSelectedDate;
tbpPopulateCategorySelect();
tbpDeleteBlockBtn.style.display = 'none';
if (blockId) {
const blocks = tbpDailyTimeBlocks[tbpCurrentSelectedDate] || [];
const block = blocks.find(b => b.id === blockId);
if (block) {
tbpBlockModalTitleEl.textContent = 'Edit Time Block';
tbpBlockIdInput.value = block.id;
tbpBlockTaskNameInput.value = block.taskName;
tbpBlockStartTimeInput.value = block.startTime;
tbpBlockEndTimeInput.value = block.endTime;
tbpBlockCategorySelectEl.value = block.categoryId || "";
tbpBlockNotesInput.value = block.notes || "";
tbpDeleteBlockBtn.style.display = 'inline-block';
}
} else {
tbpBlockModalTitleEl.textContent = 'Add New Time Block';
tbpBlockStartTimeInput.value = tbpSettings.dayStartTime;
const defaultEndMins = tbpTimeToMinutes(tbpSettings.dayStartTime) + parseInt(tbpSettings.timeSlotGranularityMinutes);
tbpBlockEndTimeInput.value = tbpMinutesToTime(Math.min(defaultEndMins, tbpTimeToMinutes(tbpSettings.dayEndTime)));
}
if(tbpBlockModalEl) tbpBlockModalEl.style.display = 'block';
}
window.tbpCloseModal = (modalId) => { // Make globally accessible
const modal = document.getElementById(modalId);
if(modal) modal.style.display = 'none';
}
function handleBlockFormSubmit(e) {
e.preventDefault();
const blockId = tbpBlockIdInput.value;
const blockDate = tbpBlockDateInput.value;
const blockData = {
taskName: tbpBlockTaskNameInput.value.trim(),
startTime: tbpBlockStartTimeInput.value,
endTime: tbpBlockEndTimeInput.value,
categoryId: tbpBlockCategorySelectEl.value,
notes: tbpBlockNotesInput.value.trim()
};
if (!blockData.taskName || !blockData.startTime || !blockData.endTime) {
alert("Task name, start time, and end time are required."); return;
}
if (tbpTimeToMinutes(blockData.endTime) <= tbpTimeToMinutes(blockData.startTime)) {
alert("End time must be after start time."); return;
}
// Overlap Check
const dayBlocks = tbpDailyTimeBlocks[blockDate] || [];
const newStartM = tbpTimeToMinutes(blockData.startTime);
const newEndM = tbpTimeToMinutes(blockData.endTime);
for (const existingBlock of dayBlocks) {
if (existingBlock.id === blockId) continue;
const existingStartM = tbpTimeToMinutes(existingBlock.startTime);
const existingEndM = tbpTimeToMinutes(existingBlock.endTime);
if (newStartM < existingEndM && newEndM > existingStartM) {
alert(`Overlap detected with task: "${existingBlock.taskName}" (${existingBlock.startTime} - ${existingBlock.endTime}). Please adjust times.`);
return;
}
}
if (!tbpDailyTimeBlocks[blockDate]) tbpDailyTimeBlocks[blockDate] = [];
if (blockId) {
const index = tbpDailyTimeBlocks[blockDate].findIndex(b => b.id === blockId);
if (index > -1) {
tbpDailyTimeBlocks[blockDate][index] = { ...tbpDailyTimeBlocks[blockDate][index], ...blockData };
}
} else {
const newBlock = { id: 'tbp-block-' + Date.now(), ...blockData };
tbpDailyTimeBlocks[blockDate].push(newBlock);
}
tbpDailyTimeBlocks[blockDate].sort((a,b) => a.startTime.localeCompare(b.startTime));
tbpSaveDailyBlocks();
tbpCloseModal('tbpBlockModal');
}
window.tbpHandleDeleteBlock = function() { // Make globally accessible
const blockId = tbpBlockIdInput.value;
const blockDate = tbpBlockDateInput.value;
if (blockId && blockDate && tbpDailyTimeBlocks[blockDate]) {
if (confirm("Are you sure you want to delete this time block?")) {
tbpDailyTimeBlocks[blockDate] = tbpDailyTimeBlocks[blockDate].filter(b => b.id !== blockId);
tbpSaveDailyBlocks();
tbpCloseModal('tbpBlockModal');
}
}
}
// --- PDF Export ---
function handlePdfDownload() {
const selectedDateKey = tbpCurrentSelectedDate;
if (!selectedDateKey || !tbpDailyTimeBlocks[selectedDateKey] || tbpDailyTimeBlocks[selectedDateKey].length === 0) {
alert("No time blocks planned for this day to export.");
return;
}
const dayBlocks = tbpDailyTimeBlocks[selectedDateKey];
const doc = new jsPDF(); // Use the global jsPDF from window.jspdf
const displayDate = new Date(selectedDateKey + "T00:00:00").toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
doc.setFontSize(18);
doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--tbp-primary-color').trim());
doc.text(`Time Blocked Plan - ${displayDate}`, 14, 22);
let yPos = 35;
const pageHeight = doc.internal.pageSize.height;
const leftMargin = 14;
const timelineWidth = doc.internal.pageSize.width - leftMargin * 2;
const timeLabelWidth = 20;
const gridStartX = leftMargin + timeLabelWidth;
const gridWidth = timelineWidth - timeLabelWidth - 5;
const dayStartMinutes = tbpTimeToMinutes(tbpSettings.dayStartTime);
const dayEndMinutes = tbpTimeToMinutes(tbpSettings.dayEndTime);
const totalVisibleDayMinutes = dayEndMinutes - dayStartMinutes;
if (totalVisibleDayMinutes <=0) {
doc.text("Invalid work hours set.", 14, yPos);
doc.save(`TimeBlockingPlan_Error_${selectedDateKey}.pdf`); return;
}
const pdfTimelineHeight = 220;
doc.setDrawColor(getComputedStyle(document.documentElement).getPropertyValue('--tbp-border-color').trim());
doc.setLineWidth(0.2);
doc.setFontSize(8);
doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--tbp-accent-color').trim());
for (let minutes = dayStartMinutes; minutes <= dayEndMinutes; minutes += parseInt(tbpSettings.timeSlotGranularityMinutes)) {
const y = yPos + ((minutes - dayStartMinutes) / totalVisibleDayMinutes) * pdfTimelineHeight;
if (y > pageHeight - 20 && minutes < dayEndMinutes) { break; }
if(minutes % 60 === 0 || parseInt(tbpSettings.timeSlotGranularityMinutes) < 60) {
doc.text(tbpMinutesToTime(minutes), leftMargin, y + 2);
}
doc.line(gridStartX, y, gridStartX + gridWidth, y);
}
dayBlocks.forEach(block => {
const blockStartMinutes = tbpTimeToMinutes(block.startTime);
const blockEndMinutes = tbpTimeToMinutes(block.endTime);
const effectiveBlockStart = Math.max(blockStartMinutes, dayStartMinutes);
const effectiveBlockEnd = Math.min(blockEndMinutes, dayEndMinutes);
const blockDurationMinutes = effectiveBlockEnd - effectiveBlockStart;
if(blockDurationMinutes <=0) return;
const blockTop = yPos + ((effectiveBlockStart - dayStartMinutes) / totalVisibleDayMinutes) * pdfTimelineHeight;
const blockHeight = (blockDurationMinutes / totalVisibleDayMinutes) * pdfTimelineHeight;
const category = tbpSettings.taskCategories.find(cat => cat.id === block.categoryId);
const color = category ? category.color : '#BDC3C7';
doc.setFillColor(color.startsWith('var') ? '#BDC3C7' : color);
doc.rect(gridStartX + 1, blockTop, gridWidth - 2, blockHeight, 'F');
doc.setFontSize(7);
if (color === "#F1C40F" || color === "#FFCA28" || color.toLowerCase() === "#ecf0f1") {
doc.setTextColor(50,50,50);
} else {
doc.setTextColor(255, 255, 255);
}
const textX = gridStartX + 3;
const textY = blockTop + 4;
const textMaxWidth = gridWidth - 6;
const taskNameLines = doc.splitTextToSize(block.taskName, textMaxWidth);
doc.text(taskNameLines, textX, textY);
let timeYOffset = taskNameLines.length * 3;
doc.setFontSize(6);
if(blockHeight > 10) {
doc.text(`${block.startTime}-${block.endTime}`, textX, textY + timeYOffset);
}
});
let tableYPos = yPos + pdfTimelineHeight + 10;
if (tableYPos > pageHeight - 30 || dayBlocks.length > 10) {
doc.addPage(); tableYPos = 20;
}
doc.setFontSize(12);
doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--tbp-text-color').trim());
doc.text("Task List for the Day:", 14, tableYPos); tableYPos += 7;
const tableColumns = ["Time", "Task", "Category", "Notes"];
const tableRows = dayBlocks.map(b => {
const cat = tbpSettings.taskCategories.find(c => c.id === b.categoryId);
return [`${b.startTime}-${b.endTime}`, b.taskName, cat ? cat.name : 'N/A', b.notes || ''];
});
if (tableRows.length > 0) {
doc.autoTable({
head: [tableColumns], body: tableRows, startY: tableYPos,
theme: 'striped', headStyles: { fillColor: getComputedStyle(document.documentElement).getPropertyValue('--tbp-primary-color').trim()}
});
}
doc.save(`TimeBlockingPlan_${selectedDateKey}.pdf`);
}
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
// Assign DOM elements to global vars after DOM is ready
tbpWorkStartTimeInput = document.getElementById('tbpWorkStartTime');
tbpWorkEndTimeInput = document.getElementById('tbpWorkEndTime');
tbpTimeSlotGranularitySelect = document.getElementById('tbpTimeSlotGranularity');
tbpAddCategoryForm = document.getElementById('tbpAddCategoryForm');
tbpCategoryNameInput = document.getElementById('tbpCategoryName');
tbpCategoryColorInput = document.getElementById('tbpCategoryColor');
tbpCategoriesListEl = document.getElementById('tbpCategoriesList');
tbpSelectedDateInput = document.getElementById('tbpSelectedDate');
tbpTimeLabelsEl = document.getElementById('tbpTimeLabels');
tbpTimelineGridEl = document.getElementById('tbpTimelineGrid');
tbpBlockModalEl = document.getElementById('tbpBlockModal');
tbpBlockModalTitleEl = document.getElementById('tbpBlockModalTitle');
tbpBlockForm = document.getElementById('tbpBlockForm');
tbpBlockIdInput = document.getElementById('tbpBlockId');
tbpBlockDateInput = document.getElementById('tbpBlockDate');
tbpBlockTaskNameInput = document.getElementById('tbpBlockTaskName');
tbpBlockStartTimeInput = document.getElementById('tbpBlockStartTime');
tbpBlockEndTimeInput = document.getElementById('tbpBlockEndTime');
tbpBlockCategorySelectEl = document.getElementById('tbpBlockCategory');
tbpBlockNotesInput = document.getElementById('tbpBlockNotes');
tbpDeleteBlockBtn = document.getElementById('tbpDeleteBlockBtn');
tbpDownloadPdfBtn = document.getElementById('tbpDownloadPdfButton');
// Attach event listeners
if(tbpAddCategoryForm) tbpAddCategoryForm.addEventListener('submit', handleAddCategoryFormSubmit);
if(tbpBlockForm) tbpBlockForm.addEventListener('submit', handleBlockFormSubmit);
if(tbpSelectedDateInput) tbpSelectedDateInput.addEventListener('change', handleSelectedDateChange);
if(tbpDownloadPdfBtn) tbpDownloadPdfBtn.addEventListener('click', handlePdfDownload);
tbpLoadData();
const firstTabButton = document.querySelector('#timeBlockingPlannerTool .tbp-tab-button');
if (firstTabButton) {
firstTabButton.click();
}
window.addEventListener('click', function(event) {
if (event.target.classList.contains('tbp-modal')) {
tbpCloseModal(event.target.id);
}
});
});
