"use client"; import { ArrowPathIcon, CalendarDaysIcon, FireIcon, SparklesIcon } from "@heroicons/react/24/outline"; import { useMemo, useState } from "react"; import { todayKey } from "@/lib/hellth-data"; import { trpc } from "@/lib/trpc"; function fmt(value: number) { return Math.round(value).toLocaleString("de-DE"); } function prognosis(balance: number) { if (balance >= 1000) return { label: "entspannt im Ziel", color: "text-emerald-700 bg-emerald-50" }; if (balance >= -300) return { label: "gut steuerbar", color: "text-blue-700 bg-blue-50" }; return { label: "leicht drüber - flexibel ausgleichen", color: "text-amber-700 bg-amber-50" }; } export default function WochenbudgetPage() { const utils = trpc.useUtils(); const [selectedDate, setSelectedDate] = useState(todayKey()); const [extraDate, setExtraDate] = useState(todayKey()); const [extraCalories, setExtraCalories] = useState("800"); const [saved, setSaved] = useState(false); const summaryQuery = trpc.health.statsSummary.useQuery({ referenceDate: selectedDate }); const summary = summaryQuery.data?.week ?? null; const prediction = summary ? prognosis(summary.balance) : null; const upsertPlanMutation = trpc.health.upsertWeeklyCaloriePlan.useMutation({ onSuccess: async () => { await utils.health.statsSummary.invalidate({ referenceDate: selectedDate }); setSaved(true); window.setTimeout(() => setSaved(false), 1500); }, }); const dayByDate = useMemo(() => new Map((summary?.days ?? []).map((day) => [day.date, day])), [summary?.days]); function updatePlan(date: string, plannedCalories: number, note?: string) { upsertPlanMutation.mutate({ date, plannedCalories: Math.max(800, Math.min(6000, Math.round(plannedCalories))), note: note ?? dayByDate.get(date)?.note ?? null, }); } function applyRestaurantDistribution() { if (!summary) return; const extra = Math.max(0, Number(extraCalories) || 0); const days = summary.days; const targetDay = days.find((day) => day.date === extraDate); if (!targetDay) return; const otherDays = days.filter((day) => day.date !== extraDate); const savingPerOtherDay = otherDays.length ? Math.ceil(extra / otherDays.length) : 0; updatePlan(extraDate, targetDay.plannedCalories + extra, targetDay.note ?? "Restaurant / höherer Tag"); for (const day of otherDays) { updatePlan(day.date, Math.max(1200, day.plannedCalories - savingPerOtherDay), day.note); } } if (!summary || !prediction) { return (

Wochenbudget wird geladen.

); } return (

Flexible Planung

Wochenbudget

Ein höherer Tag ist kein Problem. Hellth Hub verteilt den Ausgleich freundlich auf die Woche.

setSelectedDate(event.target.value || todayKey())} className="rounded-full border border-gray-200 px-4 py-3 text-sm font-bold" />

{fmt(summary.baseBudget)}

Wochenziel

{fmt(summary.eatenCalories)}

gegessen/geplant

+{fmt(summary.activityCalories)}

Sportbonus

{prediction.label}

{fmt(summary.availableCalories)}

noch verfügbar

Tagesverteilung

{summary.days.map((day) => { const date = new Date(`${day.date}T12:00:00`); const over = day.eatenCalories > day.plannedCalories + day.activityCalories; return (

{date.toLocaleDateString("de-DE", { weekday: "short" })}

{date.toLocaleDateString("de-DE")}

{fmt(day.eatenCalories)} / {fmt(day.plannedCalories)} kcal {day.activityCalories ? ` + ${fmt(day.activityCalories)} Sport` : ""} {day.note ? ` - ${day.note}` : ""}

updatePlan(day.date, Number(event.target.value) || summary.dailyGoal, day.note)} className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" />

= 0 ? "text-emerald-700" : "text-amber-700"}`}> {day.balance >= 0 ? "+" : ""}{fmt(day.balance)} kcal

); })}

Restaurantbesuch einplanen

Beispiel: Samstag +800 kcal. Die App verteilt den Ausgleich auf die restlichen Tage.

setExtraDate(event.target.value)} className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" /> setExtraCalories(event.target.value)} placeholder="+ kcal" className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-bold" />
{saved ?
Gespeichert
: null}
); }