'use client'; import { useState, useEffect } from 'react'; import { Box, Typography, Grid, Card, CardContent, Select, MenuItem, FormControl, InputLabel, CircularProgress, Alert, Paper, Divider, List, ListItem, ListItemAvatar, ListItemText, Avatar, Chip, ToggleButtonGroup, ToggleButton, Button, } from '@mui/material'; import { Restaurant, Hotel, BabyChangingStation, TrendingUp, Timeline, Assessment, ChildCare, Add, } from '@mui/icons-material'; import { useRouter } from 'next/navigation'; import { motion } from 'framer-motion'; import { trackingApi, Activity, ActivityType } from '@/lib/api/tracking'; import { childrenApi, Child } from '@/lib/api/children'; import { format, subDays, startOfDay, endOfDay, parseISO, differenceInMinutes, formatDistanceToNow } from 'date-fns'; import { BarChart, Bar, LineChart, Line, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; type DateRange = '7days' | '30days' | '3months'; interface DayData { date: string; feedings: number; sleepHours: number; diapers: number; activities: number; } interface DiaperTypeData { name: string; value: number; color: string; [key: string]: string | number; } interface ActivityTypeData { name: string; count: number; color: string; } const COLORS = { feeding: '#FFB6C1', sleep: '#B6D7FF', diaper: '#FFE4B5', medication: '#D4B5FF', milestone: '#B5FFD4', note: '#FFD3B6', wet: '#87CEEB', dirty: '#D2691E', both: '#FF8C00', dry: '#90EE90', }; const getActivityIcon = (type: ActivityType) => { switch (type) { case 'feeding': return ; case 'sleep': return ; case 'diaper': return ; default: return ; } }; const getActivityColor = (type: ActivityType) => { return COLORS[type as keyof typeof COLORS] || '#CCCCCC'; }; export const InsightsDashboard: React.FC = () => { const router = useRouter(); const [children, setChildren] = useState([]); const [selectedChild, setSelectedChild] = useState(''); const [dateRange, setDateRange] = useState('7days'); const [activities, setActivities] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Fetch children on mount useEffect(() => { const fetchChildren = async () => { try { const childrenData = await childrenApi.getChildren(); setChildren(childrenData); if (childrenData.length > 0) { setSelectedChild(childrenData[0].id); } } catch (err: any) { setError(err.response?.data?.message || 'Failed to load children'); } }; fetchChildren(); }, []); // Fetch activities when child or date range changes useEffect(() => { if (!selectedChild) return; const fetchActivities = async () => { setLoading(true); setError(null); try { const days = dateRange === '7days' ? 7 : dateRange === '30days' ? 30 : 90; const endDate = endOfDay(new Date()); const startDate = startOfDay(subDays(new Date(), days - 1)); const activitiesData = await trackingApi.getActivities( selectedChild, undefined, startDate.toISOString(), endDate.toISOString() ); setActivities(activitiesData); } catch (err: any) { setError(err.response?.data?.message || 'Failed to load activities'); } finally { setLoading(false); } }; fetchActivities(); }, [selectedChild, dateRange]); // Calculate statistics const calculateStats = () => { const totalFeedings = activities.filter((a) => a.type === 'feeding').length; const totalDiapers = activities.filter((a) => a.type === 'diaper').length; const sleepActivities = activities.filter((a) => a.type === 'sleep'); const totalSleepMinutes = sleepActivities.reduce((acc, activity) => { if (activity.data?.endTime && activity.data?.startTime) { const start = parseISO(activity.data.startTime); const end = parseISO(activity.data.endTime); return acc + differenceInMinutes(end, start); } return acc; }, 0); const days = dateRange === '7days' ? 7 : dateRange === '30days' ? 30 : 90; const avgSleepHours = days > 0 ? (totalSleepMinutes / 60 / days).toFixed(1) : '0.0'; const typeCounts: Record = {}; activities.forEach((a) => { typeCounts[a.type] = (typeCounts[a.type] || 0) + 1; }); const mostCommonType = Object.entries(typeCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'None'; return { totalFeedings, avgSleepHours, totalDiapers, mostCommonType, }; }; // Prepare chart data const prepareDailyData = (): DayData[] => { const days = dateRange === '7days' ? 7 : dateRange === '30days' ? 30 : 90; const dailyMap = new Map(); for (let i = days - 1; i >= 0; i--) { const date = format(subDays(new Date(), i), 'yyyy-MM-dd'); dailyMap.set(date, { date: format(subDays(new Date(), i), 'MMM dd'), feedings: 0, sleepHours: 0, diapers: 0, activities: 0, }); } activities.forEach((activity) => { const dateKey = format(parseISO(activity.timestamp), 'yyyy-MM-dd'); const data = dailyMap.get(dateKey); if (data) { data.activities += 1; if (activity.type === 'feeding') data.feedings += 1; if (activity.type === 'diaper') data.diapers += 1; if (activity.type === 'sleep' && activity.data?.endTime && activity.data?.startTime) { const start = parseISO(activity.data.startTime); const end = parseISO(activity.data.endTime); const hours = differenceInMinutes(end, start) / 60; data.sleepHours += hours; } } }); return Array.from(dailyMap.values()).map((d) => ({ ...d, sleepHours: Number(d.sleepHours.toFixed(1)), })); }; const prepareDiaperData = (): DiaperTypeData[] => { const diaperActivities = activities.filter((a) => a.type === 'diaper'); const typeCount: Record = {}; diaperActivities.forEach((activity) => { const type = activity.data?.type || 'unknown'; typeCount[type] = (typeCount[type] || 0) + 1; }); return Object.entries(typeCount).map(([name, value]) => ({ name: name.charAt(0).toUpperCase() + name.slice(1), value, color: COLORS[name as keyof typeof COLORS] || '#CCCCCC', })); }; const prepareActivityTypeData = (): ActivityTypeData[] => { const typeCount: Record = {}; activities.forEach((activity) => { typeCount[activity.type] = (typeCount[activity.type] || 0) + 1; }); return Object.entries(typeCount).map(([name, count]) => ({ name: name.charAt(0).toUpperCase() + name.slice(1), count, color: COLORS[name as keyof typeof COLORS] || '#CCCCCC', })); }; const stats = calculateStats(); const dailyData = prepareDailyData(); const diaperData = prepareDiaperData(); const activityTypeData = prepareActivityTypeData(); const recentActivities = [...activities] .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) .slice(0, 20); const noChildren = children.length === 0; const noActivities = activities.length === 0 && !loading; return ( Insights & Analytics Track patterns and get insights about your child's activities {/* Filters */} {children.length > 1 && ( Child )} newValue && setDateRange(newValue)} fullWidth size="large" > 7 Days 30 Days 3 Months {error && ( {error} )} {noChildren && !loading && ( No Children Added Add a child to view insights and analytics )} {loading && ( )} {noActivities && !noChildren && ( No activities found for the selected date range. Start tracking activities to see insights! )} {!loading && !noChildren && !noActivities && ( <> {/* Summary Statistics */} Feedings {stats.totalFeedings} Total count Sleep {stats.avgSleepHours}h Average per day Diapers {stats.totalDiapers} Total changes Top Activity {stats.mostCommonType} Most frequent {/* Charts */} Feeding Frequency Sleep Duration (Hours) {diaperData.length > 0 && ( Diaper Changes by Type `${name} ${(percent * 100).toFixed(0)}%`} outerRadius={80} fill="#8884d8" dataKey="value" > {diaperData.map((entry, index) => ( ))} )} 0 ? 6 : 12}> Activity Timeline {activityTypeData.length > 0 && ( Activity Distribution {activityTypeData.map((activity) => ( ))} )} {/* Recent Activities */} Recent Activities (Last 20) {recentActivities.map((activity, index) => ( {getActivityIcon(activity.type)} {activity.type} } secondary={ {activity.notes || format(parseISO(activity.timestamp), 'MMM dd, yyyy HH:mm')} } /> ))} )} ); };