Habit Tracking Dashboard

Current Streak

0

Longest Streak

0

Completion (Last 30d)

0%

Contribution Heatmap (Last 90 Days)

My Habits

Completion Log

Select a habit to see its history.

'; return; } const filteredLogs = logData.filter(log => log.HabitID === selectedHabitId).map(log => log.Date).sort(); // KPIs const [currentStreak, longestStreak] = calculateStreaks(filteredLogs); document.getElementById('kpi-current-streak').textContent = `${currentStreak} days`; document.getElementById('kpi-longest-streak').textContent = `${longestStreak} days`; const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const recentLogs = filteredLogs.filter(d => new Date(d) >= thirtyDaysAgo).length; document.getElementById('kpi-completion-rate').textContent = `${((recentLogs / 30) * 100).toFixed(0)}%`; renderHeatmap(filteredLogs); } function calculateStreaks(dates) { if (dates.length === 0) return [0, 0]; const today = new Date(new Date().toISOString().split('T')[0]); const yesterday = new Date(today); yesterday.setDate(today.getDate() - 1); let longestStreak = 0; let currentStreak = 0; let expectedDate = new Date(dates[0]); // Calculate longest streak let tempStreak = 0; for(let i=0; i 0) { const prevDate = new Date(dates[i-1]); const diff = (currentDate - prevDate) / (1000*60*60*24); if(diff === 1) { tempStreak++; } else { tempStreak = 1; } } else { tempStreak = 1; } if(tempStreak > longestStreak) longestStreak = tempStreak; } // Calculate current streak const uniqueDates = [...new Set(dates)]; let lastDate = new Date(uniqueDates[uniqueDates.length -1]); if (lastDate.getTime() === today.getTime() || lastDate.getTime() === yesterday.getTime()) { currentStreak = 1; for (let i = uniqueDates.length - 2; i >= 0; i--) { const d1 = new Date(uniqueDates[i+1]); const d2 = new Date(uniqueDates[i]); const diff = (d1 - d2) / (1000 * 60 * 60 * 24); if (diff === 1) { currentStreak++; } else { break; } } } return [currentStreak, longestStreak]; } function renderHeatmap(dates) { const container = document.getElementById('heatmap-container'); container.innerHTML = ''; const today = new Date(); const ninetyDaysAgo = new Date(); ninetyDaysAgo.setDate(today.getDate() - 90); const dateSet = new Set(dates); let day = ninetyDaysAgo; while(day <= today){ const dayStr = day.toISOString().split('T')[0]; const dayDiv = document.createElement('div'); dayDiv.className = 'heatmap-day'; if(dateSet.has(dayStr)){ dayDiv.classList.add('level-4'); } dayDiv.title = dayStr; container.appendChild(dayDiv); day.setDate(day.getDate() + 1); } } function renderHabitsTable() { const table = document.getElementById('habits-table'); table.innerHTML = `Habit NameActions ${habitsData.map(h => ` `).join('')}`; } function renderLogTable() { const table = document.getElementById('log-table'); table.innerHTML = `DateHabitActions ${logData.sort((a,b)=>new Date(b.Date)-new Date(a.Date)).map(l => ` `).join('')}`; } function populateHabitFilter() { habitSelect.innerHTML = habitsData.map(h => ``).join(''); } function handleTableEvents(e, dataArray, type) { const target = e.target; if(!target) return; const id = parseInt(target.closest('tr')?.dataset.id); if(!id) return; if(target.classList.contains(`remove-${type}-btn`)) { if(type === 'habit') habitsData = habitsData.filter(d => d.id !== id); if(type === 'log') logData = logData.filter(d => d.id !== id); } else if (target.tagName === 'INPUT' || target.tagName === 'SELECT') { const key = target.dataset.key; const item = dataArray.find(d => d.id === id); if(item) item[key] = (target.type === 'number') ? parseInt(target.value) || 0 : target.value; } renderAll(); } document.getElementById('htd-add-habit-btn').addEventListener('click', () => { habitsData.push({id: Date.now(), Name: 'New Habit'}); renderAll(); }); document.getElementById('htd-add-log-btn').addEventListener('click', () => { if(habitsData.length>0) {logData.unshift({id: Date.now(), Date: new Date().toISOString().split('T')[0], HabitID: habitsData[0].id}); renderAll();} else {alert("Please add a habit first.");} }); document.getElementById('habits-table').addEventListener('change', e => handleTableEvents(e, habitsData, 'habit')); document.getElementById('habits-table').addEventListener('click', e => handleTableEvents(e, habitsData, 'habit')); document.getElementById('log-table').addEventListener('change', e => handleTableEvents(e, logData, 'log')); document.getElementById('log-table').addEventListener('click', e => handleTableEvents(e, logData, 'log')); habitSelect.addEventListener('change', renderOverview); function initialize() { habitsData = [ { id: 1, Name: 'Workout' }, { id: 2, Name: 'Read 20 Pages' }, { id: 3, Name: 'Meditate' } ]; let logs = []; const today = new Date("2025-07-05"); for(let i=0; i<90; i++){ const date = new Date(today); date.setDate(today.getDate() - i); if(Math.random() < 0.7) logs.push({ id: Date.now()+logs.length, Date: date.toISOString().split('T')[0], HabitID: 1}); // Workout if(i % 3 !== 0 && Math.random() < 0.8) logs.push({ id: Date.now()+logs.length, Date: date.toISOString().split('T')[0], HabitID: 2}); // Read if(Math.random() < 0.5) logs.push({ id: Date.now()+logs.length, Date: date.toISOString().split('T')[0], HabitID: 3}); // Meditate } logData = logs.slice(0, 28); // Limit preloaded data renderAll(); } initialize(); });
Scroll to Top