`;
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 += 'Streak Comp. ';
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');
});
