Habit Formation Tracker

Habit Formation Tracker

Build routines, track your progress, and achieve your goals.

Today's Progress

Current Streak: ${streak} day${streak !== 1 ? 's' : ''}

`; habitList.appendChild(card); }); } function renderCalendar() { const calendarGrid = document.getElementById('calendar-grid'); const monthYearEl = document.getElementById('calendar-month-year'); const legendEl = document.getElementById('calendar-legend'); if (!calendarGrid || !monthYearEl || !legendEl) return; calendarGrid.innerHTML = ''; const month = calendarDate.getMonth(); const year = calendarDate.getFullYear(); monthYearEl.textContent = `${calendarDate.toLocaleString('default', { month: 'long' })} ${year}`; const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; daysOfWeek.forEach(day => { calendarGrid.innerHTML += `
${day}
`; }); const firstDayOfMonth = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); for (let i = 0; i < firstDayOfMonth; i++) calendarGrid.innerHTML += '
'; for (let day = 1; day <= daysInMonth; day++) { const date = new Date(year, month, day); const dateString = date.toISOString().split('T')[0]; let indicators = '
'; habits.forEach((habit, index) => { const status = habit.data[dateString]; if (status) { const color = habitColors[index % habitColors.length]; let statusClass = ''; if (status === 'completed') statusClass = `w-2 h-2 rounded-full`; else if (status === 'skipped') statusClass = `w-2 h-2 border border-current`; else if (status === 'missed') statusClass = `w-2 h-2`; indicators += `
`; } }); indicators += '
'; const isToday = dateString === getTodayString(); const todayClass = isToday ? 'bg-blue-500 text-white font-bold' : 'bg-gray-100'; calendarGrid.innerHTML += `
${day} ${indicators}
`; } legendEl.innerHTML = ''; habits.forEach((habit, index) => { const color = habitColors[index % habitColors.length]; legendEl.innerHTML += `
${habit.name}
`; }); } function renderManageHabits() { const manageList = document.getElementById('manage-habit-list'); if (!manageList) return; manageList.innerHTML = ''; if (habits.length === 0) { manageList.innerHTML = `
  • No habits added yet.
  • `; return; } habits.forEach(habit => { const listItem = document.createElement('li'); listItem.className = 'bg-white p-3 rounded-lg border border-gray-200 flex items-center justify-between'; listItem.innerHTML = ` ${habit.name} `; manageList.appendChild(listItem); }); } // SECTION: UI & EVENT HANDLING // ============================ function switchTab(tabName) { currentTab = tabName; updateTabUI(); } function updateTabUI() { document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.add('hidden')); const activePane = document.getElementById(currentTab); if(activePane) activePane.classList.remove('hidden'); document.querySelectorAll('.tab-btn').forEach(btn => { if (btn.dataset.tab === currentTab) { btn.classList.remove('tab-inactive'); btn.classList.add('tab-active'); } else { btn.classList.remove('tab-active'); btn.classList.add('tab-inactive'); } }); const prevBtn = document.getElementById('prev-tab-btn'); const nextBtn = document.getElementById('next-tab-btn'); if (!prevBtn || !nextBtn) return; prevBtn.disabled = (currentTab === 'dashboard'); nextBtn.disabled = (currentTab === 'manage'); prevBtn.classList.toggle('opacity-50', prevBtn.disabled); nextBtn.classList.toggle('opacity-50', nextBtn.disabled); } function navigateTabs(direction) { const tabs = ['dashboard', 'calendar', 'manage']; const currentIndex = tabs.indexOf(currentTab); let newIndex = currentIndex + direction; if (newIndex >= 0 && newIndex < tabs.length) { switchTab(tabs[newIndex]); } } function setupEventListeners() { const newHabitInput = document.getElementById('new-habit-input'); const addHabitBtn = document.getElementById('add-habit-btn'); if (newHabitInput && addHabitBtn) { addHabitBtn.addEventListener('click', () => { addHabit(newHabitInput.value); newHabitInput.value = ''; }); newHabitInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { addHabit(newHabitInput.value); newHabitInput.value = ''; } }); } document.getElementById('tab-content').addEventListener('click', (e) => { const target = e.target.closest('button'); if (!target) return; if (target.classList.contains('status-btn')) { const habitId = parseInt(target.dataset.habitId); const status = target.dataset.status; setHabitStatus(habitId, getTodayString(), status); } if (target.classList.contains('delete-habit-btn')) { const habitId = parseInt(target.dataset.habitId); if (confirm('Are you sure you want to delete this habit and all its data?')) { deleteHabit(habitId); } } }); document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => switchTab(btn.dataset.tab)); }); document.getElementById('prev-tab-btn')?.addEventListener('click', () => navigateTabs(-1)); document.getElementById('next-tab-btn')?.addEventListener('click', () => navigateTabs(1)); document.getElementById('prev-month-btn')?.addEventListener('click', () => { calendarDate.setMonth(calendarDate.getMonth() - 1); renderCalendar(); }); document.getElementById('next-month-btn')?.addEventListener('click', () => { calendarDate.setMonth(calendarDate.getMonth() + 1); renderCalendar(); }); document.getElementById('download-pdf-btn')?.addEventListener('click', generateProductivityReport); } // SECTION: ADVANCED PDF REPORT GENERATION // ======================================= function generateProductivityReport() { const { jsPDF } = window.jspdf; const doc = new jsPDF(); let y = 20; // Vertical position cursor const pageW = doc.internal.pageSize.getWidth(); const margin = 15; // === 1. DOCUMENT HEADER === doc.setFont('helvetica', 'bold'); doc.setFontSize(24); doc.setTextColor('#1f2937'); // gray-800 doc.text('Productivity Report', pageW / 2, y, { align: 'center' }); y += 8; doc.setFont('helvetica', 'normal'); doc.setFontSize(11); doc.setTextColor('#6b7280'); // gray-500 const reportDate = new Date(getTodayString()).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); doc.text(`Report Generated on: ${reportDate}`, pageW / 2, y, { align: 'center' }); y += 15; // === 2. OVERALL STATS SUMMARY === doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text('Monthly Summary', margin, y); y += 8; const month = new Date(getTodayString()).getMonth(); const year = new Date(getTodayString()).getFullYear(); const daysInMonth = new Date(year, month + 1, 0).getDate(); let totalCompleted = 0; let totalTrackedDays = 0; habits.forEach(habit => { for (let day = 1; day <= daysInMonth; day++) { const dateStr = new Date(year, month, day).toISOString().split('T')[0]; if (habit.data[dateStr]) { totalTrackedDays++; if (habit.data[dateStr] === 'completed') { totalCompleted++; } } } }); const overallCompletion = totalTrackedDays > 0 ? ((totalCompleted / totalTrackedDays) * 100).toFixed(0) : 0; doc.setFillColor('#f3f4f6'); // gray-100 doc.roundedRect(margin, y, pageW - (margin * 2), 25, 3, 3, 'F'); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor('#1f2937'); doc.text('Overall Completion:', margin + 10, y + 15); doc.text(`${overallCompletion}%`, pageW - margin - 10, y + 15, { align: 'right' }); y += 35; // === 3. INDIVIDUAL HABIT BREAKDOWN === doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text('Habit Breakdown', margin, y); y += 5; habits.forEach((habit, index) => { if (y > 260) { // Add new page if content overflows doc.addPage(); y = 20; } doc.setDrawColor('#e5e7eb'); // gray-200 doc.line(margin, y, pageW - margin, y); // Separator line y += 10; const color = habitColors[index % habitColors.length]; doc.setFillColor(color); doc.roundedRect(margin, y, 3, 18, 1.5, 1.5, 'F'); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor('#1f2937'); doc.text(habit.name, margin + 8, y + 5); const streak = calculateStreak(habit.id); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor('#6b7280'); doc.text(`Current Streak: ${streak} day(s)`, pageW - margin, y + 5, { align: 'right' }); y += 15; // --- Visualization: Last 30 days activity chart --- doc.setFontSize(8); const today = new Date(getTodayString()); const squareSize = 5; const spacing = 1; let startX = margin + 5; for (let i = 29; i >= 0; i--) { const date = new Date(today); date.setDate(date.getDate() - i); const dateStr = date.toISOString().split('T')[0]; const status = habit.data[dateStr]; let fillColor = '#e5e7eb'; // Default gray for no data if (status === 'completed') fillColor = color; if (status === 'skipped') fillColor = '#fcd34d'; // amber-300 if (status === 'missed') fillColor = '#fca5a5'; // red-300 doc.setFillColor(fillColor); doc.rect(startX, y, squareSize, squareSize, 'F'); startX += squareSize + spacing; } y += 15; }); // === 4. FOOTER === doc.setDrawColor('#e5e7eb'); doc.line(margin, 280, pageW - margin, 280); doc.setFontSize(9); doc.setTextColor('#9ca3af'); // gray-400 doc.text('Generated by Habit Formation Tracker', pageW / 2, 285, { align: 'center' }); // Save the PDF doc.save(`Productivity_Report_${getTodayString()}.pdf`); } // Initial Load loadHabits(); setupEventListeners(); });
    Scroll to Top