Simple Habit Tracker
Track Your Habits (Last 7 Days)
Add habits in the 'Manage Habits' tab to get started!
Add New Habit
Current Habits
| Habit Name | Action |
|---|
Add habits in the 'Manage Habits' tab to get started!
"; return; } habitIds.forEach(id => { const habit = db.habits[id]; const habitItem = document.createElement('div'); habitItem.className = 'sht-habit-item'; const streak = calculateStreak(habit.log, dates); let daysHtml = ''; dates.forEach(dateInfo => { const isChecked = habit.log[dateInfo.iso] === true; daysHtml += `
${dateInfo.label}
`;
});
habitItem.innerHTML = `
${escapeHTML(habit.name)}
${daysHtml}
Streak: ${streak}
`;
// Add event listeners AFTER setting innerHTML
const checkboxes = habitItem.querySelectorAll('.sht-day-checkbox');
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', handleCheckboxChange);
});
habitsList.appendChild(habitItem);
});
}
function handleCheckboxChange(event) {
const checkbox = event.target;
const habitId = checkbox.dataset.habitId;
const date = checkbox.dataset.date;
const isChecked = checkbox.checked;
if (db.habits[habitId]) {
if (isChecked) {
db.habits[habitId].log[date] = true;
} else {
delete db.habits[habitId].log[date]; // Save space, remove false entry
}
// Update streak display for this specific habit
const habitItem = checkbox.closest('.sht-habit-item');
if (habitItem) {
const streakSpan = habitItem.querySelector('.sht-habit-streak span');
const dates = getLastNDates(TRACKING_DAYS); // Need dates for streak calc
streakSpan.textContent = calculateStreak(db.habits[habitId].log, dates);
}
}
// console.log("DB Updated:", db.habits[habitId].log); // For debugging
}
function calculateStreak(log, dates) {
let currentStreak = 0;
// Iterate backwards from today (last element in dates)
for (let i = dates.length - 1; i >= 0; i--) {
if (log[dates[i].iso] === true) {
currentStreak++;
} else {
// Stop counting as soon as a day is missed
break;
}
}
return currentStreak;
}
// --- Date Utilities ---
function formatDateISO(date) {
return date.toISOString().split('T')[0]; // YYYY-MM-DD
}
function getShortDayLabel(date) {
// e.g., "Mon", "Tue"
return date.toLocaleDateString('en-US', { weekday: 'short' });
}
function getLastNDates(n) {
const dates = [];
const today = new Date();
today.setHours(0, 0, 0, 0); // Normalize to start of day
for (let i = 0; i < n; i++) {
const date = new Date(today);
date.setDate(today.getDate() - i);
dates.push({
iso: formatDateISO(date),
label: getShortDayLabel(date)
});
}
return dates.reverse(); // Return oldest to newest
}
// --- PDF Download Function ---
if (pdfDownloadBtn) {
pdfDownloadBtn.addEventListener("click", () => {
if (
typeof window.jspdf === "undefined" ||
typeof window.jspdf.jsPDF === "undefined"
) {
alert(
"Error: PDF library (jsPDF) is not loaded. Please check your internet connection."
);
return;
}
if (typeof window.jspdf.plugin.autotable === "undefined") {
alert(
"Error: PDF library (jsPDF-AutoTable) is not loaded. Please check your internet connection."
);
return;
}
if (Object.keys(db.habits).length === 0) {
alert("Add some habits before downloading a report.");
return;
}
generatePDF();
});
}
function generatePDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const primaryColor =
getComputedStyle(container).getPropertyValue(
"--sht-primary-color"
) || "#059669";
const headStyles = {
fillColor: primaryColor,
textColor: "#ffffff",
fontStyle: "bold",
};
// Title
doc.setFont("helvetica", "bold");
doc.setFontSize(18);
doc.setTextColor(primaryColor);
doc.text("Habit Tracker Report", 105, 20, {
align: "center",
});
doc.setFontSize(10);
doc.setTextColor("#888");
doc.text(
`Report Generated: ${new Date().toLocaleDateString()}`,
105,
26,
{ align: "center" }
);
// 1. Results Table
const dates = getLastNDates(TRACKING_DAYS);
const head = [
['Habit', ...dates.map(d => d.label), 'Current Streak']
];
const body = Object.values(db.habits).map(habit => {
const streak = calculateStreak(habit.log, dates);
const dailyStatus = dates.map(dateInfo => habit.log[dateInfo.iso] ? '✅' : '❌');
return [habit.name, ...dailyStatus, streak];
});
doc.autoTable({
startY: 40,
head: head,
body: body,
theme: "striped",
headStyles: headStyles,
columnStyles: {
// Center checkmarks and streak
1: { halign: 'center' },
2: { halign: 'center' },
3: { halign: 'center' },
4: { halign: 'center' },
5: { halign: 'center' },
6: { halign: 'center' },
7: { halign: 'center' },
8: { halign: 'center', fontStyle: 'bold' } // Streak column
},
didDrawPage: function (data) {
// Page Footer
doc.setFontSize(8);
doc.setTextColor("#888");
doc.text(
"Page " + doc.internal.getNumberOfPages(),
data.settings.margin.left,
doc.internal.pageSize.height - 10
);
},
});
doc.save("Habit-Tracker-Report.pdf");
}
// --- Utility Function ---
function escapeHTML(str) {
if (!str) return "";
const div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
// --- Initial Load ---
function loadSampleData() {
// Per spec: USA-relevant sample data (generic habits)
const samples = [
{ id: 'habit_1', name: 'Exercise for 30 minutes', log: {} },
{ id: 'habit_2', name: 'Read for 15 minutes', log: {} },
{ id: 'habit_3', name: 'Drink 8 glasses of water', log: {} },
];
samples.forEach(habit => {
db.habits[habit.id] = habit;
// Pre-populate some data for demo
const today = new Date();
if (habit.id === 'habit_1') {
db.habits[habit.id].log[formatDateISO(today)] = true;
db.habits[habit.id].log[formatDateISO(new Date(today.setDate(today.getDate()-1)))] = true;
}
if (habit.id === 'habit_2') {
db.habits[habit.id].log[formatDateISO(new Date())] = true; // Use fresh date obj
}
});
}
loadSampleData();
renderHabitConfigTable();
renderDashboard();
updateNavButtons(); // Set initial state
} // --- End of initializeHabitTracker() function ---
/*
* This new logic checks if the page is already loaded (common in Elementor).
* If it is, it runs the setup function immediately.
* If it's still loading (rare), it waits for the DOM event.
*/
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
// DOM is already ready, run the function now.
initializeHabitTracker();
} else {
// Still loading, wait for the event
document.addEventListener(
"DOMContentLoaded",
initializeHabitTracker
);
}
