import React, { useMemo, useEffect } from 'react'; import { motion } from 'framer-motion'; import { getColorIntensity, isToday, formatDate, getWeekdayLabel, getFrozenDays } from '../lib/utils-habit'; import { toggleCompletion } from '../lib/storage'; const HabitGrid = ({ habit, onUpdate, fullView = false }) => { const frozenDays = getFrozenDays(habit.completions); const weeks = useMemo(() => { const today = new Date(); // Find the Monday of the current week const todayDay = today.getDay(); // 0=Sun, 1=Mon, ... const daysSinceMonday = (todayDay + 6) % 7; // 0=Mon, 1=Tue, ..., 6=Sun const mondayThisWeek = new Date(today); mondayThisWeek.setDate(today.getDate() - daysSinceMonday); const weeksArray = []; const totalWeeks = fullView ? 52 : 12; for (let week = totalWeeks - 1; week >= 0; week--) { const weekDays = []; // For each week, calculate Monday, then add 0..6 days for each row const monday = new Date(mondayThisWeek); monday.setDate(mondayThisWeek.getDate() - week * 7); for (let day = 0; day < 7; day++) { const date = new Date(monday); date.setDate(monday.getDate() + day); weekDays.push(date); } weeksArray.push(weekDays); } return weeksArray; }, [fullView]); useEffect(() => { // Scroll to the rightmost (most recent) week on mount const gridScroll = document.querySelector('.grid-scroll'); if (gridScroll) { gridScroll.scrollLeft = gridScroll.scrollWidth; } }, []); const handleCellClick = (date) => { toggleCompletion(habit.id, formatDate(date)); onUpdate(); }; return (

Activity Calendar

Tap any day to mark it as complete

{/* Grid: Monday (top) to Sunday (bottom) */} {weeks.map((week, weekIndex) => (
{/* Month label */}
{weekIndex % 4 === 0 && week[0].toLocaleDateString('en-US', { month: 'short' })}
{/* Days: Monday (top) to Sunday (bottom) */} {week.map((date, dayIndex) => { const dateStr = formatDate(date); const isCompleted = habit.completions.includes(dateStr); const intensity = isCompleted ? getColorIntensity(habit.completions, dateStr) : 0; const isTodayCell = isToday(date); const isFuture = date > new Date(); const isFrozen = frozenDays.includes(dateStr); return ( handleCellClick(date)} className="habit-cell w-3 h-3 rounded-sm flex items-center justify-center" style={{ backgroundColor: isCompleted ? habit.color : 'transparent', opacity: isFuture ? 0 : (isCompleted ? 0.3 + (intensity * 0.7) : 1), border: isTodayCell ? `2px solid ${habit.color}` : `1px solid ${habit.color}20`, pointerEvents: isFuture ? 'none' : 'auto', visibility: isFuture ? 'hidden' : 'visible', }} title={`${dateStr}${isCompleted ? ' ✓' : ''}${isFrozen ? ' (Frozen)' : ''}`} > {isFrozen && ( ❄️ )} ); })}
))} {/* Weekday labels: Monday (top) to Sunday (bottom) */}
{[1, 2, 3, 4, 5, 6, 0].map((day) => (
{getWeekdayLabel(day)}
))}
{/* Legend */}
Less
{[0, 0.25, 0.5, 0.75, 1].map((intensity, i) => (
))}
More
); }; export default HabitGrid;