"use client"; import { useEffect, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useGUCStore } from "@/lib/store"; import { PageShell } from "@/components/ui"; interface ExerciseDay { day: string; activity: string; duration_minutes: number; intensity: string; notes: string; } interface ExerciseResponse { tier: string; tier_description: string; weekly_plan: ExerciseDay[]; general_advice: string; avoid: string[]; } const TIER_CONFIG: Record = { LIGHT_WALKING_ONLY: { color: "#22C55E", bg: "#22C55E15", icon: "๐Ÿšถ", badge: "Light Recovery" }, CARDIO_RESTRICTED: { color: "#06B6D4", bg: "#06B6D415", icon: "๐Ÿง˜", badge: "Low Intensity" }, NORMAL_ACTIVITY: { color: "#FF9933", bg: "#FF993315", icon: "๐Ÿƒ", badge: "Moderate" }, ACTIVE_ENCOURAGED: { color: "#EF4444", bg: "#EF444415", icon: "๐Ÿ’ช", badge: "Active" }, }; const INTENSITY_COLORS: Record = { "Very Low": "#64748B", "Low": "#22C55E", "Low-Moderate": "#84CC16", "Moderate": "#FF9933", "Moderate-High": "#F97316", "High": "#EF4444", "Very High": "#DC2626", "Rest": "#334155", }; const DAY_ABBR: Record = { Monday: "Mon", Tuesday: "Tue", Wednesday: "Wed", Thursday: "Thu", Friday: "Fri", Saturday: "Sat", Sunday: "Sun", }; const FALLBACK_PLAN: ExerciseResponse = { tier: "NORMAL_ACTIVITY", tier_description: "Standard moderate activity plan. 30-minute sessions, 5 days a week.", general_advice: "Stay consistent. 5 days of 30 minutes beats 1 day of 2 hours. Drink water before and after.", avoid: ["Exercising on an empty stomach", "Skipping warm-up and cool-down", "Pushing through sharp pain"], weekly_plan: [ { day: "Monday", activity: "Brisk walking 30 min", duration_minutes: 30, intensity: "Moderate", notes: "Comfortable pace, slightly breathless." }, { day: "Tuesday", activity: "Bodyweight squats, push-ups, lunges", duration_minutes: 30, intensity: "Moderate", notes: "3 sets of 12 reps each." }, { day: "Wednesday", activity: "Yoga + stretching", duration_minutes: 30, intensity: "Low", notes: "Active recovery. Focus on flexibility." }, { day: "Thursday", activity: "Brisk walk + light jog intervals", duration_minutes: 35, intensity: "Moderate", notes: "3 min walk, 2 min jog. Repeat 5 times." }, { day: "Friday", activity: "Resistance band strength training", duration_minutes: 30, intensity: "Moderate", notes: "Focus on compound movements." }, { day: "Saturday", activity: "Recreational activity โ€” badminton or cycling", duration_minutes: 45, intensity: "Moderate", notes: "Make it fun and social!" }, { day: "Sunday", activity: "Rest day", duration_minutes: 0, intensity: "Rest", notes: "Full rest. Light household activity fine." }, ], }; export default function ExercisePage() { const exerciseLevel = useGUCStore((s) => s.exerciseLevel); const latestReport = useGUCStore((s) => s.latestReport); const profile = useGUCStore((s) => s.profile); const addXP = useGUCStore((s) => s.addXP); const setAvatarState = useGUCStore((s) => s.setAvatarState); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [selectedDay, setSelectedDay] = useState(null); const [completedDays, setCompletedDays] = useState>(new Set()); const severity = latestReport?.severity_level ?? "MILD_CONCERN"; useEffect(() => { const TIER_MAP: Record = { Beginner: "LIGHT_WALKING_ONLY", Intermediate: "NORMAL_ACTIVITY", Advanced: "ACTIVE_ENCOURAGED", }; const fetchPlan = async () => { try { setLoading(true); // Call Next.js API route which proxies to backend const res = await fetch(`/api/exercise`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}), }); if (!res.ok) throw new Error(); const json = await res.json(); // Transform backend ExerciseResponse โ†’ frontend ExerciseResponse const transformed: ExerciseResponse = { tier: TIER_MAP[json.tier] ?? json.tier ?? "NORMAL_ACTIVITY", tier_description: json.tier_reason ?? json.tier_description ?? "", weekly_plan: json.weekly_plan ?? [], general_advice: json.encouragement ?? json.general_advice ?? "", avoid: json.restrictions ?? json.avoid ?? [], }; setData(transformed); setSelectedDay("Monday"); } catch { setData(FALLBACK_PLAN); setSelectedDay("Monday"); } finally { setLoading(false); } }; fetchPlan(); }, [exerciseLevel, severity, profile.language]); const handleComplete = (day: string) => { if (completedDays.has(day)) return; setCompletedDays((prev) => new Set([...prev, day])); addXP(10); setAvatarState("HAPPY"); }; const tierCfg = TIER_CONFIG[data?.tier ?? "NORMAL_ACTIVITY"] ?? TIER_CONFIG.NORMAL_ACTIVITY; const weekTotal = (data?.weekly_plan ?? []).reduce((s, d) => s + d.duration_minutes, 0); if (loading) { return (
{[...Array(7)].map((_, i) => (
))}
); } return ( {/* Header */}
{tierCfg.icon}

Exercise Plan

Adapted to your health condition

{/* Tier banner */}
{tierCfg.badge} Tier {weekTotal} min/week

{data?.tier_description}

{/* Stats strip */} {[ { label: "Days Active", value: (data?.weekly_plan ?? []).filter((d) => d.duration_minutes > 0).length }, { label: "Total Minutes", value: weekTotal }, { label: "Completed", value: completedDays.size }, ].map((stat) => (
{stat.value}
{stat.label}
))}
{/* Day selector */}
{(data?.weekly_plan ?? []).map((day, i) => { const isRest = day.duration_minutes === 0; const isDone = completedDays.has(day.day); const isSelected = selectedDay === day.day; return ( setSelectedDay(day.day)} className="flex-shrink-0 flex flex-col items-center gap-1 px-3 py-2.5 rounded-xl transition-all min-w-[52px]" style={ isSelected ? { background: tierCfg.bg, border: `1px solid ${tierCfg.color}60`, boxShadow: `0 0 16px ${tierCfg.color}30`, } : isDone ? { background: "rgba(34,197,94,0.08)", border: "1px solid rgba(34,197,94,0.2)" } : { background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.07)" } } > {DAY_ABBR[day.day]} {isDone ? "โœ…" : isRest ? "๐Ÿ’ค" : tierCfg.icon} {isRest ? "Rest" : `${day.duration_minutes}m`} ); })}
{/* Day detail */} {selectedDay && data && (() => { const day = data.weekly_plan.find((d) => d.day === selectedDay); if (!day) return null; const isDone = completedDays.has(day.day); const intensityColor = INTENSITY_COLORS[day.intensity] ?? "#FF9933"; return (

{day.day}

{day.intensity}
{day.duration_minutes > 0 && (
{day.duration_minutes}
minutes
)}
{day.duration_minutes === 0 ? (
๐Ÿ’ค

Full rest day. Your body repairs and grows stronger while you rest.

) : ( <>
{tierCfg.icon}

{day.activity}

๐Ÿ’ก {day.notes}

handleComplete(day.day)} disabled={isDone} className="w-full py-2.5 rounded-xl text-sm font-medium transition-all" style={ isDone ? { background: "rgba(34,197,94,0.1)", color: "#22C55E", border: "1px solid rgba(34,197,94,0.2)" } : { background: tierCfg.color, color: "#0d0d1a" } } > {isDone ? "โœ“ Completed ยท +10 XP earned!" : "Mark Complete ยท +10 XP"} )}
); })()}
{/* General advice */} {data?.general_advice && (

General Advice

{data.general_advice}

)} {/* Avoid list */} {(data?.avoid ?? []).length > 0 && (

โš ๏ธ Avoid

    {(data?.avoid ?? []).map((item, i) => (
  • โœ• {item}
  • ))}
)}
); }