diff --git a/habits.db b/habits.db index 9ec18cb..8276e8b 100644 Binary files a/habits.db and b/habits.db differ diff --git a/src/App.tsx b/src/App.tsx index 12b3abf..519f628 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -137,15 +137,21 @@ export default function HabitTracker() { // Prevent negative streaks if (newStreak < 0) return; - // Update in database - await db.habits.update(id, { manualStreak: newStreak }); + try { + // Update in database + await fetch(`http://localhost:5000/api/habits/${id}/streak`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ streak: newStreak }), + }); - // Update state - setHabits(habits.map(habit => - habit.id === id - ? { ...habit, manualStreak: newStreak } - : habit - )); + // Update state + setHabits(habits.map(habit => + habit.id === id ? { ...habit, manualStreak: newStreak } : habit + )); + } catch (error) { + console.error('Error updating streak:', error); + } }; return ( diff --git a/src/components/HabitList.tsx b/src/components/HabitList.tsx index a03fd64..b775b92 100644 --- a/src/components/HabitList.tsx +++ b/src/components/HabitList.tsx @@ -9,35 +9,60 @@ interface HabitListProps { onToggleHabit: (id: number, date: string) => void; onUpdateHabit: (id: number, name: string) => void; onDeleteHabit: (id: number) => void; + onUpdateStreak: (id: number, newStreak: number) => Promise; } -const calculateStreak = (completedDates: string[]): number => { - if (completedDates.length === 0) return 0; +const calculateStreak = (completedDates: string[]): { currentStreak: number; bestStreak: number } => { + if (completedDates.length === 0) return { currentStreak: 0, bestStreak: 0 }; - // Sort dates in ascending order const sortedDates = [...completedDates].sort((a, b) => new Date(a).getTime() - new Date(b).getTime() ); let currentStreak = 1; - let maxStreak = 1; + let bestStreak = 1; + let tempStreak = 1; - // Go through the dates and count consecutive completions + // Check if the last completion was today or yesterday + const lastDate = new Date(sortedDates[sortedDates.length - 1]); + const today = new Date(); + today.setHours(0, 0, 0, 0); + lastDate.setHours(0, 0, 0, 0); + + const diffDays = Math.floor((today.getTime() - lastDate.getTime()) / (24 * 60 * 60 * 1000)); + + // If the last completion was more than a day ago, current streak is 0 + if (diffDays > 1) { + currentStreak = 0; + } + + // Calculate streaks for (let i = 1; i < sortedDates.length; i++) { const prevDate = new Date(sortedDates[i - 1]); const currDate = new Date(sortedDates[i]); + prevDate.setHours(0, 0, 0, 0); + currDate.setHours(0, 0, 0, 0); - // If dates are consecutive or same day, increment streak - if (currDate.getTime() - prevDate.getTime() <= 24 * 60 * 60 * 1000) { - currentStreak++; - maxStreak = Math.max(maxStreak, currentStreak); + const diffTime = currDate.getTime() - prevDate.getTime(); + const diffDays = Math.floor(diffTime / (24 * 60 * 60 * 1000)); + + if (diffDays === 1) { + tempStreak++; + bestStreak = Math.max(bestStreak, tempStreak); + } else if (diffDays === 0) { + // Same day completions don't affect streak + continue; } else { - // Reset streak counter when there's a gap - currentStreak = 1; + tempStreak = 1; } } - return maxStreak; + // Current streak should be the same as tempStreak if the last completion was today or yesterday + if (diffDays <= 1) { + currentStreak = tempStreak; + } + + return { currentStreak, bestStreak }; }; export function HabitList({ @@ -47,6 +72,7 @@ export function HabitList({ onToggleHabit, onUpdateHabit, onDeleteHabit, + onUpdateStreak, }: HabitListProps) { return ( @@ -61,11 +87,11 @@ export function HabitList({ ))} + @@ -78,6 +104,8 @@ export function HabitList({ type="text" value={habit.name} onChange={(e) => onUpdateHabit(habit.id, e.target.value)} + aria-label="Habit name" + placeholder="Enter habit name" className="bg-transparent border-none focus:outline-none focus:ring-2 focus:ring-gray-300 rounded px-2" /> @@ -86,14 +114,28 @@ export function HabitList({ onToggleHabit(habit.id, date)} + onChange={() => { + onToggleHabit(habit.id, date); + const newCompletedDates = habit.completedDates.includes(date) + ? habit.completedDates.filter(d => d !== date) + : [...habit.completedDates, date]; + + const { currentStreak, bestStreak } = calculateStreak(newCompletedDates); + onUpdateStreak(habit.id, bestStreak); + }} + aria-label={`Mark ${habit.name} as completed for ${date}`} className="w-4 h-4 rounded border-gray-300 dark:border-gray-600" /> ))} +
+ Current Streak + Best Streak -
- consecutive completions -
Actions
- {calculateStreak(habit.completedDates)} + {calculateStreak(habit.completedDates).currentStreak} + + + + {calculateStreak(habit.completedDates).bestStreak} @@ -109,4 +151,4 @@ export function HabitList({
); -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index 4c7b8af..9586860 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,4 +2,5 @@ export interface Habit { id: number; name: string; completedDates: string[]; + bestStreak: number; } \ No newline at end of file