'use client'; import { useEffect, useState, Suspense } from 'react'; import { TrendingUp, Target, Clock, Flame, BarChart3, PieChart as PieChartIcon, } from 'lucide-react'; import dynamic from 'next/dynamic'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger, } from '@/components/ui/tabs'; // Lazy load recharts (~180KB) const AnalyticsCharts = dynamic( () => import('@/components/analytics/analytics-charts').then((m) => m.AnalyticsCharts), { ssr: false, loading: () => (
{[1, 2].map((i) => (
))}
), } ); interface AnalyticsData { taskCompletionRate: number; habitConsistency: number; totalTimeMinutes: number; activeStreaks: number; bestStreak: number; period: number; } interface TimeData { date: string; tasks: number; habits: number; time: number; } interface DomainData { name: string; value: number; color: string; } interface HabitData { name: string; streak: number; score: number; consistency: number; } function countByDate(records: Array>, field: string) { return records.reduce>((counts, record) => { const value = record[field]; if (typeof value === 'string' && !Number.isNaN(new Date(value).getTime())) { const date = new Date(value).toISOString().slice(0, 10); counts[date] = (counts[date] || 0) + 1; } return counts; }, {}); } export default function AnalyticsPage() { const [analytics, setAnalytics] = useState(null); const [timeData, setTimeData] = useState([]); const [domainData, setDomainData] = useState([]); const [habitData, setHabitData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetchAnalytics(); }, []); async function fetchAnalytics() { setLoading(true); setError(null); try { const startDate = new Date(); startDate.setDate(startDate.getDate() - 29); const start = startDate.toISOString(); const [analyticsResponse, timeResponse, habitsResponse, tasksResponse, habitLogsResponse] = await Promise.all([ fetch('/api/analytics?period=30'), fetch(`/api/time-summary?start=${encodeURIComponent(start)}`), fetch('/api/habits/streaks'), fetch('/api/tasks?perPage=500'), fetch(`/api/habit-logs?start=${encodeURIComponent(start)}`), ]); if (![analyticsResponse, timeResponse, habitsResponse, tasksResponse, habitLogsResponse].every((response) => response.ok)) { throw new Error('One or more analytics sources could not be loaded.'); } const [analyticsData, timeSummary, habitsData, tasksData, habitLogsData] = await Promise.all([ analyticsResponse.json(), timeResponse.json(), habitsResponse.json(), tasksResponse.json(), habitLogsResponse.json(), ]); const COLORS = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#06b6d4']; const domains: DomainData[] = Object.entries(timeSummary.byDomain || {}).map(([name, value], index) => ({ name, value: value as number, color: COLORS[index % COLORS.length], })); const habits: HabitData[] = (habitsData.streaks || []).map( (s: { habit: { name: string; score?: number }; current_streak: number }) => ({ name: s.habit.name, streak: s.current_streak, score: s.habit.score || 0, consistency: 0, }) ); const completedByDate = countByDate(tasksData.items || [], 'completed_at'); const habitsByDate = countByDate(habitLogsData.items || [], 'logged_at'); const minutesByDate = timeSummary.byDate || {}; const dailyData = Array.from({ length: 30 }, (_, index) => { const date = new Date(startDate); date.setDate(startDate.getDate() + index); const key = date.toISOString().slice(0, 10); return { date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), tasks: completedByDate[key] || 0, habits: habitsByDate[key] || 0, time: minutesByDate[key] || 0, }; }); setAnalytics(analyticsData); setDomainData(domains); setHabitData(habits); setTimeData(dailyData); } catch (error) { console.error('Failed to fetch analytics:', error); setError('Analytics could not be loaded. Please try again.'); } finally { setLoading(false); } } if (loading) { return

Loading analytics...

; } if (error || !analytics) { return (

{error || 'Analytics are unavailable.'}

); } return (

Analytics

Patterns behind your progress.

{/* Summary cards */}
Task Completion
{analytics.taskCompletionRate}%

Last {analytics.period} days

Habit Consistency
{analytics.habitConsistency}%

Last {analytics.period} days

Time Tracked
{Math.round(analytics.totalTimeMinutes / 60)}h

Last {analytics.period} days

Active Streaks
{analytics.activeStreaks}

Best: {analytics.bestStreak} days

{/* Charts */} Trends Habits Time {[1, 2].map((i) => (
))}
} >
} > } >
); }