Online Smart Habit-Building Tracker

Online Smart Habit-Building Tracker

Monthly Habit Progress

No habits configured yet.

`; document.getElementById('go-to-config-btn').addEventListener('click', () => switchTab('config')); return; } const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate(); const firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay(); // 0=Sun, 1=Mon... const table = document.createElement('table'); table.className = 'w-full border-collapse'; // Header let headerHtml = 'Habit'; for (let day = 1; day <= daysInMonth; day++) { headerHtml += `${day}`; } headerHtml += 'StreakComp.'; table.innerHTML = headerHtml; // Body const tbody = document.createElement('tbody'); habits.forEach(habit => { const row = document.createElement('tr'); row.className = 'border-t border-gray-200'; // Stats calculation const { currentStreak, completion } = calculateStats(habit, daysInMonth); let rowHtml = `${habit.name}`; for (let day = 1; day <= daysInMonth; day++) { const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const status = habit.dates[dateStr] || ''; const isToday = day === today.getDate(); const isFuture = day > today.getDate(); rowHtml += `
`; } rowHtml += `${currentStreak}`; rowHtml += `${completion}%`; row.innerHTML = rowHtml; tbody.appendChild(row); }); table.appendChild(tbody); trackerGridContainer.appendChild(table); }; const calculateStats = (habit, daysInMonth) => { let currentStreak = 0; let completedCount = 0; let trackableDays = 0; for (let day = 1; day <= today.getDate(); day++) { const dateStr = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const status = habit.dates[dateStr]; if (status === 'completed') { currentStreak++; completedCount++; trackableDays++; } else if (status === 'missed') { currentStreak = 0; trackableDays++; } else { currentStreak = 0; } } const completion = trackableDays > 0 ? Math.round((completedCount / trackableDays) * 100) : 0; return { currentStreak, completion }; }; const handleGridClick = (e) => { const cell = e.target; if (!cell.classList.contains('habit-grid-cell') || cell.classList.contains('future')) return; const habitId = parseInt(cell.dataset.habitId); const date = cell.dataset.date; const habit = habits.find(h => h.id === habitId); if (!habit) return; const statuses = ['', 'completed', 'missed', 'skipped']; const currentStatus = cell.classList.contains('completed') ? 'completed' : cell.classList.contains('missed') ? 'missed' : cell.classList.contains('skipped') ? 'skipped' : ''; const nextIndex = (statuses.indexOf(currentStatus) + 1) % statuses.length; const nextStatus = statuses[nextIndex]; // Update state if (nextStatus) { habit.dates[date] = nextStatus; } else { delete habit.dates[date]; } // Update UI cell.className = cell.className.replace(/completed|missed|skipped/g, '').trim(); if (nextStatus) { cell.classList.add(nextStatus); } // Re-render to update stats renderDashboard(); }; const handlePdfDownload = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-export-area'); html2canvas(pdfContent, { scale: 2, useCORS: true }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); const imgProps = pdf.getImageProperties(imgData); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; const margin = 40; pdf.addImage(imgData, 'PNG', margin, margin, pdfWidth - (margin * 2), pdfHeight); pdf.save('habit-tracker-report.pdf'); }); }; // --- Event Listeners --- Object.keys(tabs).forEach(tabName => { tabs[tabName].btn.addEventListener('click', () => switchTab(tabName)); }); navButtons.next.addEventListener('click', () => switchTab('config')); navButtons.prev.addEventListener('click', () => switchTab('dashboard')); addHabitBtn.addEventListener('click', () => addHabitRow()); saveHabitsBtn.addEventListener('click', saveHabits); trackerGridContainer.addEventListener('click', handleGridClick); downloadPdfBtn.addEventListener('click', handlePdfDownload); // --- Initial Setup --- renderHabitConfig(); // This will populate with default habits saveHabits(); // Save the initial defaults to state and render dashboard switchTab('dashboard'); });
Scroll to Top