import { useT } from "@agent-native/core/client/i18n"; import type { DailyCalories } from "@shared/types"; import { startOfWeek, endOfWeek, format, parseISO, isToday, isBefore, startOfDay, } from "date-fns"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, Cell, } from "recharts"; import { Skeleton } from "@/components/ui/skeleton"; interface WeeklyData { weekLabel: string; weekStart: string; netCalories: number; totalCalories: number; burnedCalories: number; daysTracked: number; weeklyGoal: number; projectedCalories: number; isCurrentWeek: boolean; completedDays: number; } interface WeeklyCaloriesChartProps { history: DailyCalories[] | undefined; isLoading: boolean; dailyGoal?: number; } function aggregateWeekly( history: DailyCalories[], dailyGoal: number, ): WeeklyData[] { const now = new Date(); const currentWeekStart = format( startOfWeek(now, { weekStartsOn: 1 }), "yyyy-MM-dd", ); const todayStart = startOfDay(now); const weekMap = new Map< string, { netCalories: number; totalCalories: number; burnedCalories: number; daysTracked: number; completedDayCalories: number; completedDays: number; weekStart: Date; weekEnd: Date; } >(); for (const day of history) { const date = parseISO(day.date); const ws = startOfWeek(date, { weekStartsOn: 1 }); const we = endOfWeek(date, { weekStartsOn: 1 }); const key = format(ws, "yyyy-MM-dd"); const existing = weekMap.get(key) || { netCalories: 0, totalCalories: 0, burnedCalories: 0, daysTracked: 0, completedDayCalories: 0, completedDays: 0, weekStart: ws, weekEnd: we, }; existing.netCalories += day.netCalories; existing.totalCalories += day.totalCalories; existing.burnedCalories += day.burnedCalories; existing.daysTracked += 1; const isCompletedDay = isBefore(date, todayStart) && !isToday(date); if (isCompletedDay) { existing.completedDayCalories += day.netCalories; existing.completedDays += 1; } weekMap.set(key, existing); } return Array.from(weekMap.entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([key, week]) => { const isCurrentWeek = key === currentWeekStart; let projectedCalories = 0; if (isCurrentWeek && week.completedDays > 0) { const avgPerDay = week.completedDayCalories / week.completedDays; projectedCalories = Math.round(avgPerDay * (7 - week.daysTracked)); } return { weekLabel: `${format(week.weekStart, "MMM d")} - ${format(week.weekEnd, "MMM d")}`, weekStart: key, netCalories: week.netCalories, totalCalories: week.totalCalories, burnedCalories: week.burnedCalories, daysTracked: week.daysTracked, weeklyGoal: dailyGoal * 7, projectedCalories, isCurrentWeek, completedDays: week.completedDays, }; }); } function CustomTooltip({ active, payload, t }: any) { if (!active || !payload?.[0]) return null; const data = payload[0].payload as WeeklyData; const projected = data.netCalories + data.projectedCalories; const diff = data.netCalories - data.weeklyGoal; const isOver = diff > 0; return (

{data.weekLabel}

{data.netCalories.toLocaleString()} {t("weekly.netKcal")}
{data.isCurrentWeek && data.projectedCalories > 0 && (
~{projected.toLocaleString()} {t("weekly.projected")}
)}

{t("weekly.eaten", { calories: data.totalCalories.toLocaleString() })}

{t("weekly.burned", { calories: data.burnedCalories.toLocaleString(), })}

{t("weekly.goal", { calories: data.weeklyGoal.toLocaleString() })}

{t("weekly.vsGoal", { sign: isOver ? "+" : "", diff: diff.toLocaleString(), })}

{t("weekly.daysTracked", { count: data.daysTracked })} {data.isCurrentWeek && ` ยท ${t("weekly.completed", { count: data.completedDays })}`}

); } export function WeeklyCaloriesChart({ history, isLoading, dailyGoal = 2000, }: WeeklyCaloriesChartProps) { const t = useT(); if (isLoading) return ; if (!history || history.length === 0) return (

{t("weekly.noData")}

{t("weekly.noDataDescription")}

); const weeklyData = aggregateWeekly(history, dailyGoal); const weeklyGoal = dailyGoal * 7; return (
{[ { label: t("weekly.averagePerWeek"), value: weeklyData.length > 0 ? Math.round( weeklyData.reduce((s, w) => s + w.netCalories, 0) / weeklyData.length, ).toLocaleString() : "0", unit: t("common.kcal"), }, { label: t("weekly.weeklyGoal"), value: weeklyGoal.toLocaleString(), unit: t("common.kcal"), }, { label: t("weekly.weeks"), value: String(weeklyData.length), unit: t("weekly.tracked"), }, ].map((s) => (

{s.label}

{s.value} {s.unit}
))}
} cursor={{ fill: "hsl(var(--muted) / 0.5)" }} /> e.projectedCalories > 0) ? [0, 0, 0, 0] : [6, 6, 0, 0] } > {weeklyData.map((entry, index) => ( weeklyGoal ? "hsl(0, 80%, 60%)" : "hsl(var(--foreground))" } fillOpacity={entry.netCalories > weeklyGoal ? 0.7 : 0.8} /> ))} {weeklyData.map((entry, index) => { const total = entry.netCalories + entry.projectedCalories; return ( weeklyGoal ? "hsl(0, 80%, 60%)" : "hsl(var(--foreground))" } fillOpacity={entry.projectedCalories > 0 ? 0.25 : 0} /> ); })}
); }