'use client'; import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { formatDistanceToNow } from 'date-fns'; import { Plus, TrendingUp, TrendingDown, Activity, Clock, Calendar, Target, Copy, Check, Trophy, HeartCrack, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Badge } from '@/components/ui/badge'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Separator } from '@/components/ui/separator'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; interface Habit { id: number; name: string; type: 'positive' | 'neutral' | 'negative'; lastLoggedAt: string | null; totalLogs: number; logsLastWeek: number; logsLastMonth: number; createdAt: string; } export default function Dashboard() { const router = useRouter(); const queryClient = useQueryClient(); const [userToken, setUserToken] = useState(null); const [showNewHabitDialog, setShowNewHabitDialog] = useState(false); const [newHabitName, setNewHabitName] = useState(''); const [newHabitType, setNewHabitType] = useState<'positive' | 'neutral' | 'negative'>('neutral'); const [copiedToken, setCopiedToken] = useState(false); // Check authentication const { data: authData, isLoading: authLoading } = useQuery({ queryKey: ['auth'], queryFn: async () => { const res = await fetch('/api/auth'); const data = await res.json(); if (!data.authenticated) { router.push('/'); } return data; }, }); useEffect(() => { if (authData?.token) { setUserToken(authData.token); } }, [authData]); // Fetch habits const { data: habitsData, isLoading: habitsLoading } = useQuery({ queryKey: ['habits'], queryFn: async () => { const res = await fetch('/api/habits'); if (!res.ok) throw new Error('Failed to fetch habits'); return res.json(); }, enabled: !!authData?.authenticated, }); // Log habit mutation const logHabitMutation = useMutation({ mutationFn: async (habitId: number) => { const res = await fetch(`/api/habits/${habitId}/log`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), }); if (!res.ok) throw new Error('Failed to log habit'); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['habits'] }); }, }); // Create habit mutation const createHabitMutation = useMutation({ mutationFn: async (data: { name: string; type: string }) => { const res = await fetch('/api/habits', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (!res.ok) throw new Error('Failed to create habit'); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['habits'] }); setShowNewHabitDialog(false); setNewHabitName(''); setNewHabitType('neutral'); }, }); const handleCreateHabit = () => { if (newHabitName.trim()) { createHabitMutation.mutate({ name: newHabitName.trim(), type: newHabitType, }); } }; const copyToken = () => { if (userToken) { navigator.clipboard.writeText(userToken); setCopiedToken(true); setTimeout(() => setCopiedToken(false), 2000); } }; const getHabitCardClass = (type: string) => { switch (type) { case 'positive': return 'border-emerald-600 bg-emerald-950/50 hover:bg-emerald-900/50 hover:border-emerald-500'; case 'negative': return 'border-red-600 bg-red-950/50 hover:bg-red-900/50 hover:border-red-500'; default: return 'border-zinc-700 bg-zinc-950/50 hover:bg-zinc-900/50 hover:border-zinc-600'; } }; const getHabitIcon = (type: string) => { switch (type) { case 'positive': return ; case 'negative': return ; default: return ; } }; const getHabitBadgeVariant = (type: string): 'default' | 'secondary' | 'destructive' | 'outline' => { switch (type) { case 'positive': return 'default'; case 'negative': return 'destructive'; default: return 'secondary'; } }; const getAverageFrequency = (habit: Habit) => { const daysSinceCreation = Math.max( 1, Math.floor((Date.now() - new Date(habit.createdAt).getTime()) / (1000 * 60 * 60 * 24)), ); if (daysSinceCreation <= 7) { const avg = habit.totalLogs / daysSinceCreation; return `${avg.toFixed(1)}/day`; } else if (daysSinceCreation <= 30) { const weeks = daysSinceCreation / 7; const avg = habit.totalLogs / weeks; return `${avg.toFixed(1)}/week`; } else { const months = daysSinceCreation / 30; const avg = habit.totalLogs / months; return `${avg.toFixed(1)}/month`; } }; if (authLoading || habitsLoading) { return (
Loading...
); } const habits = habitsData?.habits || []; return (
{/* Header */}

Track Every Day

Build better habits, one day at a time.

Create New Habit Add a new habit to track. Choose whether it's something you want to do more, less, or just monitor.
setNewHabitName(e.target.value)} className="border-zinc-800 bg-zinc-900" />
{/* Token Alert */} {userToken && (

Your access token:

{userToken}

{copiedToken ? 'Copied!' : 'Copy token'}

)}
{/* Habits Grid */}
{habits.map((habit: Habit) => ( logHabitMutation.mutate(habit.id)} >
{habit.name} {getHabitIcon(habit.type)}
{/* Last logged */}
{habit.lastLoggedAt ? formatDistanceToNow(new Date(habit.lastLoggedAt), { addSuffix: true }) : 'Never logged'}
{/* Stats */}

{habit.totalLogs}

Total

{habit.logsLastWeek}

This week

{/* Average frequency badge */}
{getAverageFrequency(habit)}
{/* Motivational message */} {habit.type === 'positive' && habit.totalLogs > 0 && (

Keep up the great work! 💪

)} {habit.type === 'negative' && habit.lastLoggedAt && (

Stay mindful, you've got this! 🎯

)}
))} {/* Empty state */} {habits.length === 0 && (

No habits yet

Start building better habits today

)}
); }