'use client'; import { useEffect, useState, Suspense } from 'react'; import { Flame, Plus } from 'lucide-react'; import dynamic from 'next/dynamic'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { HabitCard } from '@/components/habits/habit-card'; import { HabitCompletionDialog } from '@/components/habits/habit-completion-dialog'; import type { Habit } from '@project-e/shared'; // Lazy load react-calendar-heatmap (~15KB) const HabitHeatmap = dynamic( () => import('@/components/habits/habit-heatmap').then((m) => m.HabitHeatmap), { ssr: false, loading: () => (
), } ); /** Extended habit with server-computed fields */ interface HabitWithMeta extends Habit { logged_today: boolean; } export default function HabitsPage() { const [habits, setHabits] = useState([]); const [loading, setLoading] = useState(true); const [selectedHabit, setSelectedHabit] = useState(null); const [completionDialogOpen, setCompletionDialogOpen] = useState(false); useEffect(() => { fetchHabits(); }, []); async function fetchHabits() { try { const response = await fetch('/api/habits'); if (response.ok) { const data = await response.json(); setHabits(data.items || []); } } catch (error) { console.error('Failed to fetch habits:', error); } finally { setLoading(false); } } function handleComplete(habit: HabitWithMeta) { if (habit.completion_mode === 'quick') { logHabitCompletion(habit.id, {}); } else { setSelectedHabit(habit); setCompletionDialogOpen(true); } } async function logHabitCompletion( habitId: string, data: { mood?: number; value?: number; notes?: string } ) { try { await fetch(`/api/habits/${habitId}/logs`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); fetchHabits(); setCompletionDialogOpen(false); } catch (error) { console.error('Failed to log habit:', error); } } const completedCount = habits.filter((h) => h.logged_today).length; const completionRate = habits.length > 0 ? Math.round((completedCount / habits.length) * 100) : 0; if (loading) { return

Loading habits...

; } return (

Habits

Small actions, visible momentum.

{/* Summary banner */}

Today's progress

{completedCount} / {habits.length} habits

Completion rate

{completionRate}%

{/* Habit cards grid */}
{habits.map((habit) => ( handleComplete(habit)} /> ))}
{/* Heatmap section */} } > {/* Completion dialog */} {selectedHabit && ( logHabitCompletion(selectedHabit.id, data)} /> )}
); }